mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76bf19c8f6 | ||
|
|
b27834cbd9 | ||
|
|
eb0ecb0483 | ||
|
|
9acb0e67a7 | ||
|
|
f4f9b56b17 | ||
|
|
2196439e6c | ||
|
|
dd68c64239 | ||
|
|
0decc515e4 | ||
|
|
b3e1e89c88 | ||
|
|
32bff85526 | ||
|
|
02fc95f239 | ||
|
|
07fd6d78a0 | ||
|
|
87aa853f81 | ||
|
|
ab69eea654 | ||
|
|
edccf63b66 | ||
|
|
80413fb3de | ||
|
|
f3ed9aa82b | ||
|
|
307c563b6d | ||
|
|
f92ce1d24e | ||
|
|
a7fac16d97 | ||
|
|
20c3caaed4 | ||
|
|
bf08cd50d9 | ||
|
|
805a266059 | ||
|
|
d3c9963280 |
No files matched your search
@@ -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>
|
||||
Executable
+40
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
+5
-2
@@ -76,14 +76,14 @@ dependencies {
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.4:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.19:dev")
|
||||
|
||||
shadowImplementation(project(":glsm")) { transitive = false }
|
||||
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
|
||||
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")
|
||||
@@ -173,6 +173,9 @@ dependencies {
|
||||
testImplementation('org.junit.platform:junit-platform-launcher')
|
||||
|
||||
testImplementation('com.tngtech.archunit:archunit-junit5:1.4.1')
|
||||
|
||||
testRuntimeOnly("it.unimi.dsi:fastutil:8.5.18")
|
||||
testRuntimeOnly("org.joml:joml:1.10.8")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ dependencies {
|
||||
// Deps (normally from MC) - compile only, but needed at test runtime
|
||||
compileOnly("com.google.guava:guava:${property("guavaVersion")}")
|
||||
testRuntimeOnly("com.google.guava:guava:${property("guavaVersion")}")
|
||||
testRuntimeOnly("it.unimi.dsi:fastutil:${property("fastutilVersion")}")
|
||||
testRuntimeOnly("org.joml:joml:${property("jomlVersion")}") { isTransitive = false }
|
||||
|
||||
// Test
|
||||
testImplementation(sourceSets["stubs"].output)
|
||||
|
||||
@@ -4,7 +4,9 @@ celeritasVersion = 2.5.3-GTNH
|
||||
lombokVersion = 1.18.42
|
||||
log4jVersion = 2.0-beta9
|
||||
guavaVersion = 18.0
|
||||
gtnhlibVersion = 0.10.0
|
||||
gtnhlibVersion = 0.11.19
|
||||
fastutilVersion = 8.5.18
|
||||
jomlVersion = 1.10.8
|
||||
glslTransformLibVersion = 0.2.0-26.g5ee7d96-GTNH
|
||||
antlr4RuntimeVersion = 4.13.2
|
||||
jcppVersion = 1.4.14
|
||||
|
||||
@@ -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,12 +9,14 @@ 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;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
@@ -39,13 +42,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 +57,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 +98,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
|
||||
@@ -187,24 +188,32 @@ public class GLStateManager {
|
||||
private static boolean dirtyTexCoordAttrib;
|
||||
private static boolean dirtyLightmapAttrib = true;
|
||||
|
||||
private static boolean unit23TexCoordSetDuringDraw = false;
|
||||
|
||||
public static boolean consumeUnit23TexCoordSetDuringDraw() {
|
||||
boolean v = unit23TexCoordSetDuringDraw;
|
||||
unit23TexCoordSetDuringDraw = false;
|
||||
return v;
|
||||
}
|
||||
|
||||
// vertexFlags bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms.
|
||||
// Skip the backend vertexAttrib call in either case.
|
||||
public static void flushDeferredVertexAttribs(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 (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 (dirtyLightmapAttrib) {
|
||||
if (!hasLightmap && dirtyLightmapAttrib) {
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
|
||||
dirtyLightmapAttrib = false;
|
||||
}
|
||||
@@ -456,18 +465,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 +496,6 @@ public class GLStateManager {
|
||||
|
||||
modelViewMatrix.clear();
|
||||
projectionMatrix.clear();
|
||||
vaoEboMap.clear();
|
||||
VertexAttribState.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -626,18 +631,25 @@ 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 (defaultVAO != 0) {
|
||||
RENDER_BACKEND.provokingVertex(GL32.GL_LAST_VERTEX_CONVENTION);
|
||||
}
|
||||
if (initCallback != null) {
|
||||
initCallback.run();
|
||||
}
|
||||
@@ -1015,7 +1027,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 +1056,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 +1205,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 +1345,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 +1360,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;
|
||||
@@ -1585,6 +1614,11 @@ public class GLStateManager {
|
||||
ImmediateModeRecorder.setColor(red, green, blue, alpha);
|
||||
}
|
||||
dirtyColorAttrib = true;
|
||||
// Only set while an enchantment-glint span is active
|
||||
final GlintColorHandler glintHandler = GLSMHooks.glintColorHandler;
|
||||
if (glintHandler != null) {
|
||||
glintHandler.onColorChanged(red, green, blue, alpha);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1851,7 +1885,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;
|
||||
}
|
||||
@@ -1867,7 +1901,7 @@ public class GLStateManager {
|
||||
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;
|
||||
}
|
||||
@@ -1883,7 +1917,7 @@ public class GLStateManager {
|
||||
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;
|
||||
}
|
||||
@@ -1899,7 +1933,7 @@ public class GLStateManager {
|
||||
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;
|
||||
}
|
||||
@@ -1914,7 +1948,7 @@ public class GLStateManager {
|
||||
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);
|
||||
@@ -2138,6 +2172,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glBegin(int mode) {
|
||||
unit23TexCoordSetDuringDraw = false;
|
||||
if (DisplayListManager.isRecording()) {
|
||||
ImmediateModeRecorder.begin(mode);
|
||||
return;
|
||||
@@ -2200,72 +2235,83 @@ 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();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
@@ -2274,7 +2320,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);
|
||||
}
|
||||
@@ -2286,14 +2332,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);
|
||||
}
|
||||
|
||||
@@ -2322,60 +2367,38 @@ 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();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTrianglesInstanced(count, type, indices, primcount);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
private static void prepareClientArrays() {
|
||||
if (ShaderManager.getInstance().isEnabled() && VertexAttribState.hasAnyClientSideEnabledAttrib()) {
|
||||
uploadClientArraysToVBO();
|
||||
public static void glDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTrianglesInstanced(first, count, primcount);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArraysInstanced(GL11.GL_TRIANGLE_STRIP, first, count & ~1, primcount);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArraysInstanced(GL11.GL_TRIANGLE_FAN, first, count, primcount);
|
||||
} else {
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
private static void prepareClientArrays() {
|
||||
if (VAOManager.hasAnyClientSideEnabledAttrib()) {
|
||||
VAOManager.uploadClientArraysToVBO();
|
||||
}
|
||||
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) {
|
||||
@@ -2394,16 +2417,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);
|
||||
@@ -2550,14 +2573,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) {
|
||||
@@ -2577,7 +2600,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);
|
||||
}
|
||||
|
||||
@@ -2586,40 +2609,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); }
|
||||
@@ -2630,12 +2653,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);
|
||||
}
|
||||
|
||||
@@ -2670,7 +2693,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++;
|
||||
}
|
||||
}
|
||||
@@ -2679,7 +2702,7 @@ public class GLStateManager {
|
||||
if (clientAttribStackPointer > 0) {
|
||||
clientAttribStackPointer--;
|
||||
clientActiveTextureUnit = clientAttribSavedTextureUnit[clientAttribStackPointer];
|
||||
ShaderManager.getInstance().setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]);
|
||||
VAOManager.setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3456,6 +3479,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 -> {
|
||||
@@ -4439,7 +4477,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);
|
||||
@@ -4449,7 +4487,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);
|
||||
@@ -4459,7 +4497,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);
|
||||
@@ -4468,7 +4506,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);
|
||||
@@ -4493,9 +4531,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() {
|
||||
@@ -4548,6 +4591,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;
|
||||
@@ -4557,7 +4619,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;
|
||||
}
|
||||
@@ -4571,7 +4633,7 @@ public class GLStateManager {
|
||||
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;
|
||||
}
|
||||
@@ -4584,7 +4646,7 @@ public class GLStateManager {
|
||||
|
||||
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);
|
||||
@@ -4592,7 +4654,7 @@ public class GLStateManager {
|
||||
|
||||
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);
|
||||
@@ -4601,14 +4663,14 @@ 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);
|
||||
@@ -4616,14 +4678,14 @@ public class GLStateManager {
|
||||
|
||||
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);
|
||||
@@ -4631,7 +4693,7 @@ public class GLStateManager {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -4766,7 +4828,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);
|
||||
}
|
||||
@@ -4835,18 +4897,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
|
||||
@@ -4941,7 +5001,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);
|
||||
}
|
||||
@@ -5066,13 +5126,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5081,8 +5141,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) {
|
||||
@@ -5091,6 +5150,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);
|
||||
@@ -5158,24 +5225,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);
|
||||
@@ -5185,6 +5245,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);
|
||||
}
|
||||
@@ -5276,6 +5340,9 @@ public class GLStateManager {
|
||||
} else {
|
||||
final int unit = target - GL13.GL_TEXTURE0;
|
||||
if (unit >= 2 && unit < 4) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
unit23TexCoordSetDuringDraw = true;
|
||||
}
|
||||
ShaderManager.setCurrentTexCoord(unit, s, t, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -5981,15 +6048,15 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glUniform1(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 1) RENDER_BACKEND.uniform1i(location, values.get(values.position()));
|
||||
RENDER_BACKEND.uniform1iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform2(int location, FloatBuffer values) {
|
||||
if (values.remaining() >= 2) RENDER_BACKEND.uniform2f(location, values.get(values.position()), values.get(values.position() + 1));
|
||||
RENDER_BACKEND.uniform2(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform2(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 2) RENDER_BACKEND.uniform2i(location, values.get(values.position()), values.get(values.position() + 1));
|
||||
RENDER_BACKEND.uniform2iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform3(int location, FloatBuffer values) {
|
||||
@@ -5997,7 +6064,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glUniform3(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 3) RENDER_BACKEND.uniform3i(location, values.get(values.position()), values.get(values.position() + 1), values.get(values.position() + 2));
|
||||
RENDER_BACKEND.uniform3iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform4(int location, FloatBuffer values) {
|
||||
@@ -6005,7 +6072,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glUniform4(int location, IntBuffer values) {
|
||||
if (values.remaining() >= 4) RENDER_BACKEND.uniform4i(location, values.get(values.position()), values.get(values.position() + 1), values.get(values.position() + 2), values.get(values.position() + 3));
|
||||
RENDER_BACKEND.uniform4iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniformMatrix2(int location, boolean transpose, FloatBuffer matrices) {
|
||||
|
||||
@@ -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,15 @@ public final class QuadConverter {
|
||||
* @param vertexCount number of vertices (must be multiple of 4)
|
||||
*/
|
||||
public static void drawQuadsAsTriangles(int first, int vertexCount) {
|
||||
drawSharedQuadEbo(first, vertexCount, 1, false);
|
||||
}
|
||||
|
||||
public static void drawQuadsAsTrianglesInstanced(int first, int vertexCount, int primcount) {
|
||||
drawSharedQuadEbo(first, vertexCount, primcount, true);
|
||||
}
|
||||
|
||||
private static void drawSharedQuadEbo(int first, int vertexCount, int primcount, boolean instanced) {
|
||||
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;
|
||||
@@ -91,7 +100,11 @@ public final class QuadConverter {
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
|
||||
final long indexOffset = (long) (first / 4) * 6 * 4;
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
}
|
||||
|
||||
@@ -106,6 +119,11 @@ public final class QuadConverter {
|
||||
* @param bytesPerIndex bytes per index element
|
||||
*/
|
||||
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex) {
|
||||
uploadAndDraw(dst, triIndexCount, indexType, bytesPerIndex, 1, false);
|
||||
}
|
||||
|
||||
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex, int primcount, boolean instanced) {
|
||||
GLStateManager.preDraw();
|
||||
final int needed = triIndexCount * bytesPerIndex;
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
|
||||
@@ -124,7 +142,11 @@ public final class QuadConverter {
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
|
||||
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
memFree(dst);
|
||||
@@ -139,12 +161,20 @@ public final class QuadConverter {
|
||||
* @param offset byte offset into the currently bound EBO
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(int indexCount, int type, long offset) {
|
||||
drawQuadElementsFromEbo(indexCount, type, offset, 1, false);
|
||||
}
|
||||
|
||||
public static void drawQuadElementsAsTrianglesInstanced(int indexCount, int type, long offset, int primcount) {
|
||||
drawQuadElementsFromEbo(indexCount, type, offset, primcount, true);
|
||||
}
|
||||
|
||||
private static void drawQuadElementsFromEbo(int indexCount, int type, long offset, int primcount, boolean instanced) {
|
||||
if (indexCount == 0) return;
|
||||
assert indexCount % 4 == 0 : "QuadConverter: indexCount must be multiple of 4";
|
||||
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);
|
||||
@@ -153,7 +183,7 @@ public final class QuadConverter {
|
||||
triangulateQuads(memAddress0(src), type, memAddress0(dst), GL11.GL_UNSIGNED_INT, quadCount);
|
||||
|
||||
memFree(src);
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4);
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
+40
-5
@@ -310,6 +310,16 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL31.glDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
GL31.glDrawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provokingVertex(int provokeMode) {
|
||||
GL32.glProvokingVertex(provokeMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
org.lwjgl.opengl.GL43.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
@@ -507,11 +517,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);
|
||||
@@ -817,6 +822,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL20.glUniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2(int location, FloatBuffer value) {
|
||||
GL20.glUniform2(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3(int location, FloatBuffer value) {
|
||||
GL20.glUniform3(location, value);
|
||||
@@ -827,6 +837,26 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL20.glUniform4(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform1iv(int location, IntBuffer value) {
|
||||
GL20.glUniform1(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2iv(int location, IntBuffer value) {
|
||||
GL20.glUniform2(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3iv(int location, IntBuffer value) {
|
||||
GL20.glUniform3(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform4iv(int location, IntBuffer value) {
|
||||
GL20.glUniform4(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniformMatrix3(int location, boolean transpose, FloatBuffer value) {
|
||||
GL20.glUniformMatrix3(location, transpose, value);
|
||||
@@ -1043,6 +1073,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);
|
||||
|
||||
@@ -87,6 +87,8 @@ public abstract class RenderBackend {
|
||||
public abstract void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride);
|
||||
public abstract void copyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size);
|
||||
public abstract void drawElementsInstanced(int mode, int count, int type, long indices, int primcount);
|
||||
public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
|
||||
public void provokingVertex(int provokeMode) {}
|
||||
public abstract void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex);
|
||||
public abstract void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex);
|
||||
public abstract void drawBuffer(int mode);
|
||||
@@ -125,7 +127,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) {}
|
||||
|
||||
|
||||
@@ -200,8 +201,13 @@ public abstract class RenderBackend {
|
||||
public abstract void uniform3i(int location, int v0, int v1, int v2);
|
||||
public abstract void uniform4f(int location, float v0, float v1, float v2, float v3);
|
||||
public abstract void uniform4i(int location, int v0, int v1, int v2, int v3);
|
||||
public abstract void uniform2(int location, FloatBuffer value);
|
||||
public abstract void uniform3(int location, FloatBuffer value);
|
||||
public abstract void uniform4(int location, FloatBuffer value);
|
||||
public abstract void uniform1iv(int location, IntBuffer value);
|
||||
public abstract void uniform2iv(int location, IntBuffer value);
|
||||
public abstract void uniform3iv(int location, IntBuffer value);
|
||||
public abstract void uniform4iv(int location, IntBuffer value);
|
||||
public abstract void uniformMatrix3(int location, boolean transpose, FloatBuffer value);
|
||||
public abstract void uniformMatrix4(int location, boolean transpose, FloatBuffer value);
|
||||
public void uniformMatrix2(int location, boolean transpose, FloatBuffer matrices) {}
|
||||
@@ -247,6 +253,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));
|
||||
|
||||
@@ -78,7 +78,7 @@ public final class FragmentShaderGenerator {
|
||||
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
sb.append(" vec4 tex").append(i).append("Color = texture(u_Sampler").append(i).append(", v_TexCoord").append(i).append(".st);\n");
|
||||
sb.append(" vec4 tex").append(i).append("Color = textureProj(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ public final class VertexKey {
|
||||
private static final int BIT_WIDE_LINE = 36;
|
||||
// Per-unit texmat enable: bits 37-40 (unit 0..3). Unit 1 is the lightmap matrix.
|
||||
private static final int BIT_UNIT_TEXMAT_BASE = 37;
|
||||
private static final int BIT_UNIT23_UV_FROM_UNIT0 = 41;
|
||||
|
||||
public static final int TG_NONE = 0;
|
||||
public static final int TG_OBJ_LINEAR = 1;
|
||||
@@ -85,6 +86,7 @@ public final class VertexKey {
|
||||
public boolean hasVertexNormal() { return bit(BIT_HAS_VERTEX_NORMAL); }
|
||||
public boolean hasVertexTexCoord() { return bit(BIT_HAS_VERTEX_TEX); }
|
||||
public boolean hasVertexLightmap() { return bit(BIT_HAS_VERTEX_LIGHTMAP); }
|
||||
public boolean unit23UvFromUnit0() { return bit(BIT_UNIT23_UV_FROM_UNIT0); }
|
||||
/** Color material mode (CM_AMBIENT, CM_DIFFUSE, CM_SPECULAR, CM_EMISSION, CM_AMBIENT_AND_DIFFUSE). Only meaningful when colorMaterialEnabled(). */
|
||||
public int colorMaterialMode() { return (int)((packed >> BIT_COLOR_MAT_MODE) & 0x7); }
|
||||
|
||||
@@ -213,6 +215,10 @@ public final class VertexKey {
|
||||
if (hasTexCoord) bits |= (1L << BIT_HAS_VERTEX_TEX);
|
||||
if (hasLightmap) bits |= (1L << BIT_HAS_VERTEX_LIGHTMAP);
|
||||
|
||||
if (GLStateManager.consumeUnit23TexCoordSetDuringDraw() && hasTexCoord) {
|
||||
bits |= (1L << BIT_UNIT23_UV_FROM_UNIT0);
|
||||
}
|
||||
|
||||
if (GLStateManager.anyClipPlaneEnabled()) {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
}
|
||||
|
||||
@@ -106,8 +106,8 @@ public final class VertexShaderGenerator {
|
||||
if (key.unitTexCoordEnabled(0) && !key.hasVertexTexCoord()) {
|
||||
sb.append("uniform vec4 u_CurrentTexCoord0;\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("uniform vec4 u_CurrentTexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("uniform vec4 u_CurrentTexCoord3;\n");
|
||||
if (key.unitTexCoordEnabled(2) && !key.unit23UvFromUnit0()) sb.append("uniform vec4 u_CurrentTexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3) && !key.unit23UvFromUnit0()) sb.append("uniform vec4 u_CurrentTexCoord3;\n");
|
||||
|
||||
if (key.lightmapEnabled() && !key.hasVertexLightmap()) {
|
||||
sb.append("uniform vec2 u_CurrentLightmapCoord;\n");
|
||||
@@ -363,8 +363,10 @@ public final class VertexShaderGenerator {
|
||||
|
||||
for (int i = 2; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (!key.unitTexCoordEnabled(i)) continue;
|
||||
final String src = "u_CurrentTexCoord" + i;
|
||||
if (key.unitTexMatEnabled(i)) {
|
||||
final String src = key.unit23UvFromUnit0()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord" + i;
|
||||
if (!key.unit23UvFromUnit0() && key.unitTexMatEnabled(i)) {
|
||||
sb.append(" v_TexCoord").append(i).append(" = u_TextureMatrix").append(i).append(" * ").append(src).append(";\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord").append(i).append(" = ").append(src).append(";\n");
|
||||
@@ -383,7 +385,7 @@ public final class VertexShaderGenerator {
|
||||
|
||||
emitTexGenComponent(sb, key.texGenModeS(), "s", "S", key);
|
||||
emitTexGenComponent(sb, key.texGenModeT(), "t", "T", key);
|
||||
emitTexGenComponent(sb, key.texGenModeR(), "r", "R", key);
|
||||
emitTexGenComponent(sb, key.texGenModeR(), "p", "R", key);
|
||||
emitTexGenComponent(sb, key.texGenModeQ(), "q", "Q", key);
|
||||
|
||||
// Always apply texture matrix when texgen is active (forced on in VertexKey)
|
||||
|
||||
@@ -8,6 +8,7 @@ public final class GLSMHooks {
|
||||
public static DeferredBlendHandler blendHandler;
|
||||
public static DeferredAlphaHandler alphaHandler;
|
||||
public static DeferredDepthColorHandler depthColorHandler;
|
||||
public static GlintColorHandler glintColorHandler;
|
||||
|
||||
// EventBus instances
|
||||
public static final EventBus<TextureBindEvent> TEXTURE_BIND = EventBus.create(TextureBindEvent.class);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface GlintColorHandler {
|
||||
void onColorChanged(float red, float green, float blue, float alpha);
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
+34
-2
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+5
-5
@@ -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;
|
||||
|
||||
+52
@@ -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;
|
||||
}
|
||||
}
|
||||
+37
-69
@@ -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);
|
||||
}
|
||||
}
|
||||
+37
-69
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ public class GLSMRedirector {
|
||||
private static final String ARBMultiTexture = "org/lwjgl/opengl/ARBMultitexture";
|
||||
private static final String ARBShaderObjects = "org/lwjgl/opengl/ARBShaderObjects";
|
||||
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
|
||||
private static final String ARBDrawInstanced = "org/lwjgl/opengl/ARBDrawInstanced";
|
||||
private static final String EXTDrawInstanced = "org/lwjgl/opengl/EXTDrawInstanced";
|
||||
|
||||
// Redirect VAO related calls from NHLib
|
||||
private static final String UniversalVAO = "com/gtnewhorizon/gtnhlib/client/opengl/UniversalVAO";
|
||||
@@ -126,6 +128,7 @@ public class GLSMRedirector {
|
||||
.add("glFrustum")
|
||||
.add("glGetBoolean")
|
||||
.add("glGetFloat")
|
||||
.add("glGetFloatv", "glGetFloat")
|
||||
.add("glGetInteger")
|
||||
.add("glGetLight")
|
||||
.add("glGetMaterial")
|
||||
@@ -296,6 +299,7 @@ public class GLSMRedirector {
|
||||
.add("glStencilOpSeparate")
|
||||
.add("glUseProgram")
|
||||
.add("glShaderSource")
|
||||
.add("nglShaderSource")
|
||||
.add("glLinkProgram")
|
||||
.add("glDeleteProgram")
|
||||
.add("glCreateShader")
|
||||
@@ -321,6 +325,7 @@ public class GLSMRedirector {
|
||||
.add("glUniformMatrix2")
|
||||
.add("glUniformMatrix3")
|
||||
.add("glUniformMatrix4")
|
||||
.add("glUniformMatrix4fv", "glUniformMatrix4")
|
||||
.add("glDeleteShader")
|
||||
.add("glGetShaderi")
|
||||
.add("glGetShaderInfoLog")
|
||||
@@ -342,6 +347,7 @@ public class GLSMRedirector {
|
||||
.add("glGenVertexArrays")
|
||||
.add("glBindVertexArray")
|
||||
.add("glDeleteVertexArrays")
|
||||
.add("glIsVertexArray")
|
||||
.add("glBindFramebuffer")
|
||||
.add("glDeleteFramebuffers")
|
||||
.add("glGenFramebuffers")
|
||||
@@ -352,7 +358,8 @@ public class GLSMRedirector {
|
||||
.add("glGetFramebufferAttachmentParameteri")
|
||||
.add("glBlitFramebuffer");
|
||||
final var gl31 = RedirectMap.newMap()
|
||||
.add("glDrawElementsInstanced");
|
||||
.add("glDrawElementsInstanced")
|
||||
.add("glDrawArraysInstanced");
|
||||
final var gl32 = RedirectMap.newMap()
|
||||
.add("glFramebufferTexture");
|
||||
final var gl33 = RedirectMap.newMap()
|
||||
@@ -455,6 +462,14 @@ public class GLSMRedirector {
|
||||
methodRedirects.put(ARBInstancedArrays, RedirectMap.newMap()
|
||||
.add("glVertexAttribDivisorARB")
|
||||
);
|
||||
methodRedirects.put(ARBDrawInstanced, RedirectMap.newMap()
|
||||
.add("glDrawArraysInstancedARB", "glDrawArraysInstanced")
|
||||
.add("glDrawElementsInstancedARB", "glDrawElementsInstanced")
|
||||
);
|
||||
methodRedirects.put(EXTDrawInstanced, RedirectMap.newMap()
|
||||
.add("glDrawArraysInstancedEXT", "glDrawArraysInstanced")
|
||||
.add("glDrawElementsInstancedEXT", "glDrawElementsInstanced")
|
||||
);
|
||||
|
||||
// APPLE
|
||||
methodRedirects.put(APPLEVertexArrayObject, RedirectMap.newMap()
|
||||
@@ -587,18 +602,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 +630,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 +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) {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
class ArrayUniformUploadGLTest {
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
final int s = GL20.glCreateShader(type);
|
||||
GL20.glShaderSource(s, src);
|
||||
GL20.glCompileShader(s);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), () -> "shader compile failed: " + GL20.glGetShaderInfoLog(s, 4096));
|
||||
return s;
|
||||
}
|
||||
|
||||
private static int link(String vs, String fs) {
|
||||
final int v = compile(GL20.GL_VERTEX_SHADER, vs);
|
||||
final int f = compile(GL20.GL_FRAGMENT_SHADER, fs);
|
||||
final int p = GL20.glCreateProgram();
|
||||
GL20.glAttachShader(p, v);
|
||||
GL20.glAttachShader(p, f);
|
||||
GL20.glLinkProgram(p);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "link failed: " + GL20.glGetProgramInfoLog(p, 4096));
|
||||
GL20.glDeleteShader(v);
|
||||
GL20.glDeleteShader(f);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static final String VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n";
|
||||
|
||||
@Test
|
||||
void glUniform2FloatBufferUploadsWholeArray() {
|
||||
// Runtime index keeps the whole array active so element locations stay queryable.
|
||||
final String fs = "#version 330 core\n"
|
||||
+ "uniform vec2 cosmicuvs[10];\n"
|
||||
+ "uniform int idx;\n"
|
||||
+ "out vec4 o;\n"
|
||||
+ "void main(){ o = vec4(cosmicuvs[idx], cosmicuvs[(idx+1)%10]); }\n";
|
||||
final int p = link(VS, fs);
|
||||
try {
|
||||
GL20.glUseProgram(p);
|
||||
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(20);
|
||||
for (int i = 0; i < 20; i++) buf.put(i + 1.0f); // 1..20, all distinct, all non-zero
|
||||
buf.flip();
|
||||
|
||||
GLStateManager.glUniform2(GL20.glGetUniformLocation(p, "cosmicuvs"), buf);
|
||||
|
||||
final FloatBuffer read = BufferUtils.createFloatBuffer(2);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
final int loc = GL20.glGetUniformLocation(p, "cosmicuvs[" + i + "]");
|
||||
assertTrue(loc >= 0, "element " + i + " optimized out");
|
||||
read.clear();
|
||||
GL20.glGetUniform(p, loc, read);
|
||||
assertEquals(2 * i + 1.0f, read.get(0), 0.0f, "cosmicuvs[" + i + "].x");
|
||||
assertEquals(2 * i + 2.0f, read.get(1), 0.0f, "cosmicuvs[" + i + "].y");
|
||||
}
|
||||
} finally {
|
||||
GL20.glUseProgram(0);
|
||||
GL20.glDeleteProgram(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void glUniform1IntBufferUploadsWholeArray() {
|
||||
final String fs = "#version 330 core\n"
|
||||
+ "uniform int vals[4];\n"
|
||||
+ "uniform int idx;\n"
|
||||
+ "out vec4 o;\n"
|
||||
+ "void main(){ o = vec4(float(vals[idx])); }\n";
|
||||
final int p = link(VS, fs);
|
||||
try {
|
||||
GL20.glUseProgram(p);
|
||||
|
||||
final IntBuffer buf = BufferUtils.createIntBuffer(4);
|
||||
buf.put(11).put(22).put(33).put(44).flip();
|
||||
GLStateManager.glUniform1(GL20.glGetUniformLocation(p, "vals"), buf);
|
||||
|
||||
final IntBuffer read = BufferUtils.createIntBuffer(1);
|
||||
final int[] expect = {11, 22, 33, 44};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final int loc = GL20.glGetUniformLocation(p, "vals[" + i + "]");
|
||||
assertTrue(loc >= 0, "element " + i + " optimized out");
|
||||
read.clear();
|
||||
GL20.glGetUniform(p, loc, read);
|
||||
assertEquals(expect[i], read.get(0), "vals[" + i + "]");
|
||||
}
|
||||
} finally {
|
||||
GL20.glUseProgram(0);
|
||||
GL20.glDeleteProgram(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class CompatUniformManagerTest {
|
||||
void cleanup() {
|
||||
if (program != 0) {
|
||||
GL20.glUseProgram(0);
|
||||
GL20.glDeleteProgram(program);
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
|
||||
+215
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
class InstancedQuadDrawGLTest {
|
||||
|
||||
private static final String VS = "#version 330 core\nlayout(location=0) in vec2 aPos;\nvoid main(){ gl_Position = vec4(aPos, 0.0, 1.0); }\n";
|
||||
private static final String FS = "#version 330 core\nout vec4 o;\nvoid main(){ o = vec4(1.0); }\n";
|
||||
|
||||
private int program;
|
||||
private int vbo;
|
||||
private int ebo;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
program = buildProgram();
|
||||
GLStateManager.glUseProgram(program);
|
||||
|
||||
final FloatBuffer verts = BufferUtils.createFloatBuffer(8);
|
||||
verts.put(new float[] {0, 0, 1, 0, 1, 1, 0, 1}).flip();
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, verts, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[] {0, 1, 2, 3}).flip();
|
||||
ebo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, idx, GL15.GL_STATIC_DRAW);
|
||||
|
||||
drainErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
GLStateManager.glUseProgram(0);
|
||||
if (program != 0) GL20.glDeleteProgram(program);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
|
||||
if (ebo != 0) GLStateManager.glDeleteBuffers(ebo);
|
||||
drainErrors();
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawArraysInstanced_quads_noInvalidEnum() {
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced array draw must be emulated, not rejected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawArraysInstanced_triangles_fastPathClean() {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "non-quad instanced array draw passes through cleanly");
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawElementsInstanced_quads_noInvalidEnum() {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElementsInstanced(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0L, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced element draw must be emulated, not rejected");
|
||||
}
|
||||
|
||||
private static int buildProgram() {
|
||||
final int vs = compile(GL20.GL_VERTEX_SHADER, VS);
|
||||
final int fs = compile(GL20.GL_FRAGMENT_SHADER, FS);
|
||||
final int p = GL20.glCreateProgram();
|
||||
GL20.glAttachShader(p, vs);
|
||||
GL20.glAttachShader(p, fs);
|
||||
GL20.glLinkProgram(p);
|
||||
assertNotEquals(0, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), "test shader must link: " + GL20.glGetProgramInfoLog(p, 1024));
|
||||
GL20.glDeleteShader(vs);
|
||||
GL20.glDeleteShader(fs);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
final int s = GL20.glCreateShader(type);
|
||||
GL20.glShaderSource(s, src);
|
||||
GL20.glCompileShader(s);
|
||||
assertNotEquals(0, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), "test shader must compile: " + GL20.glGetShaderInfoLog(s, 1024));
|
||||
return s;
|
||||
}
|
||||
|
||||
private static void drainErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) { /* clear */ }
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -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);
|
||||
|
||||
+5
-5
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,10 @@ repositories {
|
||||
name = "GTNH Maven"
|
||||
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
|
||||
}
|
||||
maven {
|
||||
name = "Minecraft Libraries"
|
||||
url = uri("https://libraries.minecraft.net")
|
||||
}
|
||||
maven {
|
||||
name = "Forge"
|
||||
url = uri("https://maven.minecraftforge.net/")
|
||||
|
||||
+47
-6
@@ -348,6 +348,16 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
GL31C.glDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
GL31C.glDrawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provokingVertex(int provokeMode) {
|
||||
GL32C.glProvokingVertex(provokeMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
GL43C.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
@@ -544,11 +554,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);
|
||||
@@ -861,6 +866,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
GL20C.glUniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2(int location, FloatBuffer value) {
|
||||
GL20C.glUniform2fv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3(int location, FloatBuffer value) {
|
||||
GL20C.glUniform3fv(location, value);
|
||||
@@ -871,6 +881,26 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
GL20C.glUniform4fv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform1iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform1iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform2iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform2iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform3iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform3iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniform4iv(int location, IntBuffer value) {
|
||||
GL20C.glUniform4iv(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uniformMatrix3(int location, boolean transpose, FloatBuffer value) {
|
||||
GL20C.glUniformMatrix3fv(location, transpose, value);
|
||||
@@ -1081,6 +1111,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);
|
||||
@@ -1371,11 +1406,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);
|
||||
@@ -1403,6 +1441,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
@@ -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;
|
||||
@@ -27,6 +30,7 @@ public class ModStatus {
|
||||
public static boolean isCosmeticArmorReworkedLoaded;
|
||||
public static boolean isFluidLoggedLoaded;
|
||||
public static boolean isBOPLoaded;
|
||||
public static boolean isEtFuturumLoaded;
|
||||
|
||||
public static void preInit() {
|
||||
if (Loader.isModLoaded("backhand")) {
|
||||
@@ -35,9 +39,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");
|
||||
@@ -46,6 +50,7 @@ public class ModStatus {
|
||||
isBaublesLoaded = Loader.isModLoaded("Baubles");
|
||||
isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked");
|
||||
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
|
||||
isEtFuturumLoaded = Loader.isModLoaded("etfuturum");
|
||||
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
isBOPLoaded = Loader.isModLoaded("BiomesOPlenty");
|
||||
@@ -65,5 +70,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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.gtnewhorizons.angelica.compat.etfuturum;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
|
||||
|
||||
public final class EtFuturumCompat {
|
||||
private static final String IELYTRA_PLAYER = "ganymedes01.etfuturum.api.elytra.IElytraPlayer";
|
||||
|
||||
@Getter
|
||||
private static boolean loaded;
|
||||
private static Class<?> elytraPlayerClass;
|
||||
private static final MethodHandle isElytraFlyingHandle;
|
||||
|
||||
static {
|
||||
MethodHandle handle = null;
|
||||
try {
|
||||
if (Loader.isModLoaded("etfuturum")) {
|
||||
elytraPlayerClass = ReflectionHelper.getClass(EtFuturumCompat.class.getClassLoader(), IELYTRA_PLAYER);
|
||||
handle = MethodHandles.lookup().findVirtual(elytraPlayerClass, "etfu$isElytraFlying",
|
||||
MethodType.methodType(boolean.class));
|
||||
loaded = true;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Failed to load Et Futurum Requiem elytra compat", t);
|
||||
loaded = false;
|
||||
}
|
||||
isElytraFlyingHandle = handle;
|
||||
}
|
||||
|
||||
private EtFuturumCompat() {
|
||||
}
|
||||
|
||||
public static boolean isElytraFlying(EntityPlayer player) {
|
||||
if (!loaded || isElytraFlyingHandle == null || !elytraPlayerClass.isInstance(player)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return (boolean) isElytraFlyingHandle.invoke(player);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
@@ -269,6 +269,13 @@ public class AngelicaConfig {
|
||||
@Config.DefaultStringList({"jewelrycraft2:blockCrystal"})
|
||||
public static String[] alwaysTranslucentSprites;
|
||||
|
||||
@Config.Comment({"TileEntity classnames whose render bounds change at runtime (e.g. OpenBlocks Guide).",
|
||||
"These are always rendered and frustum-tested against their live bounds instead of the cached."})
|
||||
@Config.DefaultStringList({"openblocks.common.tileentity.TileEntityGuide",
|
||||
"openblocks.common.tileentity.TileEntityBuilderGuide"})
|
||||
@Config.RequiresMcRestart
|
||||
public static String[] dynamicBoundsTileEntities;
|
||||
|
||||
@Config.Comment("Register HardcodedCustomUniforms in Iris Shaders. May help with compatibility in certain shader packs")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean enableHardcodedCustomUniforms;
|
||||
@@ -350,4 +357,13 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean disableErrorChecks;
|
||||
|
||||
@Config.Comment("Fixes various issues with entity overlays, such as z-fighting and eyes.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean entityOverlayFixes;
|
||||
|
||||
@Config.Comment("Swap the vanilla damage overlay with one similar to modern. Fixes specific issues with z-fighting.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean entityModernDamageOverlay;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.gtnewhorizons.angelica.event;
|
||||
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public class ClientEvent extends MutableEvent {
|
||||
public static final EventBus<ClientEvent> BUS = EventBus.create(ClientEvent.class);
|
||||
|
||||
public final ClientEventType event;
|
||||
public final EntityRenderer renderer;
|
||||
public final float partialTicks;
|
||||
|
||||
private ClientEvent(ClientEventType event) {
|
||||
this.event = event;
|
||||
this.renderer = null;
|
||||
this.partialTicks = 0;
|
||||
}
|
||||
|
||||
private ClientEvent(ClientEventType event, EntityRenderer renderer, float partialTicks) {
|
||||
this.event = event;
|
||||
this.renderer = renderer;
|
||||
this.partialTicks = partialTicks;
|
||||
}
|
||||
|
||||
public static void post(ClientEventType event, EntityRenderer renderer, float partialTicks) {
|
||||
if (!BUS.hasListeners()) return;
|
||||
BUS.post(new ClientEvent(event, renderer, partialTicks));
|
||||
}
|
||||
|
||||
public static void post(ClientEventType event) {
|
||||
if (!BUS.hasListeners()) return;
|
||||
BUS.post(new ClientEvent(event));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.event;
|
||||
|
||||
public enum ClientEventType {
|
||||
PRE_RENDER_GUI,
|
||||
PRE_RENDER_HUD,
|
||||
POST_RENDER_HUD,
|
||||
ON_TICK,
|
||||
}
|
||||
@@ -6,11 +6,12 @@ import com.gtnewhorizon.gtnhlib.client.renderer.postprocessing.CustomFramebuffer
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.postprocessing.SharedDepthFramebuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VertexBufferType;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vbo.IVertexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.holoinventory.HoloInventoryReflectionCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.event.ClientEvent;
|
||||
import com.gtnewhorizons.angelica.event.ClientEventType;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameAccessor;
|
||||
@@ -65,6 +66,8 @@ public class HUDCaching {
|
||||
}
|
||||
|
||||
public static void renderCachedHud(EntityRenderer renderer, GuiIngame ingame, float partialTicks, boolean hasScreen, int mouseX, int mouseY) {
|
||||
ClientEvent.post(ClientEventType.PRE_RENDER_GUI, renderer, partialTicks);
|
||||
ClientEvent.post(ClientEventType.PRE_RENDER_HUD, renderer, partialTicks);
|
||||
if (ModStatus.isXaerosMinimapLoaded && ingame instanceof GuiIngameForge) {
|
||||
// this used to be called by asming into renderGameOverlay, but we removed it
|
||||
XaeroMinimapCore.beforeIngameGuiRender(partialTicks);
|
||||
@@ -78,6 +81,7 @@ public class HUDCaching {
|
||||
|
||||
if (!AngelicaConfig.hudCachingActive || mc.displayWidth < 16 || mc.displayHeight < 16) {
|
||||
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
|
||||
ClientEvent.post(ClientEventType.POST_RENDER_HUD, renderer, partialTicks);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,6 +174,8 @@ public class HUDCaching {
|
||||
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
mc.getTextureManager().bindTexture(Gui.icons);
|
||||
|
||||
ClientEvent.post(ClientEventType.POST_RENDER_HUD, renderer, partialTicks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,9 +47,7 @@ public enum Mixins implements IMixins {
|
||||
, "angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
|
||||
, "angelica.bugfixes.MixinRenderMooshroom_MushroomTint"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_DeferredEntityOverlay"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_OverlayTint"
|
||||
, "angelica.bugfixes.MixinRenderWither_ArmorCentering"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_EyeDepth"
|
||||
, "angelica.debug.MixinMinecraft_FPSCap"
|
||||
, "angelica.ffp.MixinTessellator_CoreProfile"
|
||||
, "angelica.glsm.MixinSplashProgressCaching"
|
||||
@@ -60,6 +58,24 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_ENTITY_OVERLAYS(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.entityOverlayFixes)
|
||||
.addExcludedMod(TargetedMod.CUSTOM_PLAYER_MODELS)
|
||||
.addClientMixins(
|
||||
"angelica.bugfixes.MixinRendererLivingEntity_EyeDepth"
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_DAMAGE_OVERLAY(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.entityModernDamageOverlay)
|
||||
.addExcludedMod(TargetedMod.CUSTOM_PLAYER_MODELS)
|
||||
.addClientMixins(
|
||||
"angelica.bugfixes.MixinRendererLivingEntity_OverlayTint"
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_VBO_CLOUDS(
|
||||
new MixinBuilder()
|
||||
.setApplyIf(() -> AngelicaConfig.enableVBOClouds)
|
||||
@@ -117,7 +133,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")
|
||||
@@ -282,6 +298,7 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinRenderEndPortal"
|
||||
, "shaders.MixinTileEntityRendererDispatcher"
|
||||
, "shaders.MixinGlProgram"
|
||||
, "shaders.MixinTextureManager_ReloadCount"
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
TINKERS_CONSTRUCT(null, "TConstruct"),
|
||||
TWILIGHT_FOREST(null, "TwilightForest"),
|
||||
BIOMES_O_PLENTY(null, "BiomesOPlenty"),
|
||||
CUSTOM_PLAYER_MODELS("com.tom.cpmcore.CPMLoadingPlugin", "customplayermodels"),
|
||||
WITCHERY(null, "witchery");
|
||||
|
||||
private final TargetModBuilder builder;
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ import net.minecraft.util.AxisAlignedBB;
|
||||
public interface ITileEntityBoundingBoxCache {
|
||||
AxisAlignedBB angelica$getCachedRenderBoundingBox();
|
||||
|
||||
boolean angelica$isInfiniteExtent();
|
||||
byte angelica$boundsClass();
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.bettercrashes.BetterCrashesCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.ConfigMigrator;
|
||||
import com.gtnewhorizons.angelica.debug.F3Direction;
|
||||
@@ -102,6 +103,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
@Override
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
ModStatus.preInit();
|
||||
for (String name : AngelicaConfig.dynamicBoundsTileEntities) TileEntityRenderBoundsRegistry.registerDynamicClass(name);
|
||||
super.preInit(event);
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
+29
-17
@@ -4,48 +4,60 @@ import it.unimi.dsi.fastutil.objects.Reference2ByteOpenHashMap;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Registry for tracking which TileEntity classes always return INFINITE_EXTENT_AABB.
|
||||
* Registry for tracking which TileEntity classes always return INFINITE_EXTENT_AABB or are known to change at runtime
|
||||
*/
|
||||
public final class TileEntityRenderBoundsRegistry {
|
||||
private static final byte UNKNOWN = 0;
|
||||
private static final byte FINITE = 1;
|
||||
private static final byte INFINITE = 2;
|
||||
public static final byte STATIC = 1;
|
||||
public static final byte INFINITE = 2;
|
||||
public static final byte DYNAMIC = 3;
|
||||
|
||||
private static final Reference2ByteOpenHashMap<Class<? extends TileEntity>> classRegistry = new Reference2ByteOpenHashMap<>();
|
||||
|
||||
private static final Set<String> dynamicClassNames = ConcurrentHashMap.newKeySet();
|
||||
|
||||
static {
|
||||
classRegistry.defaultReturnValue(UNKNOWN);
|
||||
}
|
||||
|
||||
private TileEntityRenderBoundsRegistry() {}
|
||||
|
||||
public static void registerDynamicClass(String className) {
|
||||
if (className != null && !className.isEmpty()) dynamicClassNames.add(className);
|
||||
}
|
||||
|
||||
public static boolean isInfiniteExtentsBox(AxisAlignedBB box) {
|
||||
return box == null || Double.isInfinite(box.minX) || Double.isInfinite(box.minY) || Double.isInfinite(box.minZ) || Double.isInfinite(box.maxX) || Double.isInfinite(box.maxY) || Double.isInfinite(box.maxZ);
|
||||
}
|
||||
|
||||
public static boolean isAlwaysInfiniteExtent(TileEntity te) {
|
||||
public static byte classify(TileEntity te) {
|
||||
final Class<? extends TileEntity> clazz = te.getClass();
|
||||
synchronized (classRegistry) {
|
||||
final byte result = classRegistry.getByte(clazz);
|
||||
if (result != UNKNOWN) return result == INFINITE;
|
||||
final byte cached = classRegistry.getByte(clazz);
|
||||
if (cached != UNKNOWN) return cached;
|
||||
}
|
||||
return probeAndCache(te, clazz);
|
||||
}
|
||||
|
||||
private static boolean probeAndCache(TileEntity te, Class<? extends TileEntity> clazz) {
|
||||
boolean isInfinite;
|
||||
try {
|
||||
final AxisAlignedBB aabb = te.getRenderBoundingBox();
|
||||
isInfinite = isInfiniteExtentsBox(aabb);
|
||||
} catch (Throwable t) {
|
||||
isInfinite = true;
|
||||
final byte result;
|
||||
if (dynamicClassNames.contains(clazz.getName())) {
|
||||
result = DYNAMIC;
|
||||
} else {
|
||||
boolean isInfinite;
|
||||
try {
|
||||
isInfinite = isInfiniteExtentsBox(te.getRenderBoundingBox());
|
||||
} catch (Throwable t) {
|
||||
isInfinite = true;
|
||||
}
|
||||
result = isInfinite ? INFINITE : STATIC;
|
||||
}
|
||||
|
||||
synchronized (classRegistry) {
|
||||
classRegistry.put(clazz, isInfinite ? INFINITE : FINITE);
|
||||
classRegistry.put(clazz, result);
|
||||
}
|
||||
return isInfinite;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
|
||||
+2
-1
@@ -151,7 +151,8 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
final TileEntity tileEntity = region.getTileEntity(x, y, z);
|
||||
if (tileEntity != null && TileEntityRendererDispatcher.instance.hasSpecialRenderer(tileEntity)) {
|
||||
final boolean isGlobal;
|
||||
if (TileEntityRenderBoundsRegistry.isAlwaysInfiniteExtent(tileEntity)) {
|
||||
final byte boundsClass = TileEntityRenderBoundsRegistry.classify(tileEntity);
|
||||
if (boundsClass == TileEntityRenderBoundsRegistry.INFINITE || boundsClass == TileEntityRenderBoundsRegistry.DYNAMIC) {
|
||||
isGlobal = true;
|
||||
} else {
|
||||
AxisAlignedBB aabb;
|
||||
|
||||
+3
-2
@@ -346,8 +346,9 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
|
||||
final ITileEntityBoundingBoxCache teCache = (ITileEntityBoundingBoxCache) tileEntity;
|
||||
|
||||
if (!teCache.angelica$isInfiniteExtent()) {
|
||||
final AxisAlignedBB aabb = teCache.angelica$getCachedRenderBoundingBox();
|
||||
final byte boundsClass = teCache.angelica$boundsClass();
|
||||
if (boundsClass != TileEntityRenderBoundsRegistry.INFINITE) {
|
||||
final AxisAlignedBB aabb = boundsClass == TileEntityRenderBoundsRegistry.DYNAMIC ? tileEntity.getRenderBoundingBox() : teCache.angelica$getCachedRenderBoundingBox();
|
||||
if (aabb != null && !this.currentViewport.isBoxVisible(aabb.minX, aabb.minY, aabb.minZ, aabb.maxX, aabb.maxY, aabb.maxZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@ 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;
|
||||
@@ -55,7 +56,7 @@ 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;
|
||||
}
|
||||
@@ -84,7 +85,7 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
|
||||
|
||||
private int lookupFluidMeta(Block block, int metadata) {
|
||||
final Int2IntMap metaMap = blockMetaMatches.get(block);
|
||||
return metaMap != null ? metaMap.get(metadata) : -1;
|
||||
return metaMap != null ? BlockMaterialMapping.resolveId(metaMap, metadata) : -1;
|
||||
}
|
||||
|
||||
private static Block liquidCounterpart(Block block) {
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.gtnewhorizons.angelica.shadercompat;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Maybe overcomplicated, but it's required to bridge the gap between the modern glint texture and white texture 1.7.10
|
||||
* has. Some shaders are able to render the glint just fine as they seem to have some amount of support for older
|
||||
* Minecraft, but others like AstraLex do *not* render correctly. It expects the color to be within the texture.
|
||||
* This might introduce some oddities that I'm not seeing, but for now this appears to fix the issue.
|
||||
*/
|
||||
public final class ShaderGlint {
|
||||
|
||||
private static final ResourceLocation GLINT_MASK = new ResourceLocation("textures/misc/enchanted_item_glint.png");
|
||||
private static final int MAX_CACHE = 64;
|
||||
|
||||
private static boolean maskLoaded;
|
||||
private static boolean maskFailed;
|
||||
private static int maskW;
|
||||
private static int maskH;
|
||||
private static int[] maskLum;
|
||||
|
||||
private static final Int2IntLinkedOpenHashMap colorToTexture = new Int2IntLinkedOpenHashMap();
|
||||
static {
|
||||
colorToTexture.defaultReturnValue(0);
|
||||
}
|
||||
|
||||
private static final GlintColorHandler COLOR_HANDLER = ShaderGlint::onColorChanged;
|
||||
|
||||
private static boolean injecting;
|
||||
private static boolean swapped;
|
||||
private static int depth;
|
||||
|
||||
private static int prevTexture;
|
||||
|
||||
private static ByteBuffer tintBuffer;
|
||||
|
||||
private ShaderGlint() {}
|
||||
|
||||
public static void beginGlint() {
|
||||
if (ShadowRenderer.ACTIVE || !IrisApi.getInstance().isShaderPackInUse()) return;
|
||||
|
||||
// If a glint span is already active, keep the outer span's saved state and
|
||||
// let the nested render ride on its handler.
|
||||
if (depth++ > 0) return;
|
||||
|
||||
swapped = false;
|
||||
injecting = false;
|
||||
|
||||
// Save the texture bound before the glint section so endGlint can put it back if we swap
|
||||
prevTexture = GLStateManager.getBoundTextureForServerState(0);
|
||||
|
||||
// Observe vertex-color changes for the rest of the glint section
|
||||
GLSMHooks.glintColorHandler = COLOR_HANDLER;
|
||||
}
|
||||
|
||||
public static void endGlint() {
|
||||
if (depth == 0 || --depth > 0) return;
|
||||
if (GLSMHooks.glintColorHandler != COLOR_HANDLER) return;
|
||||
GLSMHooks.glintColorHandler = null;
|
||||
|
||||
if (swapped) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, prevTexture);
|
||||
}
|
||||
swapped = false;
|
||||
injecting = false;
|
||||
}
|
||||
|
||||
private static void onColorChanged(float red, float green, float blue, float alpha) {
|
||||
if (injecting) return;
|
||||
|
||||
final int texture = getTintedTexture(red, green, blue);
|
||||
if (texture <= 0) return;
|
||||
|
||||
injecting = true;
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, alpha);
|
||||
injecting = false;
|
||||
swapped = true;
|
||||
}
|
||||
|
||||
private static int getTintedTexture(float r, float g, float b) {
|
||||
ensureMaskLoaded();
|
||||
if (!maskLoaded) return -1;
|
||||
|
||||
final int ri = clamp8(r);
|
||||
final int gi = clamp8(g);
|
||||
final int bi = clamp8(b);
|
||||
final int key = (ri << 16) | (gi << 8) | bi;
|
||||
|
||||
final int cached = colorToTexture.getAndMoveToLast(key);
|
||||
if (cached != 0) return cached;
|
||||
|
||||
if (colorToTexture.size() >= MAX_CACHE) {
|
||||
GLStateManager.glDeleteTextures(colorToTexture.remove(colorToTexture.firstIntKey()));
|
||||
}
|
||||
|
||||
final int texture = uploadTinted(ri / 255.0F, gi / 255.0F, bi / 255.0F);
|
||||
colorToTexture.putAndMoveToLast(key, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static int uploadTinted(float r, float g, float b) {
|
||||
final ByteBuffer pixels = tintBuffer;
|
||||
pixels.clear();
|
||||
for (final int l : maskLum) {
|
||||
pixels.put((byte) Math.round(l * r));
|
||||
pixels.put((byte) Math.round(l * g));
|
||||
pixels.put((byte) Math.round(l * b));
|
||||
pixels.put((byte) 0xFF);
|
||||
}
|
||||
pixels.flip();
|
||||
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
// Match the vanilla glint texture
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, maskW, maskH, 0,
|
||||
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static void ensureMaskLoaded() {
|
||||
if (maskLoaded || maskFailed) return;
|
||||
try (final InputStream in = Minecraft.getMinecraft().getResourceManager().getResource(GLINT_MASK).getInputStream()) {
|
||||
final BufferedImage img = ImageIO.read(in);
|
||||
maskW = img.getWidth();
|
||||
maskH = img.getHeight();
|
||||
maskLum = new int[maskW * maskH];
|
||||
final int[] argb = img.getRGB(0, 0, maskW, maskH, null, 0, maskW);
|
||||
for (int i = 0; i < argb.length; i++) {
|
||||
final int p = argb[i];
|
||||
final int rr = (p >> 16) & 0xFF;
|
||||
final int gg = (p >> 8) & 0xFF;
|
||||
final int bb = p & 0xFF;
|
||||
maskLum[i] = (rr + gg + bb) / 3;
|
||||
}
|
||||
tintBuffer = BufferUtils.createByteBuffer(maskW * maskH * 4);
|
||||
maskLoaded = true;
|
||||
} catch (final Exception e) {
|
||||
maskFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clamp8(float v) {
|
||||
final int i = Math.round(v * 255.0F);
|
||||
if (i < 0) return 0;
|
||||
return Math.min(i, 255);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import jss.notfine.core.Settings;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
@@ -19,6 +21,13 @@ public class Zoom {
|
||||
|
||||
@Getter
|
||||
private static float zoom = ZOOM_DEFAULT;
|
||||
|
||||
public static final float ZOOM_SMOOTH_SNAP_EPSILON = 0.001F;
|
||||
|
||||
@Getter
|
||||
private static float currentZoom = 1.0F;
|
||||
private static long zoomLerpLastNano = 0L;
|
||||
|
||||
@Getter
|
||||
private static final KeyBinding zoomKey = new KeyBinding("Zoom", 0, "key.categories.misc");
|
||||
|
||||
@@ -38,6 +47,29 @@ public class Zoom {
|
||||
zoom = MathHelper.clamp_float((float) (zoom * Math.pow(ZOOM_STEP, Integer.signum(eventDWheel))), ZOOM_MIN, ZOOM_MAX);
|
||||
}
|
||||
|
||||
public static void updateZoomLerp() {
|
||||
final long now = System.nanoTime();
|
||||
float dt = zoomLerpLastNano == 0L ? 0.0F : (now - zoomLerpLastNano) / 1.0e9F;
|
||||
zoomLerpLastNano = now;
|
||||
|
||||
final float target = zoomEnabled ? zoom : 1.0F;
|
||||
|
||||
if (dt <= 0.0F || !(boolean) Settings.ZOOM_SMOOTH.option.getStore()) {
|
||||
currentZoom = target;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp dt so a long frame (alt-tab, GC pause) does not snap the zoom.
|
||||
dt = Math.min(dt, 0.1F);
|
||||
|
||||
final int rate = (int) Settings.ZOOM_SMOOTH_SPEED.option.getStore();
|
||||
currentZoom += (target - currentZoom) * (1.0F - (float) Math.exp(-dt * rate));
|
||||
|
||||
if (Math.abs(target - currentZoom) < ZOOM_SMOOTH_SNAP_EPSILON) {
|
||||
currentZoom = target;
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetMouseFilters(Minecraft mc) {
|
||||
if (mc.entityRenderer != null) {
|
||||
((IMouseFilterExt) mc.entityRenderer.mouseFilterXAxis).angelica$reset();
|
||||
@@ -79,4 +111,10 @@ public class Zoom {
|
||||
handleKeyEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderTick(TickEvent.RenderTickEvent e) {
|
||||
if (e.phase != TickEvent.Phase.START) return;
|
||||
updateZoomLerp();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.prupe.mcpatcher.ctm.TileOverrideImpl.CTMCompactExpanded;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class CTMTextureGenerator {
|
||||
|
||||
public static void generateTextures(BufferedImage[] compactIcons, CTMCompactExpanded override,
|
||||
ResourceLocation propsLoc, ResourceLocation blankResource){
|
||||
BufferedImage cFull = compactIcons[0]; // Full
|
||||
BufferedImage cNone = compactIcons[1]; // None
|
||||
BufferedImage cVert = compactIcons[2]; // Vertical
|
||||
BufferedImage cHori = compactIcons[3]; // Horizontal
|
||||
BufferedImage cCorn = compactIcons[4]; // Corner
|
||||
|
||||
BufferedImage[] expanded = new BufferedImage[47];
|
||||
|
||||
// Generate all 47 textures to match normal CTM
|
||||
expanded[0] = cFull;
|
||||
expanded[1] = generate(cFull, cHori, cFull, cHori);
|
||||
expanded[2] = cHori;
|
||||
expanded[3] = generate(cHori, cFull, cHori, cFull);
|
||||
expanded[4] = generate(cFull, cHori, cVert, cCorn);
|
||||
expanded[5] = generate(cHori, cFull, cCorn, cVert);
|
||||
expanded[6] = generate(cVert, cCorn, cVert, cCorn);
|
||||
expanded[7] = generate(cHori, cHori, cCorn, cCorn);
|
||||
expanded[8] = generate(cCorn, cNone, cCorn, cCorn);
|
||||
expanded[9] = generate(cCorn, cCorn, cCorn, cNone);
|
||||
expanded[10] = generate(cNone, cCorn, cNone, cCorn);
|
||||
expanded[11] = generate(cNone, cNone, cCorn, cCorn);
|
||||
expanded[12] = generate(cFull, cFull, cVert, cVert);
|
||||
expanded[13] = generate(cFull, cHori, cVert, cNone);
|
||||
expanded[14] = generate(cHori, cHori, cNone, cNone);
|
||||
expanded[15] = generate(cHori, cFull, cNone, cVert);
|
||||
expanded[16] = generate(cVert, cCorn, cFull, cHori);
|
||||
expanded[17] = generate(cCorn, cVert, cHori, cFull);
|
||||
expanded[18] = generate(cCorn, cCorn, cHori, cHori);
|
||||
expanded[19] = generate(cCorn, cVert, cCorn, cVert);
|
||||
expanded[20] = generate(cNone, cCorn, cCorn, cCorn);
|
||||
expanded[21] = generate(cCorn, cCorn, cNone, cCorn);
|
||||
expanded[22] = generate(cCorn, cCorn, cNone, cNone);
|
||||
expanded[23] = generate(cCorn, cNone, cCorn, cNone);
|
||||
expanded[24] = cVert;
|
||||
expanded[25] = generate(cVert, cNone, cVert, cNone);
|
||||
expanded[26] = cNone;
|
||||
expanded[27] = generate(cNone, cVert, cNone, cVert);
|
||||
expanded[28] = generate(cVert, cCorn, cVert, cNone);
|
||||
expanded[29] = generate(cHori, cHori, cNone, cCorn);
|
||||
expanded[30] = generate(cVert, cNone, cVert, cCorn);
|
||||
expanded[31] = generate(cHori, cHori, cCorn, cNone);
|
||||
expanded[32] = generate(cNone, cNone, cNone, cCorn);
|
||||
expanded[33] = generate(cNone, cNone, cCorn, cNone);
|
||||
expanded[34] = generate(cCorn, cNone, cNone, cCorn);
|
||||
expanded[35] = generate(cNone, cCorn, cCorn, cNone);
|
||||
expanded[36] = generate(cVert, cVert, cFull, cFull);
|
||||
expanded[37] = generate(cVert, cNone, cFull, cHori);
|
||||
expanded[38] = generate(cNone, cNone, cHori, cHori);
|
||||
expanded[39] = generate(cNone, cVert, cHori, cFull);
|
||||
expanded[40] = generate(cCorn, cNone, cHori, cHori);
|
||||
expanded[41] = generate(cNone, cVert, cNone, cVert);
|
||||
expanded[42] = generate(cNone, cCorn, cHori, cHori);
|
||||
expanded[43] = generate(cCorn, cVert, cNone, cVert);
|
||||
expanded[44] = generate(cNone, cCorn, cNone, cNone);
|
||||
expanded[45] = generate(cCorn, cNone, cNone, cNone);
|
||||
expanded[46] = cCorn;
|
||||
|
||||
for (int i = 0; i < expanded.length; i++){
|
||||
ResourceLocation resource =
|
||||
TileLoader.parseTileAddress(propsLoc, String.valueOf(i), blankResource);
|
||||
override.addIcon(resource, expanded[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static BufferedImage generate(BufferedImage tl, BufferedImage tr, BufferedImage bl, BufferedImage br){
|
||||
int w = tl.getWidth();
|
||||
int hw = w/2;
|
||||
BufferedImage img = new BufferedImage(tl.getWidth(), tl.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
int numFrames = tl.getHeight() / tl.getWidth();
|
||||
Graphics2D g = img.createGraphics();
|
||||
for (int i = 0; i < numFrames; i++){
|
||||
int yOff = w * i;
|
||||
copy(tl, g, 0, yOff, hw);
|
||||
copy(tr, g, hw, yOff, hw);
|
||||
copy(bl, g, 0, yOff + hw, hw);
|
||||
copy(br, g, hw, yOff + hw, hw);
|
||||
}
|
||||
g.dispose();
|
||||
return img;
|
||||
}
|
||||
|
||||
private static void copy(BufferedImage src, Graphics2D g, int x, int y, int s){
|
||||
g.drawImage(src, x, y, x+s, y+s, x, y, x+s, y+s, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,12 +10,12 @@ import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import com.prupe.mcpatcher.MCLogger;
|
||||
import com.prupe.mcpatcher.MCPatcherUtils;
|
||||
import com.prupe.mcpatcher.mal.block.BlockAPI;
|
||||
@@ -32,7 +32,7 @@ import jss.notfine.config.MCPatcherForgeConfig;
|
||||
|
||||
public class CTMUtils {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
public static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
private static class Overrides {
|
||||
private final List<TileOverride> all = new ArrayList<>();
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import jss.notfine.render.InterpolatedIcon;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.resources.SimpleResource;
|
||||
import net.minecraft.client.resources.data.AnimationMetadataSection;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class GeneratedCTMAtlasSprite extends InterpolatedIcon {
|
||||
|
||||
private final TileLoader loader;
|
||||
private final boolean aniFiltering;
|
||||
private boolean interpolated;
|
||||
|
||||
public GeneratedCTMAtlasSprite(TileLoader loader, boolean aniFiltering, String name) {
|
||||
super(name);
|
||||
this.loader = loader;
|
||||
this.aniFiltering = aniFiltering;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAnimationInterpolated() throws IllegalArgumentException, IllegalAccessException {
|
||||
if(interpolated) super.updateAnimationInterpolated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomLoader(IResourceManager manager, ResourceLocation location) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean load(IResourceManager manager, ResourceLocation location) {
|
||||
BufferedImage img = loader.getImageForPath(location);
|
||||
if(img == null) return true;
|
||||
try {
|
||||
ResourceLocation metaLoc = getMetaLoc(location);
|
||||
IResource iresource = manager.getResource(metaLoc);
|
||||
AnimationMetadataSection anim = (AnimationMetadataSection)iresource.getMetadata("animation");
|
||||
interpolated = anim != null && AngelicaConfig.enableNotFineFeatures && shouldInterpolate(iresource);
|
||||
loadSprite(new BufferedImage[]{img, null, null, null, null}, anim, aniFiltering);
|
||||
}catch (Exception exception){
|
||||
CTMUtils.logger.error("Error loading sprite for " + location);
|
||||
exception.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean shouldInterpolate(IResource resource){
|
||||
try {
|
||||
if (resource instanceof SimpleResource simple) {
|
||||
return simple.mcmetaJson.getAsJsonObject("animation")
|
||||
.getAsJsonPrimitive("interpolate").getAsBoolean();
|
||||
}
|
||||
}catch (Exception _){}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the mcmeta file associated with 0.png
|
||||
*/
|
||||
private static ResourceLocation getMetaLoc(ResourceLocation location) {
|
||||
String path = location.getResourcePath();
|
||||
int index = path.lastIndexOf('/');
|
||||
if(index == -1) return new ResourceLocation(location.getResourceDomain(), "0.png");
|
||||
path = path.substring(0, index) + "/0.png";
|
||||
return new ResourceLocation(location.getResourceDomain(), path);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_MATERIAL;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_TILE;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.NORMALS;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashSet;
|
||||
@@ -32,10 +33,10 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
private final PropertiesFile properties;
|
||||
protected final PropertiesFile properties;
|
||||
private final String baseFilename;
|
||||
private final TileLoader tileLoader;
|
||||
private final int renderPass;
|
||||
protected final TileLoader tileLoader;
|
||||
protected final int renderPass;
|
||||
private final int weight;
|
||||
private final List<BlockStateMatcher> matchBlocks;
|
||||
private final Set<String> matchTiles;
|
||||
@@ -45,7 +46,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
private final BitSet biomes;
|
||||
private final BitSet height;
|
||||
|
||||
private final List<ResourceLocation> tileNames = new ArrayList<>();
|
||||
protected final List<ResourceLocation> tileNames = new ArrayList<>();
|
||||
protected IIcon[] icons;
|
||||
|
||||
// Index into this array is formed from these bit values:
|
||||
@@ -103,6 +104,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
case "sandstone", "top" -> override = new TileOverrideImpl.Top(properties, tileLoader);
|
||||
case "repeat", "pattern" -> override = new TileOverrideImpl.Repeat(properties, tileLoader);
|
||||
case "ctm_compact", "compact" -> override = new TileOverrideImpl.CTMCompact(properties, tileLoader);
|
||||
case "compact_expanded" -> override = new TileOverrideImpl.CTMCompactExpanded(properties, tileLoader);
|
||||
default -> properties.error("unknown method \"%s\"", method);
|
||||
}
|
||||
|
||||
@@ -196,7 +198,12 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
return tileLoader.preloadTile(resource, renderPass > RenderPassAPI.MAX_BASE_RENDER_PASS);
|
||||
}
|
||||
|
||||
private void loadIcons() {
|
||||
public void addIcon(ResourceLocation resource, BufferedImage img){
|
||||
tileNames.add(resource);
|
||||
tileLoader.addTile(resource, img, null, true);
|
||||
}
|
||||
|
||||
protected void loadIcons() {
|
||||
tileNames.clear();
|
||||
String tileList = properties.getString("tiles", "");
|
||||
ResourceLocation blankResource = RenderPassAPI.instance.getBlankResource(renderPass);
|
||||
@@ -221,16 +228,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
try {
|
||||
int from = Integer.parseInt(matcher.group(1));
|
||||
int to = Integer.parseInt(matcher.group(2));
|
||||
for (int i = from; i <= to; i++) {
|
||||
ResourceLocation resource = TileLoader
|
||||
.parseTileAddress(properties.getResource(), String.valueOf(i), blankResource);
|
||||
if (TexturePackAPI.hasResource(resource)) {
|
||||
addIcon(resource);
|
||||
} else {
|
||||
properties.warning("could not find image %s", resource);
|
||||
tileNames.add(null);
|
||||
}
|
||||
}
|
||||
loadOverrideIcons(from, to, blankResource);
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -250,6 +248,19 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadOverrideIcons(int from, int to, ResourceLocation blankResource){
|
||||
for (int i = from; i <= to; i++) {
|
||||
ResourceLocation resource = TileLoader
|
||||
.parseTileAddress(properties.getResource(), String.valueOf(i), blankResource);
|
||||
if (TexturePackAPI.hasResource(resource)) {
|
||||
addIcon(resource);
|
||||
} else {
|
||||
properties.warning("could not find image %s", resource);
|
||||
tileNames.add(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<BlockStateMatcher> getBlockList(String property, String defaultMetadata) {
|
||||
List<BlockStateMatcher> blocks = new ArrayList<>();
|
||||
if (!MCPatcherUtils.isNullOrEmpty(defaultMetadata)) {
|
||||
|
||||
@@ -12,15 +12,22 @@ import static com.prupe.mcpatcher.ctm.RenderBlockState.REL_UL;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.REL_UR;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.TOP_FACE;
|
||||
|
||||
import com.prupe.mcpatcher.mal.block.RenderPassAPI;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
import com.prupe.mcpatcher.mal.util.WeightedIndex;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TileOverrideImpl {
|
||||
|
||||
final static class CTM extends TileOverride {
|
||||
static class CTM extends TileOverride {
|
||||
|
||||
CTM(PropertiesFile properties, TileLoader tileLoader) {
|
||||
super(properties, tileLoader);
|
||||
@@ -505,4 +512,48 @@ public class TileOverrideImpl {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
final public static class CTMCompactExpanded extends CTM {
|
||||
|
||||
CTMCompactExpanded(PropertiesFile propertiesFile, TileLoader tileLoader) {
|
||||
super(propertiesFile, tileLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
String checkTileMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethod() {
|
||||
return "compact_expanded";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadOverrideIcons(int from, int to, ResourceLocation blankResource) {
|
||||
List<BufferedImage> compactIcons = new ArrayList<>();
|
||||
if(from != 0){
|
||||
properties.error("expanded compact CTM requires range to start at 0, starts at %d", from);
|
||||
}
|
||||
if(to - from + 1 != 5){
|
||||
properties.error("expanded compact CTM requires exactly 5 icons, got range %d - %d", from, to);
|
||||
}
|
||||
for (int i = from; i <= to; i++) {
|
||||
ResourceLocation resource = TileLoader
|
||||
.parseTileAddress(properties.getResource(), String.valueOf(i), blankResource);
|
||||
if (TexturePackAPI.hasResource(resource)) {
|
||||
compactIcons.add(tileLoader.loadResourceImage(resource,
|
||||
renderPass > RenderPassAPI.MAX_BASE_RENDER_PASS));
|
||||
} else {
|
||||
// Promote from warning to error for compact CTM
|
||||
properties.error("could not find image %s, required for expanded compact CTM", resource);
|
||||
tileNames.add(null);
|
||||
}
|
||||
}
|
||||
if(compactIcons.size() == 5){
|
||||
CTMTextureGenerator.generateTextures(compactIcons.toArray(new BufferedImage[0]),
|
||||
this, properties.getResource(), blankResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.prupe.mcpatcher.ctm.GeneratedCTMAtlasSprite;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
@@ -44,6 +45,7 @@ public class TileLoader {
|
||||
private TextureMap baseTextureMap;
|
||||
private final Map<String, TextureAtlasSprite> baseTexturesByName = new HashMap<>();
|
||||
private final Set<ResourceLocation> tilesToRegister = new HashSet<>();
|
||||
private final Set<ResourceLocation> generatedTiles = new HashSet<>();
|
||||
private final Map<ResourceLocation, BufferedImage> tileImages = new HashMap<>();
|
||||
private final Map<String, IIcon> iconMap = new HashMap<>();
|
||||
|
||||
@@ -119,6 +121,10 @@ public class TileLoader {
|
||||
logger.fine("after registerIcons(%s) %d icons", mapName, map.size());
|
||||
}
|
||||
|
||||
public BufferedImage getImageForPath(ResourceLocation rl) {
|
||||
return tileImages.get(rl);
|
||||
}
|
||||
|
||||
public static String getOverridePath(String prefix, String basePath, String name, String ext) {
|
||||
String path;
|
||||
if (name.endsWith(".png")) {
|
||||
@@ -218,6 +224,22 @@ public class TileLoader {
|
||||
if (tileImages.containsKey(resource)) {
|
||||
return true;
|
||||
}
|
||||
return addTile(resource, loadResourceImage(resource, alternate), special, false);
|
||||
}
|
||||
|
||||
public boolean addTile(ResourceLocation resource, BufferedImage image, String special, boolean generated) {
|
||||
tilesToRegister.add(resource);
|
||||
tileImages.put(resource, image);
|
||||
if(generated){
|
||||
generatedTiles.add(resource);
|
||||
}
|
||||
if (special != null) {
|
||||
specialTextures.put(resource.toString(), special);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public BufferedImage loadResourceImage(ResourceLocation resource, boolean alternate){
|
||||
BufferedImage image = null;
|
||||
if (!MCPatcherForgeConfig.ConnectedTextures.debugTextures) {
|
||||
image = TexturePackAPI.getImage(resource);
|
||||
@@ -228,12 +250,7 @@ public class TileLoader {
|
||||
if (image == null) {
|
||||
image = generateDebugTexture(resource.getResourcePath(), 64, 64, alternate);
|
||||
}
|
||||
tilesToRegister.add(resource);
|
||||
tileImages.put(resource, image);
|
||||
if (special != null) {
|
||||
specialTextures.put(resource.toString(), special);
|
||||
}
|
||||
return true;
|
||||
return image;
|
||||
}
|
||||
|
||||
public boolean preloadTile(ResourceLocation resource, boolean alternate) {
|
||||
@@ -287,8 +304,14 @@ public class TileLoader {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
IIcon icon = textureMap.registerIcon(name);
|
||||
map.put(name, (TextureAtlasSprite) icon);
|
||||
TextureAtlasSprite icon;
|
||||
if(generatedTiles.contains(resource)){
|
||||
icon = new GeneratedCTMAtlasSprite(this, textureMap.anisotropicFiltering > 1, name);
|
||||
textureMap.setTextureEntry(name, icon);
|
||||
}else{
|
||||
icon = (TextureAtlasSprite)textureMap.registerIcon(name);
|
||||
}
|
||||
map.put(name, icon);
|
||||
iconMap.put(name, icon);
|
||||
String extra = (width == height ? "" : ", " + (height / width) + " frames");
|
||||
subLogger.finer("%s -> %s icon %dx%d%s", name, mapName, width, width, extra);
|
||||
@@ -299,6 +322,7 @@ public class TileLoader {
|
||||
public void finish() {
|
||||
tilesToRegister.clear();
|
||||
tileImages.clear();
|
||||
generatedTiles.clear();
|
||||
}
|
||||
|
||||
public IIcon getIcon(String name) {
|
||||
|
||||
@@ -174,7 +174,9 @@ public enum Settings {
|
||||
RenderStars.reloadStarRenderList(Minecraft.getMinecraft().renderGlobal);
|
||||
}
|
||||
},
|
||||
VOID_FOG(new NotFineOptionTickBox(false, OptionImpact.LOW));
|
||||
VOID_FOG(new NotFineOptionTickBox(false, OptionImpact.LOW)),
|
||||
ZOOM_SMOOTH(new NotFineOptionTickBox(true, OptionImpact.LOW)),
|
||||
ZOOM_SMOOTH_SPEED(new NotFineOptionSlider(14, 1, 30, 1, OptionImpact.LOW));
|
||||
|
||||
public final NotFineOption<?> option;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jss.notfine.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import jss.notfine.config.NotFineConfig;
|
||||
import jss.notfine.core.Settings;
|
||||
@@ -169,6 +170,12 @@ public class NotFineGameOptionPages {
|
||||
.build());
|
||||
}
|
||||
|
||||
if (AngelicaConfig.enableZoom) {
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.ZOOM_SMOOTH.option)
|
||||
.add(Settings.ZOOM_SMOOTH_SPEED.option)
|
||||
.build());
|
||||
}
|
||||
|
||||
return new OptionPage(I18n.format("options.video"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class InterpolatedIcon extends TextureAtlasSprite {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnimationInterpolated() throws IllegalArgumentException, IllegalAccessException {
|
||||
protected void updateAnimationInterpolated() throws IllegalArgumentException, IllegalAccessException {
|
||||
|
||||
double d0 = 1.0D - tickCounter / (double) animationMetadata.getFrameTimeSingle(frameCounter);
|
||||
int i = animationMetadata.getFrameIndex(frameCounter);
|
||||
|
||||
@@ -532,6 +532,13 @@ public class SodiumGameOptionPages {
|
||||
.build())
|
||||
.build());
|
||||
|
||||
if (AngelicaConfig.enableZoom) {
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.ZOOM_SMOOTH.option)
|
||||
.add(Settings.ZOOM_SMOOTH_SPEED.option)
|
||||
.build());
|
||||
}
|
||||
|
||||
return new OptionPage(I18n.format("sodium.options.pages.appearance"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -35,6 +36,7 @@ import net.coderbot.iris.shaderpack.option.Profile;
|
||||
import net.coderbot.iris.shaderpack.option.values.MutableOptionValues;
|
||||
import net.coderbot.iris.shaderpack.option.values.OptionValues;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
@@ -566,11 +568,11 @@ public class Iris {
|
||||
|
||||
// If it's a folder-based shaderpack, just use the shaders subdirectory
|
||||
shaderPackPath = shaderPackRoot.resolve("shaders");
|
||||
}
|
||||
|
||||
if (!Files.exists(shaderPackPath)) {
|
||||
logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name);
|
||||
return false;
|
||||
if (!Files.exists(shaderPackPath)) {
|
||||
logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -761,6 +763,9 @@ public class Iris {
|
||||
// allows shaderpacks to be changed at runtime
|
||||
irisConfig.initialize();
|
||||
|
||||
// Reset the texture reload counter
|
||||
CapturedRenderingState.INSTANCE.resetTextureReloadCount();
|
||||
|
||||
// Destroy all allocated resources
|
||||
destroyEverything();
|
||||
|
||||
@@ -948,7 +953,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();
|
||||
|
||||
@@ -10,13 +10,13 @@ import java.util.function.BooleanSupplier;
|
||||
public enum FeatureFlags {
|
||||
SEPARATE_HARDWARE_SAMPLERS(() -> true, () -> true),
|
||||
PER_BUFFER_BLENDING(() -> true, RenderSystem::supportsBufferBlending),
|
||||
COMPUTE_SHADERS(() -> true, RenderSystem::supportsCompute),
|
||||
COMPUTE_SHADERS(() -> false, RenderSystem::supportsCompute),
|
||||
ENTITY_TRANSLUCENT(() -> true, () -> true),
|
||||
BLOCK_EMISSION_ATTRIBUTE(() -> true, () -> true),
|
||||
CUSTOM_IMAGES(() -> true, RenderSystem::supportsImageLoadStore),
|
||||
SSBO(() -> true, RenderSystem::supportsSSBO),
|
||||
HIGHER_SHADOWCOLOR(() -> true, () -> true),
|
||||
TESSELLATION_SHADERS(() -> true, RenderSystem::supportsTesselation),
|
||||
TESSELLATION_SHADERS(() -> false, RenderSystem::supportsTesselation),
|
||||
REVERSED_CULLING(() -> true, () -> true),
|
||||
CAN_DISABLE_WEATHER(() -> true, () -> true),
|
||||
UNKNOWN(() -> false, () -> false);
|
||||
|
||||
@@ -4,5 +4,7 @@ public enum GlVersion {
|
||||
GL_11,
|
||||
GL_12,
|
||||
GL_30,
|
||||
GL_31
|
||||
GL_31,
|
||||
GL_33,
|
||||
GL_41
|
||||
}
|
||||
@@ -132,6 +132,11 @@ public class StandardMacros {
|
||||
getBiomeCategoryDefines().forEach((category, ordinal) -> define(standardDefines, category, ordinal));
|
||||
getVanillaBiomeDefines().forEach((biome, id) -> define(standardDefines, biome, id));
|
||||
|
||||
// rain type constants
|
||||
define(standardDefines, "PPT_NONE", "0");
|
||||
define(standardDefines, "PPT_RAIN", "1");
|
||||
define(standardDefines, "PPT_SNOW", "2");
|
||||
|
||||
return ImmutableList.copyOf(standardDefines);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import net.coderbot.iris.gl.GlVersion;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL41;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -72,10 +74,16 @@ public enum InternalTextureFormat {
|
||||
RG32UI(GL30.GL_RG32UI, GlVersion.GL_30, PixelFormat.RG_INTEGER),
|
||||
RGB32UI(GL30.GL_RGB32UI, GlVersion.GL_30, PixelFormat.RGB_INTEGER),
|
||||
RGBA32UI(GL30.GL_RGBA32UI, GlVersion.GL_30, PixelFormat.RGBA_INTEGER),
|
||||
// 2-bit normalized
|
||||
RGBA2(GL11.GL_RGBA2, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
// 4-bit normalized
|
||||
RGBA4(GL11.GL_RGBA4, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
// Mixed
|
||||
R3_G3_B2(GL11.GL_R3_G3_B2, GlVersion.GL_11, PixelFormat.RGB),
|
||||
RGB5_A1(GL11.GL_RGB5_A1, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
RGB565(GL41.GL_RGB565, GlVersion.GL_41, PixelFormat.RGB),
|
||||
RGB10_A2(GL11.GL_RGB10_A2, GlVersion.GL_11, PixelFormat.RGBA),
|
||||
RGB10_A2UI(GL33.GL_RGB10_A2UI, GlVersion.GL_33, PixelFormat.RGBA_INTEGER),
|
||||
R11F_G11F_B10F(GL30.GL_R11F_G11F_B10F, GlVersion.GL_30, PixelFormat.RGB),
|
||||
RGB9_E5(GL30.GL_RGB9_E5, GlVersion.GL_30, PixelFormat.RGB);
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import net.coderbot.iris.gl.texture.DepthBufferFormat;
|
||||
import net.coderbot.iris.gl.texture.TextureType;
|
||||
import net.coderbot.iris.helpers.Tri;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.transform.GlintScrollInjector;
|
||||
import net.coderbot.iris.pipeline.transform.PatchShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.TransformPatcher;
|
||||
import net.coderbot.iris.postprocess.BufferFlipper;
|
||||
@@ -838,13 +839,16 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
|
||||
if (transformed == null) {
|
||||
// Fallback to synchronous transform if not pre-computed
|
||||
String vertex = source.getVertexSource().orElseThrow(NullPointerException::new);
|
||||
final boolean scrollGlint = GlintScrollInjector.shouldInject(id, source);
|
||||
transformed = TransformPatcher.patchAttributes(
|
||||
source.getVertexSource().orElseThrow(NullPointerException::new),
|
||||
vertex,
|
||||
source.getGeometrySource().orElse(null),
|
||||
source.getTessControlSource().orElse(null),
|
||||
source.getTessEvalSource().orElse(null),
|
||||
source.getFragmentSource().orElseThrow(NullPointerException::new),
|
||||
availability);
|
||||
availability,
|
||||
scrollGlint);
|
||||
}
|
||||
|
||||
String vertex = transformed.get(PatchShaderType.VERTEX);
|
||||
@@ -1784,9 +1788,12 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
ProgramSource source = resolver.resolveNullable(id);
|
||||
if (source != null && !processedSourceNames.contains(source.getName())) {
|
||||
processedSourceNames.add(source.getName());
|
||||
final ProgramSource src = source;
|
||||
final String vertexSource = src.getVertexSource().orElse(null);
|
||||
final boolean scrollGlint = GlintScrollInjector.shouldInject(id, src);
|
||||
for (InputAvailability avail : INPUT_AVAILABILITIES) {
|
||||
Pair<String, InputAvailability> key = Pair.of(source.getName(), avail);
|
||||
futures.put(key, Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchAttributes(source.getVertexSource().orElse(null), source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElse(null), avail)));
|
||||
Pair<String, InputAvailability> key = Pair.of(src.getName(), avail);
|
||||
futures.put(key, Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchAttributes(vertexSource, src.getGeometrySource().orElse(null), src.getTessControlSource().orElse(null), src.getTessEvalSource().orElse(null), src.getFragmentSource().orElse(null), avail, scrollGlint)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.coderbot.iris.pipeline.transform;
|
||||
import net.coderbot.iris.gl.shader.ShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.AttributeParameters;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -13,6 +14,10 @@ import java.util.Map;
|
||||
*/
|
||||
class AttributeTransformer {
|
||||
public static void transform(Transformer transformer, AttributeParameters parameters, int version) {
|
||||
if (parameters.scrollGlint && parameters.type == ShaderType.VERTEX) {
|
||||
transformer.replaceExpression("gl_MultiTexCoord0", "(gl_TextureMatrix[0] * gl_MultiTexCoord0)", GLSLParser::postfix_expression);
|
||||
}
|
||||
|
||||
// Always core profile — minimum GLSL version is 330 (see ShaderTransformer.getStageMinimumVersion)
|
||||
CommonTransformer.transform(transformer, parameters, true, version);
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ class CeleritasTransformer {
|
||||
vertexReplacements.put("gl_MultiTexCoord2", "iris_LightTexCoord");
|
||||
vertexReplacements.forEach(transformer::replaceExpression);
|
||||
|
||||
if (transformer.hasVariable("chunkOffset")) {
|
||||
transformer.removeVariable("chunkOffset");
|
||||
}
|
||||
|
||||
final Map<String, String> vertexRenames = new HashMap<>();
|
||||
vertexRenames.put("gl_Color", "_vert_color");
|
||||
vertexRenames.put("ftransform", "iris_ftransform");
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import net.coderbot.iris.gl.shader.ShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.Parameters;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -29,6 +30,16 @@ public class CommonTransformer {
|
||||
root.rename("gl_Color", "iris_FrontColor");
|
||||
}
|
||||
|
||||
if (root.hasVariable("gl_TexCoord")) {
|
||||
if (parameters.type == ShaderType.VERTEX) {
|
||||
root.rename("gl_TexCoord", "irs_texCoords");
|
||||
root.injectVariable("out vec4 irs_texCoords[3];");
|
||||
} else if (parameters.type == ShaderType.FRAGMENT) {
|
||||
root.rename("gl_TexCoord", "irs_texCoords");
|
||||
root.injectVariable("in vec4 irs_texCoords[3];");
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.type == ShaderType.FRAGMENT) {
|
||||
if (root.containsCall("gl_FragColor")) {
|
||||
root.replaceExpression("gl_FragColor", "gl_FragData[0]");
|
||||
@@ -91,6 +102,27 @@ public class CommonTransformer {
|
||||
root.renameAndWrapShadow("shadow2D", "texture");
|
||||
root.renameAndWrapShadow("shadow2DLod", "textureLod");
|
||||
|
||||
if (root.containsCall("textureLodOffset")) {
|
||||
root.injectFunction("vec4 iris_textureLodOffset(sampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.injectFunction("float iris_textureLodOffset(sampler2DShadow iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, vec3(iris_tlo_c.xy + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_c.z), iris_tlo_l); }");
|
||||
root.injectFunction("vec4 iris_textureLodOffset(sampler3D iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec3 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec3(iris_tlo_o) / vec3(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.injectFunction("vec4 iris_textureLodOffset(sampler2DArray iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, vec3(iris_tlo_c.xy + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l)).xy), iris_tlo_c.z), iris_tlo_l); }");
|
||||
root.injectFunction("ivec4 iris_textureLodOffset(isampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.injectFunction("uvec4 iris_textureLodOffset(usampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }");
|
||||
root.renameFunctionCall("textureLodOffset", "iris_textureLodOffset");
|
||||
}
|
||||
|
||||
if (parameters.type == ShaderType.VERTEX) {
|
||||
root.mutateTree(tree -> {
|
||||
if (tree.children != null) {
|
||||
tree.children.removeIf(child ->
|
||||
child instanceof GLSLParser.External_declarationContext
|
||||
&& ((GLSLParser.External_declarationContext) child).declaration() != null
|
||||
&& child.getText().contains("gl_PerVertex"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (parameters.patch == Patch.ATTRIBUTES && parameters.type == ShaderType.VERTEX) {
|
||||
root.injectVariable("uniform bool angelica_ClipPlanesEnabled;");
|
||||
root.injectVariable("uniform vec4 angelica_ClipPlane[8];");
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.coderbot.iris.pipeline.transform;
|
||||
import net.coderbot.iris.gl.shader.ShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.Parameters;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.taumc.glsl.ShaderPrinter;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
@@ -109,7 +110,10 @@ public class CompatibilityTransformer {
|
||||
continue;
|
||||
}
|
||||
|
||||
prevTransformer.makeOutDeclaration(inDec.get(in), in);
|
||||
String outDeclaration = ShaderPrinter.getFormattedShader(inDec.get(in).fully_specified_type())
|
||||
+ " " + in + ";";
|
||||
prevTransformer.variable = null;
|
||||
prevTransformer.injectVariable(outDeclaration.replaceFirst("in", "out"));
|
||||
|
||||
if (!prevTransformer.hasAssigment(in)) {
|
||||
prevTransformer.initialize(inDec.get(in), in);
|
||||
@@ -126,6 +130,23 @@ public class CompatibilityTransformer {
|
||||
if (!prevTransformer.hasAssigment(in)) {
|
||||
prevTransformer.initialize(inDec.get(in), in);
|
||||
}
|
||||
} else {
|
||||
int outComp = vecComponents(outType.getText());
|
||||
int inComp = vecComponents(inType.getText());
|
||||
if (outComp < 0 || inComp < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String temp = "iris_template_" + in;
|
||||
prevTransformer.rename(in, temp);
|
||||
String newOut = ShaderPrinter.getFormattedShader(inDec.get(in).fully_specified_type())
|
||||
+ " " + in + ";";
|
||||
prevTransformer.variable = null;
|
||||
prevTransformer.injectVariable(newOut.replaceFirst("in", "out"));
|
||||
String cast = outComp < inComp
|
||||
? in + " = " + inType.getText() + "(" + temp + ", vec4(0));"
|
||||
: in + " = " + inType.getText() + "(" + temp + ");";
|
||||
prevTransformer.appendMain(cast);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,4 +154,13 @@ public class CompatibilityTransformer {
|
||||
prevType = type;
|
||||
}
|
||||
}
|
||||
|
||||
private static int vecComponents(String type) {
|
||||
return switch (type) {
|
||||
case "vec2" -> 2;
|
||||
case "vec3" -> 3;
|
||||
case "vec4" -> 4;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class CompositeDepthTransformer {
|
||||
final int type = transformer.findType("centerDepthSmooth");
|
||||
if (type != 0) {
|
||||
transformer.injectVariable("uniform sampler2D iris_centerDepthSmooth;");
|
||||
transformer.replaceExpression("centerDepthSmooth", "texture2D(iris_centerDepthSmooth, vec2(0.5)).r");
|
||||
transformer.replaceExpression("centerDepthSmooth", "texture(iris_centerDepthSmooth, vec2(0.5)).r");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,13 @@ class CoreTransformHelper {
|
||||
transformer.rename("gl_Vertex", "iris_Vertex");
|
||||
transformer.rename("gl_MultiTexCoord0", "iris_MultiTexCoord0");
|
||||
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
transformer.replaceExpression("gl_MultiTexCoord" + i, "vec4(0.0, 0.0, 0.0, 1.0)");
|
||||
}
|
||||
|
||||
transformer.replaceExpression("gl_Color", "vec4(1.0, 1.0, 1.0, 1.0)");
|
||||
transformer.replaceExpression("gl_Normal", "vec3(0.0, 0.0, 1.0)");
|
||||
|
||||
transformer.renameFunctionCall("ftransform", "iris_ftransform");
|
||||
transformer.injectFunction("vec4 iris_ftransform() { return (iris_ProjectionMatrix * iris_ModelViewMatrix) * iris_Vertex; }");
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.coderbot.iris.pipeline.transform;
|
||||
|
||||
import net.coderbot.iris.shaderpack.ProgramSource;
|
||||
import net.coderbot.iris.shaderpack.loading.ProgramId;
|
||||
|
||||
/**
|
||||
* For some reason not all shaders read from gl_MultiTexCoord0 through the texture matrix to scroll the enchant glint?
|
||||
* Maybe a modern thing but a few shaders just look wrong without it. Technically not in spec, but it looks better.
|
||||
*/
|
||||
public final class GlintScrollInjector {
|
||||
|
||||
private GlintScrollInjector() {}
|
||||
|
||||
public static boolean shouldInject(ProgramId id, ProgramSource source) {
|
||||
if (id != ProgramId.ArmorGlint || source == null) return false;
|
||||
if (!ProgramId.ArmorGlint.getSourceName().equals(source.getName())) return false;
|
||||
|
||||
final String vertex = source.getVertexSource().orElse(null);
|
||||
if (vertex == null) return false;
|
||||
// Already scrolls through the texture matrix itself
|
||||
if (vertex.contains("gl_TextureMatrix[0]")) return false;
|
||||
return vertex.contains("gl_MultiTexCoord0");
|
||||
}
|
||||
}
|
||||
@@ -104,8 +104,12 @@ public class TransformPatcher {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchAttributes(String vertex, String geometry, String tessControl, String tessEval, String fragment, InputAvailability inputs, boolean scrollGlint) {
|
||||
return transform(vertex, geometry, tessControl, tessEval, fragment, new AttributeParameters(Patch.ATTRIBUTES, geometry != null, inputs, scrollGlint));
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchAttributes(String vertex, String geometry, String tessControl, String tessEval, String fragment, InputAvailability inputs) {
|
||||
return transform(vertex, geometry, tessControl, tessEval, fragment, new AttributeParameters(Patch.ATTRIBUTES, geometry != null, inputs));
|
||||
return patchAttributes(vertex, geometry, tessControl, tessEval, fragment, inputs, false);
|
||||
}
|
||||
|
||||
public static Map<PatchShaderType, String> patchAttributes(String vertex, String geometry, String fragment, InputAvailability inputs) {
|
||||
|
||||
+6
-1
@@ -7,12 +7,14 @@ import net.coderbot.iris.shaderpack.texture.TextureStage;
|
||||
public class AttributeParameters extends Parameters {
|
||||
public final boolean hasGeometry;
|
||||
public final InputAvailability inputs;
|
||||
public final boolean scrollGlint;
|
||||
// WARNING: adding new fields requires updating hashCode and equals methods!
|
||||
|
||||
public AttributeParameters(Patch patch, boolean hasGeometry, InputAvailability inputs) {
|
||||
public AttributeParameters(Patch patch, boolean hasGeometry, InputAvailability inputs, boolean scrollGlint) {
|
||||
super(patch, null);
|
||||
this.hasGeometry = hasGeometry;
|
||||
this.inputs = inputs;
|
||||
this.scrollGlint = scrollGlint;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -25,6 +27,7 @@ public class AttributeParameters extends Parameters {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + (hasGeometry ? 1231 : 1237);
|
||||
result = prime * result + (scrollGlint ? 1231 : 1237);
|
||||
result = prime * result + ((inputs == null) ? 0 : inputs.hashCode());
|
||||
return result;
|
||||
}
|
||||
@@ -40,6 +43,8 @@ public class AttributeParameters extends Parameters {
|
||||
AttributeParameters other = (AttributeParameters) obj;
|
||||
if (hasGeometry != other.hasGeometry)
|
||||
return false;
|
||||
if (scrollGlint != other.scrollGlint)
|
||||
return false;
|
||||
if (inputs == null) {
|
||||
if (other.inputs != null)
|
||||
return false;
|
||||
|
||||
@@ -77,11 +77,22 @@ public class IdMap {
|
||||
private static final Pattern LEGACY_DIRECTIVE_PATTERN = Pattern.compile(
|
||||
"(?m)^\\s*#\\s*(?:if|elif|ifdef|ifndef)\\b[^\\n]*\\bMC_VERSION\\b[^\\n]*\\b10710\\b");
|
||||
|
||||
/**
|
||||
* Detects whether a pack's block.properties has a dedicated 1.7.10 section.
|
||||
*/
|
||||
public static boolean detectLegacySection(Path shaderPath) {
|
||||
String rawBlockProperties = readProperties(shaderPath, "block.properties");
|
||||
return rawBlockProperties != null && LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find();
|
||||
}
|
||||
|
||||
public static String modernFallbackMcVersion() {
|
||||
return AngelicaConfig.modernFallbackMcVersion > 0
|
||||
? String.valueOf(AngelicaConfig.modernFallbackMcVersion) : "260101";
|
||||
}
|
||||
|
||||
IdMap(Path shaderPath, ShaderPackOptions shaderPackOptions, Iterable<StringPair> environmentDefines) {
|
||||
// Check if block.properties has a dedicated 1.7.10 section
|
||||
String rawBlockProperties = readProperties(shaderPath, "block.properties");
|
||||
this.hasLegacySection = rawBlockProperties != null
|
||||
&& LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find();
|
||||
this.hasLegacySection = detectLegacySection(shaderPath);
|
||||
|
||||
Iterable<StringPair> resolvedDefines;
|
||||
if (this.hasLegacySection) {
|
||||
@@ -100,9 +111,7 @@ public class IdMap {
|
||||
}
|
||||
}
|
||||
|
||||
String modernVersion = AngelicaConfig.modernFallbackMcVersion > 0
|
||||
? String.valueOf(AngelicaConfig.modernFallbackMcVersion) : "260101";
|
||||
modernDefines.add(new StringPair("MC_VERSION", modernVersion));
|
||||
modernDefines.add(new StringPair("MC_VERSION", modernFallbackMcVersion()));
|
||||
resolvedDefines = modernDefines;
|
||||
|
||||
loadProperties(shaderPath, "block.properties", shaderPackOptions, modernDefines).ifPresent(blockProperties -> {
|
||||
|
||||
@@ -55,7 +55,7 @@ public class PackRenderTargetDirectives {
|
||||
|
||||
renderTargetSettings.forEach((index, settings) -> {
|
||||
if (settings.shouldClear()) {
|
||||
buffersToBeCleared.add(index.intValue());
|
||||
buffersToBeCleared.add(index);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.coderbot.iris.shaderpack;
|
||||
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.features.FeatureFlags;
|
||||
import net.coderbot.iris.gl.blending.BlendModeOverride;
|
||||
import net.coderbot.iris.shaderpack.include.AbsolutePackPath;
|
||||
import net.coderbot.iris.shaderpack.loading.ProgramId;
|
||||
@@ -529,7 +530,8 @@ public class ProgramSet {
|
||||
|
||||
String tessControlSource = null;
|
||||
String tessEvalSource = null;
|
||||
if (programSet.pack.hasFeature(net.coderbot.iris.features.FeatureFlags.TESSELLATION_SHADERS)) {
|
||||
if (programSet.pack.hasFeature(FeatureFlags.TESSELLATION_SHADERS)
|
||||
&& FeatureFlags.TESSELLATION_SHADERS.isUsable()) {
|
||||
AbsolutePackPath tessControlPath = directory.resolve(program + ".tcs");
|
||||
tessControlSource = sourceProvider.apply(tessControlPath);
|
||||
|
||||
@@ -547,6 +549,10 @@ public class ProgramSet {
|
||||
private static ComputeSource readComputeSource(AbsolutePackPath directory,
|
||||
Function<AbsolutePackPath, String> sourceProvider, String program,
|
||||
ProgramSet programSet) {
|
||||
if (!FeatureFlags.COMPUTE_SHADERS.isUsable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AbsolutePackPath computePath = directory.resolve(program + ".csh");
|
||||
String computeSource = sourceProvider.apply(computePath);
|
||||
|
||||
|
||||
@@ -207,8 +207,16 @@ public class ShaderPack {
|
||||
this.shaderPackOptions = new ShaderPackOptions(graph, changedConfigs);
|
||||
graph = this.shaderPackOptions.getIncludes();
|
||||
|
||||
final String mcVersionOverride = IdMap.detectLegacySection(root) ? null : IdMap.modernFallbackMcVersion();
|
||||
|
||||
List<StringPair> finalEnvironmentDefines = new ArrayList<>();
|
||||
environmentDefines.forEach(finalEnvironmentDefines::add);
|
||||
for (StringPair define : environmentDefines) {
|
||||
if (mcVersionOverride != null && "MC_VERSION".equals(define.getKey())) {
|
||||
finalEnvironmentDefines.add(new StringPair("MC_VERSION", mcVersionOverride));
|
||||
} else {
|
||||
finalEnvironmentDefines.add(define);
|
||||
}
|
||||
}
|
||||
for (FeatureFlags flag : FeatureFlags.values()) {
|
||||
boolean usable = flag.isUsable();
|
||||
Iris.logger.info("FeatureFlag {} usable: {}", flag.name(), usable);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.coderbot.iris.shaderpack.loading;
|
||||
|
||||
public enum ProgramArrayId {
|
||||
Setup(ProgramGroup.Setup, 100),
|
||||
Begin(ProgramGroup.Begin, 100),
|
||||
ShadowComposite(ProgramGroup.ShadowComposite, 99),
|
||||
Prepare(ProgramGroup.Prepare, 99),
|
||||
Deferred(ProgramGroup.Deferred, 99),
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package net.coderbot.iris.shaderpack.loading;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum ProgramGroup {
|
||||
Setup("setup"),
|
||||
Begin("begin"),
|
||||
Shadow("shadow"),
|
||||
ShadowComposite("shadowcomp"),
|
||||
Prepare("prepare"),
|
||||
@@ -17,7 +22,4 @@ public enum ProgramGroup {
|
||||
this.baseName = baseName;
|
||||
}
|
||||
|
||||
public String getBaseName() {
|
||||
return baseName;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package net.coderbot.iris.shaderpack.materialmap;
|
||||
|
||||
import net.coderbot.iris.Iris;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -263,7 +263,19 @@ public final class PropertiesTokenizer {
|
||||
);
|
||||
}
|
||||
|
||||
final String[] splitStates = baseEntry.split(":");
|
||||
// Drop empty segments produced by consecutive colons (like "betterendforge::lumecorn")
|
||||
final String[] splitStates = Arrays.stream(baseEntry.split(":"))
|
||||
.filter(s -> !s.isEmpty())
|
||||
.toArray(String[]::new);
|
||||
|
||||
if (splitStates.length == 0) {
|
||||
return new ParsedBlockIdentifier(
|
||||
new NamespacedId("minecraft", baseEntry),
|
||||
Collections.emptySet(),
|
||||
Collections.emptyMap(),
|
||||
nbtProperties
|
||||
);
|
||||
}
|
||||
|
||||
// Vanilla block (minecraft namespaced)
|
||||
if (splitStates.length == 1) {
|
||||
@@ -277,7 +289,7 @@ public final class PropertiesTokenizer {
|
||||
|
||||
// Two-segment with no metas/state properties: "minecraft:stone"
|
||||
if (splitStates.length == 2
|
||||
&& !StringUtils.isNumeric(splitStates[1].substring(0, 1))
|
||||
&& !startsWithDigit(splitStates[1])
|
||||
&& !splitStates[1].contains("=")) {
|
||||
return new ParsedBlockIdentifier(
|
||||
new NamespacedId(splitStates[0], splitStates[1]),
|
||||
@@ -296,7 +308,7 @@ public final class PropertiesTokenizer {
|
||||
id = new NamespacedId("minecraft", splitStates[0]);
|
||||
statesStart = 1;
|
||||
|
||||
} else if (StringUtils.isNumeric(splitStates[1].substring(0, 1)) || splitStates[1].contains("=")) {
|
||||
} else if (startsWithDigit(splitStates[1]) || splitStates[1].contains("=")) {
|
||||
// "stone:0:something" or "stone:lit=true:something", likely will need this for snowy blocks in the future.
|
||||
id = new NamespacedId("minecraft", splitStates[0]);
|
||||
statesStart = 1;
|
||||
@@ -339,6 +351,10 @@ public final class PropertiesTokenizer {
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean startsWithDigit(String s) {
|
||||
return !s.isEmpty() && Character.isDigit(s.charAt(0));
|
||||
}
|
||||
|
||||
private static void appendCurrent(boolean inBracket, boolean readingValue,
|
||||
StringBuilder blockIdBuilder, StringBuilder nbtKey,
|
||||
StringBuilder nbtValue, char c) {
|
||||
|
||||
Loaded 100 of 279 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user