mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73f5556595 | ||
|
|
19bc7cbaed | ||
|
|
d189a644e2 | ||
|
|
94ba40ae4d | ||
|
|
961b23fd2d | ||
|
|
61b5301f1a | ||
|
|
0e539ae034 | ||
|
|
e581326641 | ||
|
|
c707cc51f1 | ||
|
|
7d890bf3e7 | ||
|
|
ffc324bab6 | ||
|
|
0dfffca88e | ||
|
|
a66db1517a | ||
|
|
269b7245d3 | ||
|
|
67f2484976 | ||
|
|
1c8687e6fd | ||
|
|
e6bbd155da | ||
|
|
2f64fef6a6 | ||
|
|
b0fd1ea46e | ||
|
|
6d8cbf9c2f | ||
|
|
096dd01446 | ||
|
|
22c9479946 | ||
|
|
b2f597a73a | ||
|
|
553dec5b6d | ||
|
|
bc8116006c | ||
|
|
bf9492dffb | ||
|
|
69aed42b89 | ||
|
|
ffe54f61b9 | ||
|
|
c86e1de9f6 | ||
|
|
6af401c834 | ||
|
|
b17bb42c49 | ||
|
|
3659b59db9 | ||
|
|
8c31eec525 | ||
|
|
166116d692 | ||
|
|
4b21bc77b6 | ||
|
|
8da561d3b6 | ||
|
|
413df76ad4 | ||
|
|
18fe05eda8 | ||
|
|
4fa8cfe3e4 | ||
|
|
b5dd1c04ce | ||
|
|
6c5eedb7c1 | ||
|
|
9d5eb01091 | ||
|
|
0d54a65026 | ||
|
|
8148d8f927 | ||
|
|
5c441e4418 | ||
|
|
78562f12d9 | ||
|
|
031456f224 | ||
|
|
ff35c684ca | ||
|
|
5c4ef022d5 | ||
|
|
4eb837a2c0 | ||
|
|
6570b6a0a5 | ||
|
|
493b641d56 | ||
|
|
3f4995466c | ||
|
|
56d7568fd3 | ||
|
|
4910cfd6d3 | ||
|
|
d1c6677caf | ||
|
|
8cf0296284 | ||
|
|
4fd98e5026 | ||
|
|
8e293f13fb | ||
|
|
2db20ad866 | ||
|
|
10a5b550f2 | ||
|
|
c1403dcf13 | ||
|
|
5d1fabfca9 | ||
|
|
555b2b7dd2 | ||
|
|
7017786a7c | ||
|
|
1c49649685 | ||
|
|
2bae79c4ee | ||
|
|
c22935660a | ||
|
|
79c488102b | ||
|
|
cc1353b73a | ||
|
|
d1e6dc8c94 | ||
|
|
ecbf8fea2f | ||
|
|
610209605f | ||
|
|
112aae3735 | ||
|
|
5844b1b5c1 | ||
|
|
05f01c17e6 | ||
|
|
f07d96316a | ||
|
|
5b704d1bc2 | ||
|
|
26135124f4 | ||
|
|
3abc89b7b2 | ||
|
|
03d908d658 | ||
|
|
76bf19c8f6 | ||
|
|
b27834cbd9 | ||
|
|
eb0ecb0483 | ||
|
|
9acb0e67a7 | ||
|
|
f4f9b56b17 | ||
|
|
2196439e6c | ||
|
|
dd68c64239 | ||
|
|
0decc515e4 | ||
|
|
b3e1e89c88 | ||
|
|
32bff85526 | ||
|
|
02fc95f239 | ||
|
|
07fd6d78a0 | ||
|
|
87aa853f81 | ||
|
|
ab69eea654 | ||
|
|
edccf63b66 | ||
|
|
80413fb3de | ||
|
|
f3ed9aa82b | ||
|
|
307c563b6d | ||
|
|
f92ce1d24e | ||
|
|
a7fac16d97 | ||
|
|
20c3caaed4 | ||
|
|
bf08cd50d9 | ||
|
|
805a266059 | ||
|
|
d3c9963280 | ||
|
|
4630bc4fd8 | ||
|
|
ce32a3d3df | ||
|
|
1991dc58ec | ||
|
|
ae19236e1a | ||
|
|
7ad368c937 | ||
|
|
5c86c87bcf | ||
|
|
99ba7fa775 | ||
|
|
5ae4ab8c5c | ||
|
|
ddd68e023f | ||
|
|
f78cfdd5fb | ||
|
|
183bcfbf41 | ||
|
|
502f30c0f9 | ||
|
|
ceff04adba | ||
|
|
b4dfdb8cc5 | ||
|
|
3064818477 | ||
|
|
27524e6a89 | ||
|
|
5d5e910fc0 | ||
|
|
f325fd7fc3 | ||
|
|
ec278d3d7a | ||
|
|
eef3d2f50a | ||
|
|
3da597ab18 | ||
|
|
0e79380618 | ||
|
|
b639d5c646 | ||
|
|
0d209d426d | ||
|
|
bf24084033 | ||
|
|
9267ecc735 | ||
|
|
dcc8d8deed | ||
|
|
5dc4f1fab8 | ||
|
|
d3cea1bfe4 | ||
|
|
5db2b248d7 | ||
|
|
21ab526284 | ||
|
|
4c8ea679ef | ||
|
|
2317379df2 | ||
|
|
24dae49c7f | ||
|
|
865de3cbab | ||
|
|
9c45549e35 | ||
|
|
2a20198292 | ||
|
|
f0b68f93af | ||
|
|
dd1d3aa518 | ||
|
|
c9633fd745 | ||
|
|
e2e94b2958 | ||
|
|
3046b043a0 | ||
|
|
8a6c8035db | ||
|
|
2b15370c00 | ||
|
|
3ca9e32191 | ||
|
|
efbca29381 | ||
|
|
9c1b6c75ff | ||
|
|
fa91b70528 | ||
|
|
61de8385ca | ||
|
|
962e7527a4 | ||
|
|
2ab2713fe9 | ||
|
|
7cf652cd4e | ||
|
|
48eba2fd96 | ||
|
|
356ba376fe | ||
|
|
ef6b0742a9 | ||
|
|
787929d0bd | ||
|
|
0e1fd9d0e9 | ||
|
|
58938e82a7 | ||
|
|
be6c8c85b5 | ||
|
|
39bca37d0d | ||
|
|
1c61be04a7 | ||
|
|
7a97cd9386 | ||
|
|
5b09bde7a0 | ||
|
|
edc0081b7c | ||
|
|
913256b032 | ||
|
|
e1f5083d3e | ||
|
|
b55e693ad2 | ||
|
|
21c01a1aa4 | ||
|
|
83a26b2291 | ||
|
|
7e44c1efcb | ||
|
|
155f209db5 | ||
|
|
c4a6c165a0 | ||
|
|
624fe6b453 | ||
|
|
fd45fda0ab | ||
|
|
00fc97a611 | ||
|
|
445dc487b9 | ||
|
|
f43fbd4d9f | ||
|
|
dbabc4b3dc | ||
|
|
e01097264b | ||
|
|
0a922f3129 | ||
|
|
4a2dcd0b22 | ||
|
|
0db691db0b | ||
|
|
e5cd1372e7 | ||
|
|
fbd07cc575 | ||
|
|
91f2cf9dfb | ||
|
|
34a6967c71 | ||
|
|
9c295a1e22 | ||
|
|
9fd02900ef | ||
|
|
27e9a4d4fd | ||
|
|
dfc08053d5 | ||
|
|
1af03fa369 | ||
|
|
3ae3b2527d | ||
|
|
fd2a14a000 | ||
|
|
9f0f6a6232 | ||
|
|
2942c7eb84 | ||
|
|
47762f921e | ||
|
|
bdc4431760 | ||
|
|
7e1beadfb5 | ||
|
|
59a4b84023 | ||
|
|
8d53e25e0a | ||
|
|
bbf83db379 | ||
|
|
ec82280b9b | ||
|
|
d7588e4c14 | ||
|
|
54c99386d4 | ||
|
|
465abf889a | ||
|
|
4acb83be6a | ||
|
|
80f9724ee6 | ||
|
|
8cf55bb216 | ||
|
|
3ebc8868c7 | ||
|
|
0938e6d975 | ||
|
|
d3022a40d2 | ||
|
|
2645c803f9 | ||
|
|
d91c356a99 | ||
|
|
189ff4a80f | ||
|
|
ffb4ca9472 | ||
|
|
7d8c8d306d | ||
|
|
117ff0cacf | ||
|
|
83fdd57534 | ||
|
|
951be04e9a | ||
|
|
0ad69f3433 | ||
|
|
0dabecc784 | ||
|
|
9b3fad4c6d | ||
|
|
e1e8256861 | ||
|
|
e65c01c2a3 | ||
|
|
b9de4df18e | ||
|
|
a5d31cd1b6 | ||
|
|
29f8d718cc | ||
|
|
52b0db2566 | ||
|
|
e9eb45f4f2 | ||
|
|
92e28a4cd5 | ||
|
|
f46a86889c | ||
|
|
c827d446d5 | ||
|
|
00ef355ce8 | ||
|
|
1282056fff | ||
|
|
6c422d47a2 | ||
|
|
057ea8035c | ||
|
|
3ea47c8385 | ||
|
|
306d732d81 | ||
|
|
a483669579 | ||
|
|
53996500d7 | ||
|
|
04dd8ce6c7 | ||
|
|
8c9edfa4c3 | ||
|
|
3f44195756 | ||
|
|
c98f056d74 | ||
|
|
7e25f5d03e | ||
|
|
b5553654d0 | ||
|
|
a3fffb47be | ||
|
|
09cc557be0 | ||
|
|
aff5ff7e7b | ||
|
|
e7d08a53b6 | ||
|
|
3b2a0af5c4 | ||
|
|
8c44e79990 | ||
|
|
d2e491ed47 | ||
|
|
bccc5b00ca | ||
|
|
5ed26f3af7 | ||
|
|
9e7d19b883 | ||
|
|
5f61e387fb | ||
|
|
70cfa2ae90 | ||
|
|
37717bd4db | ||
|
|
b179340a0f | ||
|
|
465e36148c | ||
|
|
a3f0efd629 | ||
|
|
2386d630c5 | ||
|
|
6a784d8d3b | ||
|
|
fe9028d300 | ||
|
|
4c5e5df290 | ||
|
|
39ee364418 | ||
|
|
4c9cae5e0b | ||
|
|
11b53c9f8f | ||
|
|
c769a08a4e | ||
|
|
90dac9f39d |
No files matched your search
@@ -20,3 +20,9 @@ indent_size = 2
|
||||
|
||||
[*.lang]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.java]
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_class_count_to_use_import_on_demand = 999
|
||||
ij_java_names_count_to_use_import_on_demand = 999
|
||||
ij_java_packages_to_use_import_on_demand = empty
|
||||
@@ -0,0 +1,14 @@
|
||||
# Description of 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"
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds the libTracyClient shared library for the tracy-client module.
|
||||
set -euo pipefail
|
||||
|
||||
TRACY_VERSION="${TRACY_VERSION:-v0.14.0}"
|
||||
TRACY_VERSION_BARE="${TRACY_VERSION#v}"
|
||||
OUT_DIR="${OUT_DIR:-dist/tracy-natives}"
|
||||
TRACY_REPO="https://github.com/wolfpld/tracy"
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
case "$(uname -s)" in
|
||||
Darwin) PLAT_OS="macos" ;;
|
||||
Linux) PLAT_OS="linux" ;;
|
||||
*) PLAT_OS="windows" ;;
|
||||
esac
|
||||
case "$(uname -m)" in
|
||||
arm64|aarch64) PLAT_ARCH="arm64" ;;
|
||||
*) PLAT_ARCH="x64" ;;
|
||||
esac
|
||||
RES_DIR="${RES_DIR:-$REPO_ROOT/tracy-client/src/main/resources/natives/tracy/$TRACY_VERSION_BARE/$PLAT_OS-$PLAT_ARCH}"
|
||||
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||
|
||||
if [[ -n "${TRACY_SRC:-}" ]]; then
|
||||
SRC_DIR="$WORK_DIR/tracy"
|
||||
git clone --shared --no-checkout "$TRACY_SRC" "$SRC_DIR"
|
||||
git -C "$SRC_DIR" checkout --detach "$TRACY_VERSION"
|
||||
else
|
||||
SRC_DIR="$WORK_DIR/tracy"
|
||||
git clone --depth 1 --branch "$TRACY_VERSION" "$TRACY_REPO" "$SRC_DIR"
|
||||
fi
|
||||
|
||||
BUILD_DIR="$WORK_DIR/build"
|
||||
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-11.0}"
|
||||
|
||||
cmake -S "$REPO_ROOT/tracy-client/src/main/native" -B "$BUILD_DIR" \
|
||||
-DTRACY_SRC_DIR="$SRC_DIR" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DTRACY_STATIC=OFF \
|
||||
-DTRACY_ENABLE=ON \
|
||||
-DTRACY_ON_DEMAND=ON \
|
||||
-DTRACY_DELAYED_INIT=ON \
|
||||
-DTRACY_MANUAL_LIFETIME=ON \
|
||||
-DTRACY_NO_SAMPLING=ON \
|
||||
-DTRACY_NO_SYSTEM_TRACING=ON \
|
||||
-DTRACY_NO_CRASH_HANDLER=ON \
|
||||
-DTRACY_LTO=OFF
|
||||
cmake --build "$BUILD_DIR" --config Release --parallel
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
for NAME in libTracyClient.so libTracyClient.dylib TracyClient.dll; do
|
||||
LIB="$(find "$BUILD_DIR" -name "$NAME" | head -1)"
|
||||
if [[ -n "$LIB" ]]; then
|
||||
cp -L "$LIB" "$OUT_DIR/$NAME"
|
||||
cp "$SRC_DIR/LICENSE" "$OUT_DIR/LICENSE"
|
||||
mkdir -p "$RES_DIR"
|
||||
cp -L "$LIB" "$RES_DIR/$NAME"
|
||||
cp "$SRC_DIR/LICENSE" "$RES_DIR/LICENSE"
|
||||
echo "built: $OUT_DIR/$NAME ($TRACY_VERSION), staged: $RES_DIR/$NAME"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
echo "error: built library not found under $BUILD_DIR" >&2
|
||||
exit 1
|
||||
@@ -0,0 +1,81 @@
|
||||
name: Build Tracy natives
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "tracy-v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publish:
|
||||
description: Publish angelica-tracy.jar to the GTNH maven
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [linux-x64, windows-x64, macos-x64, macos-arm64]
|
||||
include:
|
||||
- platform: linux-x64
|
||||
os: ubuntu-22.04
|
||||
- platform: windows-x64
|
||||
os: windows-2025
|
||||
- platform: macos-x64
|
||||
os: macos-15-intel
|
||||
- platform: macos-arm64
|
||||
os: macos-15
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Build libTracyClient
|
||||
shell: bash
|
||||
env:
|
||||
OUT_DIR: dist/tracy-natives
|
||||
run: |
|
||||
export TRACY_VERSION="v$(sed -nE 's/^tracy = "(.*)"$/\1/p' gradle/libs.versions.toml)"
|
||||
bash .github/scripts/build-tracy-natives.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tracy-natives-${{ matrix.platform }}
|
||||
path: dist/tracy-natives/*
|
||||
if-no-files-found: error
|
||||
|
||||
assemble:
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- uses: gradle/actions/setup-gradle@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: tracy-natives-*
|
||||
path: staging
|
||||
- name: Stage natives into tracy-client resources
|
||||
shell: bash
|
||||
run: |
|
||||
version="$(sed -nE 's/^tracy = "(.*)"$/\1/p' gradle/libs.versions.toml)"
|
||||
for d in staging/tracy-natives-*; do
|
||||
dest="tracy-client/src/main/resources/natives/tracy/$version/${d#staging/tracy-natives-}"
|
||||
mkdir -p "$dest"
|
||||
cp "$d"/* "$dest/"
|
||||
done
|
||||
find tracy-client/src/main/resources/natives -type f
|
||||
- name: Build angelica-tracy.jar
|
||||
run: ./gradlew :tracy-client:distJar
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: angelica-tracy
|
||||
path: tracy-client/build/libs/angelica-tracy-*.jar
|
||||
if-no-files-found: error
|
||||
- name: Publish to GTNH maven
|
||||
if: github.ref_type == 'tag' || inputs.publish
|
||||
env:
|
||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||
run: ./gradlew -PpublishTracy=true :tracy-client:publish
|
||||
@@ -3,7 +3,7 @@ name: Release tagged build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ '*' ]
|
||||
tags: [ '*', '!tracy-v*' ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -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
|
||||
+10
-2
@@ -1,10 +1,11 @@
|
||||
.gradle
|
||||
.kotlin/
|
||||
.settings
|
||||
/.idea/
|
||||
/.vs/
|
||||
/.vscode/
|
||||
/run/
|
||||
/build/
|
||||
run/
|
||||
build/
|
||||
/eclipse/
|
||||
.classpath
|
||||
.project
|
||||
@@ -12,6 +13,7 @@
|
||||
/config/
|
||||
/crash-reports/
|
||||
/logs/
|
||||
tmp/
|
||||
options.txt
|
||||
/saves/
|
||||
usernamecache.json
|
||||
@@ -37,3 +39,9 @@ addon.local.gradle.kts
|
||||
addon.late.local.gradle
|
||||
addon.late.local.gradle.kts
|
||||
local/
|
||||
tracy-client/src/main/resources/natives/
|
||||
tracy-client/angelica/
|
||||
dist/tracy-natives/
|
||||
*.tracy
|
||||
|
||||
tracy-client/angelica/
|
||||
@@ -0,0 +1,57 @@
|
||||
# AI Usage Policy
|
||||
|
||||
🌐 **Languages:** [English](AI_POLICY.md) | [Українська](AI_POLICY_uk.md)
|
||||
|
||||
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
|
||||
@@ -0,0 +1,56 @@
|
||||
# Політика використання ШІ
|
||||
|
||||
🌐 **Мови:** [English](AI_POLICY.md) | [Українська](AI_POLICY_uk.md)
|
||||
|
||||
Цей проєкт має суворі правила щодо використання штучного інтелекту (ШІ):
|
||||
|
||||
* **Людина, яка контролює процес, повинна повністю розуміти весь код.**
|
||||
Якщо ви не можете пояснити, що роблять ваші зміни та як вони взаємодіють із
|
||||
більшою системою без допомоги інструментів ШІ, не пропонуйте ці зміни.
|
||||
|
||||
* **Обговорення та задачі можуть створюватися за допомогою ШІ, але повинні перебувати
|
||||
під повним контролем людини.** Це означає, що будь-який контент, згенерований за допомогою ШІ,
|
||||
перед надсиланням має бути перевірений *та відредагований* людиною.
|
||||
Автор повинен сам розібратися в питанні та відсіяти все зайве.
|
||||
|
||||
* **Медіаконтент, згенерований ШІ, заборонений (текстури, відео, аудіо тощо).**
|
||||
Текст і код є єдиним контентом, згенерованим ШІ, який дозволено використовувати
|
||||
згідно з іншими правилами цієї політики.
|
||||
|
||||
* **Повідомлення до комітів та описи PR повинні бути написані у вашому власному стилі.**
|
||||
Коміти, повністю або частково створені ШІ, заборонені, включно з тегами `Co-authored-by` з вказівкою ШІ.
|
||||
|
||||
* **Подавайте виправлення лише для тих багів, із якими ви особисто зіткнулися в грі.**
|
||||
Якщо ви особисто не стикалися з багом, не відтворили його власноруч і ще не знаєте
|
||||
добре відповідний код, не натравлюйте на нього ШІ, щоб потім перенаправити результат нам.
|
||||
Патчі, написані для проблем, із якими автор особисто ніколи не стикався, зазвичай
|
||||
виправляють не те, що потрібно, та/або створюють нові баги.
|
||||
|
||||
* **Стиль та суть письмового контенту повинні належати вам.**
|
||||
Для документації та особливо для квестів тон і думка, яку ви намагаєтеся донести, мають
|
||||
виходити від вас, а не від ШІ. Ви можете використовувати ШІ для покращення формулювань
|
||||
або вичитки, якщо бажаєте, але якщо суть і стиль не ваші, не пропонуйте такий контент.
|
||||
|
||||
* **За зловживання ШІ можна отримати блокування.** Користувачі, які створюють
|
||||
низькоякісний контент, очевидно згенерований ШІ (слоп), можуть бути заблоковані для подальших внесків.
|
||||
Ми залюбки допомагаємо розробникам-початківцям навчатися та рости, але якщо ви зацікавлені
|
||||
в цьому, не використовуйте ШІ, і ми вам допоможемо. Нам прикро, що недобросовісні
|
||||
користувачі ШІ зіпсували це для вас.
|
||||
|
||||
|
||||
## Тут працюють люди
|
||||
|
||||
Будь ласка, пам’ятайте, що цей проєкт підтримують люди.
|
||||
|
||||
Кожне обговорення, задача та запит на злиття читаються й перевіряються
|
||||
людьми (а іноді й машинами). Це точка перетину, де люди взаємодіють
|
||||
один з одним та виконують роботу. Неґречно та неповажно приходити сюди
|
||||
з неякісною, некваліфікованою та зробленою абияк роботою, оскільки це
|
||||
перекладає тягар перевірки на мейнтейнерів.
|
||||
|
||||
В ідеальному світі ШІ щоразу створював би високоякісну та точну роботу.
|
||||
Але сьогодні ця реальність залежить від користувача ШІ. І сьогодні чимало
|
||||
користувачів використовують ШІ неналежним чином. Тому, поки це не зміниться,
|
||||
ми змушені мати суворі правила, щоб захистити мейнтейнерів.
|
||||
|
||||
Створено на основі https://github.com/ghostty-org/ghostty/blob/main/AI_POLICY.md
|
||||
@@ -0,0 +1,17 @@
|
||||
# Angelica dev commands
|
||||
|
||||
# Build with headless display
|
||||
build:
|
||||
xvfb-run --server-args="-screen 0 1366x768x24" ./gradlew build
|
||||
|
||||
# Run tests with headless display
|
||||
test:
|
||||
xvfb-run --server-args="-screen 0 1366x768x24" ./gradlew test
|
||||
|
||||
# Build jar only (no tests)
|
||||
jar:
|
||||
./gradlew jar
|
||||
|
||||
# Clean build directory
|
||||
clean:
|
||||
./gradlew clean
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
<sup>**[CurseForge](https://www.curseforge.com/minecraft/mc-mods/angelica) | [Modrinth](https://modrinth.com/mod/angelica)**</sup>
|
||||
|
||||
🌐 **Languages:** [English](README.MD) | [Українська](README_uk.MD)
|
||||
|
||||
* The little Angle [sic] that supports shaders while saving your fps from a certain death -boubou
|
||||
* Is it obtuse, or accute? -Caedis
|
||||
* It's a cute angle -mitchej123
|
||||
@@ -23,7 +25,7 @@ Targets GL 3.3+ core profile
|
||||
|
||||
# Required Dependencies
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.9.0
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
|
||||
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
|
||||
|
||||
# Known (temporary) Incompatibilities
|
||||
@@ -50,6 +52,7 @@ Targets GL 3.3+ core profile
|
||||
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 - Threading issues, use 1.3.0+
|
||||
* Neodymium - Untested, but unlikely to be compatible. (Chunk Meshing is definitely incompatible, other features might work depending on config)
|
||||
* SmoothFont - Equivalent functionality added
|
||||
* ClientFixer - Use a resource pack like [GTNH-FTI-Standard-Font](https://github.com/Eldrinn-Elantey/GTNH-FTI-Standard-Font)
|
||||
|
||||
# Credits
|
||||
* ShadersMod
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
# Angelica<img src="img/logo-readme.png" align="right">
|
||||
|
||||
<sup>**[CurseForge](https://www.curseforge.com/minecraft/mc-mods/angelica) | [Modrinth](https://modrinth.com/mod/angelica)**</sup>
|
||||
|
||||
🌐 **Мови:** [English](README.MD) | [Українська](README_uk.MD)
|
||||
|
||||
* Маленький Кут [sic], що підтримує шейдери, водночас рятуючи ваші FPS від вірної смерті — boubou
|
||||
* Він тупий чи гострий? — Caedis
|
||||
* Це милий кут — mitchej123
|
||||
* Це янгол, який витягує вас із пекла OptiFine — Roadhog360
|
||||
|
||||
## **Бета-якість — Тут водяться дракони**
|
||||
* Шейдери — Бета
|
||||
* Complementary + Euphoria Patches є головною ціллю для сумісності
|
||||
* Sildurs, Seus та будь-які інші шейдери можуть працювати
|
||||
|
||||
## Базовий профіль
|
||||
Цільовим є базовий профіль GL 3.3+
|
||||
* Емуляція FFP через згенеровані GLSL-шейдери
|
||||
* Емуляція списків відображення
|
||||
* Програмні стеки матриць та керування станом через GLSM
|
||||
* Конвертація GL_QUADS → GL_TRIANGLES
|
||||
* Автоматична трансформація шейдерів з модів, які використовують вбудовані функції GLSL виключно для сумісності (`gl_ModelViewMatrix`, `texture2D` тощо)
|
||||
|
||||
|
||||
# Обов’язкові залежності
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
|
||||
* ПРИМІТКА: Деякі моди не є обов’язковими, але якщо вони встановлені, вимагається їхня конкретна версія — див. розділ `Постійна несумісність` нижче.
|
||||
|
||||
# Відома (тимчасова) несумісність
|
||||
* Forge Relocation — Наразі не є потокобезпечним; безпечно, якщо не використовується
|
||||
* ProjectRed Frames — Наразі не є потокобезпечним; безпечно, якщо не використовується
|
||||
|
||||
# Можлива несумісність
|
||||
* EndlessIDs — Коміти для сумісності були зроблені, може працювати, а може й ні — не повністю протестовано
|
||||
|
||||
# Відома несумісність
|
||||
* FalseTweaks — Наразі не планується (Шукаємо волонтерів) [Може працювати, якщо фактично вимкнути більшу частину мода]
|
||||
* RPLE — Наразі не планується (Шукаємо волонтерів)
|
||||
* LiteLoader — Встановіть [LiteloaderLoader](https://github.com/Midnight145/LiteloaderLoader)
|
||||
|
||||
# Постійна несумісність
|
||||
* OptiFine [Вимкнено, виправлятись не буде]
|
||||
* FastCraft [Вимкнено, виправлятись не буде]
|
||||
* BetterFPS [Вимкнено, виправлятись не буде]
|
||||
* OptimizationsAndTweaks [Вимкнено, виправлятись не буде]
|
||||
* [Amazing Trophies 1.2.1 і нижче](https://github.com/GTNewHorizons/Amazing-Trophies/releases) — Використовуйте 1.2.2+
|
||||
* [LWJGL3ify 1.5.4 і нижче](https://github.com/GTNewHorizons/lwjgl3ify/releases/) — Використовуйте найновішу доступну версію, 1.5.7+
|
||||
* [ArchaicFix 0.6.2 і нижче](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) — Використовуйте 0.7.0 або вище
|
||||
* [Hodgepodge 2.4.3 і нижче](https://github.com/GTNewHorizons/Hodgepodge/releases) — Використовуйте 2.4.5 або вище
|
||||
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 — Проблеми з багатопотоковістю, використовуйте 1.3.0+
|
||||
* Neodymium — Не перевірено, але навряд чи буде сумісним. (Побудова сітки чанків точно несумісна, інші функції можуть працювати залежно від конфігурації)
|
||||
* SmoothFont — Додано еквівалентний функціонал
|
||||
* ClientFixer — Використовуйте пакет ресурсів, як-от [GTNH-FTI-Standard-Font](https://github.com/Eldrinn-Elantey/GTNH-FTI-Standard-Font)
|
||||
|
||||
# Подяки
|
||||
* ShadersMod
|
||||
* Оригінальні ShadersMods: karyonix, sonic ether, id_miner, daxnitro
|
||||
* eigenraven, Caedis, glowredman та mitchej123 за конвертацію ASM у Mixin
|
||||
* eigenraven та vlaetansky за те, що змусили оригінальний ShadersMod знову компілюватися
|
||||
* ArchaicFix
|
||||
* Включає графічні покращення з [ArchaicFix](https://github.com/embeddedt/ArchaicFix/tree/4afb943751883b27a1f0aa7c5ba71698bc897e95) від embeddedt за ліцензією [LGPL](https://github.com/embeddedt/ArchaicFix/blob/main/LICENSE.md)
|
||||
* NotFine
|
||||
* Включає модифіковану версію [NotFine](https://github.com/jss2a98aj/NotFine/tree/a2652e24b5654f437fb7138f6d1f5e947a7d7125) від jss2a98aj за ліцензією LGPL
|
||||
* Iris Shaders
|
||||
* Команда [Iris](https://github.com/IrisShaders/Iris): coderbot, IMS212, Justsnoopy30, FoundationGames
|
||||
* Asek3 за порт Iris для Forge [Oculus](https://github.com/Asek3/Oculus/tree/839ce8eca8cf0c4f6b7a1322b906e5c99125ca69)
|
||||
* Бекпортований код за ліцензією LGPL
|
||||
* Sodium / Celeritas
|
||||
* JellySquid та команді CaffeineMC за створення Sodium
|
||||
* Embeddedt за Celeritas, що використовується за ліцензією LGPL 3.0
|
||||
* Reese's Sodium Options
|
||||
* FlashyReese за [Reese's Sodium Options](https://github.com/FlashyReese/reeses-sodium-options/tree/aa4ea67047cf53e54976e93c6bb1ab5ba16b142b) за ліцензією [MIT](https://github.com/FlashyReese/reeses-sodium-options/blob/aa4ea67047cf53e54976e93c6bb1ab5ba16b142b/LICENSE.txt)
|
||||
* Neodymium
|
||||
* Makamys за [Neodymium](https://github.com/makamys/Neodymium) — Різноманітний функціонал Tessellator -> Quad взято за ліцензією LGPL 3
|
||||
* MCPatcherForge
|
||||
* Оригінальний [MCPatcher](https://bitbucket.org/prupe/mcpatcher/src/master/): Paul Rupe
|
||||
* [Версія для Forge](https://github.com/mist475/MCPatcherForge): Mist475 за ліцензією LGPL 3.0 з типовими ctm-текстурами для скла та книжкових полиць від DarkBum за ліцензією CC-BY-SA
|
||||
* HUDCaching
|
||||
* Створено [Moulberry](https://github.com/Moulberry/MCHUDCaching), бекпортовано з мода [Patcher](https://github.com/Sk1erLLC/Patcher) від Alexdoru за ліцензією CC-BY-SA-NC 4.0
|
||||
* Angelica
|
||||
* mitchej123, Omni, Makamys, Embeddedt, NanoLive, Caedis, Cardinalstar, Alexdoru, Eigenraven, mist475, Clepto, kurrycat за допомогу в запуску та налаштуванні Angelica!
|
||||
* IMS, jss2a98aj, || dbp ||, roadhog360 за поради та моральну підтримку
|
||||
* Forge 1.12+
|
||||
* Деякі файли були взяті з Minecraft Forge за ліцензією LGPL 2.1 і позначені відповідно
|
||||
* FRAPI та Indigo
|
||||
* API для побудови моделей значною мірою натхненний/скопійований з Fabric; автори оригінального коду (upstream) можуть використовувати його за оригінальною ліцензією Apache 2.0,
|
||||
див. LICENSE у `com.gtnewhorizons.angelica.models`
|
||||
* Dynamic Lights
|
||||
* LambdAurora за [LambDynamicLights](https://github.com/LambdAurora/LambDynamicLights) за ліцензією MIT, та anthxnymc за порт для Forge [DynamicLightsReforged](https://github.com/anthxnymc/DynamicLightsReforged), також за ліцензією MIT
|
||||
* Embeddedt та Ferri_Arnus за [glsl-transformation-lib](https://github.com/TauMC/glsl-transformation-lib)
|
||||
* Також за роботу над [Monocle](https://github.com/ferriarnus/Monocle), який використовувався як еталон для реалізації glsl-transformation-lib
|
||||
* Код з обох glsl-transformation-lib та Monocle взято за їхніми відповідними ліцензіями LGPL 3.0
|
||||
* FalseTweaks
|
||||
* FalsePattern за [FalseTweaks](https://github.com/falsepattern/falsetweaks) — Оптимізації рендерингу предметів взято з FalseTweaks за ліцензією LGPL 3.0
|
||||
* Beddium
|
||||
* Venn та Falsepattern за [Beddium](https://github.com/vfx-dev/Beddium) — MinimapRenderer, SafeChunkTracker та деякі команди для налагодження адаптовано за ліцензією LGPL 3.0
|
||||
* [Mesa](https://gitlab.freedesktop.org/mesa/mesa) — Код портований з Mesa та/або натхненний нею — за ліцензією MIT
|
||||
* Розподілювач ID списків відображення портовано з [util_idalloc](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/util/u_idalloc.c) (Copyright 2017 Valve Corporation)
|
||||
* Генерація вершинних шейдерів FFP натхненна ffvertex_prog.c (Copyright 2007 VMware, Inc.)
|
||||
* Генерація фрагментних шейдерів FFP натхненна ff_fragment_shader.c (Copyright 2007, 2009 VMware, Inc. / 2010-2011 Intel Corporation)
|
||||
* Оптимізація параметрів туману натхненна st_nir_lower_fog.c (Copyright 2023 Google LLC) та prog_statevars.c (Copyright 1999-2007 Brian Paul)
|
||||
+222
-24
@@ -1,34 +1,129 @@
|
||||
import com.modrinth.minotaur.TaskModrinthUpload
|
||||
import net.darkhax.curseforgegradle.TaskPublishCurseForge
|
||||
import xyz.wagyourtail.jvmdg.gradle.flags.DowngradeFlags
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhconvention")
|
||||
}
|
||||
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dangelica.enableTestBlocks=true")
|
||||
extraRunJvmArguments.add("-Dangelica.dumpClass=true")
|
||||
// extraRunJvmArguments.add("-Dorg.lwjgl.util.Debug=true")
|
||||
tasks.withType<DowngradeJar>().configureEach { logLevel.set("FATAL") }
|
||||
tasks.withType<DowngradeFiles>().configureEach { logLevel.set("FATAL") }
|
||||
val lwjglDebug = false
|
||||
val gpuHud = false
|
||||
val renderdoc = false
|
||||
val rgp = false
|
||||
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dangelica.debug.testBlocks=true")
|
||||
extraRunJvmArguments.add("-Dangelica.tracy=true")
|
||||
extraRunJvmArguments.add("-Dangelica.sdlgpu.enable=true")
|
||||
extraRunJvmArguments.add("-Dangelica.unmappedGL=fail")
|
||||
extraRunJvmArguments.add("-Dorg.lwjgl.util.Debug=$lwjglDebug")
|
||||
|
||||
extraRunJvmArguments.add("-Dsun.net.client.defaultConnectTimeout=5000")
|
||||
extraRunJvmArguments.add("-Dsun.net.client.defaultReadTimeout=5000")
|
||||
// TODO: Remove once GTNHGradle includes shaderc/spvc in the default lwjgl3Bindings list
|
||||
lwjgl3Bindings.addAll("shaderc", "spvc")
|
||||
lwjgl3Version = libs.versions.lwjgl3.get()
|
||||
// extraRunJvmArguments.addAll("-Dlegacy.debugClassLoadingSave=true")
|
||||
// extraRunJvmArguments.addAll("-Drfb.dumpLoadedClasses=true", "-Drfb.dumpLoadedClassesPerTransformer=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.redirectorLogspam=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.debug.redirectorLogspam=true")
|
||||
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events("passed", "skipped", "failed")
|
||||
}
|
||||
// On macOS ARM64, use an ARM64-compatible JVM for tests
|
||||
val isMacOsArm64 = System.getProperty("os.name").lowercase().contains("mac") &&
|
||||
System.getProperty("os.arch") == "aarch64"
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
if (isMacOsArm64) {
|
||||
vendor = JvmVendorSpec.AZUL
|
||||
// Forward -Dangelica.* from the gradle invocation to the client
|
||||
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
|
||||
for ((key, value) in System.getProperties()) {
|
||||
val name = key.toString()
|
||||
if (name.startsWith("angelica.")) {
|
||||
jvmArgs("-D$name=$value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gpuHud) {
|
||||
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isMacOsX) {
|
||||
environment("MTL_HUD_ENABLED", "1")
|
||||
} else {
|
||||
environment("MANGOHUD", "1")
|
||||
environment("LD_PRELOAD", "/usr/\$LIB/mangohud/libMangoHud_shim.so")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
configurations.all {
|
||||
exclude(group = "com.github.GTNewHorizons", module = "Angelica")
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(module("com.github.GTNewHorizons:lwjgl3ify"))
|
||||
.using(module("com.github.GTNewHorizons:lwjgl3ify:${libs.versions.lwjgl3ify.get()}"))
|
||||
}
|
||||
}
|
||||
|
||||
// lwjgl3ify pulls a pinned lwjgl 3.4.2 snapshot transitively from here.
|
||||
repositories {
|
||||
maven {
|
||||
name = "Maven Central Snapshots"
|
||||
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
|
||||
content { includeGroup("org.lwjgl") }
|
||||
}
|
||||
}
|
||||
|
||||
val osName = System.getProperty("os.name").lowercase()
|
||||
val isMacOs = org.gradle.internal.os.OperatingSystem.current().isMacOsX
|
||||
val isMacOsArm64 = isMacOs && System.getProperty("os.arch") == "aarch64"
|
||||
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.startsWith("runClient") && name != "runClient") {
|
||||
if (isMacOs) {
|
||||
// SDL3 / Cocoa / Metal must initialize on the JVM main thread.
|
||||
jvmArgs("-XstartOnFirstThread")
|
||||
jvmArgs("-Dangelica.sdlgpu.encoderAssertions=" + if (lwjglDebug) "fatal" else "warn")
|
||||
if (lwjglDebug) {
|
||||
environment("METAL_DEVICE_WRAPPER_TYPE", "1")
|
||||
environment("METAL_DEBUG_ERROR_MODE", "0")
|
||||
environment("MTL_SHADER_VALIDATION", "1")
|
||||
environment("MTL_SHADER_VALIDATION_REPORT_TO_STDERR", "1")
|
||||
environment("MTL_DEBUG_LAYER", "1")
|
||||
environment("MallocScribble", "1")
|
||||
environment("MallocPreScribble", "1")
|
||||
environment("MallocGuardEdges", "1")
|
||||
environment("MallocStackLogging", "1")
|
||||
environment("MallocStackLoggingNoCompact", "1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linux runClient: RenderDoc frame capture (F12) OR RADV RGP/SQTT capture. Mutually exclusive; RGP wins
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.startsWith("runClient") && !isMacOs && !osName.contains("windows")) {
|
||||
environment("WAYLAND_DISPLAY", "")
|
||||
environment("XDG_SESSION_TYPE", "x11")
|
||||
val rgpFrame = project.findProperty("rgpFrame") as String?
|
||||
val rgpCapture = rgp || project.hasProperty("rgp") || rgpFrame != null
|
||||
if (rgpCapture) {
|
||||
environment("MESA_VK_TRACE", "rgp")
|
||||
if (rgpFrame != null) {
|
||||
environment("MESA_VK_TRACE_FRAME", rgpFrame)
|
||||
doFirst { logger.lifecycle("RGP: capturing frame $rgpFrame -> /tmp/java_*.rgp") }
|
||||
} else {
|
||||
environment("MESA_VK_TRACE_TRIGGER", "/tmp/angelica_rgp.trigger")
|
||||
doFirst { logger.lifecycle("RGP: touch /tmp/angelica_rgp.trigger (or press F1) to capture -> /tmp/java_*.rgp") }
|
||||
}
|
||||
} else if (renderdoc || project.hasProperty("renderdoc")) {
|
||||
val renderdocLib = file(project.findProperty("renderdocLib") as String? ?: "${System.getProperty("user.home")}/Downloads/renderdoc_1.44/lib/librenderdoc.so")
|
||||
if (renderdocLib.isFile) {
|
||||
environment("LD_PRELOAD", renderdocLib.absolutePath)
|
||||
doFirst { logger.lifecycle("RENDERDOC: LD_PRELOAD set, press F12 to capture") }
|
||||
} else {
|
||||
doFirst { logger.lifecycle("RENDERDOC: enabled but ${renderdocLib.absolutePath} not found; set -PrenderdocLib=<path>") }
|
||||
}
|
||||
}
|
||||
}
|
||||
dependsOn(tasks.extractNatives2)
|
||||
jvmArgs("-Djava.library.path=${tasks.extractNatives2.get().destinationFolder.asFile.get().path}")
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
@@ -47,22 +142,125 @@ tasks.register<Copy>("copyDependencies") {
|
||||
}
|
||||
|
||||
val embedOnly: Configuration by configurations
|
||||
val shadowImplementation: Configuration by configurations
|
||||
|
||||
// Downgrade embedOnly jars for the tests
|
||||
val downgradeEmbedOnlyForTest by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = embedOnly
|
||||
outputs.dir(temporaryDir)
|
||||
}
|
||||
|
||||
val stripModuleInfoFromShadow by tasks.registering(Jar::class) {
|
||||
dependsOn(shadowImplementation)
|
||||
from(shadowImplementation.map(::zipTree))
|
||||
exclude("module-info.class")
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
archiveFileName = "shadow-stripped.jar"
|
||||
}
|
||||
|
||||
val downgradeShadowImplForTest by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = files(stripModuleInfoFromShadow.map { it.archiveFile })
|
||||
outputs.dir(temporaryDir)
|
||||
}
|
||||
|
||||
val glsmTestFixtures: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
|
||||
}
|
||||
}
|
||||
|
||||
val downgradeGlsmTestFixturesForTest by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = glsmTestFixtures
|
||||
outputs.dir(temporaryDir)
|
||||
}
|
||||
|
||||
fun downgraded(task: TaskProvider<DowngradeFiles>): FileCollection = files(task.map { it.outputMap.values })
|
||||
|
||||
fun Test.configureAngelicaJava8() {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
if (isMacOsArm64) {
|
||||
vendor = JvmVendorSpec.AZUL
|
||||
}
|
||||
}
|
||||
dependsOn(tasks.extractNatives2)
|
||||
jvmArgs("-Djava.library.path=${tasks.extractNatives2.get().destinationFolder.asFile.get().path}")
|
||||
if (isMacOs) {
|
||||
jvmArgs("-Dapple.awt.UIElement=true")
|
||||
}
|
||||
testLogging { events("passed", "skipped", "failed") }
|
||||
|
||||
val swaps = mapOf(
|
||||
embedOnly to downgradeEmbedOnlyForTest,
|
||||
shadowImplementation to downgradeShadowImplForTest,
|
||||
glsmTestFixtures to downgradeGlsmTestFixturesForTest,
|
||||
)
|
||||
dependsOn(swaps.values)
|
||||
for ((original, downgrade) in swaps) {
|
||||
classpath = classpath.minus(original).plus(downgraded(downgrade))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
dependsOn(downgradeEmbedOnlyForTest)
|
||||
classpath = classpath
|
||||
.minus(embedOnly)
|
||||
.plus(files(downgradeEmbedOnlyForTest.map { it.outputCollection }))
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
|
||||
}
|
||||
|
||||
val downgradeRootMainClasses = tasks.named<DowngradeFiles>("downgradeMainClasses")
|
||||
val downgradeRootTestClasses = tasks.named<DowngradeFiles>("downgradeTestClasses")
|
||||
|
||||
val glCoreTest by tasks.registering(Test::class) {
|
||||
description = "Runs the gl-core tests in their own JVM with a real GL context."
|
||||
group = "verification"
|
||||
useJUnitPlatform { includeTags = setOf("gl-core") }
|
||||
|
||||
dependsOn(downgradeRootMainClasses, downgradeRootTestClasses)
|
||||
val rawMain = sourceSets["main"].output.classesDirs
|
||||
val rawTest = sourceSets["test"].output.classesDirs
|
||||
val downgradedTest = downgraded(downgradeRootTestClasses)
|
||||
|
||||
setTestClassesDirs(downgradedTest)
|
||||
classpath = downgradedTest
|
||||
.plus(downgraded(downgradeRootMainClasses))
|
||||
.plus(sourceSets["test"].runtimeClasspath.minus(rawMain).minus(rawTest))
|
||||
|
||||
configureAngelicaJava8()
|
||||
mustRunAfter(tasks.test)
|
||||
}
|
||||
|
||||
tasks.test { finalizedBy(glCoreTest) }
|
||||
|
||||
tasks.shadowJar {
|
||||
dependsOn(embedOnly)
|
||||
from(embedOnly.map(::zipTree))
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
mergeServiceFiles()
|
||||
|
||||
minimize {
|
||||
exclude(project(rootProject.path))
|
||||
exclude(project(":glsm"))
|
||||
exclude(project(":lwjgl3-backend"))
|
||||
exclude(project(":sdl-gpu"))
|
||||
exclude(dependency("org.taumc:.*:.*"))
|
||||
exclude(dependency("org.antlr:.*:.*"))
|
||||
}
|
||||
|
||||
relocate("com.mitchej123", "com.mitchej123")
|
||||
relocate("org.embeddedt", "org.embeddedt")
|
||||
relocate("com.gtnewhorizons.angelica.config", "com.gtnewhorizons.angelica.config")
|
||||
relocate("com.gtnewhorizons.angelica.glsm", "com.gtnewhorizons.angelica.glsm")
|
||||
relocate("com.gtnewhorizons.angelica.lwjgl3", "com.gtnewhorizons.angelica.lwjgl3")
|
||||
relocate("com.gtnewhorizons.angelica.sdlgpu", "com.gtnewhorizons.angelica.sdlgpu")
|
||||
}
|
||||
|
||||
tasks.withType<TaskPublishCurseForge>().configureEach {
|
||||
uploadArtifacts.forEach { it.addGameVersion("Client") }
|
||||
}
|
||||
|
||||
tasks.withType<TaskModrinthUpload>().configureEach {
|
||||
setDependsOn(dependsOn.filterNot { it == "build" } + "assemble")
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
// Re-export Gradle's generated typed catalog accessors so applied scripts (dependencies.gradle.kts) can use them.
|
||||
dependencies {
|
||||
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
|
||||
implementation(libs.asm.build)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") { from(files("../gradle/libs.versions.toml")) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import org.objectweb.asm.AnnotationVisitor
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.Opcodes
|
||||
import java.io.File
|
||||
|
||||
private const val LWJGL3_AWARE_DESC = "Lme/eigenraven/lwjgl3ify/api/Lwjgl3Aware;"
|
||||
|
||||
/**
|
||||
* Stamps @Lwjgl3Aware onto every class under [classesDir] - classes that already carry the annotation are left untouched.
|
||||
*/
|
||||
fun injectLwjgl3Aware(classesDir: File) {
|
||||
if (!classesDir.isDirectory) return
|
||||
|
||||
classesDir.walkTopDown()
|
||||
.filter { it.isFile && it.extension == "class" && it.name != "module-info.class" }
|
||||
.forEach { classFile ->
|
||||
val reader = ClassReader(classFile.readBytes())
|
||||
if (hasLwjgl3Aware(reader)) return@forEach
|
||||
|
||||
val writer = ClassWriter(reader, 0)
|
||||
reader.accept(object : ClassVisitor(Opcodes.ASM9, writer) {
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String?,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
super.visitAnnotation(LWJGL3_AWARE_DESC, true)?.visitEnd()
|
||||
}
|
||||
}, 0)
|
||||
classFile.writeBytes(writer.toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasLwjgl3Aware(reader: ClassReader): Boolean {
|
||||
var found = false
|
||||
reader.accept(object : ClassVisitor(Opcodes.ASM9) {
|
||||
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
|
||||
if (descriptor == LWJGL3_AWARE_DESC) found = true
|
||||
return null
|
||||
}
|
||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
||||
return found
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Add your dependencies here. Supported configurations:
|
||||
* - api("group:name:version:classifier"): if you use the types from this dependency in the public API of this mod
|
||||
* Available at runtime and compiletime for mods depending on this mod
|
||||
* - implementation("g:n:v:c"): if you need this for internal implementation details of the mod, but none of it is visible via the public API
|
||||
* Available at runtime but not compiletime for mods depending on this mod
|
||||
* - compileOnly("g:n:v:c"): if the mod you're building doesn't need this dependency during runtime at all, e.g. for optional mods
|
||||
* Not available at all for mods depending on this mod, only visible at compiletime for this mod
|
||||
* - compileOnlyApi("g:n:v:c"): like compileOnly, but also visible at compiletime for mods depending on this mod
|
||||
* Available at compiletime but not runtime for mods depending on this mod
|
||||
* - runtimeOnlyNonPublishable("g:n:v:c"): if you want to include a mod in this mod's runClient/runServer runs, but not publish it as a dependency
|
||||
* Not available at all for mods depending on this mod, only visible at runtime for this mod
|
||||
* - devOnlyNonPublishable("g:n:v:c"): a combination of runtimeOnlyNonPublishable and compileOnly for dependencies present at both compiletime and runtime,
|
||||
* but not published as Maven dependencies - useful for RFG-deobfuscated dependencies or local testing
|
||||
* - runtimeOnly("g:n:v:c"): if you don't need this at compile time, but want it to be present at runtime
|
||||
* Available at runtime for mods depending on this mod
|
||||
* - annotationProcessor("g:n:v:c"): mostly for java compiler plugins, if you know you need this, use it, otherwise don't worry
|
||||
* - testCONFIG("g:n:v:c") - replace CONFIG by one of the above (except api), same as above but for the test sources instead of main
|
||||
*
|
||||
* - shadowImplementation("g:n:v:c"): effectively the same as API, but the dependency is included in your jar under a renamed package name
|
||||
* Requires you to enable usesShadowedDependencies in gradle.properties
|
||||
*
|
||||
* - compile("g:n:v:c"): deprecated, replace with "api" (works like the old "compile") or "implementation" (can be more efficient)
|
||||
*
|
||||
* You can exclude transitive dependencies (dependencies of the chosen dependency) by appending { transitive = false } if needed,
|
||||
* but use this sparingly as it can break using your mod as another mod's dependency if you're not careful.
|
||||
*
|
||||
* To depend on obfuscated jars you can use `devOnlyNonPublishable(rfg.deobf("dep:spec:1.2.3"))` to fetch an obfuscated jar from maven,
|
||||
* or `devOnlyNonPublishable(rfg.deobf(project.files("libs/my-mod-jar.jar")))` to use a file.
|
||||
*
|
||||
* Gradle names for some of the configuration can be misleading, compileOnlyApi and runtimeOnly both get published as dependencies in Maven, but compileOnly does not.
|
||||
* The buildscript adds runtimeOnlyNonPublishable to also have a runtime dependency that's not published.
|
||||
*
|
||||
* For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph
|
||||
*/
|
||||
configurations {
|
||||
transformedMod // Mods that can be transformed - used for compiling angelica, but not necessary at runtime
|
||||
transformedMod.canBeConsumed = false
|
||||
transformedModCompileOnly
|
||||
transformedModCompileOnly.canBeConsumed = false
|
||||
|
||||
// Add the transformed mod dependencies to the compilation classpaths, but don't publish them in the Maven metadata
|
||||
compileClasspath.extendsFrom(transformedMod, transformedModCompileOnly)
|
||||
testCompileClasspath.extendsFrom(transformedMod, transformedModCompileOnly)
|
||||
|
||||
// Can be enabled for testing if desired
|
||||
// runtimeClasspath.extendsFrom(transformedMod)
|
||||
// testRuntimeClasspath.extendsFrom(transformedMod)
|
||||
|
||||
// Embed in JAR without relocation (compile only, shadowed into JAR)
|
||||
embedOnly
|
||||
embedOnly.canBeConsumed = false
|
||||
embedOnly.canBeResolved = true
|
||||
compileClasspath.extendsFrom(embedOnly)
|
||||
testCompileClasspath.extendsFrom(embedOnly)
|
||||
testRuntimeClasspath.extendsFrom(embedOnly)
|
||||
}
|
||||
|
||||
|
||||
// Exclude self-reference from all configurations - prevents transitive Angelica deps from resolving against this project
|
||||
configurations.all {
|
||||
exclude group: "com.github.GTNewHorizons", module: "Angelica"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.14:dev")
|
||||
|
||||
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.7") { transitive = false }
|
||||
|
||||
compileOnly("org.projectlombok:lombok:1.18.42") { transitive = false }
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.42")
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.9.36:dev")
|
||||
|
||||
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
|
||||
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-26.g5ee7d96-GTNH") {
|
||||
exclude module: "antlr4" // we only want to shadow the runtime module
|
||||
}
|
||||
shadowImplementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
compileOnly "org.apache.ant:ant:1.8.2"
|
||||
|
||||
// Celeritas (transitive=false: deps provided by GTNHLib/runtime)
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.2-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.2-GTNH") { transitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.35-GTNH:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
|
||||
|
||||
// Notfine Deps
|
||||
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.2-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:twilightforest:2.7.13:dev") { transitive = false }
|
||||
compileOnly(rfg.deobf('curse.maven:witchery-69673:2234410'))
|
||||
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.6-GTNH:dev") { transitive = false }
|
||||
compileOnly("com.github.GTNewHorizons:Natura:2.8.9:dev")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.8.4:dev")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.3:dev") { transitive = false }
|
||||
|
||||
compileOnly("com.falsepattern:chunkapi-mc1.7.10:0.5.1:api") { transitive = false }
|
||||
compileOnly("com.falsepattern:endlessids-mc1.7.10:1.5-beta0003:dev") { transitive = false }
|
||||
|
||||
compileOnly(rfg.deobf("curse.maven:extrautils-225561:2264383"))
|
||||
compileOnly(rfg.deobf("curse.maven:dynamiclights-227874:2337326"))
|
||||
|
||||
compileOnly("curse.maven:minefactory-reloaded-66672:2277486")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.10:dev")
|
||||
|
||||
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5593_H261"))
|
||||
|
||||
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
|
||||
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
|
||||
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
|
||||
compileOnly(rfg.deobf("curse.maven:et-futurum-requiem-441392:5873837"))
|
||||
|
||||
// For testing alternative splash screens — https://github.com/MalTeeez/ModernSplash/releases
|
||||
// devOnlyNonPublishable("com.github.MalTeeez:modernsplash:1.7.10-1.2.4:dev")
|
||||
|
||||
// Hodgepodge
|
||||
transformedMod("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev")
|
||||
|
||||
// Better Crashes
|
||||
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.1-GTNH:dev")
|
||||
|
||||
afterEvaluate {
|
||||
dependencies {
|
||||
testImplementation(platform('org.junit:junit-bom:5.14.1'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
testImplementation("org.mockito:mockito-core:3.+")
|
||||
testImplementation("org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209")
|
||||
|
||||
testImplementation('org.junit.platform:junit-platform-engine')
|
||||
testImplementation('org.junit.platform:junit-platform-reporting')
|
||||
testImplementation('org.junit.platform:junit-platform-launcher')
|
||||
}
|
||||
}
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Angelica's dependency declarations. Supported configurations:
|
||||
* - api("group:name:version:classifier"): if you use the types from this dependency in the public API of this mod
|
||||
* Available at runtime and compiletime for mods depending on this mod
|
||||
* - implementation("g:n:v:c"): if you need this for internal implementation details of the mod, but none of it is visible via the public API
|
||||
* Available at runtime but not compiletime for mods depending on this mod
|
||||
* - compileOnly("g:n:v:c"): if the mod you're building doesn't need this dependency during runtime at all, e.g. for optional mods
|
||||
* Not available at all for mods depending on this mod, only visible at compiletime for this mod
|
||||
* - runtimeOnlyNonPublishable("g:n:v:c"): if you want to include a mod in this mod's runClient/runServer runs, but not publish it as a dependency
|
||||
* - devOnlyNonPublishable("g:n:v:c"): a combination of runtimeOnlyNonPublishable and compileOnly, not published as a Maven dependency
|
||||
* - shadowImplementation("g:n:v:c"): like api, but included in your jar under a renamed package name
|
||||
* - embedOnly("g:n:v:c"): embedded in the jar without relocation
|
||||
*
|
||||
* Versions come from gradle/libs.versions.toml. Mod coordinates stay literal: nearly all carry a
|
||||
* :dev classifier, which a version catalog cannot express, and curse/modrinth "versions" are file ids.
|
||||
*
|
||||
* To depend on obfuscated jars use `rfg.deobf("dep:spec:1.2.3")`.
|
||||
*/
|
||||
import com.gtnewhorizons.retrofuturagradle.modutils.ModUtils
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
||||
|
||||
val libs = the<LibrariesForLibs>()
|
||||
val rfg = (dependencies as ExtensionAware).extensions.getByType<ModUtils.RfgDependencyExtension>()
|
||||
|
||||
val api = configurations["api"]
|
||||
val compileOnly = configurations["compileOnly"]
|
||||
val annotationProcessor = configurations["annotationProcessor"]
|
||||
val shadowImplementation = configurations["shadowImplementation"]
|
||||
val runtimeOnlyNonPublishable = configurations["runtimeOnlyNonPublishable"]
|
||||
val devOnlyNonPublishable = configurations["devOnlyNonPublishable"]
|
||||
|
||||
// Mods that can be transformed - used for compiling angelica, but not necessary at runtime
|
||||
val transformedMod by configurations.creating { isCanBeConsumed = false }
|
||||
val transformedModCompileOnly by configurations.creating { isCanBeConsumed = false }
|
||||
|
||||
// Embed in JAR without relocation (compile only, shadowed into JAR)
|
||||
val embedOnly by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
// Add the transformed mod dependencies to the compilation classpaths, but don't publish them in the Maven metadata
|
||||
configurations["compileClasspath"].extendsFrom(transformedMod, transformedModCompileOnly, embedOnly)
|
||||
configurations["testCompileClasspath"].extendsFrom(transformedMod, transformedModCompileOnly, embedOnly)
|
||||
configurations["testRuntimeClasspath"].extendsFrom(embedOnly)
|
||||
configurations["testRuntimeClasspath"].exclude(group = "com.github.GTNewHorizons", module = "lwjgl3ify")
|
||||
|
||||
// Prevents transitive Angelica deps from resolving against this project
|
||||
configurations.configureEach {
|
||||
exclude(group = "com.github.GTNewHorizons", module = "Angelica")
|
||||
}
|
||||
|
||||
DependencyHandlerScope.of(dependencies).apply {
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
|
||||
|
||||
runtimeOnlyNonPublishable(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-994-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BetterLoadingScreen:1.7.9-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BetterQuesting:3.8.72-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BuildCraft:7.1.63:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Botania:1.13.32-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:LogisticsPipes:1.5.32-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.55-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Mobs-Info:0.5.18-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:IguanaTweaksTConstruct:2.7.10:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenBlocks:1.12.18-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Schematica:1.12.6-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.93-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.4.1:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ironchest:6.1.13:dev")
|
||||
runtimeOnlyNonPublishable("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.19.30:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Draconic-Evolution:1.5.30-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.15-GTNH:dev")
|
||||
|
||||
runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
|
||||
|
||||
compileOnly(libs.retrofuturabootstrap) { isTransitive = false }
|
||||
// runtimeOnlyNonPublishable(libs.lwjgl3ify)
|
||||
|
||||
compileOnly(libs.lombok) { isTransitive = false }
|
||||
annotationProcessor(libs.lombok)
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly(libs.annotations)
|
||||
api(libs.gtnhlib) { artifact { classifier = "dev" } }
|
||||
|
||||
shadowImplementation(project(":glsm")) { isTransitive = false }
|
||||
shadowImplementation(project(":lwjgl3-backend")) { isTransitive = false }
|
||||
shadowImplementation(project(":sdl-gpu")) { isTransitive = false }
|
||||
runtimeOnlyNonPublishable(project(":tracy-client")) { isTransitive = false }
|
||||
shadowImplementation(libs.eventbus)
|
||||
|
||||
shadowImplementation(libs.jcpp) // Apache 2.0
|
||||
shadowImplementation(libs.glsl.transformation.lib) {
|
||||
exclude(module = "antlr4") // we only want to shadow the runtime module
|
||||
}
|
||||
shadowImplementation(libs.antlr4.runtime)
|
||||
compileOnly(libs.ant)
|
||||
|
||||
// Celeritas (isTransitive=false: deps provided by GTNHLib/runtime)
|
||||
embedOnly(libs.celeritas.common) { isTransitive = false }
|
||||
embedOnly(libs.celeritas.lwjgl2.service) { isTransitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.109-GTNH:dev")
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:CodeChickenCore:1.4.16:dev")
|
||||
|
||||
// BQ Testing
|
||||
// devOnlyNonPublishable("com.github.GTNewHorizons:BetterQuesting:3.8.74-GTNH:dev")
|
||||
|
||||
// Display List Testing
|
||||
/*
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-994-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BuildCraft:7.1.61:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:IguanaTweaksTConstruct:2.7.10:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenBlocks:1.12.18-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.47-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.93-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.4.1:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ironchest:6.1.13:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.19.30:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Botania:1.13.25-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Mobs-Info:0.5.18-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Draconic-Evolution:1.5.27-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.15-GTNH:dev")
|
||||
*/
|
||||
|
||||
//devOnlyNonPublishable("com.github.GTNewHorizons:worldedit-gtnh:v0.0.9")
|
||||
|
||||
// Notfine Deps
|
||||
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.21-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:twilightforest:2.7.36:dev") { isTransitive = false }
|
||||
compileOnly(rfg.deobf("curse.maven:witchery-69673:2234410"))
|
||||
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.93-GTNH:dev") { isTransitive = false }
|
||||
compileOnly("com.github.GTNewHorizons:Natura:2.8.19:dev")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.8.20:dev")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.3:dev") { isTransitive = false }
|
||||
|
||||
compileOnly("com.falsepattern:chunkapi-mc1.7.10:0.5.1:api") { isTransitive = false }
|
||||
compileOnly("com.falsepattern:endlessids-mc1.7.10:1.5-beta0003:dev") { isTransitive = false }
|
||||
|
||||
compileOnly(rfg.deobf("curse.maven:extrautils-225561:2264383"))
|
||||
compileOnly(rfg.deobf("curse.maven:dynamiclights-227874:2337326"))
|
||||
|
||||
compileOnly("curse.maven:minefactory-reloaded-66672:2277486")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.11:dev")
|
||||
|
||||
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5663_H261"))
|
||||
|
||||
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
|
||||
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
|
||||
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
|
||||
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:OpenBlocks:1.12.19-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:Chisel:2.17.31-GTNH:dev")
|
||||
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.6-GTNH:dev")
|
||||
|
||||
// For testing alternative splash screens - https://github.com/MalTeeez/ModernSplash/releases
|
||||
// devOnlyNonPublishable("com.github.MalTeeez:modernsplash:1.7.10-1.2.4:dev")
|
||||
|
||||
// Hodgepodge
|
||||
transformedMod("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev")
|
||||
|
||||
// Better Crashes
|
||||
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.5-GTNH:dev")
|
||||
|
||||
// Distant Horizons
|
||||
compileOnly("maven.modrinth:DistantHorizonsApi:5.1.0")
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
compileOnly("com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.5-alpha:dev") { isTransitive = false }
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
val glsmTestFixtures = configurations["glsmTestFixtures"]
|
||||
val testImplementation = configurations["testImplementation"]
|
||||
val testCompileOnly = configurations["testCompileOnly"]
|
||||
val testRuntimeOnly = configurations["testRuntimeOnly"]
|
||||
|
||||
DependencyHandlerScope.of(dependencies).apply {
|
||||
testImplementation(platform(libs.junit.bom))
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(libs.mockito.core)
|
||||
testImplementation(libs.lwjgl2)
|
||||
|
||||
testImplementation(libs.junit.platform.engine)
|
||||
testImplementation(libs.junit.platform.reporting)
|
||||
testImplementation(libs.junit.platform.launcher)
|
||||
|
||||
testImplementation(libs.archunit.junit5)
|
||||
|
||||
glsmTestFixtures(testFixtures(project(":glsm")) as ModuleDependency) { isTransitive = false }
|
||||
testCompileOnly(testFixtures(project(":glsm")))
|
||||
testImplementation("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
testRuntimeOnly(libs.fastutil)
|
||||
testRuntimeOnly(libs.joml)
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -1,4 +1,7 @@
|
||||
# A specification for models
|
||||
|
||||
🌐 **Languages:** [English](model-spec.md) | [Українська](model-spec_uk.md)
|
||||
|
||||
This is borrowed from [the unofficial Minecraft wiki](https://minecraft.wiki/w/Tutorials/Models) under the CC BY-NC-SA 3.0.
|
||||
|
||||
The folder `assets/<namespace>/models/block` holds the model files for all the specified variants. The names of the files can be changed, but must always correspond with the names used in the variant files.
|
||||
@@ -8,7 +11,7 @@ The folder `assets/<namespace>/models/block` holds the model files for all the s
|
||||
* Can be set to `"builtin/generated"` to use a model that is created out of the specified icon. Note that only the first layer is supported, and rotation can be achieved only by using block states files.
|
||||
* `[TAG_byte]` **ambientocclusion**: Whether to use [ambient occlusion](http://en.wikipedia.org/wiki/Ambient_occlusion) (`true` - default), or not (`false`). Note:only works on Parent file
|
||||
* `[TAG_object]` **display**: Holds the different places where item models are displayed.
|
||||
* `[TAG_compound]` ***Position***: Named `thirdperson_righthand`, `thirdperson_lefthand`, `firstperson_righthand`, `firstperson_lefthand`, `gui`, `head`, `ground`, or `fixed`. Place where an item model is displayed. Holds its rotation, translation and scale for the specified situation. '''fixed''' refers to item frames, while the rest are as their name states. Note that translations are applied to the model before rotations.
|
||||
* `[TAG_compound]` ***Position***: Named `thirdperson_righthand`, `thirdperson_lefthand`, `firstperson_righthand`, `firstperson_lefthand`, `gui`, `head`, `ground`, or `fixed`. Place where an item model is displayed. Holds its rotation, translation and scale for the specified situation. **'''fixed'''** refers to item frames, while the rest are as their name states. Note that translations are applied to the model before rotations.
|
||||
* `[TAG_list]` **rotation**: Specifies the rotation of the model according to the scheme `[x, y, z]`.
|
||||
* `[TAG_list]` **translation**: Specifies the position of the model according to the scheme `[x, y, z]`. The values are clamped between -80 and 80.
|
||||
* `[TAG_list]` **scale**: Specifies the scale of the model according to the scheme `[x, y, z]`. If the value is greater than 4, it is displayed as 4.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Специфікація моделей
|
||||
|
||||
🌐 **Мови:** [English](model-spec.md) | [Українська](model-spec_uk.md)
|
||||
|
||||
Запозичено з [неофіційної Minecraft wiki](https://minecraft.wiki/w/Tutorials/Models) на умовах ліцензії CC BY-NC-SA 3.0.
|
||||
|
||||
Папка `assets/<namespace>/models/block` містить файли моделей для усіх вказаних варіантів. Назви файлів можна змінювати, але вони завжди повинні відповідати назвам, що використовуються у файлах варіантів.
|
||||
|
||||
* `[TAG_compound]` Кореневий тег
|
||||
* `[TAG_string]` **parent**: Завантажує іншу модель із вказаного шляху у формі [локації ресурсу](https://minecraft.wiki/w/Tutorials/Models#File_path). Якщо задано і `"parent"`, і `"elements"`, тег `"elements"` перевизначає тег `"elements"` із попередньої моделі.
|
||||
* Може мати значення `"builtin/generated"`, щоб використовувати модель, створену із вказаної піктограми. Зверніть увагу: підтримується лише перший шар, а обертання можна досягти лише за допомогою файлів станів блоку.
|
||||
* `[TAG_byte]` **ambientocclusion**: Визначає, чи використовувати [розсіяне затінення](http://en.wikipedia.org/wiki/Ambient_occlusion) (`true` — за замовчуванням), чи ні (`false`). Примітка: працює лише у батьківському файлі.
|
||||
* `[TAG_object]` **display**: Містить різні місця відображення моделей предметів.
|
||||
* `[TAG_compound]` ***Position***: Має назву `thirdperson_righthand`, `thirdperson_lefthand`, `firstperson_righthand`, `firstperson_lefthand`, `gui`, `head`, `ground` або `fixed`. Місце, де відображається модель предмета. Містить її обертання, зміщення та масштаб для вказаної ситуації. **'''fixed'''** стосується рамок для предметів, а решта відповідають своїм назвам. Зверніть увагу: зміщення застосовується до моделі перед обертанням.
|
||||
* `[TAG_list]` **rotation**: Задає обертання моделі за схемою `[x, y, z]`.
|
||||
* `[TAG_list]` **translation**: Задає зміщення моделі за схемою `[x, y, z]`. Значення обмежені в діапазоні від -80 до 80.
|
||||
* `[TAG_list]` **scale**: Задає масштаб моделі за схемою `[x, y, z]`. Якщо значення більше за 4, воно відображається як 4.
|
||||
* `[TAG_compound]` **textures**: Містить текстури моделі у формі [локації ресурсу](https://minecraft.wiki/w/Tutorials/Models#File_path) або у вигляді іншої текстурної змінної.
|
||||
* `[TAG_string]` **particle**: Текстура, з якої завантажуються частинки. Ця текстура також використовується як оверлей, якщо ви перебуваєте в [порталі Незеру](https://minecraft.wiki/w/Nether_portal), а також для нерухомих текстур [води](https://minecraft.wiki/w/water) та [лави](https://minecraft.wiki/w/Lava).<sup>[[1]](https://bugs.mojang.com/browse/MC-240042)</sup> Ця текстура також вважається текстурною змінною, на яку можна посилатися як `"#particle"`. Примітка: Усі частинки руйнування для блоків без моделей вшиті в код, наприклад, для [бар’єрів](https://minecraft.wiki/w/Barrier).
|
||||
* `[TAG_string]` ***Texture variable***: Визначає текстурну змінну з ім’ям `name` та призначає її текстурі або іншій змінній.
|
||||
* `[TAG_list]` **elements**: Містить усі елементи моделі. Вони можуть мати лише кубічну форму. Якщо задано і `"parent"`, і `"elements"`, тег `"elements"` перевизначає тег `"elements"` із попередньої моделі.
|
||||
* `[TAG_compound]` Елемент.
|
||||
* `[TAG_list]` **from**: Початкова точка кубоїда за схемою `[x, y, z]`. Значення мають бути від -16 до 32.
|
||||
* `[TAG_list]` **to**: Кінцева точка кубоїда за схемою `[x, y, z]`. Значення мають бути від -16 до 32.
|
||||
* `[TAG_compound]` **rotation**: Визначає обертання елемента.
|
||||
* `[TAG_list]` **origin**: Встановлює центр обертання за схемою `[x, y, z]`.
|
||||
* `[TAG_string]` **axis**: Задає вісь обертання: може бути `"x"`, `"y"` або `"z"`.
|
||||
* `[TAG_float]` **angle**: Задає кут обертання. Може бути від 45 до -45 градусів із кроком у 22,5 градуса.
|
||||
* `[TAG_byte]` **rescale**: Визначає, чи масштабувати грані по всьому блоку. Може бути `true` або `false`. За замовчуванням — `false`.
|
||||
* `[TAG_byte]` **shade**: Визначає, чи рендерити тіні (`true` — за замовчуванням), чи ні (`false`).
|
||||
* `[TAG_compound]` **faces**: Містить усі грані кубоїда. Якщо грань пропущено, вона не рендериться.
|
||||
* `[TAG_compound]` ***Face***: Має назву `down`, `up`, `north`, `south`, `west` або `east`. Містить властивості вказаної грані.
|
||||
* `[TAG_list]` **uv**: Визначає ділянку текстури для використання за схемою `[x1, y1, x2, y2]`. Поведінка текстури стає непередбачуваною, якщо значення UV виходять за межі 0 або 16. Якщо значення `x1` та `x2` поміняти місцями (наприклад, з `0, 0, 16, 16` на `16, 0, 0, 16`), текстура віддзеркалюється. Параметр UV є необов’язковим; якщо його не вказано, він автоматично генерується на основі позиції елемента.
|
||||
* `[TAG_string]` **texture**: Вказує текстуру у формі текстурної змінної зі знаком `#` на початку.
|
||||
* `[TAG_string]` **cullface**: Визначає, чи не потрібно рендерити грань, якщо до неї в указаній позиції торкається інший блок. Позиція може бути: `down`, `up`, `north`, `south`, `west` або `east`. Цей параметр також визначає сторону блоку, з якої береться рівень освітлення для цієї грані; якщо параметр не задано, за замовчуванням використовується відповідна сторона.
|
||||
* `[TAG_int]` **rotation**: Обертає текстуру за годинниковою стрілкою на вказаний кут у градусах. Може бути 0, 90, 180 або 270. За замовчуванням — 0. Обертання не впливає на те, яка частина текстури використовується. Натомість воно зводиться до перестановки вершин вибраної текстури (вибраних неявно або явно через `uv`).
|
||||
* `[TAG_int]` **tintindex**: Визначає, чи потрібно тонувати текстуру за допомогою вшитого індексу відтінку. Значення за замовчуванням, -1, вказує на те, що відтінок не використовується. Будь-яке інше число передається в `BlockColors` для отримання значення відтінку, відповідного цьому індексу. Однак більшість блоків не мають визначеного значення відтінку (у такому разі використовується білий колір). Крім того, жоден ванільний блок наразі не використовує кілька значень відтінку, тому значення індексу відтінку ігнорується (якщо воно відмінне від -1); втім, його можна використовувати для модифікованих блоків, яким потрібні кілька різних значень відтінку в одному блоці.
|
||||
@@ -0,0 +1,334 @@
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`java-test-fixtures`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
apply(plugin = "xyz.wagyourtail.jvmdowngrader")
|
||||
|
||||
version = rootProject.version
|
||||
|
||||
val lwjglNatives: String by extra
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
// RFG variant attributes: the root Angelica project uses RetroFuturaGradle which expects
|
||||
// these attributes on dependencies. Without them, Gradle can't resolve the subproject variant.
|
||||
val rfgObfAttr = Attribute.of("com.gtnewhorizons.retrofuturagradle.obfuscation", String::class.java)
|
||||
val rfgTransformedAttr = Attribute.of("rfgDeobfuscatorTransformed", Boolean::class.javaObjectType)
|
||||
|
||||
configurations {
|
||||
listOf("apiElements", "runtimeElements", "testFixturesApiElements", "testFixturesRuntimeElements").forEach { name ->
|
||||
named(name) {
|
||||
attributes {
|
||||
attribute(rfgObfAttr, "mcp")
|
||||
attribute(rfgTransformedAttr, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compile stubs: MC class signatures needed to resolve GTNHLib's type hierarchy.
|
||||
// Not included in the published jar - real MC classes are on the classpath at runtime.
|
||||
sourceSets {
|
||||
create("stubs") {
|
||||
java.srcDir("src/stubs/java")
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude stubs from all output jars
|
||||
tasks.withType<Jar>().configureEach {
|
||||
exclude("net/minecraft/**")
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Mojang"
|
||||
url = uri("https://libraries.minecraft.net")
|
||||
}
|
||||
maven {
|
||||
name = "GTNH Maven"
|
||||
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
|
||||
}
|
||||
maven {
|
||||
name = "Forge"
|
||||
url = uri("https://maven.minecraftforge.net/")
|
||||
}
|
||||
maven {
|
||||
name = "taumc"
|
||||
url = uri("https://maven.taumc.org/releases")
|
||||
}
|
||||
maven {
|
||||
name = "WagYourTail"
|
||||
url = uri("https://maven.wagyourtail.xyz/releases")
|
||||
content { includeGroup("xyz.wagyourtail.jvmdowngrader") }
|
||||
}
|
||||
maven {
|
||||
name = "Sonatype Snapshots"
|
||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
maven {
|
||||
name = "Maven Central Snapshots"
|
||||
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
|
||||
}
|
||||
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// MC compile stubs
|
||||
compileOnly(sourceSets["stubs"].output)
|
||||
|
||||
// LWJGL
|
||||
compileOnly(libs.lwjgl2)
|
||||
compileOnly(libs.lwjgl2.util)
|
||||
|
||||
compileOnly(libs.asm.tree.compile)
|
||||
|
||||
// Our Deps
|
||||
api(libs.gtnhlib) { artifact { classifier = "dev" } }
|
||||
api(libs.eventbus)
|
||||
|
||||
compileOnly(libs.celeritas.common) { isTransitive = false }
|
||||
api(libs.glsl.transformation.lib) { exclude(module = "antlr4") }
|
||||
api(libs.antlr4.runtime)
|
||||
implementation(libs.jcpp)
|
||||
|
||||
// shaderc + SPIRV-Cross for the SpirvShaderTranslator (GLSL -> SPIR-V -> GLSL ES).
|
||||
// TODO: Remove
|
||||
compileOnly(libs.lwjgl3.shaderc)
|
||||
compileOnly(libs.lwjgl3.spvc)
|
||||
testImplementation(libs.lwjgl3.shaderc)
|
||||
testImplementation(libs.lwjgl3.spvc)
|
||||
// Host natives so the JNI wrapper can find libshaderc / libspirv-cross / liblwjgl.
|
||||
testRuntimeOnly(libs.lwjgl3.shaderc) { artifact { classifier = lwjglNatives } }
|
||||
testRuntimeOnly(libs.lwjgl3.spvc) { artifact { classifier = lwjglNatives } }
|
||||
testRuntimeOnly(libs.lwjgl3)
|
||||
testRuntimeOnly(libs.lwjgl3) { artifact { classifier = lwjglNatives } }
|
||||
// @Lwjgl3Aware annotation
|
||||
compileOnly(libs.lwjgl3ify) { artifact { classifier = "dev" }; isTransitive = false }
|
||||
|
||||
compileOnly(libs.lombok) { isTransitive = false }
|
||||
annotationProcessor(libs.lombok)
|
||||
compileOnly(libs.annotations)
|
||||
compileOnly(libs.log4j.api)
|
||||
|
||||
// Deps (normally from MC) - compile only, but needed at test runtime
|
||||
compileOnly(libs.guava)
|
||||
testRuntimeOnly(libs.guava)
|
||||
testRuntimeOnly(libs.fastutil)
|
||||
testRuntimeOnly(libs.joml) { isTransitive = false }
|
||||
|
||||
testFixturesCompileOnly(sourceSets["stubs"].output)
|
||||
testFixturesApi(platform(libs.junit.bom))
|
||||
testFixturesApi(libs.junit.jupiter.api)
|
||||
testFixturesApi(libs.lwjgl2)
|
||||
|
||||
// Test
|
||||
testImplementation(sourceSets["stubs"].output)
|
||||
testImplementation(platform(libs.junit.bom))
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(libs.asm.tree.test)
|
||||
testImplementation(libs.lwjgl2)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
testRuntimeOnly(libs.log4j.core)
|
||||
testImplementation(libs.celeritas.common) { isTransitive = false }
|
||||
testRuntimeOnly(libs.celeritas.lwjgl2.service) { isTransitive = false }
|
||||
testRuntimeOnly(libs.jvmdowngrader.java.api) { artifact { classifier = "downgraded-8" } }
|
||||
testRuntimeOnly(libs.commons.lang3)
|
||||
}
|
||||
|
||||
val depsToDowngrade by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
depsToDowngrade(libs.eventbus)
|
||||
depsToDowngrade(libs.celeritas.common) { isTransitive = false }
|
||||
}
|
||||
|
||||
tasks.withType<DowngradeFiles>().configureEach {
|
||||
downgradeTo.set(JavaVersion.VERSION_1_8)
|
||||
multiReleaseOriginal.set(false)
|
||||
multiReleaseVersions.set(emptySet())
|
||||
logLevel.set("FATAL")
|
||||
}
|
||||
|
||||
val downgradeDepsForTest by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = depsToDowngrade
|
||||
}
|
||||
|
||||
val downgradeMainClasses by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = sourceSets["main"].output.classesDirs.plus(sourceSets["stubs"].output.classesDirs)
|
||||
classpath = sourceSets["main"].compileClasspath
|
||||
(sourceSets["main"].output.classesDirs.files + sourceSets["stubs"].output.classesDirs.files)
|
||||
.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
|
||||
dependsOn(tasks.named("classes"), tasks.named("stubsClasses"))
|
||||
}
|
||||
|
||||
val downgradeTestFixturesClasses by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = sourceSets["testFixtures"].output.classesDirs
|
||||
classpath = sourceSets["testFixtures"].compileClasspath
|
||||
sourceSets["testFixtures"].output.classesDirs.files.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
|
||||
dependsOn(tasks.named("testFixturesClasses"))
|
||||
}
|
||||
|
||||
val downgradeTestClasses by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = sourceSets["test"].output.classesDirs
|
||||
classpath = sourceSets["test"].compileClasspath
|
||||
sourceSets["test"].output.classesDirs.files.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
|
||||
dependsOn(tasks.named("testClasses"))
|
||||
}
|
||||
|
||||
val GL_TASK_TAGS = mapOf(
|
||||
"glCompatTest" to "gl-compat",
|
||||
"glCoreTest" to "gl-core",
|
||||
"glSharedTest" to "gl-shared",
|
||||
)
|
||||
|
||||
fun Test.configureGlsmJava8() {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")
|
||||
&& System.getProperty("os.arch") == "aarch64") {
|
||||
vendor = JvmVendorSpec.AZUL
|
||||
}
|
||||
}
|
||||
|
||||
dependsOn(downgradeMainClasses, downgradeTestClasses, downgradeTestFixturesClasses, downgradeDepsForTest)
|
||||
|
||||
val mainClassesDirs = sourceSets["main"].output.classesDirs
|
||||
.plus(sourceSets["stubs"].output.classesDirs)
|
||||
val testClassesDirs = sourceSets["test"].output.classesDirs
|
||||
|
||||
val downgradedTest = files(downgradeTestClasses.map { it.outputCollection })
|
||||
val downgradedMain = files(downgradeMainClasses.map { it.outputCollection })
|
||||
val downgradedDeps = files(downgradeDepsForTest.map { it.outputCollection })
|
||||
|
||||
setTestClassesDirs(downgradedTest)
|
||||
classpath = downgradedTest
|
||||
.plus(downgradedMain)
|
||||
.plus(downgradedDeps)
|
||||
.plus(files(downgradeTestFixturesClasses.map { it.outputCollection }))
|
||||
.plus(sourceSets["test"].runtimeClasspath.minus(mainClassesDirs).minus(testClassesDirs)
|
||||
.minus(sourceSets["testFixtures"].output.classesDirs).minus(depsToDowngrade))
|
||||
|
||||
val extractNatives = rootProject.tasks.named("extractNatives2")
|
||||
dependsOn(extractNatives)
|
||||
jvmArgs("-Djava.library.path=${extractNatives.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}")
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")) {
|
||||
jvmArgs("-Dapple.awt.UIElement=true")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform {
|
||||
excludeTags = GL_TASK_TAGS.values.toSet() + "lwjgl3"
|
||||
}
|
||||
filter {
|
||||
excludeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
}
|
||||
configureGlsmJava8()
|
||||
}
|
||||
|
||||
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
tasks.register<Test>(taskName) {
|
||||
description = "Runs the $tag GL tests in their own JVM, so they get their own Display and GL profile."
|
||||
group = "verification"
|
||||
useJUnitPlatform {
|
||||
includeTags = setOf(tag)
|
||||
}
|
||||
configureGlsmJava8()
|
||||
}
|
||||
}
|
||||
|
||||
(listOf(tasks.test) + glTestTasks).zipWithNext { earlier, later ->
|
||||
later.configure { mustRunAfter(earlier) }
|
||||
}
|
||||
tasks.test { finalizedBy(glTestTasks) }
|
||||
|
||||
val verifyTestsRan by tasks.registering {
|
||||
dependsOn(tasks.test, glTestTasks)
|
||||
}
|
||||
(listOf(tasks.test) + glTestTasks).forEach { testTask ->
|
||||
val name = testTask.name
|
||||
val verify = tasks.register("verify${name.replaceFirstChar { it.uppercase() }}Ran") {
|
||||
val resultsDir = layout.buildDirectory.dir("test-results/$name")
|
||||
dependsOn(testTask)
|
||||
doLast {
|
||||
val dir = resultsDir.get().asFile
|
||||
val xmls = dir.listFiles { f -> f.name.startsWith("TEST-") && f.name.endsWith(".xml") } ?: emptyArray()
|
||||
check(xmls.isNotEmpty()) {
|
||||
":glsm:$name produced no TEST-*.xml in $dir - test task likely went NO-SOURCE"
|
||||
}
|
||||
}
|
||||
}
|
||||
verifyTestsRan.configure { dependsOn(verify) }
|
||||
}
|
||||
tasks.check { dependsOn(verifyTestsRan) }
|
||||
|
||||
val spirvTest by tasks.registering(Test::class) {
|
||||
description = "Runs SpirvShaderTranslator* tests against native shaderc + spvc on Java 21."
|
||||
group = "verification"
|
||||
useJUnitPlatform()
|
||||
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
|
||||
testClassesDirs = sourceSets["test"].output.classesDirs
|
||||
classpath = sourceSets["test"].runtimeClasspath.filter { file ->
|
||||
// Strip the LWJGL2 jars - they collide with LWJGL3's sealed org.lwjgl package.
|
||||
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||
}
|
||||
|
||||
filter {
|
||||
includeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
includeTestsMatching("com.gtnewhorizons.angelica.glsm.ffp.FFPUniformBlockSpirvLayoutTest")
|
||||
}
|
||||
|
||||
// libshaderc.so / libspirv-cross.so are extracted by extractNatives3.
|
||||
val extractNatives3 = rootProject.tasks.named("extractNatives3")
|
||||
dependsOn(extractNatives3)
|
||||
jvmArgs(
|
||||
"-Djava.library.path=${extractNatives3.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}",
|
||||
"--enable-native-access=ALL-UNNAMED"
|
||||
)
|
||||
|
||||
testLogging {
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("check") { dependsOn(spirvTest) }
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
groupId = "com.gtnewhorizons.angelica"
|
||||
artifactId = "glsm"
|
||||
version = rootProject.version.toString()
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
if (System.getenv("MAVEN_USER") != null) {
|
||||
maven {
|
||||
name = "GTNHMaven"
|
||||
url = uri(rootProject.findProperty("mavenPublishUrl")?.toString() ?: "https://nexus.gtnewhorizons.com/repository/releases/")
|
||||
credentials {
|
||||
username = System.getenv("MAVEN_USER")
|
||||
password = System.getenv("MAVEN_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.gtnewhorizons.angelica.config;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class SystemProperties {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("SystemProperties");
|
||||
|
||||
// Backend selection
|
||||
public static final String KEY_USE_SDL_GPU = "angelica.sdlgpu.enable";
|
||||
public static final boolean USE_SDL_GPU = Boolean.getBoolean(KEY_USE_SDL_GPU);
|
||||
public static final String GL_PROFILE = System.getProperty("angelica.glProfile", "");
|
||||
public static final boolean DISABLE_NVIDIA_WORKAROUND = Boolean.getBoolean("angelica.disableNvidiaWorkaround");
|
||||
public static final boolean DISABLE_LTW_WORKAROUND = Boolean.getBoolean("angelica.disableLtwWorkaround");
|
||||
|
||||
// Redirector
|
||||
public static final UnmappedGLMode UNMAPPED_GL = parseEnum("angelica.unmappedGL", UnmappedGLMode.WARN, UnmappedGLMode.WARN, UnmappedGLMode.class);
|
||||
public static final boolean FFP_WARN_ON_UNSUPPORTED = Boolean.getBoolean("angelica.ffp.warnOnUnsupported");
|
||||
|
||||
// SDL-GPU
|
||||
public static final String KEY_SDL_GPU_DRIVER = "angelica.sdlgpu.driver";
|
||||
public static final String SDL_GPU_DRIVER = System.getProperty(KEY_SDL_GPU_DRIVER, "");
|
||||
public static final boolean SDL_GPU_DEBUG = Boolean.getBoolean("angelica.sdlgpu.debug");
|
||||
private static final SdlAssertionMode ENCODER_ASSERTIONS = parseEnum("angelica.sdlgpu.encoderAssertions", SdlAssertionMode.OFF, SdlAssertionMode.WARN, SdlAssertionMode.class);
|
||||
public static final boolean SDL_ENCODER_ASSERTIONS = ENCODER_ASSERTIONS != SdlAssertionMode.OFF;
|
||||
public static final boolean SDL_ENCODER_ASSERTIONS_FATAL = ENCODER_ASSERTIONS == SdlAssertionMode.FATAL;
|
||||
public static final boolean SDL_VERIFY_STATE_SYNC = Boolean.getBoolean("angelica.sdlgpu.verifyStateSync");
|
||||
public static final boolean SDL_VERIFY_PER_FRAME_UNIFORM_BLOCK = Boolean.getBoolean("angelica.sdlgpu.verifyPerFrameUniformBlock");
|
||||
public static final int SDL_FRAMES_IN_FLIGHT = Integer.getInteger("angelica.sdlgpu.framesInFlight", 2);
|
||||
public static final boolean DISABLE_SDL_PRESENTER_THREAD = Boolean.getBoolean("angelica.sdlgpu.disablePresenterThread");
|
||||
|
||||
// Tracy
|
||||
public static final boolean TRACY = Boolean.getBoolean("angelica.tracy");
|
||||
public static final boolean TRACY_FINE_ZONES = Boolean.getBoolean("angelica.tracy.fineZones");
|
||||
public static final String TRACY_DIR = System.getProperty("angelica.tracy.dir", "angelica" + File.separator + "natives" + File.separator + "tracy");
|
||||
public static final int TRACY_MAX_SRC_LOCS = Math.max(16, Integer.getInteger("angelica.tracy.maxSrcLocs", 4096));
|
||||
|
||||
// Flyby
|
||||
public static final String FLYBY_ROUTE = System.getProperty("angelica.flyby.route", "");
|
||||
public static final boolean FLYBY_WAIT_FOR_TRACY = Boolean.getBoolean("angelica.flyby.waitForTracy");
|
||||
public static final boolean FLYBY_WAIT_FOR_FOCUS = Boolean.getBoolean("angelica.flyby.waitForFocus");
|
||||
public static final int FLYBY_WARMUP_TICKS = Integer.getInteger("angelica.flyby.warmupTicks", 400);
|
||||
public static final int FLYBY_LENGTH = Integer.getInteger("angelica.flyby.length", 0); // Blocks for moving routes, ticks for stationary
|
||||
public static final double FLYBY_SPEED = parseDouble("angelica.flyby.speed"); // Travel speed in blocks per tick
|
||||
public static final boolean FLYBY_EXIT_WHEN_DONE = Boolean.getBoolean("angelica.flyby.exitWhenDone");
|
||||
|
||||
// Debug
|
||||
public static final boolean LWJGL_DEBUG = Boolean.getBoolean("org.lwjgl.util.Debug");
|
||||
public static final boolean DEBUG_MARKERS = Boolean.getBoolean("angelica.debug.markers");
|
||||
public static final boolean DEBUG_F3_DETAIL = Boolean.getBoolean("angelica.debug.f3Detail");
|
||||
public static final boolean ENABLE_TEST_BLOCKS = Boolean.getBoolean("angelica.debug.testBlocks");
|
||||
public static final boolean DUMP_CLASS = Boolean.getBoolean("angelica.debug.dumpClass");
|
||||
private static final boolean DUMP_SHADERS_PROP = Boolean.getBoolean("angelica.debug.dumpShaders");
|
||||
public static final boolean REDIRECTOR_LOGSPAM = Boolean.getBoolean("angelica.debug.redirectorLogspam");
|
||||
public static final boolean DEBUG_DISPLAY_LISTS = Boolean.getBoolean("angelica.debug.displayLists");
|
||||
public static final boolean LOG_DISPLAY_LIST_COMPILATION = Boolean.getBoolean("angelica.debug.displayLists.compilation");
|
||||
public static final boolean FORCE_ORPHAN_STREAMING = Boolean.getBoolean("angelica.debug.forceOrphanStreaming");
|
||||
public static final String SHADER_DUMP_ROOT = "angelica_dumps";
|
||||
|
||||
// Set by us, read by celeritas
|
||||
public static final String KEY_CELERITAS_ENABLE_GL_DEBUG = "celeritas.enableGLDebug";
|
||||
|
||||
public static boolean dumpShaders() {
|
||||
return DUMP_SHADERS_PROP || isDeobf();
|
||||
}
|
||||
|
||||
public static Path shaderDumpDir(String phase) {
|
||||
return dumpShaders() ? Paths.get(SHADER_DUMP_ROOT, phase) : null;
|
||||
}
|
||||
|
||||
public enum UnmappedGLMode {
|
||||
OFF,
|
||||
WARN,
|
||||
FAIL,
|
||||
STRICT;
|
||||
|
||||
public boolean detects() {
|
||||
return this != OFF;
|
||||
}
|
||||
|
||||
public boolean failsUnmapped() {
|
||||
return this == FAIL || this == STRICT;
|
||||
}
|
||||
|
||||
public boolean failsAwareUnroutable() {
|
||||
return this == STRICT;
|
||||
}
|
||||
}
|
||||
|
||||
private enum SdlAssertionMode {
|
||||
OFF,
|
||||
WARN,
|
||||
FATAL
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> E parseEnum(String key, E whenAbsent, E whenInvalid, Class<E> type) {
|
||||
final String raw = System.getProperty(key);
|
||||
if (raw == null || raw.isEmpty()) return whenAbsent;
|
||||
try {
|
||||
return Enum.valueOf(type, raw.trim().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.warn("Unrecognized value '{}' for -D{}; using {}. Valid values: {}", raw, key, whenInvalid, Arrays.toString(type.getEnumConstants()));
|
||||
return whenInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
private static double parseDouble(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
if (raw == null || raw.isEmpty()) return 0.0D;
|
||||
try {
|
||||
return Double.parseDouble(raw);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0.0D;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDeobf() {
|
||||
return Launch.blackboard != null && Boolean.TRUE.equals(Launch.blackboard.get("fml.deobfuscatedEnvironment"));
|
||||
}
|
||||
|
||||
private SystemProperties() {}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/** Decides whether instrumentation is worth emitting this frame. */
|
||||
public final class CaptureGate {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
private static final boolean IS_LINUX = System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("linux");
|
||||
private static final Path PROC_SELF_MAPS = Paths.get("/proc/self/maps");
|
||||
private static final String[] CAPTURE_ENV = { "RENDERDOC_CAPTURE_KEYPRESS", "ENABLE_VULKAN_RENDERDOC_CAPTURE", "RENDERDOC_HOOK_EGL" };
|
||||
private static final String[] CAPTURE_LIBS = { "renderdoc", "glxtrace", "egltrace", "apitrace" };
|
||||
public static final boolean FORCED = SystemProperties.DEBUG_MARKERS || SystemProperties.LWJGL_DEBUG;
|
||||
public static final boolean TOOL_ATTACHED = detectCaptureTool();
|
||||
public static boolean markersThisFrame = true;
|
||||
|
||||
private CaptureGate() {}
|
||||
|
||||
public static void refresh() {
|
||||
markersThisFrame = gateValue(FORCED, TOOL_ATTACHED, Tracy.isConnected());
|
||||
}
|
||||
|
||||
static boolean gateValue(boolean forced, boolean toolAttached, boolean tracyConnected) {
|
||||
return forced || toolAttached || tracyConnected;
|
||||
}
|
||||
|
||||
public static boolean enabledAtStartup() {
|
||||
return FORCED || TOOL_ATTACHED;
|
||||
}
|
||||
|
||||
private static boolean detectCaptureTool() {
|
||||
if (detectFromEnv(System::getenv)) {
|
||||
LOGGER.info("Capture tool detected via environment; instrumentation enabled");
|
||||
return true;
|
||||
}
|
||||
if (!IS_LINUX) return false;
|
||||
try {
|
||||
if (detectFromMaps(Files.readAllLines(PROC_SELF_MAPS, StandardCharsets.ISO_8859_1))) {
|
||||
LOGGER.info("Capture tool library detected in process map; instrumentation enabled");
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not read {} for capture tool detection: {}", PROC_SELF_MAPS, e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean detectFromEnv(UnaryOperator<String> env) {
|
||||
for (String key : CAPTURE_ENV) {
|
||||
if (env.apply(key) != null) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean detectFromMaps(List<String> mapLines) {
|
||||
for (int i = 0; i < mapLines.size(); i++) {
|
||||
final String line = mapLines.get(i).toLowerCase(Locale.ROOT);
|
||||
for (String lib : CAPTURE_LIBS) {
|
||||
if (line.contains(lib)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+298
-89
@@ -1,6 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import org.taumc.glsl.ShaderParser;
|
||||
import org.taumc.glsl.Transformer;
|
||||
|
||||
@@ -9,16 +17,19 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Compat transformation for mod shaders running under core profile.
|
||||
@@ -27,25 +38,23 @@ import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
* <ul>
|
||||
* <li>Matrix builtin replacement (gl_ModelViewMatrix, gl_ProjectionMatrix, etc.)</li>
|
||||
* <li>Vertex attribute replacement (gl_Vertex, gl_Color, gl_MultiTexCoord0/1, gl_Normal)</li>
|
||||
* <li>gl_TexCoord[N] varying array → per-index in/out declarations</li>
|
||||
* <li>Texture function renames (texture2D → texture, etc.)</li>
|
||||
* <li>Fragment output handling (gl_FragColor → layout-qualified out declarations)</li>
|
||||
* <li>gl_TexCoord[N] varying array -> per-index in/out declarations</li>
|
||||
* <li>Texture function renames (texture2D -> texture, etc.)</li>
|
||||
* <li>Fragment output handling (gl_FragColor -> layout-qualified out declarations)</li>
|
||||
* <li>Fog builtins (gl_Fog, gl_FogFragCoord)</li>
|
||||
* <li>gl_FrontColor → local variable in vertex shaders</li>
|
||||
* <li>shadow2D/shadow2DLod → texture/textureLod with vec4 wrapping</li>
|
||||
* <li>gl_FrontColor -> local variable in vertex shaders</li>
|
||||
* <li>shadow2D/shadow2DLod -> texture/textureLod with vec4 wrapping</li>
|
||||
* <li>Version upgrade to 330 core minimum</li>
|
||||
* <li>Reserved word pre-parse renaming (texture-as-variable, sample, etc.)</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class CompatShaderTransformer {
|
||||
|
||||
private static final Pattern VERSION_PATTERN =
|
||||
Pattern.compile("#version\\s+(\\d+)(?:\\s+(\\w+))?");
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("#version[ \\t]+(\\d+)(?:[ \\t]+(\\w+))?");
|
||||
|
||||
private static final Pattern DEFINE_PATTERN =
|
||||
Pattern.compile("^\\s*#\\s*define\\s+.+$", Pattern.MULTILINE);
|
||||
private static final Pattern DEFINE_PATTERN = Pattern.compile("^\\s*#\\s*define\\s+.+$", Pattern.MULTILINE);
|
||||
|
||||
/** Compat builtins that trigger AST transformation. */
|
||||
/** Compat builtins that trigger AST transformation. */
|
||||
private static final Set<String> COMPAT_BUILTINS = Set.of(
|
||||
"gl_ModelView", "gl_Projection", "gl_NormalMatrix", "gl_TextureMatrix",
|
||||
"gl_FragColor", "gl_Fog", "gl_FrontColor", "gl_Color",
|
||||
@@ -67,35 +76,59 @@ public class CompatShaderTransformer {
|
||||
"gl_NormalMatrix", "angelica_NormalMatrix"
|
||||
);
|
||||
|
||||
private static final Path DUMP_DIR;
|
||||
private static final AtomicInteger dumpCounter = new AtomicInteger(0);
|
||||
static {
|
||||
boolean isDev = false;
|
||||
try {
|
||||
final Object deobfEnv = Launch.blackboard != null ? Launch.blackboard.get("fml.deobfuscatedEnvironment") : null;
|
||||
isDev = Boolean.TRUE.equals(deobfEnv);
|
||||
} catch (Exception ignored) {}
|
||||
DUMP_DIR = (isDev || Boolean.parseBoolean(System.getProperty("angelica.compat.dumpShaders", "false")))
|
||||
? Paths.get("compat_shaders") : null;
|
||||
}
|
||||
|
||||
private static final int CACHE_SIZE = 32;
|
||||
private static final int CACHE_SIZE = 256;
|
||||
private record CacheKey(String source, boolean isFragment) {}
|
||||
|
||||
private static final Map<CacheKey, String> cache = Collections.synchronizedMap(
|
||||
new LinkedHashMap<>(32, 0.75f, true) {
|
||||
new LinkedHashMap<>(CACHE_SIZE, 0.75f, true) {
|
||||
@Override protected boolean removeEldestEntry(Map.Entry<CacheKey, String> eldest) {
|
||||
return size() > CACHE_SIZE;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
private record EsCacheKey(String source, int glShaderType, boolean isFragment) {}
|
||||
private static final Map<EsCacheKey, String> esCache = Collections.synchronizedMap(
|
||||
new LinkedHashMap<>(CACHE_SIZE, 0.75f, true) {
|
||||
@Override protected boolean removeEldestEntry(Map.Entry<EsCacheKey, String> eldest) {
|
||||
return size() > CACHE_SIZE;
|
||||
}
|
||||
});
|
||||
|
||||
public static void clearCache() {
|
||||
cache.clear();
|
||||
esCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a mod shader source for core profile compatibility.
|
||||
*/
|
||||
public static void prewarm(String source, int glShaderType, boolean isFragment) {
|
||||
if (!RenderSystem.isGLES()) return;
|
||||
final EsCacheKey key = new EsCacheKey(source, glShaderType, isFragment);
|
||||
if (esCache.containsKey(key)) return;
|
||||
transform(source, glShaderType, isFragment);
|
||||
}
|
||||
|
||||
/** Transform a mod shader for core profile; under GLES, also rewrites to ES 320. */
|
||||
public static String transform(String source, boolean isFragment) {
|
||||
return transform(source, isFragment ? GL20.GL_FRAGMENT_SHADER : GL20.GL_VERTEX_SHADER, isFragment);
|
||||
}
|
||||
|
||||
/** Variant carrying the exact GL shader type so the GLES re-emit dispatches to the right shaderc kind (geometry/tess/compute). */
|
||||
public static String transform(String source, int glShaderType, boolean isFragment) {
|
||||
if (!source.isEmpty() && source.charAt(source.length() - 1) == '\0') {
|
||||
source = source.substring(0, source.length() - 1);
|
||||
}
|
||||
final boolean gles = RenderSystem.isGLES();
|
||||
|
||||
if (gles) {
|
||||
final EsCacheKey esKey = new EsCacheKey(source, glShaderType, isFragment);
|
||||
final String esCached = esCache.get(esKey);
|
||||
if (esCached != null) {
|
||||
dumpShader(source, esCached, isFragment, needsTransformation(source));
|
||||
return esCached;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean needsTransform = needsTransformation(source);
|
||||
String result;
|
||||
if (!needsTransform) {
|
||||
@@ -104,30 +137,44 @@ public class CompatShaderTransformer {
|
||||
final CacheKey key = new CacheKey(source, isFragment);
|
||||
final String cached = cache.get(key);
|
||||
if (cached != null) {
|
||||
dumpShader(source, cached, isFragment, needsTransform);
|
||||
return cached;
|
||||
result = cached;
|
||||
} else {
|
||||
try {
|
||||
result = transformInternal(source, isFragment);
|
||||
cache.put(key, result);
|
||||
} catch (Exception e) {
|
||||
GLStateManager.LOGGER.warn("CompatShaderTransformer: AST transformation failed, falling back to version fixup only", e);
|
||||
result = fixupVersion(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gles) {
|
||||
final String preGles = result;
|
||||
try {
|
||||
result = transformInternal(source, isFragment);
|
||||
cache.put(key, result);
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("CompatShaderTransformer: AST transformation failed, falling back to version fixup only", e);
|
||||
result = fixupVersion(source);
|
||||
result = toGLES(result, glShaderType, isFragment);
|
||||
} catch (RuntimeException e) {
|
||||
dumpShader(source, preGles, isFragment, needsTransform);
|
||||
throw e;
|
||||
}
|
||||
esCache.put(new EsCacheKey(source, glShaderType, isFragment), result);
|
||||
}
|
||||
|
||||
dumpShader(source, result, isFragment, needsTransform);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean needsTransformation(String source) {
|
||||
// Shaders already at 330+ core don't need compat transformation
|
||||
public static boolean isCoreShader(String source) {
|
||||
final Matcher vm = VERSION_PATTERN.matcher(source);
|
||||
if (vm.find()) {
|
||||
final int version = Integer.parseInt(vm.group(1));
|
||||
if (version >= 330 && "core".equals(vm.group(2))) return false;
|
||||
return version >= 330 && "core".equals(vm.group(2));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean needsTransformation(String source) {
|
||||
if (isCoreShader(source)) return false;
|
||||
|
||||
return NEEDS_TRANSFORM_PATTERN.matcher(source).find();
|
||||
}
|
||||
@@ -139,48 +186,46 @@ public class CompatShaderTransformer {
|
||||
declaredVersion = Integer.parseInt(versionMatcher.group(1));
|
||||
}
|
||||
|
||||
final int targetVersion = Math.max(declaredVersion, 330);
|
||||
final int targetVersion = Math.max(declaredVersion, RENDER_BACKEND != null ? RENDER_BACKEND.getMinGLSLVersion() : 330);
|
||||
|
||||
// Pre-parse reserved word renaming — prevents ANTLR parse failures
|
||||
// Pre-parse reserved word renaming - prevents ANTLR parse failures
|
||||
source = GlslTransformUtils.replaceTexture(source);
|
||||
source = GlslTransformUtils.renameReservedWords(source, targetVersion);
|
||||
|
||||
final ShaderParser.ParsedShader parsedShader = ShaderParser.parseShader(source);
|
||||
final Transformer transformer = new Transformer(parsedShader.full());
|
||||
final GlslTransformUtils.QuietParse parsed = GlslTransformUtils.parseBothQuiet(source);
|
||||
final Transformer transformer = new Transformer(parsed.full());
|
||||
|
||||
injectMatrixUniforms(transformer);
|
||||
|
||||
transformFog(transformer, isFragment, source);
|
||||
|
||||
// gl_FrontLightModelProduct.sceneColor → angelica_SceneColor uniform
|
||||
// gl_FrontLightModelProduct.sceneColor -> angelica_SceneColor uniform
|
||||
if (source.contains("gl_FrontLightModelProduct")) {
|
||||
transformer.injectVariable("uniform vec4 angelica_SceneColor;");
|
||||
transformer.replaceExpression("gl_FrontLightModelProduct.sceneColor", "angelica_SceneColor");
|
||||
}
|
||||
|
||||
// gl_LightSource[i] → struct + uniform array (both via injectFunction to keep struct before uniform)
|
||||
// gl_LightSource[i] -> struct + uniform array (both via injectFunction to keep struct before uniform)
|
||||
if (source.contains("gl_LightSource")) {
|
||||
transformer.injectFunction(
|
||||
"struct angelica_LightSourceParameters {"
|
||||
+ "vec4 ambient;vec4 diffuse;vec4 specular;vec4 position;vec4 halfVector;"
|
||||
+ "vec3 spotDirection;float spotExponent;float spotCutoff;float spotCosCutoff;"
|
||||
+ "float constantAttenuation;float linearAttenuation;float quadraticAttenuation;"
|
||||
+ "};");
|
||||
transformer.injectFunction("struct angelica_LightSourceParameters {"
|
||||
+ "vec4 ambient;vec4 diffuse;vec4 specular;vec4 position;vec4 halfVector;"
|
||||
+ "vec3 spotDirection;float spotExponent;float spotCutoff;float spotCosCutoff;"
|
||||
+ "float constantAttenuation;float linearAttenuation;float quadraticAttenuation;"
|
||||
+ "};");
|
||||
transformer.injectFunction("uniform angelica_LightSourceParameters angelica_LightSource[2];");
|
||||
transformer.rename("gl_LightSource", "angelica_LightSource");
|
||||
}
|
||||
|
||||
// gl_FrontMaterial → struct + uniform (both via injectFunction to keep struct before uniform)
|
||||
// gl_FrontMaterial -> struct + uniform (both via injectFunction to keep struct before uniform)
|
||||
if (source.contains("gl_FrontMaterial")) {
|
||||
transformer.injectFunction(
|
||||
"struct angelica_MaterialParameters {"
|
||||
+ "vec4 emission;vec4 ambient;vec4 diffuse;vec4 specular;float shininess;"
|
||||
+ "};");
|
||||
transformer.injectFunction("struct angelica_MaterialParameters {"
|
||||
+ "vec4 emission;vec4 ambient;vec4 diffuse;vec4 specular;float shininess;"
|
||||
+ "};");
|
||||
transformer.injectFunction("uniform angelica_MaterialParameters angelica_FrontMaterial;");
|
||||
transformer.rename("gl_FrontMaterial", "angelica_FrontMaterial");
|
||||
}
|
||||
|
||||
// gl_FrontColor (vertex) → gl_Color (fragment) varying chain
|
||||
// gl_FrontColor (vertex) -> gl_Color (fragment) varying chain
|
||||
// Vertex side is unconditional: fragment may read gl_Color without vertex writing gl_FrontColor.
|
||||
// Uses angelica_ prefix to avoid colliding with user-declared varyings (e.g. "v_Color").
|
||||
if (!isFragment) {
|
||||
@@ -188,8 +233,9 @@ public class CompatShaderTransformer {
|
||||
transformer.rename("gl_FrontColor", "angelica_FrontColor");
|
||||
transformer.prependMain("angelica_FrontColor = vec4(1.0);");
|
||||
|
||||
// Vertex attributes — replaces removed FFP vertex inputs with explicit in declarations
|
||||
transformVertexAttributes(transformer, source);
|
||||
final FFPInputs ffp = FFPInputs.detect(source);
|
||||
assignVertexInputLocations(transformer, ffp);
|
||||
transformVertexAttributes(transformer, source, ffp);
|
||||
} else {
|
||||
if (source.contains("gl_Color")) {
|
||||
transformer.injectVariable("in vec4 angelica_FrontColor;");
|
||||
@@ -197,7 +243,7 @@ public class CompatShaderTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
// gl_TexCoord[N] varying array → per-index in/out declarations
|
||||
// gl_TexCoord[N] varying array -> per-index in/out declarations
|
||||
final Set<Integer> texCoordIndices = new HashSet<>();
|
||||
transformer.renameArray("gl_TexCoord", "angelica_TexCoord", texCoordIndices);
|
||||
for (Integer i : texCoordIndices) {
|
||||
@@ -224,7 +270,7 @@ public class CompatShaderTransformer {
|
||||
transformer.renameAndWrapShadow("shadow2DLod", "textureLod");
|
||||
|
||||
final String versionDirective = "#version " + targetVersion + " core\n";
|
||||
final String extensions = VERSION_PATTERN.matcher(GlslTransformUtils.getFormattedShader(parsedShader.pre(), "")).replaceFirst("").trim();
|
||||
final String extensions = VERSION_PATTERN.matcher(GlslTransformUtils.getFormattedShader(parsed.pre(), "")).replaceFirst("").trim();
|
||||
|
||||
// Preserve #define directives
|
||||
final StringBuilder defines = new StringBuilder();
|
||||
@@ -240,7 +286,7 @@ public class CompatShaderTransformer {
|
||||
// Restore pre-parse renames
|
||||
String output = GlslTransformUtils.restoreReservedWords(result.toString());
|
||||
|
||||
// Core profile: attribute → in, varying → out (vertex) / in (fragment)
|
||||
// Core profile: attribute -> in, varying -> out (vertex) / in (fragment)
|
||||
output = fixupQualifiers(output, isFragment);
|
||||
|
||||
return output;
|
||||
@@ -263,7 +309,9 @@ public class CompatShaderTransformer {
|
||||
transformer.replaceExpression("gl_ModelViewProjectionMatrix", "(angelica_ProjectionMatrix * angelica_ModelViewMatrix)");
|
||||
transformer.replaceExpression("gl_TextureMatrix[0]", "mat4(1.0)");
|
||||
transformer.replaceExpression("gl_TextureMatrix[1]", "angelica_LightmapTextureMatrix");
|
||||
transformer.replaceExpression("gl_TextureMatrix", "mat4[8](mat4(1.0), angelica_LightmapTextureMatrix, mat4(1.0), mat4(1.0), mat4(1.0), mat4(1.0), mat4(1.0), mat4(1.0))");
|
||||
transformer.replaceExpression(
|
||||
"gl_TextureMatrix",
|
||||
"mat4[8](mat4(1.0), angelica_LightmapTextureMatrix, mat4(1.0), mat4(1.0), mat4(1.0), mat4(1.0), mat4(1.0), mat4(1.0))");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,34 +354,34 @@ public class CompatShaderTransformer {
|
||||
transformer.injectVariable("uniform float angelica_FogEnd;");
|
||||
transformer.injectVariable("uniform vec4 angelica_FogColor;");
|
||||
transformer.injectFunction("struct angelica_FogParameters {vec4 color;float density;float start;float end;float scale;};");
|
||||
transformer.injectFunction(
|
||||
"angelica_FogParameters angelica_Fog = angelica_FogParameters("
|
||||
+ "angelica_FogColor, angelica_FogDensity, angelica_FogStart, angelica_FogEnd, "
|
||||
+ "1.0 / (angelica_FogEnd - angelica_FogStart));");
|
||||
transformer.injectFunction("angelica_FogParameters angelica_Fog;");
|
||||
transformer.prependMain("angelica_Fog = angelica_FogParameters("
|
||||
+ "angelica_FogColor, angelica_FogDensity, angelica_FogStart, angelica_FogEnd, "
|
||||
+ "1.0 / (angelica_FogEnd - angelica_FogStart));");
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace removed FFP vertex attributes with explicit {@code in} declarations at core profile attribute locations.
|
||||
*/
|
||||
private static void transformVertexAttributes(Transformer transformer, String source) {
|
||||
if (source.contains("gl_Vertex") || source.contains("ftransform")) {
|
||||
private static void transformVertexAttributes(Transformer transformer, String source, FFPInputs ffp) {
|
||||
if (ffp.position()) {
|
||||
transformer.injectVariable("layout(location = 0) in vec4 angelica_Vertex;");
|
||||
transformer.rename("gl_Vertex", "angelica_Vertex");
|
||||
}
|
||||
// gl_Color in vertex shaders is the per-vertex color attribute, distinct from the fragment gl_Color (interpolated gl_FrontColor) handled above
|
||||
if (source.contains("gl_Color")) {
|
||||
if (ffp.color()) {
|
||||
transformer.injectVariable("layout(location = 1) in vec4 angelica_Color;");
|
||||
transformer.rename("gl_Color", "angelica_Color");
|
||||
}
|
||||
if (source.contains("gl_MultiTexCoord0")) {
|
||||
if (ffp.uv0()) {
|
||||
transformer.injectVariable("layout(location = 2) in vec4 angelica_MultiTexCoord0;");
|
||||
transformer.rename("gl_MultiTexCoord0", "angelica_MultiTexCoord0");
|
||||
}
|
||||
if (source.contains("gl_MultiTexCoord1")) {
|
||||
if (ffp.uv1()) {
|
||||
transformer.injectVariable("layout(location = 3) in vec4 angelica_MultiTexCoord1;");
|
||||
transformer.rename("gl_MultiTexCoord1", "angelica_MultiTexCoord1");
|
||||
}
|
||||
if (source.contains("gl_Normal")) {
|
||||
if (ffp.normal()) {
|
||||
transformer.injectVariable("layout(location = 4) in vec3 angelica_Normal;");
|
||||
transformer.rename("gl_Normal", "angelica_Normal");
|
||||
}
|
||||
@@ -342,21 +390,161 @@ public class CompatShaderTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MAX_VERTEX_ATTRIBS = 16;
|
||||
|
||||
private record FFPInputs(boolean position, boolean color, boolean uv0, boolean uv1, boolean normal) {
|
||||
static FFPInputs detect(String source) {
|
||||
return new FFPInputs(
|
||||
source.contains("gl_Vertex") || source.contains("ftransform"),
|
||||
source.contains("gl_Color"),
|
||||
source.contains("gl_MultiTexCoord0"),
|
||||
source.contains("gl_MultiTexCoord1"),
|
||||
source.contains("gl_Normal"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Adaptation of Mesa's assign_attribute_or_color_locations */
|
||||
private static void assignVertexInputLocations(Transformer transformer, FFPInputs ffp) {
|
||||
int used = 0;
|
||||
if (ffp.position()) used |= 1 << Usage.POSITION.getAttributeLocation();
|
||||
if (ffp.color()) used |= 1 << Usage.COLOR.getAttributeLocation();
|
||||
if (ffp.uv0()) used |= 1 << Usage.PRIMARY_UV.getAttributeLocation();
|
||||
if (ffp.uv1()) used |= 1 << Usage.SECONDARY_UV.getAttributeLocation();
|
||||
if (ffp.normal()) used |= 1 << Usage.NORMAL.getAttributeLocation();
|
||||
|
||||
record Input(String name, String baseType, String arraySuffix, int slots, int index) {}
|
||||
final List<Input> toAssign = new ArrayList<>();
|
||||
int index = 0;
|
||||
|
||||
for (TerminalNode storage : transformer.collectStorage()) {
|
||||
final int tokenType = storage.getSymbol().getType();
|
||||
if (tokenType != GLSLLexer.ATTRIBUTE && tokenType != GLSLLexer.IN) continue;
|
||||
|
||||
ParseTree node = storage.getParent();
|
||||
while (node != null && !(node instanceof GLSLParser.Single_declarationContext)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
if (!(node instanceof GLSLParser.Single_declarationContext decl)) continue;
|
||||
if (decl.typeless_declaration() == null) continue;
|
||||
|
||||
final GLSLParser.Type_specifierContext typeSpec = decl.fully_specified_type().type_specifier();
|
||||
final String baseType = GlslTransformUtils.getFormattedShader(typeSpec.type_specifier_nonarray(), "").trim();
|
||||
final int typeArraySlots = arraySlots(typeSpec.array_specifier());
|
||||
final String typeArray = arraySuffix(typeSpec.array_specifier());
|
||||
|
||||
if (hasLayoutQualifier(decl)) {
|
||||
final int loc = explicitLocation(decl);
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(decl.typeless_declaration().array_specifier());
|
||||
if (loc >= 0) used |= slotMask(slots) << loc;
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<GLSLParser.Typeless_declarationContext> declarators = new ArrayList<>();
|
||||
declarators.add(decl.typeless_declaration());
|
||||
if (decl.getParent() instanceof GLSLParser.Init_declarator_listContext list) {
|
||||
declarators.addAll(list.typeless_declaration());
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext d : declarators) {
|
||||
if (d.IDENTIFIER() == null) continue;
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(d.array_specifier());
|
||||
final String arraySuffix = typeArray + arraySuffix(d.array_specifier());
|
||||
toAssign.add(new Input(d.IDENTIFIER().getText(), baseType, arraySuffix, slots, index++));
|
||||
}
|
||||
}
|
||||
|
||||
toAssign.sort((a, b) -> a.slots() != b.slots() ? b.slots() - a.slots() : a.index() - b.index());
|
||||
|
||||
for (Input in : toAssign) {
|
||||
final int loc = findAvailableSlots(used, in.slots());
|
||||
if (loc < 0) continue;
|
||||
used |= slotMask(in.slots()) << loc;
|
||||
transformer.removeVariable(in.name());
|
||||
transformer.variable = null;
|
||||
transformer.injectVariable("layout(location = " + loc + ") in " + in.baseType() + " " + in.name() + in.arraySuffix() + ";");
|
||||
}
|
||||
}
|
||||
|
||||
private static String arraySuffix(GLSLParser.Array_specifierContext arraySpec) {
|
||||
return arraySpec != null ? GlslTransformUtils.getFormattedShader(arraySpec, "").trim() : "";
|
||||
}
|
||||
|
||||
private static int arraySlots(GLSLParser.Array_specifierContext arraySpec) {
|
||||
if (arraySpec == null) return 1;
|
||||
int product = 1;
|
||||
for (GLSLParser.DimensionContext dim : arraySpec.dimension()) {
|
||||
if (dim.constant_expression() == null) continue;
|
||||
try {
|
||||
product *= Integer.decode(GlslTransformUtils.getFormattedShader(dim.constant_expression(), "").trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
private static int baseSlots(String type) {
|
||||
final String dims = type.startsWith("dmat") ? type.substring(4) : type.startsWith("mat") ? type.substring(3) : null;
|
||||
if (dims != null && !dims.isEmpty() && Character.isDigit(dims.charAt(0))) {
|
||||
return dims.charAt(0) - '0';
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int slotMask(int slots) {
|
||||
return (slots >= 32) ? -1 : (1 << slots) - 1;
|
||||
}
|
||||
|
||||
private static int findAvailableSlots(int used, int needed) {
|
||||
if (needed <= 0) return -1;
|
||||
final int mask = slotMask(needed);
|
||||
for (int i = 0; i <= MAX_VERTEX_ATTRIBS - needed; i++) {
|
||||
if (((mask << i) & ~used) == (mask << i)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int explicitLocation(GLSLParser.Single_declarationContext decl) {
|
||||
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
|
||||
if (qualifier == null) return -1;
|
||||
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
|
||||
final GLSLParser.Layout_qualifierContext layout = single.layout_qualifier();
|
||||
if (layout == null) continue;
|
||||
for (GLSLParser.Layout_qualifier_idContext id : layout.layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() != null && "location".equals(id.IDENTIFIER().getText()) && id.constant_expression() != null) {
|
||||
try {
|
||||
return Integer.decode(GlslTransformUtils.getFormattedShader(id.constant_expression(), "").trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean hasLayoutQualifier(GLSLParser.Single_declarationContext decl) {
|
||||
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
|
||||
if (qualifier == null) return false;
|
||||
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
|
||||
if (single.layout_qualifier() != null) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void dumpShader(String original, String transformed, boolean isFragment, boolean wasTransformed) {
|
||||
if (DUMP_DIR == null) return;
|
||||
final Path dir = SystemProperties.shaderDumpDir("compat");
|
||||
if (dir == null) return;
|
||||
final int id = dumpCounter.getAndIncrement();
|
||||
final String suffix = isFragment ? ".frag.glsl" : ".vert.glsl";
|
||||
try {
|
||||
Files.createDirectories(DUMP_DIR);
|
||||
Files.createDirectories(dir);
|
||||
// Capture caller info for identification
|
||||
final String caller = identifyCaller();
|
||||
final String header = "// Compat shader dump #" + id + " (" + (isFragment ? "fragment" : "vertex") + ")"
|
||||
+ "\n// Transformed: " + wasTransformed
|
||||
+ "\n// Caller: " + caller + "\n\n";
|
||||
Files.writeString(DUMP_DIR.resolve(id + "_original" + suffix), header + original, StandardCharsets.UTF_8);
|
||||
Files.writeString(DUMP_DIR.resolve(id + "_transformed" + suffix), header + transformed, StandardCharsets.UTF_8);
|
||||
Files.writeString(dir.resolve(id + "_original" + suffix), header + original, StandardCharsets.UTF_8);
|
||||
Files.writeString(dir.resolve(id + "_transformed" + suffix), header + transformed, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Failed to dump compat shader: {}", e.getMessage());
|
||||
GLStateManager.LOGGER.warn("Failed to dump compat shader: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,10 +570,10 @@ public class CompatShaderTransformer {
|
||||
return source;
|
||||
}
|
||||
|
||||
// Patterns for parsing fragment shader in declarations to generate passthrough vertex shaders
|
||||
private static final Pattern FRAG_IN_COLOR = Pattern.compile("\\bin\\s+vec4\\s+angelica_FrontColor\\b");
|
||||
private static final Pattern FRAG_IN_TEXCOORD = Pattern.compile("\\bin\\s+vec4\\s+angelica_TexCoord(\\d+)\\b");
|
||||
private static final Pattern FRAG_IN_FOGCOORD = Pattern.compile("\\bin\\s+float\\s+angelica_FogFragCoord\\b");
|
||||
private static final String PRECISION_QUALIFIER = "(?:highp\\s+|mediump\\s+|lowp\\s+)?";
|
||||
private static final Pattern FRAG_IN_COLOR = Pattern.compile("\\bin\\s+" + PRECISION_QUALIFIER + "(vec[234])\\s+angelica_FrontColor\\b");
|
||||
private static final Pattern FRAG_IN_TEXCOORD = Pattern.compile("\\bin\\s+" + PRECISION_QUALIFIER + "(vec[234])\\s+angelica_TexCoord(\\d+)\\b");
|
||||
private static final Pattern FRAG_IN_FOGCOORD = Pattern.compile("\\bin\\s+" + PRECISION_QUALIFIER + "float\\s+angelica_FogFragCoord\\b");
|
||||
|
||||
/**
|
||||
* Generate a passthrough vertex shader for a fragment-only program.
|
||||
@@ -406,22 +594,26 @@ public class CompatShaderTransformer {
|
||||
|
||||
final StringBuilder varyings = new StringBuilder();
|
||||
final StringBuilder assignments = new StringBuilder();
|
||||
if (FRAG_IN_COLOR.matcher(fragmentSource).find()) {
|
||||
sb.append("layout(location = 1) in vec4 a_Color;\n");
|
||||
varyings.append("out vec4 angelica_FrontColor;\n");
|
||||
final Matcher colorMatcher = FRAG_IN_COLOR.matcher(fragmentSource);
|
||||
if (colorMatcher.find()) {
|
||||
final String colorType = colorMatcher.group(1);
|
||||
sb.append("layout(location = 1) in ").append(colorType).append(" a_Color;\n");
|
||||
varyings.append("out ").append(colorType).append(" angelica_FrontColor;\n");
|
||||
assignments.append(" angelica_FrontColor = a_Color;\n");
|
||||
}
|
||||
|
||||
final Matcher texCoordMatcher = FRAG_IN_TEXCOORD.matcher(fragmentSource);
|
||||
final Set<Integer> texCoordIndices = new HashSet<>();
|
||||
final Map<Integer, String> texCoordTypes = new HashMap<>();
|
||||
while (texCoordMatcher.find()) {
|
||||
texCoordIndices.add(Integer.parseInt(texCoordMatcher.group(1)));
|
||||
texCoordTypes.putIfAbsent(Integer.parseInt(texCoordMatcher.group(2)), texCoordMatcher.group(1));
|
||||
}
|
||||
for (int i : texCoordIndices) {
|
||||
for (Map.Entry<Integer, String> e : texCoordTypes.entrySet()) {
|
||||
final int i = e.getKey();
|
||||
final String type = e.getValue();
|
||||
// PRIMARY_UV=2, SECONDARY_UV=3. Only indices 0 and 1 are supported; higher indices collide at location 3.
|
||||
final int location = i == 0 ? 2 : 3;
|
||||
sb.append("layout(location = ").append(location).append(") in vec4 a_TexCoord").append(i).append(";\n");
|
||||
varyings.append("out vec4 angelica_TexCoord").append(i).append(";\n");
|
||||
sb.append("layout(location = ").append(location).append(") in ").append(type).append(" a_TexCoord").append(i).append(";\n");
|
||||
varyings.append("out ").append(type).append(" angelica_TexCoord").append(i).append(";\n");
|
||||
assignments.append(" angelica_TexCoord").append(i).append(" = a_TexCoord").append(i).append(";\n");
|
||||
}
|
||||
|
||||
@@ -439,6 +631,23 @@ public class CompatShaderTransformer {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** 330-core -> GLSL ES 320 via shaderc + SPIRV-Cross. Throws on shaderc failure. */
|
||||
public static String toGLES(String source, int glShaderType, boolean isFragment) {
|
||||
final Matcher m = VERSION_PATTERN.matcher(source);
|
||||
if (m.find() && "es".equalsIgnoreCase(m.group(2))) return source;
|
||||
if (glShaderType == 0) throw new IllegalArgumentException("toGLES requires a valid GL shader type");
|
||||
final String translated = SpirvShaderTranslator.glslToGlslEs(source, glShaderType, "compat");
|
||||
if (translated == null) {
|
||||
throw new IllegalStateException("SpirvShaderTranslator failed for shader kind 0x"
|
||||
+ Integer.toHexString(glShaderType) + "; see preceding shaderc error + run/client/angelica_dumps/compat/");
|
||||
}
|
||||
return translated;
|
||||
}
|
||||
|
||||
public static String toGLES(String source, boolean isFragment) {
|
||||
return toGLES(source, isFragment ? GL20.GL_FRAGMENT_SHADER : GL20.GL_VERTEX_SHADER, isFragment);
|
||||
}
|
||||
|
||||
/** Ensure #version is at least 330 core, strip 'compatibility' profile. */
|
||||
private static String fixupVersion(String source) {
|
||||
final Matcher m = VERSION_PATTERN.matcher(source);
|
||||
+153
-112
@@ -7,20 +7,25 @@ import com.gtnewhorizons.angelica.glsm.states.LightModelState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Per-program compat uniform location cache and upload manager.
|
||||
*
|
||||
* <p>Manages {@code angelica_*} uniforms injected by {@link CompatShaderTransformer} and {@code iris_*} matrix uniforms used by Iris/celeritas shader programs.
|
||||
* <p>Manages {@code angelica_*} uniforms injected by {@link CompatShaderTransformer} and {@code iris_*} matrix uniforms used by Iris/celeritas shader
|
||||
* programs.
|
||||
*/
|
||||
public class CompatUniformManager {
|
||||
|
||||
@@ -39,10 +44,11 @@ public class CompatUniformManager {
|
||||
static final int LOC_IRIS_PROJECTION = 11;
|
||||
static final int LOC_IRIS_NORMAL = 12;
|
||||
static final int LOC_IRIS_LIGHTMAP_TEXTURE_MATRIX = 13;
|
||||
static final int LOC_ALPHA_TEST_REF = 14;
|
||||
static final int LOC_SCENE_COLOR = 15;
|
||||
static final int LOC_CLIP_PLANES = 16;
|
||||
static final int LOC_CLIP_PLANES_ENABLED = 17;
|
||||
static final int LOC_IRIS_TEXTURE_MATRIX = 14;
|
||||
static final int LOC_ALPHA_TEST_REF = 15;
|
||||
static final int LOC_SCENE_COLOR = 16;
|
||||
static final int LOC_CLIP_PLANES = 17;
|
||||
static final int LOC_CLIP_PLANES_ENABLED = 18;
|
||||
|
||||
// Light source fields: 12 per light × 2 lights = 24 locations
|
||||
static final int LIGHT_FIELDS = 12;
|
||||
@@ -50,14 +56,17 @@ public class CompatUniformManager {
|
||||
static final int LF_HALF_VECTOR = 4, LF_SPOT_DIRECTION = 5, LF_SPOT_EXPONENT = 6;
|
||||
static final int LF_SPOT_CUTOFF = 7, LF_SPOT_COS_CUTOFF = 8;
|
||||
static final int LF_CONSTANT_ATTEN = 9, LF_LINEAR_ATTEN = 10, LF_QUADRATIC_ATTEN = 11;
|
||||
static final int LOC_LIGHT_BASE = 18;
|
||||
static final int LOC_LIGHT_BASE = 19;
|
||||
|
||||
// Material fields: 5 locations
|
||||
static final int MF_EMISSION = 0, MF_AMBIENT = 1, MF_DIFFUSE = 2, MF_SPECULAR = 3, MF_SHININESS = 4;
|
||||
static final int MAT_FIELDS = 5;
|
||||
static final int LOC_MAT_BASE = LOC_LIGHT_BASE + 2 * LIGHT_FIELDS; // 42
|
||||
static final int LOC_MAT_BASE = LOC_LIGHT_BASE + 2 * LIGHT_FIELDS; // 43
|
||||
|
||||
static final int LOC_COUNT = LOC_MAT_BASE + MAT_FIELDS; // 47
|
||||
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",
|
||||
@@ -69,6 +78,7 @@ public class CompatUniformManager {
|
||||
};
|
||||
|
||||
private static final String[] UNIFORM_NAMES;
|
||||
|
||||
static {
|
||||
UNIFORM_NAMES = new String[LOC_COUNT];
|
||||
UNIFORM_NAMES[LOC_MODELVIEW] = "angelica_ModelViewMatrix";
|
||||
@@ -85,14 +95,16 @@ public class CompatUniformManager {
|
||||
UNIFORM_NAMES[LOC_IRIS_PROJECTION] = "iris_ProjectionMatrix";
|
||||
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]";
|
||||
UNIFORM_NAMES[LOC_CLIP_PLANES_ENABLED] = "angelica_ClipPlanesEnabled";
|
||||
for (int li = 0; li < 2; li++) {
|
||||
for (int fi = 0; fi < LIGHT_FIELDS; fi++) {
|
||||
UNIFORM_NAMES[LOC_LIGHT_BASE + li * LIGHT_FIELDS + fi] =
|
||||
"angelica_LightSource[" + li + "]." + LIGHT_FIELD_NAMES[fi];
|
||||
UNIFORM_NAMES[LOC_LIGHT_BASE + li * LIGHT_FIELDS + fi] = "angelica_LightSource[" + li + "]." + LIGHT_FIELD_NAMES[fi];
|
||||
}
|
||||
}
|
||||
for (int fi = 0; fi < MAT_FIELDS; fi++) {
|
||||
@@ -100,8 +112,15 @@ public class CompatUniformManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-program cached uniform locations. Maps program ID → int[LOC_COUNT]. */
|
||||
private static final Int2ObjectOpenHashMap<int[]> programLocations = new Int2ObjectOpenHashMap<>();
|
||||
/** Per-program cached uniform locations + last-uploaded generations. Maps program ID -> state. */
|
||||
private static final Int2ObjectOpenHashMap<ProgramUniforms> programUniforms = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static final class ProgramUniforms {
|
||||
final int[] locs;
|
||||
int lastMvGen = -1, lastMvLinearGen = -1, lastProjGen = -1, lastTexMatGen = -1;
|
||||
int lastFragmentGen = -1, lastLightingGen = -1, lastClipPlaneGen = -1;
|
||||
ProgramUniforms(int[] locs) { this.locs = locs; }
|
||||
}
|
||||
|
||||
// Reusable NIO buffers for upload
|
||||
private static final FloatBuffer mat4Buf = BufferUtils.createFloatBuffer(16);
|
||||
@@ -110,74 +129,81 @@ public class CompatUniformManager {
|
||||
private static final FloatBuffer clipPlaneBuf = BufferUtils.createFloatBuffer(32); // 8 planes * vec4
|
||||
private static final Matrix3f normalMatrix = new Matrix3f();
|
||||
private static final Matrix4f scratchMatrix = new Matrix4f();
|
||||
private static final Vector3f scratchCol = new Vector3f();
|
||||
|
||||
private static final float LIGHTMAP_SCALE = 1.0f / 256.0f;
|
||||
private static final FloatBuffer lightmapMatrixBuf;
|
||||
|
||||
static {
|
||||
lightmapMatrixBuf = BufferUtils.createFloatBuffer(16);
|
||||
new Matrix4f().scale(LIGHTMAP_SCALE).translate(8.0f, 8.0f, 8.0f).get(lightmapMatrixBuf);
|
||||
}
|
||||
|
||||
// Dirty tracking: skip uploads when state hasn't changed and program is the same
|
||||
private static int lastProgram = -1;
|
||||
private static int lastMvGen = -1;
|
||||
private static int lastProjGen = -1;
|
||||
private static int lastTexMatGen = -1;
|
||||
private static int lastFragmentGen = -1;
|
||||
private static int lastLightingGen = -1;
|
||||
private static int lastClipPlaneGen = -1;
|
||||
|
||||
private CompatUniformManager() {}
|
||||
|
||||
/**
|
||||
* Normalize each column of a 3x3 matrix in place. Used to strip the residual 1/scale that
|
||||
* inverse-transpose introduces.
|
||||
*/
|
||||
private static void normalizeColumnsInPlace(Matrix3f m) {
|
||||
m.setColumn(0, m.getColumn(0, scratchCol).normalize());
|
||||
m.setColumn(1, m.getColumn(1, scratchCol).normalize());
|
||||
m.setColumn(2, m.getColumn(2, scratchCol).normalize());
|
||||
}
|
||||
|
||||
public static void onLinkProgram(int program) {
|
||||
int[] locs = new int[LOC_COUNT];
|
||||
boolean hasAny = false;
|
||||
|
||||
for (int i = 0; i < LOC_COUNT; i++) {
|
||||
locs[i] = GL20.glGetUniformLocation(program, UNIFORM_NAMES[i]);
|
||||
locs[i] = RENDER_BACKEND.getUniformLocation(program, UNIFORM_NAMES[i]);
|
||||
if (locs[i] != -1) hasAny = true;
|
||||
}
|
||||
|
||||
if (hasAny) {
|
||||
programLocations.put(program, locs);
|
||||
LOGGER.debug("CompatUniformManager: program {} has compat uniforms", program);
|
||||
programUniforms.put(program, new ProgramUniforms(locs));
|
||||
GLStateManager.LOGGER.debug("CompatUniformManager: program {} has compat uniforms", program);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onUseProgram(int program) {
|
||||
if (program == 0) return;
|
||||
/**
|
||||
* Uploads the emulated uniforms.
|
||||
*
|
||||
* @return Whether the program needs emulation (FFP or Iris Program)
|
||||
*/
|
||||
public static boolean refreshCompatUniforms(int program) {
|
||||
final ProgramUniforms pu = programUniforms.get(program);
|
||||
if (pu == null) return false;
|
||||
final int[] locs = pu.locs;
|
||||
|
||||
int[] locs = programLocations.get(program);
|
||||
if (locs == null) return;
|
||||
|
||||
final boolean programChanged = program != lastProgram;
|
||||
lastProgram = program;
|
||||
|
||||
// Matrix uniforms — skip if generation unchanged and same program
|
||||
final int mvGen = GLStateManager.mvGeneration;
|
||||
final int projGen = GLStateManager.projGeneration;
|
||||
final int texMatGen = GLStateManager.texMatrixGeneration;
|
||||
final boolean mvChanged = programChanged || mvGen != lastMvGen;
|
||||
final boolean projChanged = programChanged || projGen != lastProjGen;
|
||||
final boolean texMatChanged = programChanged || texMatGen != lastTexMatGen;
|
||||
// Matrix uniforms — skip if this program's storage already holds the current generation
|
||||
final int mvGen = GLStateManager.getMvGeneration();
|
||||
final int mvLinearGen = GLStateManager.getMvLinearGeneration();
|
||||
final int projGen = GLStateManager.getProjGeneration();
|
||||
final int texMatGen = GLStateManager.getTexMatrixGeneration();
|
||||
final boolean mvChanged = mvGen != pu.lastMvGen;
|
||||
final boolean mvLinearChanged = mvLinearGen != pu.lastMvLinearGen;
|
||||
final boolean projChanged = projGen != pu.lastProjGen;
|
||||
final boolean texMatChanged = texMatGen != pu.lastTexMatGen;
|
||||
if (mvChanged || projChanged || texMatChanged) {
|
||||
uploadMatrices(locs, mvChanged, projChanged, texMatChanged);
|
||||
lastMvGen = mvGen;
|
||||
lastProjGen = projGen;
|
||||
lastTexMatGen = texMatGen;
|
||||
uploadMatrices(locs, mvChanged, mvLinearChanged, projChanged, texMatChanged);
|
||||
pu.lastMvGen = mvGen;
|
||||
pu.lastMvLinearGen = mvLinearGen;
|
||||
pu.lastProjGen = projGen;
|
||||
pu.lastTexMatGen = texMatGen;
|
||||
}
|
||||
|
||||
// Fragment-category uniforms (fog, alpha) — skip if generation unchanged
|
||||
final int fragGen = GLStateManager.fragmentGeneration;
|
||||
if (programChanged || fragGen != lastFragmentGen) {
|
||||
lastFragmentGen = fragGen;
|
||||
final int fragGen = GLStateManager.getFragmentGeneration();
|
||||
if (fragGen != pu.lastFragmentGen) {
|
||||
pu.lastFragmentGen = fragGen;
|
||||
uploadFragmentUniforms(locs);
|
||||
}
|
||||
|
||||
// Lighting-derived uniforms (scene color, light sources, material)
|
||||
final int litGen = GLStateManager.lightingGeneration;
|
||||
if (programChanged || litGen != lastLightingGen) {
|
||||
lastLightingGen = litGen;
|
||||
final int litGen = GLStateManager.getLightingGeneration();
|
||||
if (litGen != pu.lastLightingGen) {
|
||||
pu.lastLightingGen = litGen;
|
||||
if (locs[LOC_SCENE_COLOR] != -1) uploadSceneColor(locs);
|
||||
uploadLightSources(locs);
|
||||
uploadMaterial(locs);
|
||||
@@ -185,15 +211,17 @@ public class CompatUniformManager {
|
||||
|
||||
// Clip plane equations + enabled bool — uploaded when enable state or equations change
|
||||
if (locs[LOC_CLIP_PLANES] != -1 || locs[LOC_CLIP_PLANES_ENABLED] != -1) {
|
||||
final int cpGen = GLStateManager.clipPlaneGeneration;
|
||||
if (programChanged || cpGen != lastClipPlaneGen) {
|
||||
lastClipPlaneGen = cpGen;
|
||||
final int cpGen = GLStateManager.getClipPlaneGeneration();
|
||||
if (cpGen != pu.lastClipPlaneGen) {
|
||||
pu.lastClipPlaneGen = cpGen;
|
||||
uploadClipPlanes(locs);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void uploadMatrices(int[] locs, boolean mvChanged, boolean projChanged, boolean texMatChanged) {
|
||||
private static void uploadMatrices(int[] locs, boolean mvChanged, boolean mvLinearChanged, boolean projChanged, boolean texMatChanged) {
|
||||
final Matrix4f mv = GLStateManager.getModelViewMatrix();
|
||||
final Matrix4f proj = GLStateManager.getProjectionMatrix();
|
||||
|
||||
@@ -202,29 +230,39 @@ public class CompatUniformManager {
|
||||
if (locs[LOC_MODELVIEW] != -1 || locs[LOC_IRIS_MODELVIEW] != -1) {
|
||||
mv.get(mat4Buf);
|
||||
if (locs[LOC_MODELVIEW] != -1) {
|
||||
GL20.glUniformMatrix4(locs[LOC_MODELVIEW], false, mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW], false, mat4Buf);
|
||||
}
|
||||
if (locs[LOC_IRIS_MODELVIEW] != -1) {
|
||||
GL20.glUniformMatrix4(locs[LOC_IRIS_MODELVIEW], false, mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_MODELVIEW], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
GL20.glUniformMatrix4(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mvLinearChanged) {
|
||||
// Normal Matrix (inverse-transpose of upper-left 3x3 of ModelView)
|
||||
if (locs[LOC_NORMAL] != -1 || locs[LOC_IRIS_NORMAL] != -1) {
|
||||
mv.normal(normalMatrix);
|
||||
normalMatrix.get(mat3Buf);
|
||||
if (locs[LOC_NORMAL] != -1) {
|
||||
GL20.glUniformMatrix3(locs[LOC_NORMAL], false, mat3Buf);
|
||||
normalMatrix.get(mat3Buf);
|
||||
RENDER_BACKEND.uniformMatrix3(locs[LOC_NORMAL], false, mat3Buf);
|
||||
}
|
||||
// Column-normalized to unit magnitude
|
||||
if (locs[LOC_IRIS_NORMAL] != -1) {
|
||||
GL20.glUniformMatrix3(locs[LOC_IRIS_NORMAL], false, mat3Buf);
|
||||
normalizeColumnsInPlace(normalMatrix);
|
||||
normalMatrix.get(mat3Buf);
|
||||
RENDER_BACKEND.uniformMatrix3(locs[LOC_IRIS_NORMAL], false, mat3Buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,31 +272,42 @@ public class CompatUniformManager {
|
||||
if (locs[LOC_PROJECTION] != -1 || locs[LOC_IRIS_PROJECTION] != -1) {
|
||||
proj.get(mat4Buf);
|
||||
if (locs[LOC_PROJECTION] != -1) {
|
||||
GL20.glUniformMatrix4(locs[LOC_PROJECTION], false, mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_PROJECTION], false, mat4Buf);
|
||||
}
|
||||
if (locs[LOC_IRIS_PROJECTION] != -1) {
|
||||
GL20.glUniformMatrix4(locs[LOC_IRIS_PROJECTION], false, mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_PROJECTION], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
GL20.glUniformMatrix4(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (texMatChanged) {
|
||||
// Iris lightmap texture matrix (constant)
|
||||
if (locs[LOC_IRIS_LIGHTMAP_TEXTURE_MATRIX] != -1) {
|
||||
GL20.glUniformMatrix4(locs[LOC_IRIS_LIGHTMAP_TEXTURE_MATRIX], false, lightmapMatrixBuf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_LIGHTMAP_TEXTURE_MATRIX], false, lightmapMatrixBuf);
|
||||
}
|
||||
|
||||
// angelica_LightmapTextureMatrix — actual GLSM texture unit 1 matrix (set by enableLightmap())
|
||||
if (locs[LOC_LIGHTMAP_TEXTURE_MATRIX] != -1) {
|
||||
GLStateManager.getTextures().getTextureUnitMatrix(1).get(mat4Buf);
|
||||
GL20.glUniformMatrix4(locs[LOC_LIGHTMAP_TEXTURE_MATRIX], false, mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_LIGHTMAP_TEXTURE_MATRIX], false, mat4Buf);
|
||||
}
|
||||
|
||||
// iris_TextureMatrix — texture unit 0 matrix (animated by glints, etc.)
|
||||
if (locs[LOC_IRIS_TEXTURE_MATRIX] != -1) {
|
||||
GLStateManager.getTextures().getTextureUnitMatrix(0).get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_TEXTURE_MATRIX], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,39 +316,36 @@ public class CompatUniformManager {
|
||||
// Fog uniforms
|
||||
FogStateStack fog = GLStateManager.getFogState();
|
||||
if (locs[LOC_FOG_DENSITY] != -1) {
|
||||
GL20.glUniform1f(locs[LOC_FOG_DENSITY], Math.max(0.0f, fog.getDensity()));
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_DENSITY], Math.max(0.0f, fog.getDensity()));
|
||||
}
|
||||
if (locs[LOC_FOG_START] != -1) {
|
||||
GL20.glUniform1f(locs[LOC_FOG_START], fog.getStart());
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_START], fog.getStart());
|
||||
}
|
||||
if (locs[LOC_FOG_END] != -1) {
|
||||
GL20.glUniform1f(locs[LOC_FOG_END], fog.getEnd());
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_END], fog.getEnd());
|
||||
}
|
||||
if (locs[LOC_FOG_COLOR] != -1) {
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put((float) fog.getFogColor().x).put((float) fog.getFogColor().y)
|
||||
.put((float) fog.getFogColor().z).put(fog.getFogAlpha());
|
||||
vec4Buf.put((float) fog.getFogColor().x).put((float) fog.getFogColor().y).put((float) fog.getFogColor().z).put(fog.getFogAlpha());
|
||||
vec4Buf.flip();
|
||||
GL20.glUniform4(locs[LOC_FOG_COLOR], vec4Buf);
|
||||
RENDER_BACKEND.uniform4(locs[LOC_FOG_COLOR], vec4Buf);
|
||||
}
|
||||
|
||||
// Alpha test reference — core profile replacement for GL_ALPHA_TEST
|
||||
// Return -1.0f for ALWAYS or disabled so `alpha <= ref` never discards.
|
||||
if (locs[LOC_ALPHA_TEST_REF] != -1) {
|
||||
final float ref;
|
||||
if (!GLStateManager.getAlphaTest().isEnabled()
|
||||
|| GLStateManager.getAlphaState().getFunction() == GL11.GL_ALWAYS) {
|
||||
if (!GLStateManager.getAlphaTest().isEnabled() || GLStateManager.getAlphaState().getFunction() == GL11.GL_ALWAYS) {
|
||||
ref = -1.0f;
|
||||
} else {
|
||||
ref = GLStateManager.getAlphaState().getReference();
|
||||
}
|
||||
GL20.glUniform1f(locs[LOC_ALPHA_TEST_REF], ref);
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_ALPHA_TEST_REF], ref);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scene color = emission + ambient * lightModel.ambient, alpha = diffuse.a
|
||||
* Same computation as {@code Uniforms.uploadLightingPreComputed()}.
|
||||
* Scene color = emission + ambient * lightModel.ambient, alpha = diffuse.a Same computation as {@code Uniforms.uploadLightingPreComputed()}.
|
||||
*/
|
||||
private static void uploadSceneColor(int[] locs) {
|
||||
final MaterialState mat = GLStateManager.getFrontMaterial();
|
||||
@@ -310,7 +356,7 @@ public class CompatUniformManager {
|
||||
.put(mat.emission.z + mat.ambient.z * lm.ambient.z)
|
||||
.put(mat.diffuse.w);
|
||||
vec4Buf.flip();
|
||||
GL20.glUniform4(locs[LOC_SCENE_COLOR], vec4Buf);
|
||||
RENDER_BACKEND.uniform4(locs[LOC_SCENE_COLOR], vec4Buf);
|
||||
}
|
||||
|
||||
private static void uploadLightSources(int[] locs) {
|
||||
@@ -322,19 +368,20 @@ public class CompatUniformManager {
|
||||
final LightState light = lights[li];
|
||||
|
||||
if (locs[base + LF_AMBIENT] != -1)
|
||||
GL20.glUniform4f(locs[base + LF_AMBIENT], light.ambient.x, light.ambient.y, light.ambient.z, light.ambient.w);
|
||||
RENDER_BACKEND.uniform4f(locs[base + LF_AMBIENT], light.ambient.x, light.ambient.y, light.ambient.z, light.ambient.w);
|
||||
if (locs[base + LF_DIFFUSE] != -1)
|
||||
GL20.glUniform4f(locs[base + LF_DIFFUSE], light.diffuse.x, light.diffuse.y, light.diffuse.z, light.diffuse.w);
|
||||
RENDER_BACKEND.uniform4f(locs[base + LF_DIFFUSE], light.diffuse.x, light.diffuse.y, light.diffuse.z, light.diffuse.w);
|
||||
if (locs[base + LF_SPECULAR] != -1)
|
||||
GL20.glUniform4f(locs[base + LF_SPECULAR], light.specular.x, light.specular.y, light.specular.z, light.specular.w);
|
||||
RENDER_BACKEND.uniform4f(locs[base + LF_SPECULAR], light.specular.x, light.specular.y, light.specular.z, light.specular.w);
|
||||
if (locs[base + LF_POSITION] != -1)
|
||||
GL20.glUniform4f(locs[base + LF_POSITION], light.position.x, light.position.y, light.position.z, light.position.w);
|
||||
RENDER_BACKEND.uniform4f(locs[base + LF_POSITION], light.position.x, light.position.y, light.position.z, light.position.w);
|
||||
if (locs[base + LF_HALF_VECTOR] != -1) {
|
||||
// halfVector = normalize(normalize(position.xyz) + eye), eye=(0,0,1) for infinite viewer
|
||||
float hx, hy, hz;
|
||||
if (light.position.w == 0.0f) {
|
||||
float len = (float) Math.sqrt(light.position.x * light.position.x
|
||||
+ light.position.y * light.position.y + light.position.z * light.position.z);
|
||||
final float len = (float) Math.sqrt(light.position.x * light.position.x
|
||||
+ light.position.y * light.position.y
|
||||
+ light.position.z * light.position.z);
|
||||
if (len > 0) {
|
||||
hx = light.position.x / len;
|
||||
hy = light.position.y / len;
|
||||
@@ -345,24 +392,17 @@ public class CompatUniformManager {
|
||||
} else {
|
||||
hx = 0; hy = 0; hz = 1;
|
||||
}
|
||||
float hlen = (float) Math.sqrt(hx * hx + hy * hy + hz * hz);
|
||||
final float hlen = (float) Math.sqrt(hx * hx + hy * hy + hz * hz);
|
||||
if (hlen > 0) { hx /= hlen; hy /= hlen; hz /= hlen; }
|
||||
GL20.glUniform4f(locs[base + LF_HALF_VECTOR], hx, hy, hz, 0.0f);
|
||||
RENDER_BACKEND.uniform4f(locs[base + LF_HALF_VECTOR], hx, hy, hz, 0.0f);
|
||||
}
|
||||
if (locs[base + LF_SPOT_DIRECTION] != -1)
|
||||
GL20.glUniform3f(locs[base + LF_SPOT_DIRECTION], light.spotDirection.x, light.spotDirection.y, light.spotDirection.z);
|
||||
if (locs[base + LF_SPOT_EXPONENT] != -1)
|
||||
GL20.glUniform1f(locs[base + LF_SPOT_EXPONENT], light.spotExponent);
|
||||
if (locs[base + LF_SPOT_CUTOFF] != -1)
|
||||
GL20.glUniform1f(locs[base + LF_SPOT_CUTOFF], light.spotCutoff);
|
||||
if (locs[base + LF_SPOT_COS_CUTOFF] != -1)
|
||||
GL20.glUniform1f(locs[base + LF_SPOT_COS_CUTOFF], light.spotCosCutoff);
|
||||
if (locs[base + LF_CONSTANT_ATTEN] != -1)
|
||||
GL20.glUniform1f(locs[base + LF_CONSTANT_ATTEN], light.constantAttenuation);
|
||||
if (locs[base + LF_LINEAR_ATTEN] != -1)
|
||||
GL20.glUniform1f(locs[base + LF_LINEAR_ATTEN], light.linearAttenuation);
|
||||
if (locs[base + LF_QUADRATIC_ATTEN] != -1)
|
||||
GL20.glUniform1f(locs[base + LF_QUADRATIC_ATTEN], light.quadraticAttenuation);
|
||||
if (locs[base + LF_SPOT_DIRECTION] != -1) RENDER_BACKEND.uniform3f(locs[base + LF_SPOT_DIRECTION], light.spotDirection.x, light.spotDirection.y, light.spotDirection.z);
|
||||
if (locs[base + LF_SPOT_EXPONENT] != -1) RENDER_BACKEND.uniform1f(locs[base + LF_SPOT_EXPONENT], light.spotExponent);
|
||||
if (locs[base + LF_SPOT_CUTOFF] != -1) RENDER_BACKEND.uniform1f(locs[base + LF_SPOT_CUTOFF], light.spotCutoff);
|
||||
if (locs[base + LF_SPOT_COS_CUTOFF] != -1) RENDER_BACKEND.uniform1f(locs[base + LF_SPOT_COS_CUTOFF], light.spotCosCutoff);
|
||||
if (locs[base + LF_CONSTANT_ATTEN] != -1) RENDER_BACKEND.uniform1f(locs[base + LF_CONSTANT_ATTEN], light.constantAttenuation);
|
||||
if (locs[base + LF_LINEAR_ATTEN] != -1) RENDER_BACKEND.uniform1f(locs[base + LF_LINEAR_ATTEN], light.linearAttenuation);
|
||||
if (locs[base + LF_QUADRATIC_ATTEN] != -1) RENDER_BACKEND.uniform1f(locs[base + LF_QUADRATIC_ATTEN], light.quadraticAttenuation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,20 +411,20 @@ public class CompatUniformManager {
|
||||
if (locs[LOC_MAT_BASE] == -1) return;
|
||||
final MaterialState mat = GLStateManager.getFrontMaterial();
|
||||
if (locs[LOC_MAT_BASE + MF_EMISSION] != -1)
|
||||
GL20.glUniform4f(locs[LOC_MAT_BASE + MF_EMISSION], mat.emission.x, mat.emission.y, mat.emission.z, mat.emission.w);
|
||||
RENDER_BACKEND.uniform4f(locs[LOC_MAT_BASE + MF_EMISSION], mat.emission.x, mat.emission.y, mat.emission.z, mat.emission.w);
|
||||
if (locs[LOC_MAT_BASE + MF_AMBIENT] != -1)
|
||||
GL20.glUniform4f(locs[LOC_MAT_BASE + MF_AMBIENT], mat.ambient.x, mat.ambient.y, mat.ambient.z, mat.ambient.w);
|
||||
RENDER_BACKEND.uniform4f(locs[LOC_MAT_BASE + MF_AMBIENT], mat.ambient.x, mat.ambient.y, mat.ambient.z, mat.ambient.w);
|
||||
if (locs[LOC_MAT_BASE + MF_DIFFUSE] != -1)
|
||||
GL20.glUniform4f(locs[LOC_MAT_BASE + MF_DIFFUSE], mat.diffuse.x, mat.diffuse.y, mat.diffuse.z, mat.diffuse.w);
|
||||
RENDER_BACKEND.uniform4f(locs[LOC_MAT_BASE + MF_DIFFUSE], mat.diffuse.x, mat.diffuse.y, mat.diffuse.z, mat.diffuse.w);
|
||||
if (locs[LOC_MAT_BASE + MF_SPECULAR] != -1)
|
||||
GL20.glUniform4f(locs[LOC_MAT_BASE + MF_SPECULAR], mat.specular.x, mat.specular.y, mat.specular.z, mat.specular.w);
|
||||
RENDER_BACKEND.uniform4f(locs[LOC_MAT_BASE + MF_SPECULAR], mat.specular.x, mat.specular.y, mat.specular.z, mat.specular.w);
|
||||
if (locs[LOC_MAT_BASE + MF_SHININESS] != -1)
|
||||
GL20.glUniform1f(locs[LOC_MAT_BASE + MF_SHININESS], mat.shininess);
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_MAT_BASE + MF_SHININESS], mat.shininess);
|
||||
}
|
||||
|
||||
private static void uploadClipPlanes(int[] locs) {
|
||||
if (locs[LOC_CLIP_PLANES_ENABLED] != -1) {
|
||||
GL20.glUniform1i(locs[LOC_CLIP_PLANES_ENABLED], GLStateManager.anyClipPlaneEnabled() ? 1 : 0);
|
||||
RENDER_BACKEND.uniform1i(locs[LOC_CLIP_PLANES_ENABLED], GLStateManager.anyClipPlaneEnabled() ? 1 : 0);
|
||||
}
|
||||
if (locs[LOC_CLIP_PLANES] != -1) {
|
||||
final ClipPlaneState cps = GLStateManager.getClipPlaneState();
|
||||
@@ -393,19 +433,20 @@ public class CompatUniformManager {
|
||||
cps.putEyePlane(i, clipPlaneBuf);
|
||||
}
|
||||
clipPlaneBuf.flip();
|
||||
GL20.glUniform4(locs[LOC_CLIP_PLANES], clipPlaneBuf);
|
||||
RENDER_BACKEND.uniform4(locs[LOC_CLIP_PLANES], clipPlaneBuf);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onDeleteProgram(int program) {
|
||||
programLocations.remove(program);
|
||||
programUniforms.remove(program);
|
||||
}
|
||||
|
||||
public static boolean hasProgram(int program) {
|
||||
return programLocations.containsKey(program);
|
||||
return programUniforms.containsKey(program);
|
||||
}
|
||||
|
||||
public static int[] getLocations(int program) {
|
||||
return programLocations.get(program);
|
||||
final ProgramUniforms pu = programUniforms.get(program);
|
||||
return pu == null ? null : pu.locs;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File diff suppressed because it is too large.
Load diff
+129
-126
@@ -76,29 +76,30 @@ public class Feature {
|
||||
*/
|
||||
private static void addTextureGenStates(Set<IStateStack<?>> collection) {
|
||||
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
|
||||
collection.add(GLStateManager.textures.getTexGenSStates(i));
|
||||
collection.add(GLStateManager.textures.getTexGenTStates(i));
|
||||
collection.add(GLStateManager.textures.getTexGenRStates(i));
|
||||
collection.add(GLStateManager.textures.getTexGenQStates(i));
|
||||
collection.add(GLStateManager.getTextures().getTexGenSStates(i));
|
||||
collection.add(GLStateManager.getTextures().getTexGenTStates(i));
|
||||
collection.add(GLStateManager.getTextures().getTexGenRStates(i));
|
||||
collection.add(GLStateManager.getTextures().getTexGenQStates(i));
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
attribToFeatures.put(GL11.GL_COLOR_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.alphaTest // GL_ALPHA_TEST enable bit
|
||||
, GLStateManager.alphaState // Alpha test function and reference value
|
||||
, GLStateManager.blendMode // GL_BLEND enable bit
|
||||
, GLStateManager.blendState // Blending source/destination functions, equation, blend color
|
||||
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP enable bit
|
||||
, GLStateManager.ditherState // GL_DITHER enable bit
|
||||
, GLStateManager.drawBuffer // GL_DRAW_BUFFER setting
|
||||
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP enable bit
|
||||
, GLStateManager.logicOpMode // Logic op function
|
||||
, GLStateManager.colorMask // Color-mode and index-mode writemasks
|
||||
, GLStateManager.clearColor // Color-mode and index-mode clear values
|
||||
GLStateManager.getAlphaTest()
|
||||
, GLStateManager.getAlphaState()
|
||||
, GLStateManager.getBlendMode()
|
||||
, GLStateManager.getBlendState()
|
||||
, GLStateManager.getColorLogicOpState()
|
||||
, GLStateManager.getDitherState()
|
||||
, GLStateManager.getDrawBuffer()
|
||||
, GLStateManager.getIndexLogicOpState()
|
||||
, GLStateManager.getLogicOpMode()
|
||||
, GLStateManager.getColorMask()
|
||||
, GLStateManager.getClearColor()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_CURRENT_BIT, ImmutableSet.of(
|
||||
GLStateManager.color // Current RGBA color
|
||||
GLStateManager.getColor()
|
||||
, GLStateManager.getSecondaryColor() // Current secondary color
|
||||
, ShaderManager.getNormalStack() // Current normal vector
|
||||
, ShaderManager.getTexCoordStack() // Current texture coordinates
|
||||
// Current color index
|
||||
@@ -110,78 +111,79 @@ public class Feature {
|
||||
// GL_EDGE_FLAG flag
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_DEPTH_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.depthTest // GL_DEPTH_TEST enable bit
|
||||
, GLStateManager.depthState // Depth buffer test function
|
||||
GLStateManager.getDepthTest()
|
||||
, GLStateManager.getDepthState()
|
||||
// Depth buffer clear value
|
||||
// GL_DEPTH_WRITEMASK enable bit
|
||||
));
|
||||
|
||||
final HashSet<IStateStack<?>> enableBits = new HashSet<>(ImmutableSet.of(
|
||||
GLStateManager.alphaTest // GL_ALPHA_TEST flag
|
||||
, GLStateManager.autoNormalState // GL_AUTO_NORMAL flag
|
||||
, GLStateManager.blendMode // GL_BLEND flag
|
||||
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP flag
|
||||
, GLStateManager.colorMaterial // GL_COLOR_MATERIAL
|
||||
, GLStateManager.cullState // GL_CULL_FACE flag
|
||||
, GLStateManager.depthTest // GL_DEPTH_TEST flag
|
||||
, GLStateManager.ditherState // GL_DITHER flag
|
||||
, GLStateManager.fogMode // GL_FOG flag
|
||||
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP flag
|
||||
, GLStateManager.lightStates[0] // GL_LIGHT0
|
||||
, GLStateManager.lightStates[1] // GL_LIGHT1
|
||||
, GLStateManager.lightStates[2] // GL_LIGHT2
|
||||
, GLStateManager.lightStates[3] // GL_LIGHT3
|
||||
, GLStateManager.lightStates[4] // GL_LIGHT4
|
||||
, GLStateManager.lightStates[5] // GL_LIGHT5
|
||||
, GLStateManager.lightStates[6] // GL_LIGHT6
|
||||
, GLStateManager.lightStates[7] // GL_LIGHT7
|
||||
, GLStateManager.lightingState // GL_LIGHTING flag
|
||||
, GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
|
||||
, GLStateManager.lineStippleState // GL_LINE_STIPPLE flag
|
||||
, GLStateManager.map1Color4State // GL_MAP1_COLOR_4
|
||||
, GLStateManager.map1IndexState // GL_MAP1_INDEX
|
||||
, GLStateManager.map1NormalState // GL_MAP1_NORMAL
|
||||
, GLStateManager.map1TextureCoord1State // GL_MAP1_TEXTURE_COORD_1
|
||||
, GLStateManager.map1TextureCoord2State // GL_MAP1_TEXTURE_COORD_2
|
||||
, GLStateManager.map1TextureCoord3State // GL_MAP1_TEXTURE_COORD_3
|
||||
, GLStateManager.map1TextureCoord4State // GL_MAP1_TEXTURE_COORD_4
|
||||
, GLStateManager.map1Vertex3State // GL_MAP1_VERTEX_3
|
||||
, GLStateManager.map1Vertex4State // GL_MAP1_VERTEX_4
|
||||
, GLStateManager.map2Color4State // GL_MAP2_COLOR_4
|
||||
, GLStateManager.map2IndexState // GL_MAP2_INDEX
|
||||
, GLStateManager.map2NormalState // GL_MAP2_NORMAL
|
||||
, GLStateManager.map2TextureCoord1State // GL_MAP2_TEXTURE_COORD_1
|
||||
, GLStateManager.map2TextureCoord2State // GL_MAP2_TEXTURE_COORD_2
|
||||
, GLStateManager.map2TextureCoord3State // GL_MAP2_TEXTURE_COORD_3
|
||||
, GLStateManager.map2TextureCoord4State // GL_MAP2_TEXTURE_COORD_4
|
||||
, GLStateManager.map2Vertex3State // GL_MAP2_VERTEX_3
|
||||
, GLStateManager.map2Vertex4State // GL_MAP2_VERTEX_4
|
||||
, GLStateManager.multisampleState // GL_MULTISAMPLE flag
|
||||
, GLStateManager.normalizeState // GL_NORMALIZE flag
|
||||
, GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
|
||||
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
|
||||
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
|
||||
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
|
||||
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
|
||||
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE flag
|
||||
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
|
||||
, GLStateManager.sampleAlphaToCoverageState // GL_SAMPLE_ALPHA_TO_COVERAGE flag
|
||||
, GLStateManager.sampleAlphaToOneState // GL_SAMPLE_ALPHA_TO_ONE flag
|
||||
, GLStateManager.sampleCoverageState // GL_SAMPLE_COVERAGE flag
|
||||
, GLStateManager.scissorTest // GL_SCISSOR_TEST flag
|
||||
, GLStateManager.stencilTest // GL_STENCIL_TEST flag
|
||||
GLStateManager.getAlphaTest()
|
||||
, GLStateManager.getAutoNormalState()
|
||||
, GLStateManager.getBlendMode()
|
||||
, GLStateManager.getColorLogicOpState()
|
||||
, GLStateManager.getColorMaterial()
|
||||
, GLStateManager.getColorSumState()
|
||||
, GLStateManager.getCullState()
|
||||
, GLStateManager.getDepthTest()
|
||||
, GLStateManager.getDitherState()
|
||||
, GLStateManager.getFogMode()
|
||||
, GLStateManager.getIndexLogicOpState()
|
||||
, GLStateManager.getLightStates()[0] // GL_LIGHT0
|
||||
, GLStateManager.getLightStates()[1] // GL_LIGHT1
|
||||
, GLStateManager.getLightStates()[2] // GL_LIGHT2
|
||||
, GLStateManager.getLightStates()[3] // GL_LIGHT3
|
||||
, GLStateManager.getLightStates()[4] // GL_LIGHT4
|
||||
, GLStateManager.getLightStates()[5] // GL_LIGHT5
|
||||
, GLStateManager.getLightStates()[6] // GL_LIGHT6
|
||||
, GLStateManager.getLightStates()[7] // GL_LIGHT7
|
||||
, GLStateManager.getLightingState()
|
||||
, GLStateManager.getLineSmoothState()
|
||||
, GLStateManager.getLineStippleState()
|
||||
, GLStateManager.getMap1Color4State()
|
||||
, GLStateManager.getMap1IndexState()
|
||||
, GLStateManager.getMap1NormalState()
|
||||
, GLStateManager.getMap1TextureCoord1State()
|
||||
, GLStateManager.getMap1TextureCoord2State()
|
||||
, GLStateManager.getMap1TextureCoord3State()
|
||||
, GLStateManager.getMap1TextureCoord4State()
|
||||
, GLStateManager.getMap1Vertex3State()
|
||||
, GLStateManager.getMap1Vertex4State()
|
||||
, GLStateManager.getMap2Color4State()
|
||||
, GLStateManager.getMap2IndexState()
|
||||
, GLStateManager.getMap2NormalState()
|
||||
, GLStateManager.getMap2TextureCoord1State()
|
||||
, GLStateManager.getMap2TextureCoord2State()
|
||||
, GLStateManager.getMap2TextureCoord3State()
|
||||
, GLStateManager.getMap2TextureCoord4State()
|
||||
, GLStateManager.getMap2Vertex3State()
|
||||
, GLStateManager.getMap2Vertex4State()
|
||||
, GLStateManager.getMultisampleState()
|
||||
, GLStateManager.getNormalizeState()
|
||||
, GLStateManager.getPointSmoothState()
|
||||
, GLStateManager.getPolygonOffsetPointState()
|
||||
, GLStateManager.getPolygonOffsetLineState()
|
||||
, GLStateManager.getPolygonOffsetFillState()
|
||||
, GLStateManager.getPolygonSmoothState()
|
||||
, GLStateManager.getPolygonStippleState()
|
||||
, GLStateManager.getRescaleNormalState()
|
||||
, GLStateManager.getSampleAlphaToCoverageState()
|
||||
, GLStateManager.getSampleAlphaToOneState()
|
||||
, GLStateManager.getSampleCoverageState()
|
||||
, GLStateManager.getScissorTest()
|
||||
, GLStateManager.getStencilTest()
|
||||
));
|
||||
|
||||
// Enable bits for the user-definable clipping planes
|
||||
for(int i = 0; i < GLStateManager.clipPlaneStates.length; i++) {
|
||||
enableBits.add(GLStateManager.clipPlaneStates[i]);
|
||||
for(int i = 0; i < GLStateManager.getClipPlaneStates().length; i++) {
|
||||
enableBits.add(GLStateManager.getClipPlaneStates()[i]);
|
||||
}
|
||||
|
||||
// GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D flags
|
||||
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
|
||||
enableBits.add(GLStateManager.textures.getTexture1DStates(i));
|
||||
enableBits.add(GLStateManager.textures.getTextureUnitStates(i));
|
||||
enableBits.add(GLStateManager.textures.getTexture3DStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTexture1DStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTextureUnitStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTexture3DStates(i));
|
||||
}
|
||||
|
||||
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
@@ -196,8 +198,9 @@ public class Feature {
|
||||
// GL_AUTO_NORMAL enable bit
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_FOG_BIT, ImmutableSet.of(
|
||||
GLStateManager.fogMode // GL_FOG enable bit
|
||||
, GLStateManager.fogState // Fog color
|
||||
GLStateManager.getFogMode()
|
||||
, GLStateManager.getColorSumState()
|
||||
, GLStateManager.getFogState()
|
||||
// ^^ Fog density
|
||||
// ^^ Linear fog start
|
||||
// ^^ Linear fog end
|
||||
@@ -214,42 +217,42 @@ public class Feature {
|
||||
// GL_TEXTURE_COMPRESSION_HINT setting
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LIGHTING_BIT, ImmutableSet.of(
|
||||
GLStateManager.colorMaterial // GL_COLOR_MATERIAL enable bit
|
||||
, GLStateManager.colorMaterialFace // GL_COLOR_MATERIAL_FACE value
|
||||
, GLStateManager.colorMaterialParameter // Color material parameters that are tracking the current color
|
||||
, GLStateManager.lightModel // Ambient scene color, GL_LIGHT_MODEL_LOCAL_VIEWER, GL_LIGHT_MODEL_TWO_SIDE
|
||||
, GLStateManager.lightingState // GL_LIGHTING enable bit
|
||||
GLStateManager.getColorMaterial()
|
||||
, GLStateManager.getColorMaterialFace()
|
||||
, GLStateManager.getColorMaterialParameter()
|
||||
, GLStateManager.getLightModel()
|
||||
, GLStateManager.getLightingState()
|
||||
// Enable bit for each light
|
||||
, GLStateManager.lightStates[0] // GL_LIGHT0
|
||||
, GLStateManager.lightStates[1] // GL_LIGHT1
|
||||
, GLStateManager.lightStates[2] // GL_LIGHT2
|
||||
, GLStateManager.lightStates[3] // GL_LIGHT3
|
||||
, GLStateManager.lightStates[4] // GL_LIGHT4
|
||||
, GLStateManager.lightStates[5] // GL_LIGHT5
|
||||
, GLStateManager.lightStates[6] // GL_LIGHT6
|
||||
, GLStateManager.lightStates[7] // GL_LIGHT7
|
||||
, GLStateManager.getLightStates()[0] // GL_LIGHT0
|
||||
, GLStateManager.getLightStates()[1] // GL_LIGHT1
|
||||
, GLStateManager.getLightStates()[2] // GL_LIGHT2
|
||||
, GLStateManager.getLightStates()[3] // GL_LIGHT3
|
||||
, GLStateManager.getLightStates()[4] // GL_LIGHT4
|
||||
, GLStateManager.getLightStates()[5] // GL_LIGHT5
|
||||
, GLStateManager.getLightStates()[6] // GL_LIGHT6
|
||||
, GLStateManager.getLightStates()[7] // GL_LIGHT7
|
||||
// Ambient, diffuse, and specular intensity for each light
|
||||
// Direction, position, exponent, and cutoff angle for each light
|
||||
// Constant, linear, and quadratic attenuation factors for each light
|
||||
, GLStateManager.lightDataStates[0]
|
||||
, GLStateManager.lightDataStates[1]
|
||||
, GLStateManager.lightDataStates[2]
|
||||
, GLStateManager.lightDataStates[3]
|
||||
, GLStateManager.lightDataStates[4]
|
||||
, GLStateManager.lightDataStates[5]
|
||||
, GLStateManager.lightDataStates[6]
|
||||
, GLStateManager.lightDataStates[7]
|
||||
, GLStateManager.getLightDataStates()[0]
|
||||
, GLStateManager.getLightDataStates()[1]
|
||||
, GLStateManager.getLightDataStates()[2]
|
||||
, GLStateManager.getLightDataStates()[3]
|
||||
, GLStateManager.getLightDataStates()[4]
|
||||
, GLStateManager.getLightDataStates()[5]
|
||||
, GLStateManager.getLightDataStates()[6]
|
||||
, GLStateManager.getLightDataStates()[7]
|
||||
// Ambient, diffuse, specular, and emissive color for each material
|
||||
// Ambient, diffuse, and specular color indices for each material
|
||||
// Specular exponent for each material
|
||||
, GLStateManager.frontMaterial
|
||||
, GLStateManager.backMaterial
|
||||
, GLStateManager.shadeModelState // GL_SHADE_MODEL setting
|
||||
, GLStateManager.getFrontMaterial()
|
||||
, GLStateManager.getBackMaterial()
|
||||
, GLStateManager.getShadeModelState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LINE_BIT, ImmutableSet.of(
|
||||
GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
|
||||
, GLStateManager.lineStippleState // GL_LINE_STIPPLE enable bit
|
||||
, GLStateManager.lineState // Line stipple pattern, repeat counter, and width
|
||||
GLStateManager.getLineSmoothState()
|
||||
, GLStateManager.getLineStippleState()
|
||||
, GLStateManager.getLineState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LIST_BIT, ImmutableSet.of(
|
||||
// GL_LIST_BASE setting
|
||||
@@ -274,31 +277,31 @@ public class Feature {
|
||||
// GL_READ_BUFFER setting
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POINT_BIT, ImmutableSet.of(
|
||||
GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
|
||||
, GLStateManager.pointState // Point size
|
||||
GLStateManager.getPointSmoothState()
|
||||
, GLStateManager.getPointState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POLYGON_BIT, ImmutableSet.of(
|
||||
GLStateManager.cullState // GL_CULL_FACE enable bit
|
||||
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
|
||||
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE enable bit
|
||||
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
|
||||
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
|
||||
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
|
||||
, GLStateManager.polygonState // GL_CULL_FACE_MODE, GL_FRONT_FACE, GL_POLYGON_MODE, GL_POLYGON_OFFSET_FACTOR/UNITS
|
||||
GLStateManager.getCullState()
|
||||
, GLStateManager.getPolygonSmoothState()
|
||||
, GLStateManager.getPolygonStippleState()
|
||||
, GLStateManager.getPolygonOffsetFillState()
|
||||
, GLStateManager.getPolygonOffsetLineState()
|
||||
, GLStateManager.getPolygonOffsetPointState()
|
||||
, GLStateManager.getPolygonState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POLYGON_STIPPLE_BIT, ImmutableSet.of(
|
||||
// Polygon stipple pattern
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_SCISSOR_BIT, ImmutableSet.of(
|
||||
GLStateManager.scissorTest // GL_SCISSOR_TEST enable bit
|
||||
GLStateManager.getScissorTest()
|
||||
// Scissor box
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_STENCIL_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.stencilTest // GL_STENCIL_TEST enable bit
|
||||
, GLStateManager.stencilState // Stencil function, ref, mask, ops, writemask, clear value
|
||||
GLStateManager.getStencilTest()
|
||||
, GLStateManager.getStencilState()
|
||||
));
|
||||
final Set<IStateStack<?>> textureAttribs = new HashSet<>(ImmutableSet.of(
|
||||
GLStateManager.activeTextureUnit // Active texture unit
|
||||
GLStateManager.getActiveTextureUnitStack()
|
||||
// GL_TEXTURE_ENV_MODE + GL_TEXTURE_ENV_COLOR — now per-unit in TexEnvState (added below)
|
||||
// Enable bits for the four texture coordinates
|
||||
|
||||
@@ -315,8 +318,8 @@ public class Feature {
|
||||
|
||||
// Current Texture Bindings - GL_TEXTURE_BINDING_2D + per-unit TexEnvState
|
||||
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
|
||||
textureAttribs.add(GLStateManager.textures.getTextureUnitBindings(i));
|
||||
textureAttribs.add(GLStateManager.textures.getTexEnvState(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTextureUnitBindings(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTexEnvState(i));
|
||||
}
|
||||
|
||||
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
@@ -326,14 +329,14 @@ public class Feature {
|
||||
|
||||
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, ImmutableSet.of(
|
||||
// Coefficients of the six clipping planes
|
||||
// Enable bits for the user-definable clipping planes
|
||||
GLStateManager.matrixMode
|
||||
, GLStateManager.normalizeState // GL_NORMALIZE flag
|
||||
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
|
||||
|
||||
GLStateManager.getMatrixMode()
|
||||
, GLStateManager.getNormalizeState()
|
||||
, GLStateManager.getRescaleNormalState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_VIEWPORT_BIT, ImmutableSet.of(
|
||||
// Depth range (near and far)
|
||||
GLStateManager.viewportState
|
||||
|
||||
GLStateManager.getViewportState()
|
||||
));
|
||||
}
|
||||
|
||||
+51
-13
@@ -1,9 +1,8 @@
|
||||
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 com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -15,6 +14,8 @@ 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
|
||||
*/
|
||||
@@ -39,6 +40,7 @@ public class FeedbackManager {
|
||||
|
||||
@FunctionalInterface
|
||||
private interface IndexReader {
|
||||
|
||||
int read(int i);
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ public class FeedbackManager {
|
||||
|
||||
public static void glFeedbackBuffer(int type, FloatBuffer buffer) {
|
||||
if (renderMode == GL11.GL_FEEDBACK) {
|
||||
AngelicaTweaker.LOGGER.error("glFeedbackBuffer called while already in feedback mode");
|
||||
GLStateManager.LOGGER.error("glFeedbackBuffer called while already in feedback mode");
|
||||
return;
|
||||
}
|
||||
feedbackBuffer = buffer;
|
||||
@@ -65,7 +67,7 @@ public class FeedbackManager {
|
||||
};
|
||||
|
||||
if (mode == GL11.GL_SELECT) {
|
||||
AngelicaTweaker.LOGGER.warn("glRenderMode(GL_SELECT): selection mode not emulated");
|
||||
GLStateManager.LOGGER.warn("glRenderMode(GL_SELECT): selection mode not emulated");
|
||||
}
|
||||
|
||||
feedbackCount = 0;
|
||||
@@ -118,20 +120,19 @@ public class FeedbackManager {
|
||||
final ViewportState vp = GLStateManager.getViewportState();
|
||||
final float winX = vp.x + vp.width * (ndcX + 1.0f) * 0.5f;
|
||||
final float winY = vp.y + vp.height * (ndcY + 1.0f) * 0.5f;
|
||||
final float winZ = (float) ((vp.depthRangeFar - vp.depthRangeNear) * ndcZ
|
||||
+ (vp.depthRangeFar + vp.depthRangeNear)) * 0.5f;
|
||||
final float winZ = (float) ((vp.depthRangeFar - vp.depthRangeNear) * ndcZ + (vp.depthRangeFar + vp.depthRangeNear)) * 0.5f;
|
||||
final float winW = 1.0f / w;
|
||||
|
||||
feedbackVertex(winX, winY, winZ, winW);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -141,7 +142,7 @@ public class FeedbackManager {
|
||||
final int byteCount = count * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
GL15.glGetBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) first * stride + posOffset, readbackBuf);
|
||||
|
||||
emitAllPrimitives(mode, count, i -> i, stride);
|
||||
}
|
||||
@@ -150,11 +151,11 @@ public class FeedbackManager {
|
||||
computeMVP();
|
||||
final int stride = positionStride();
|
||||
|
||||
final int elementSize = VertexAttribState.Attrib.glTypeSizeBytes(type);
|
||||
final int elementSize = GLTypes.sizeBytes(type);
|
||||
final int indexByteCount = indexCount * elementSize;
|
||||
ensureIndexReadbackCapacity(indexByteCount);
|
||||
indexReadbackBuf.clear().limit(indexByteCount);
|
||||
GL15.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, eboOffset, indexReadbackBuf);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, eboOffset, indexReadbackBuf);
|
||||
final ByteBuffer indexBuf = indexReadbackBuf;
|
||||
|
||||
int minIdx = Integer.MAX_VALUE, maxIdx = Integer.MIN_VALUE;
|
||||
@@ -169,7 +170,7 @@ public class FeedbackManager {
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
GL15.glGetBufferSubData(GL15.GL_ARRAY_BUFFER, (long) minIdx * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf);
|
||||
|
||||
final int base = minIdx;
|
||||
emitAllPrimitives(mode, indexCount, i -> ImmediateModeRecorder.readIndex(indexBuf, type, 0, i) - base, stride);
|
||||
@@ -250,7 +251,23 @@ public class FeedbackManager {
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
GL15.glGetBufferSubData(GL15.GL_ARRAY_BUFFER, (long) minIdx * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf);
|
||||
}
|
||||
|
||||
private static void readPositionBuffer(long offset, ByteBuffer destination) {
|
||||
final int positionVbo = VAOManager.getAttribVBO(0);
|
||||
final int previousVbo = GLStateManager.getBoundVBO();
|
||||
|
||||
if (positionVbo != 0 && positionVbo != previousVbo) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionVbo);
|
||||
}
|
||||
try {
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, offset, destination);
|
||||
} finally {
|
||||
if (positionVbo != 0 && positionVbo != previousVbo) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, previousVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitVertex(int vertexIndex, int stride) {
|
||||
@@ -344,6 +361,27 @@ public class FeedbackManager {
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_QUAD_STRIP:
|
||||
for (int i = 0; i + 3 < count; i += 2) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(4);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
emitVertex(reader.read(i + 3), stride);
|
||||
emitVertex(reader.read(i + 2), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_POLYGON:
|
||||
if (count >= 3) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
emitVertex(reader.read(i), stride);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("Feedback mode: unsupported primitive mode 0x" + Integer.toHexString(mode));
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Color4Stack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ColorMaskStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.DepthStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.FogStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.IntegerStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.LightModelStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.LightStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.LineStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.MaterialStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
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.ImageUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntStack;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class GLContextState {
|
||||
final List<BooleanStateStack> allBooleanStates = new ArrayList<>();
|
||||
|
||||
private BooleanStateStack track(BooleanStateStack s) {
|
||||
allBooleanStates.add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public int programGeneration;
|
||||
public int drawFramebufferGeneration;
|
||||
public boolean poppingAttributes;
|
||||
// modelview matrix changes (any: translation, rotation, scale)
|
||||
public int mvGeneration;
|
||||
public int mvLinearGeneration;
|
||||
// projection matrix changes
|
||||
public int projGeneration;
|
||||
// texture matrix changes
|
||||
public int texMatrixGeneration;
|
||||
public int lightingGeneration;
|
||||
// fog + alpha ref + overlay color
|
||||
public int fragmentGeneration;
|
||||
// current vertex color
|
||||
public int colorGeneration;
|
||||
// clip plane equation changes
|
||||
public int clipPlaneGeneration;
|
||||
public float overlayR = 0.0f;
|
||||
public float overlayG = 0.0f;
|
||||
public float overlayB = 0.0f;
|
||||
public float overlayA = 0.0f;
|
||||
public float shaderColorR = 1.0f;
|
||||
public float shaderColorG = 1.0f;
|
||||
public float shaderColorB = 1.0f;
|
||||
public float shaderColorA = 1.0f;
|
||||
// Deferred vertex attribute upload flags - set when state changes, flushed before draw
|
||||
public boolean dirtyColorAttrib = true;
|
||||
public boolean dirtyNormalAttrib;
|
||||
public boolean dirtyTexCoordAttrib;
|
||||
public boolean dirtyLightmapAttrib = true;
|
||||
public boolean unit23TexCoordSetDuringDraw = false;
|
||||
public int maxBoundTextureUnit = 0;
|
||||
public int maxBoundImageUnit = 0;
|
||||
public final IntStack attribs = new IntArrayList(GLStateManager.MAX_ATTRIB_STACK_DEPTH);
|
||||
public int attribDepth = 0;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final List<IStateStack<?>>[] modifiedAtDepth = new List[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedMvGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedMvLinearGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedProjGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedTexMatGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedLightingGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedFragmentGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedColorGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedNormalGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedTexCoordGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final IntegerStateStack activeTextureUnit = new IntegerStateStack(0);
|
||||
public int clientActiveTextureUnit = 0;
|
||||
public final IntegerStateStack shadeModelState = new IntegerStateStack(GL11.GL_SMOOTH);
|
||||
public final TextureUnitArray textures = new TextureUnitArray();
|
||||
public final ImageUnitArray imageUnits = new ImageUnitArray();
|
||||
public final BlendStateStack blendState = new BlendStateStack();
|
||||
public final BooleanStateStack blendMode = track(new BooleanStateStack(GL11.GL_BLEND));
|
||||
public final BooleanStateStack scissorTest = track(new BooleanStateStack(GL11.GL_SCISSOR_TEST));
|
||||
public final DepthStateStack depthState = new DepthStateStack();
|
||||
public final BooleanStateStack depthTest = track(new BooleanStateStack(GL11.GL_DEPTH_TEST));
|
||||
public final FogStateStack fogState = new FogStateStack();
|
||||
public final BooleanStateStack fogMode = track(new BooleanStateStack(GL11.GL_FOG, false, true));
|
||||
public final Color4Stack color = new Color4Stack();
|
||||
// GL_CURRENT_SECONDARY_COLOR - alpha is always 1 and unused, the color sum only adds RGB
|
||||
public final Color4Stack secondaryColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 1.0F));
|
||||
public final BooleanStateStack colorSumState = track(new BooleanStateStack(GL14.GL_COLOR_SUM, false, true));
|
||||
public final Color4Stack clearColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
public final ColorMaskStack colorMask = new ColorMaskStack();
|
||||
public final IntegerStateStack drawBuffer = new IntegerStateStack(GLStateManager.DEFAULT_DRAW_BUFFER);
|
||||
public final IntegerStateStack logicOpMode = new IntegerStateStack(GL11.GL_COPY);
|
||||
public final BooleanStateStack cullState = track(new BooleanStateStack(GL11.GL_CULL_FACE));
|
||||
public final AlphaStateStack alphaState = new AlphaStateStack();
|
||||
public final BooleanStateStack alphaTest = track(new BooleanStateStack(GL11.GL_ALPHA_TEST, false, true));
|
||||
public final BooleanStateStack lightingState = track(new BooleanStateStack(GL11.GL_LIGHTING, false, true));
|
||||
public final BooleanStateStack rescaleNormalState = track(new BooleanStateStack(GL12.GL_RESCALE_NORMAL, false, true));
|
||||
public final BooleanStateStack normalizeState = track(new BooleanStateStack(GL11.GL_NORMALIZE, false, true));
|
||||
public final BooleanStateStack ditherState = track(new BooleanStateStack(GL11.GL_DITHER, true));
|
||||
public final BooleanStateStack stencilTest = track(new BooleanStateStack(GL11.GL_STENCIL_TEST));
|
||||
public final BooleanStateStack lineSmoothState = track(new BooleanStateStack(GL11.GL_LINE_SMOOTH));
|
||||
public final BooleanStateStack lineStippleState = track(new BooleanStateStack(GL11.GL_LINE_STIPPLE, false, true));
|
||||
public final BooleanStateStack pointSmoothState = track(new BooleanStateStack(GL11.GL_POINT_SMOOTH, false, true));
|
||||
public final BooleanStateStack polygonSmoothState = track(new BooleanStateStack(GL11.GL_POLYGON_SMOOTH));
|
||||
public final BooleanStateStack polygonStippleState = track(new BooleanStateStack(GL11.GL_POLYGON_STIPPLE, false, true));
|
||||
public final BooleanStateStack multisampleState = track(new BooleanStateStack(GL13.GL_MULTISAMPLE, true));
|
||||
public final BooleanStateStack sampleAlphaToCoverageState = track(new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_COVERAGE));
|
||||
public final BooleanStateStack sampleAlphaToOneState = track(new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_ONE));
|
||||
public final BooleanStateStack sampleCoverageState = track(new BooleanStateStack(GL13.GL_SAMPLE_COVERAGE));
|
||||
public final BooleanStateStack colorLogicOpState = track(new BooleanStateStack(GL11.GL_COLOR_LOGIC_OP));
|
||||
public final BooleanStateStack indexLogicOpState = track(new BooleanStateStack(GL11.GL_INDEX_LOGIC_OP, false, true));
|
||||
public final BooleanStateStack polygonOffsetPointState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_POINT));
|
||||
public final BooleanStateStack polygonOffsetLineState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_LINE));
|
||||
public final BooleanStateStack polygonOffsetFillState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_FILL));
|
||||
public final LineStateStack lineState = new LineStateStack();
|
||||
public final PointStateStack pointState = new PointStateStack();
|
||||
public final PolygonStateStack polygonState = new PolygonStateStack();
|
||||
public final StencilStateStack stencilState = new StencilStateStack();
|
||||
public int stencilBitMask = 0xFFFFFFFF;
|
||||
public final BooleanStateStack autoNormalState = track(new BooleanStateStack(GL11.GL_AUTO_NORMAL, false, true));
|
||||
public final BooleanStateStack map1Color4State = track(new BooleanStateStack(GL11.GL_MAP1_COLOR_4, false, true));
|
||||
public final BooleanStateStack map1IndexState = track(new BooleanStateStack(GL11.GL_MAP1_INDEX, false, true));
|
||||
public final BooleanStateStack map1NormalState = track(new BooleanStateStack(GL11.GL_MAP1_NORMAL, false, true));
|
||||
public final BooleanStateStack map1TextureCoord1State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_1, false, true));
|
||||
public final BooleanStateStack map1TextureCoord2State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_2, false, true));
|
||||
public final BooleanStateStack map1TextureCoord3State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_3, false, true));
|
||||
public final BooleanStateStack map1TextureCoord4State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_4, false, true));
|
||||
public final BooleanStateStack map1Vertex3State = track(new BooleanStateStack(GL11.GL_MAP1_VERTEX_3, false, true));
|
||||
public final BooleanStateStack map1Vertex4State = track(new BooleanStateStack(GL11.GL_MAP1_VERTEX_4, false, true));
|
||||
public final BooleanStateStack map2Color4State = track(new BooleanStateStack(GL11.GL_MAP2_COLOR_4, false, true));
|
||||
public final BooleanStateStack map2IndexState = track(new BooleanStateStack(GL11.GL_MAP2_INDEX, false, true));
|
||||
public final BooleanStateStack map2NormalState = track(new BooleanStateStack(GL11.GL_MAP2_NORMAL, false, true));
|
||||
public final BooleanStateStack map2TextureCoord1State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_1, false, true));
|
||||
public final BooleanStateStack map2TextureCoord2State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_2, false, true));
|
||||
public final BooleanStateStack map2TextureCoord3State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_3, false, true));
|
||||
public final BooleanStateStack map2TextureCoord4State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_4, false, true));
|
||||
public final BooleanStateStack map2Vertex3State = track(new BooleanStateStack(GL11.GL_MAP2_VERTEX_3, false, true));
|
||||
public final BooleanStateStack map2Vertex4State = track(new BooleanStateStack(GL11.GL_MAP2_VERTEX_4, false, true));
|
||||
public final BooleanStateStack[] clipPlaneStates = new BooleanStateStack[GLStateManager.MAX_CLIP_PLANES];
|
||||
public final ClipPlaneState clipPlaneState = new ClipPlaneState();
|
||||
public final FloatBuffer queryScratch = BufferUtils.createFloatBuffer(16);
|
||||
public final DoubleBuffer queryScratchDouble = BufferUtils.createDoubleBuffer(16);
|
||||
public final MatrixModeStack matrixMode = new MatrixModeStack();
|
||||
public final Matrix4fStack modelViewMatrix = new Matrix4fStack(GLStateManager.MAX_MODELVIEW_STACK_DEPTH);
|
||||
public final Matrix4fStack projectionMatrix = new Matrix4fStack(GLStateManager.MAX_PROJECTION_STACK_DEPTH);
|
||||
public final BooleanStateStack[] lightStates = new BooleanStateStack[8];
|
||||
public final LightStateStack[] lightDataStates = new LightStateStack[8];
|
||||
public final BooleanStateStack colorMaterial = track(new BooleanStateStack(GL11.GL_COLOR_MATERIAL, false, true));
|
||||
public final IntegerStateStack colorMaterialFace = new IntegerStateStack(GL11.GL_FRONT_AND_BACK);
|
||||
public final IntegerStateStack colorMaterialParameter = new IntegerStateStack(GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
public final LightModelStateStack lightModel = new LightModelStateStack();
|
||||
public final MaterialStateStack frontMaterial = new MaterialStateStack(GL11.GL_FRONT);
|
||||
public final MaterialStateStack backMaterial = new MaterialStateStack(GL11.GL_BACK);
|
||||
public final ViewPortStateStack viewportState = new ViewPortStateStack();
|
||||
public int activeProgram = 0;
|
||||
public int listBase = 0;
|
||||
public int boundVBO;
|
||||
public int boundVAO;
|
||||
public int boundPixelUnpackBuffer;
|
||||
public int boundPixelPackBuffer;
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
public final IntSet writeMappedBuffers = new IntOpenHashSet();
|
||||
public PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
|
||||
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public final boolean[] restoreUnitChanged = new boolean[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged, restoreSecondaryColorChanged;
|
||||
public final BlendState vanillaBlendBefore = new BlendState();
|
||||
public final BlendState vanillaBlendAfter = new BlendState();
|
||||
public boolean vanillaBlendEnabledBefore;
|
||||
public int drawFramebuffer = 0;
|
||||
public int readFramebuffer = 0;
|
||||
public int texGenGeneration;
|
||||
|
||||
GLContextState() {
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) modifiedAtDepth[i] = new ArrayList<>();
|
||||
for (int i = 0; i < lightStates.length; i++) {
|
||||
lightStates[i] = track(new BooleanStateStack(GL11.GL_LIGHT0 + i, false, true));
|
||||
}
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
clipPlaneStates[i] = track(new BooleanStateStack(GL11.GL_CLIP_PLANE0 + i));
|
||||
}
|
||||
}
|
||||
|
||||
void init() {
|
||||
for (int i = 0; i < lightDataStates.length; i++) {
|
||||
lightDataStates[i] = new LightStateStack(GL11.GL_LIGHT0 + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
+213
-296
@@ -5,10 +5,9 @@
|
||||
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.AMDDebugOutput;
|
||||
import org.lwjgl.opengl.AMDDebugOutputCallback;
|
||||
import org.lwjgl.opengl.ARBDebugOutput;
|
||||
import org.lwjgl.opengl.ARBDebugOutputCallback;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import org.lwjgl.opengl.EXTBlendColor;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -16,155 +15,76 @@ import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.opengl.KHRDebug;
|
||||
import org.lwjgl.opengl.KHRDebugCallback;
|
||||
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
import static org.lwjgl.opengl.ARBDebugOutput.glDebugMessageCallbackARB;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
public final class GLDebug {
|
||||
|
||||
/**
|
||||
* Sets up debug callbacks
|
||||
*
|
||||
* @return 0 for failure, 1 for success, 2 for restart required.
|
||||
*/
|
||||
public static int setupDebugMessageCallback() {
|
||||
if (Thread.currentThread() != GLStateManager.getMainThread()) {
|
||||
LOGGER.warn("setupDebugMessageCallback called from non-main thread!");
|
||||
GLStateManager.LOGGER.warn("setupDebugMessageCallback called from non-main thread!");
|
||||
return 0;
|
||||
}
|
||||
return setupDebugMessageCallbackImpl();
|
||||
}
|
||||
|
||||
public static Throwable filterStackTrace(Throwable throwable, int offset) {
|
||||
StackTraceElement[] elems = throwable.getStackTrace();
|
||||
StackTraceElement[] filtered = new StackTraceElement[elems.length];
|
||||
int j = 0;
|
||||
for (int i = offset; i < elems.length; i++) {
|
||||
String className = elems[i].getClassName();
|
||||
if (className == null) {
|
||||
className = "";
|
||||
}
|
||||
filtered[j++] = elems[i];
|
||||
}
|
||||
StackTraceElement[] newElems = new StackTraceElement[j];
|
||||
System.arraycopy(filtered, 0, newElems, 0, j);
|
||||
throwable.setStackTrace(newElems);
|
||||
return throwable;
|
||||
}
|
||||
|
||||
private static String buildStackTrace() {
|
||||
StackTraceElement[] elems = filterStackTrace(new Throwable(), 4).getStackTrace();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (StackTraceElement elem : elems) {
|
||||
sb.append("\n\t").append(elem.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void logDebugMessage(int id, String source, String type, String severity, String message) {
|
||||
String fullMessage = String.format("[GL] %s %s (0x%X) from %s: %s%s", severity, type, id, source, message, buildStackTrace());
|
||||
|
||||
if ("HIGH".equals(severity)) {
|
||||
LOGGER.error(fullMessage);
|
||||
} else if ("MEDIUM".equals(severity)) {
|
||||
LOGGER.warn(fullMessage);
|
||||
} else {
|
||||
LOGGER.info(fullMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static void logDebugMessageAMD(int id, String category, String severity, String message) {
|
||||
String fullMessage = String.format("[GL] %s %s (0x%X): %s%s", severity, category, id, message, buildStackTrace());
|
||||
|
||||
if ("HIGH".equals(severity)) {
|
||||
LOGGER.error(fullMessage);
|
||||
} else if ("MEDIUM".equals(severity)) {
|
||||
LOGGER.warn(fullMessage);
|
||||
} else {
|
||||
LOGGER.info(fullMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up debug callbacks
|
||||
* @return 0 for failure, 1 for success, 2 for restart required.
|
||||
*/
|
||||
private static int setupDebugMessageCallbackImpl() {
|
||||
if (GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_KHR_debug) {
|
||||
LOGGER.info("[GL] Using OpenGL 4.3 for error logging.");
|
||||
final KHRDebugCallback proc = new KHRDebugCallback((source, type, id, severity, message) -> {
|
||||
logDebugMessage(id, getDebugSource(source), getDebugType(type), getDebugSeverity(severity), message);
|
||||
});
|
||||
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
|
||||
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
|
||||
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_LOW, null, false);
|
||||
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_NOTIFICATION, null, false);
|
||||
GL43.glDebugMessageCallback(proc);
|
||||
|
||||
// Enable synchronous debug output so errors are reported immediately with accurate stack traces
|
||||
GL11.glEnable(GL43.GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
|
||||
if ((GL11.glGetInteger(GL30.GL_CONTEXT_FLAGS) & GL43.GL_CONTEXT_FLAG_DEBUG_BIT) == 0) {
|
||||
LOGGER.warn("[GL] Warning: A non-debug context may not produce any debug output.");
|
||||
GL11.glEnable(GL43.GL_DEBUG_OUTPUT);
|
||||
return 2;
|
||||
public static Throwable filterStackTrace(Throwable throwable, int offset) {
|
||||
final StackTraceElement[] elems = throwable.getStackTrace();
|
||||
final StackTraceElement[] filtered = new StackTraceElement[elems.length];
|
||||
int j = 0;
|
||||
for (int i = offset; i < elems.length; i++) {
|
||||
String className = elems[i].getClassName();
|
||||
if (className == null) {
|
||||
className = "";
|
||||
}
|
||||
return 1;
|
||||
} else if (GLStateManager.capabilities.GL_ARB_debug_output) {
|
||||
LOGGER.info("[GL] Using ARB_debug_output for error logging.");
|
||||
final ARBDebugOutputCallback proc = new ARBDebugOutputCallback((source, type, id, severity, message) -> {
|
||||
logDebugMessage(id, getSourceARB(source), getTypeARB(type), getSeverityARB(severity), message);
|
||||
});
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_LOW, null, false);
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_NOTIFICATION, null, false);
|
||||
ARBDebugOutput.glDebugMessageCallbackARB(proc);
|
||||
filtered[j++] = elems[i];
|
||||
}
|
||||
final StackTraceElement[] newElems = new StackTraceElement[j];
|
||||
System.arraycopy(filtered, 0, newElems, 0, j);
|
||||
throwable.setStackTrace(newElems);
|
||||
return throwable;
|
||||
}
|
||||
|
||||
// Enable synchronous debug output so errors are reported immediately with accurate stack traces
|
||||
GL11.glEnable(ARBDebugOutput.GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
return 1;
|
||||
} else if (GLStateManager.capabilities.GL_AMD_debug_output) {
|
||||
LOGGER.info("[GL] Using AMD_debug_output for error logging.");
|
||||
final AMDDebugOutputCallback proc = new AMDDebugOutputCallback((id, category, severity, message) -> {
|
||||
logDebugMessageAMD(id, getCategoryAMD(category), getSeverityAMD(severity), message);
|
||||
});
|
||||
AMDDebugOutput.glDebugMessageEnableAMD(0, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
|
||||
AMDDebugOutput.glDebugMessageEnableAMD(0, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
|
||||
AMDDebugOutput.glDebugMessageEnableAMD(0, GL43.GL_DEBUG_SEVERITY_LOW, null, false);
|
||||
AMDDebugOutput.glDebugMessageEnableAMD(0, GL43.GL_DEBUG_SEVERITY_NOTIFICATION, null, false);
|
||||
AMDDebugOutput.glDebugMessageCallbackAMD(proc);
|
||||
return 1;
|
||||
private static String buildStackTrace() {
|
||||
final StackTraceElement[] elems = filterStackTrace(new Throwable(), 4).getStackTrace();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (StackTraceElement elem : elems) {
|
||||
sb.append("\n\t").append(elem.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void logDebugMessage(int id, String source, String type, String severity, String message) {
|
||||
final String fullMessage = String.format("[GL] %s %s (0x%X) from %s: %s%s", severity, type, id, source, message, buildStackTrace());
|
||||
|
||||
if ("HIGH".equals(severity)) {
|
||||
GLStateManager.LOGGER.error(fullMessage);
|
||||
} else if ("MEDIUM".equals(severity)) {
|
||||
GLStateManager.LOGGER.warn(fullMessage);
|
||||
} else {
|
||||
LOGGER.info("[GL] No debug output implementation is available, cannot return debug info.");
|
||||
return 0;
|
||||
GLStateManager.LOGGER.info(fullMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static int disableDebugMessages() {
|
||||
if (GLStateManager.capabilities.OpenGL43) {
|
||||
GL43.glDebugMessageCallback(null);
|
||||
return 1;
|
||||
} else if (GLStateManager.capabilities.GL_KHR_debug) {
|
||||
KHRDebug.glDebugMessageCallback(null);
|
||||
if (GLStateManager.capabilities.OpenGL30 && (GL11.glGetInteger(GL30.GL_CONTEXT_FLAGS) & 2) == 0) {
|
||||
GL11.glDisable(GL43.GL_DEBUG_OUTPUT);
|
||||
}
|
||||
return 1;
|
||||
} else if (GLStateManager.capabilities.GL_ARB_debug_output) {
|
||||
glDebugMessageCallbackARB(null);
|
||||
return 1;
|
||||
} else if (GLStateManager.capabilities.GL_AMD_debug_output) {
|
||||
AMDDebugOutput.glDebugMessageCallbackAMD(null);
|
||||
return 1;
|
||||
} else {
|
||||
LOGGER.info("[GL] No debug output implementation is available, cannot disable debug info.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
private static int setupDebugMessageCallbackImpl() {
|
||||
return BackendManager.RENDER_BACKEND.setupDebugOutput(
|
||||
(source, type, id, severity, message) ->
|
||||
logDebugMessage(id, getDebugSource(source), getDebugType(type), getDebugSeverity(severity), message)
|
||||
);
|
||||
}
|
||||
|
||||
public static int disableDebugMessages() {
|
||||
return BackendManager.RENDER_BACKEND.disableDebugOutput();
|
||||
}
|
||||
|
||||
private static String getDebugSource(int source) {
|
||||
private static String getDebugSource(int source) {
|
||||
return switch (source) {
|
||||
case GL43.GL_DEBUG_SOURCE_API -> "API";
|
||||
case GL43.GL_DEBUG_SOURCE_WINDOW_SYSTEM -> "WINDOW SYSTEM";
|
||||
@@ -174,9 +94,9 @@ public final class GLDebug {
|
||||
case GL43.GL_DEBUG_SOURCE_OTHER -> "OTHER";
|
||||
default -> String.format("Unknown [0x%X]", source);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDebugType(int type) {
|
||||
private static String getDebugType(int type) {
|
||||
return switch (type) {
|
||||
case GL43.GL_DEBUG_TYPE_ERROR -> "ERROR";
|
||||
case GL43.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR -> "DEPRECATED BEHAVIOR";
|
||||
@@ -187,9 +107,9 @@ public final class GLDebug {
|
||||
case GL43.GL_DEBUG_TYPE_MARKER -> "MARKER";
|
||||
default -> String.format("Unknown [0x%X]", type);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDebugSeverity(int severity) {
|
||||
private static String getDebugSeverity(int severity) {
|
||||
return switch (severity) {
|
||||
case GL43.GL_DEBUG_SEVERITY_NOTIFICATION -> "NOTIFICATION";
|
||||
case GL43.GL_DEBUG_SEVERITY_HIGH -> "HIGH";
|
||||
@@ -197,65 +117,9 @@ public final class GLDebug {
|
||||
case GL43.GL_DEBUG_SEVERITY_LOW -> "LOW";
|
||||
default -> String.format("Unknown [0x%X]", severity);
|
||||
};
|
||||
}
|
||||
|
||||
private static String getSourceARB(int source) {
|
||||
return switch (source) {
|
||||
case ARBDebugOutput.GL_DEBUG_SOURCE_API_ARB -> "API";
|
||||
case ARBDebugOutput.GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB -> "WINDOW SYSTEM";
|
||||
case ARBDebugOutput.GL_DEBUG_SOURCE_SHADER_COMPILER_ARB -> "SHADER COMPILER";
|
||||
case ARBDebugOutput.GL_DEBUG_SOURCE_THIRD_PARTY_ARB -> "THIRD PARTY";
|
||||
case ARBDebugOutput.GL_DEBUG_SOURCE_APPLICATION_ARB -> "APPLICATION";
|
||||
case ARBDebugOutput.GL_DEBUG_SOURCE_OTHER_ARB -> "OTHER";
|
||||
default -> String.format("Unknown [0x%X]", source);
|
||||
};
|
||||
}
|
||||
|
||||
private static String getTypeARB(int type) {
|
||||
return switch (type) {
|
||||
case ARBDebugOutput.GL_DEBUG_TYPE_ERROR_ARB -> "ERROR";
|
||||
case ARBDebugOutput.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB -> "DEPRECATED BEHAVIOR";
|
||||
case ARBDebugOutput.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB -> "UNDEFINED BEHAVIOR";
|
||||
case ARBDebugOutput.GL_DEBUG_TYPE_PORTABILITY_ARB -> "PORTABILITY";
|
||||
case ARBDebugOutput.GL_DEBUG_TYPE_PERFORMANCE_ARB -> "PERFORMANCE";
|
||||
case ARBDebugOutput.GL_DEBUG_TYPE_OTHER_ARB -> "OTHER";
|
||||
default -> String.format("Unknown [0x%X]", type);
|
||||
};
|
||||
}
|
||||
|
||||
private static String getSeverityARB(int severity) {
|
||||
return switch (severity) {
|
||||
case ARBDebugOutput.GL_DEBUG_SEVERITY_HIGH_ARB -> "HIGH";
|
||||
case ARBDebugOutput.GL_DEBUG_SEVERITY_MEDIUM_ARB -> "MEDIUM";
|
||||
case ARBDebugOutput.GL_DEBUG_SEVERITY_LOW_ARB -> "LOW";
|
||||
default -> String.format("Unknown [0x%X]", severity);
|
||||
};
|
||||
}
|
||||
|
||||
private static String getCategoryAMD(int category) {
|
||||
return switch (category) {
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_API_ERROR_AMD -> "API ERROR";
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD -> "WINDOW SYSTEM";
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_DEPRECATION_AMD -> "DEPRECATION";
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD -> "UNDEFINED BEHAVIOR";
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_PERFORMANCE_AMD -> "PERFORMANCE";
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD -> "SHADER COMPILER";
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_APPLICATION_AMD -> "APPLICATION";
|
||||
case AMDDebugOutput.GL_DEBUG_CATEGORY_OTHER_AMD -> "OTHER";
|
||||
default -> String.format("Unknown [0x%X]", category);
|
||||
};
|
||||
}
|
||||
|
||||
private static String getSeverityAMD(int severity) {
|
||||
return switch (severity) {
|
||||
case AMDDebugOutput.GL_DEBUG_SEVERITY_HIGH_AMD -> "HIGH";
|
||||
case AMDDebugOutput.GL_DEBUG_SEVERITY_MEDIUM_AMD -> "MEDIUM";
|
||||
case AMDDebugOutput.GL_DEBUG_SEVERITY_LOW_AMD -> "LOW";
|
||||
default -> String.format("Unknown [0x%X]", severity);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getMatrixModeName(int mode) {
|
||||
public static String getMatrixModeName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_MODELVIEW -> "MODELVIEW";
|
||||
case GL11.GL_PROJECTION -> "PROJECTION";
|
||||
@@ -263,9 +127,9 @@ public final class GLDebug {
|
||||
case GL11.GL_COLOR -> "COLOR";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCapabilityName(int cap) {
|
||||
public static String getCapabilityName(int cap) {
|
||||
return switch (cap) {
|
||||
case GL11.GL_ALPHA_TEST -> "ALPHA_TEST";
|
||||
case GL11.GL_BLEND -> "BLEND";
|
||||
@@ -295,9 +159,9 @@ public final class GLDebug {
|
||||
case GL11.GL_LIGHT7 -> "LIGHT7";
|
||||
default -> String.format("0x%X", cap);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getComparisonFuncName(int func) {
|
||||
public static String getComparisonFuncName(int func) {
|
||||
return switch (func) {
|
||||
case GL11.GL_NEVER -> "NEVER";
|
||||
case GL11.GL_LESS -> "LESS";
|
||||
@@ -309,9 +173,9 @@ public final class GLDebug {
|
||||
case GL11.GL_ALWAYS -> "ALWAYS";
|
||||
default -> String.format("0x%X", func);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getBlendFactorName(int factor) {
|
||||
public static String getBlendFactorName(int factor) {
|
||||
return switch (factor) {
|
||||
case GL11.GL_ZERO -> "ZERO";
|
||||
case GL11.GL_ONE -> "ONE";
|
||||
@@ -330,17 +194,17 @@ public final class GLDebug {
|
||||
case GL11.GL_SRC_ALPHA_SATURATE -> "SRC_ALPHA_SATURATE";
|
||||
default -> String.format("0x%X", factor);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getShadeModelName(int mode) {
|
||||
public static String getShadeModelName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_FLAT -> "FLAT";
|
||||
case GL11.GL_SMOOTH -> "SMOOTH";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTextureTargetName(int target) {
|
||||
public static String getTextureTargetName(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_TEXTURE_1D -> "TEXTURE_1D";
|
||||
case GL11.GL_TEXTURE_2D -> "TEXTURE_2D";
|
||||
@@ -348,9 +212,9 @@ public final class GLDebug {
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> "TEXTURE_CUBE_MAP";
|
||||
default -> String.format("0x%X", target);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTexturePnameName(int pname) {
|
||||
public static String getTexturePnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_MIN_FILTER -> "TEXTURE_MIN_FILTER";
|
||||
case GL11.GL_TEXTURE_MAG_FILTER -> "TEXTURE_MAG_FILTER";
|
||||
@@ -367,9 +231,9 @@ public final class GLDebug {
|
||||
case GL14.GL_GENERATE_MIPMAP -> "GENERATE_MIPMAP";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTextureFormatName(int format) {
|
||||
public static String getTextureFormatName(int format) {
|
||||
return switch (format) {
|
||||
case GL11.GL_RED -> "RED";
|
||||
case GL11.GL_GREEN -> "GREEN";
|
||||
@@ -392,28 +256,9 @@ public final class GLDebug {
|
||||
case GL11.GL_LUMINANCE8_ALPHA8 -> "LUMINANCE8_ALPHA8";
|
||||
default -> String.format("0x%X", format);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDataTypeName(int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> "UNSIGNED_BYTE";
|
||||
case GL11.GL_BYTE -> "BYTE";
|
||||
case GL11.GL_UNSIGNED_SHORT -> "UNSIGNED_SHORT";
|
||||
case GL11.GL_SHORT -> "SHORT";
|
||||
case GL11.GL_UNSIGNED_INT -> "UNSIGNED_INT";
|
||||
case GL11.GL_INT -> "INT";
|
||||
case GL11.GL_FLOAT -> "FLOAT";
|
||||
case GL11.GL_DOUBLE -> "DOUBLE";
|
||||
case GL12.GL_UNSIGNED_BYTE_3_3_2 -> "UNSIGNED_BYTE_3_3_2";
|
||||
case GL12.GL_UNSIGNED_SHORT_4_4_4_4 -> "UNSIGNED_SHORT_4_4_4_4";
|
||||
case GL12.GL_UNSIGNED_SHORT_5_5_5_1 -> "UNSIGNED_SHORT_5_5_5_1";
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8 -> "UNSIGNED_INT_8_8_8_8";
|
||||
case GL12.GL_UNSIGNED_INT_10_10_10_2 -> "UNSIGNED_INT_10_10_10_2";
|
||||
default -> String.format("0x%X", type);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getLightName(int light) {
|
||||
public static String getLightName(int light) {
|
||||
return switch (light) {
|
||||
case GL11.GL_LIGHT0 -> "LIGHT0";
|
||||
case GL11.GL_LIGHT1 -> "LIGHT1";
|
||||
@@ -425,9 +270,9 @@ public final class GLDebug {
|
||||
case GL11.GL_LIGHT7 -> "LIGHT7";
|
||||
default -> String.format("0x%X", light);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getLightPnameName(int pname) {
|
||||
public static String getLightPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> "AMBIENT";
|
||||
case GL11.GL_DIFFUSE -> "DIFFUSE";
|
||||
@@ -441,9 +286,9 @@ public final class GLDebug {
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> "QUADRATIC_ATTENUATION";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMaterialPnameName(int pname) {
|
||||
public static String getMaterialPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> "AMBIENT";
|
||||
case GL11.GL_DIFFUSE -> "DIFFUSE";
|
||||
@@ -454,18 +299,18 @@ public final class GLDebug {
|
||||
case GL11.GL_COLOR_INDEXES -> "COLOR_INDEXES";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFaceName(int face) {
|
||||
public static String getFaceName(int face) {
|
||||
return switch (face) {
|
||||
case GL11.GL_FRONT -> "FRONT";
|
||||
case GL11.GL_BACK -> "BACK";
|
||||
case GL11.GL_FRONT_AND_BACK -> "FRONT_AND_BACK";
|
||||
default -> String.format("0x%X", face);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getColorMaterialModeName(int mode) {
|
||||
public static String getColorMaterialModeName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_AMBIENT -> "AMBIENT";
|
||||
case GL11.GL_DIFFUSE -> "DIFFUSE";
|
||||
@@ -474,9 +319,9 @@ public final class GLDebug {
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> "AMBIENT_AND_DIFFUSE";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFogPnameName(int pname) {
|
||||
public static String getFogPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_FOG_MODE -> "FOG_MODE";
|
||||
case GL11.GL_FOG_DENSITY -> "FOG_DENSITY";
|
||||
@@ -486,9 +331,9 @@ public final class GLDebug {
|
||||
case GL11.GL_FOG_COLOR -> "FOG_COLOR";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getLightModelPnameName(int pname) {
|
||||
public static String getLightModelPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> "LIGHT_MODEL_AMBIENT";
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> "LIGHT_MODEL_LOCAL_VIEWER";
|
||||
@@ -496,9 +341,9 @@ public final class GLDebug {
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> "LIGHT_MODEL_COLOR_CONTROL";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getClearMaskString(int mask) {
|
||||
public static String getClearMaskString(int mask) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean first = true;
|
||||
if ((mask & GL11.GL_COLOR_BUFFER_BIT) != 0) {
|
||||
@@ -520,18 +365,18 @@ public final class GLDebug {
|
||||
sb.append("ACCUM");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCullFaceName(int mode) {
|
||||
public static String getCullFaceName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_FRONT -> "FRONT";
|
||||
case GL11.GL_BACK -> "BACK";
|
||||
case GL11.GL_FRONT_AND_BACK -> "FRONT_AND_BACK";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getLogicOpName(int opcode) {
|
||||
public static String getLogicOpName(int opcode) {
|
||||
return switch (opcode) {
|
||||
case GL11.GL_CLEAR -> "CLEAR";
|
||||
case GL11.GL_AND -> "AND";
|
||||
@@ -551,9 +396,9 @@ public final class GLDebug {
|
||||
case GL11.GL_SET -> "SET";
|
||||
default -> String.format("0x%X", opcode);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFramebufferStatusName(int status) {
|
||||
public static String getFramebufferStatusName(int status) {
|
||||
return switch (status) {
|
||||
case GL30.GL_FRAMEBUFFER_COMPLETE -> "COMPLETE";
|
||||
case GL30.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -> "INCOMPLETE_ATTACHMENT";
|
||||
@@ -565,9 +410,9 @@ public final class GLDebug {
|
||||
case 0 -> "NO_FRAMEBUFFER_BOUND";
|
||||
default -> String.format("UNKNOWN(0x%X)", status);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static String getInternalFormatName(int format) {
|
||||
public static String getInternalFormatName(int format) {
|
||||
return switch (format) {
|
||||
// Basic
|
||||
case GL11.GL_RGBA -> "RGBA";
|
||||
@@ -602,33 +447,37 @@ public final class GLDebug {
|
||||
case GL11.GL_RGB10_A2 -> "RGB10_A2";
|
||||
default -> String.format("0x%X", format);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static DebugState debugState;
|
||||
private static DebugState debugState;
|
||||
|
||||
private interface DebugState {
|
||||
void nameObject(int id, int object, String name);
|
||||
void pushGroup(String name);
|
||||
void popGroup();
|
||||
|
||||
void nameObject(int id, int object, String name);
|
||||
|
||||
void pushGroup(String name);
|
||||
|
||||
void popGroup();
|
||||
|
||||
void debugMessage(String name);
|
||||
|
||||
String getObjectLabel(int glProgram, int program);
|
||||
}
|
||||
|
||||
private static class KHRDebugState implements DebugState {
|
||||
private static class KHRDebugState implements DebugState {
|
||||
|
||||
private static final int ID = 0;
|
||||
private int depth = 0;
|
||||
private static final int maxDepth = GL11.glGetInteger(KHRDebug.GL_MAX_DEBUG_GROUP_STACK_DEPTH);
|
||||
private static final int maxNameLength = GL11.glGetInteger(KHRDebug.GL_MAX_LABEL_LENGTH);
|
||||
private int depth = 0;
|
||||
private static final int maxDepth = BackendManager.RENDER_BACKEND.getInteger(GL43.GL_MAX_DEBUG_GROUP_STACK_DEPTH);
|
||||
private final String[] groupStack = new String[maxDepth + 1];
|
||||
|
||||
@Override
|
||||
public void nameObject(int id, int object, String name) {
|
||||
KHRDebug.glObjectLabel(id, object, name);
|
||||
}
|
||||
@Override
|
||||
public void nameObject(int id, int object, String name) {
|
||||
BackendManager.RENDER_BACKEND.objectLabel(id, object, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushGroup(String name) {
|
||||
@Override
|
||||
public void pushGroup(String name) {
|
||||
if (depth + 1 > maxDepth) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Debug group stack overflow (maxDepth=").append(maxDepth).append("), current stack:\n");
|
||||
@@ -639,46 +488,51 @@ public final class GLDebug {
|
||||
throw new RuntimeException(sb.toString());
|
||||
}
|
||||
groupStack[depth] = name;
|
||||
KHRDebug.glPushDebugGroup(KHRDebug.GL_DEBUG_SOURCE_APPLICATION, ID, name);
|
||||
BackendManager.RENDER_BACKEND.pushDebugGroup(GL43.GL_DEBUG_SOURCE_APPLICATION, ID, name);
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popGroup() {
|
||||
@Override
|
||||
public void popGroup() {
|
||||
if (depth - 1 < 0) {
|
||||
throw new RuntimeException("Stack underflow");
|
||||
}
|
||||
KHRDebug.glPopDebugGroup();
|
||||
BackendManager.RENDER_BACKEND.popDebugGroup();
|
||||
depth--;
|
||||
groupStack[depth] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugMessage(String message) {
|
||||
KHRDebug.glDebugMessageInsert(KHRDebug.GL_DEBUG_SOURCE_APPLICATION, KHRDebug.GL_DEBUG_TYPE_MARKER, ID, KHRDebug.GL_DEBUG_SEVERITY_NOTIFICATION, message);
|
||||
BackendManager.RENDER_BACKEND.debugMessageInsert(
|
||||
GL43.GL_DEBUG_SOURCE_APPLICATION,
|
||||
GL43.GL_DEBUG_TYPE_MARKER,
|
||||
ID,
|
||||
GL43.GL_DEBUG_SEVERITY_NOTIFICATION,
|
||||
message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getObjectLabel(int glProgram, int program) {
|
||||
if(program == 0)
|
||||
return "";
|
||||
if (program == 0) return "";
|
||||
|
||||
return KHRDebug.glGetObjectLabel(glProgram, program, maxNameLength);
|
||||
return BackendManager.RENDER_BACKEND.getObjectLabel(glProgram, program);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UnsupportedDebugState implements DebugState {
|
||||
@Override
|
||||
public void nameObject(int id, int object, String name) {
|
||||
}
|
||||
private static class UnsupportedDebugState implements DebugState {
|
||||
|
||||
@Override
|
||||
public void pushGroup(String name) {
|
||||
}
|
||||
@Override
|
||||
public void nameObject(int id, int object, String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popGroup() {
|
||||
}
|
||||
@Override
|
||||
public void pushGroup(String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popGroup() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugMessage(String name) {
|
||||
@@ -691,40 +545,103 @@ public final class GLDebug {
|
||||
}
|
||||
}
|
||||
|
||||
public static void initDebugState() {
|
||||
if (GLStateManager.capabilities.GL_KHR_debug || GLStateManager.capabilities.OpenGL43) {
|
||||
debugState = new KHRDebugState();
|
||||
} else {
|
||||
debugState = new UnsupportedDebugState();
|
||||
}
|
||||
}
|
||||
public static void initDebugState() {
|
||||
final GLSMInitConfig cfg = GLStateManager.getInitConfig();
|
||||
final boolean userRequested = cfg != null && (cfg.isLwjglDebug() || CaptureGate.enabledAtStartup());
|
||||
if (userRequested && BackendManager.RENDER_BACKEND.supportsDebugOutput()) {
|
||||
debugState = new KHRDebugState();
|
||||
} else {
|
||||
debugState = new UnsupportedDebugState();
|
||||
}
|
||||
}
|
||||
|
||||
public static void nameObject(int id, int object, String name) {
|
||||
if(debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.nameObject(id, object, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload that lets you gate the String concat behind the debugState
|
||||
*/
|
||||
public static void pushGroup(String prefix, int value) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.pushGroup(prefix + value);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isActive() {
|
||||
return debugState instanceof KHRDebugState && CaptureGate.markersThisFrame && Thread.currentThread() == GLStateManager.getMainThread();
|
||||
}
|
||||
|
||||
public static void pushGroup(String group) {
|
||||
if(debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.pushGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
public static void popGroup() {
|
||||
if(debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.popGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static void debugMessage(String message) {
|
||||
if(debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.debugMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getObjectLabel(int glProgram, int program) {
|
||||
if(debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
return debugState.getObjectLabel(glProgram, program);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static final Scope NOOP_SCOPE = () -> {};
|
||||
private static final Scope ACTIVE_SCOPE = GLDebug::popGroup;
|
||||
|
||||
public static Scope scope(String name) {
|
||||
if (isActive()) {
|
||||
debugState.pushGroup(name);
|
||||
return ACTIVE_SCOPE;
|
||||
}
|
||||
return NOOP_SCOPE;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Scope extends AutoCloseable {
|
||||
@Override void close();
|
||||
}
|
||||
|
||||
private static MethodHandle khrCallbackInvoke;
|
||||
private static boolean khrCallbackInvokeResolved;
|
||||
|
||||
/// Use reflection because of lwjgl3ify
|
||||
static GLDebugMessageListener adaptDebugCallback(Object callback) {
|
||||
if (callback == null) return null;
|
||||
final MethodHandle invoke = resolveKhrCallbackInvoke(callback.getClass());
|
||||
if (invoke == null) return null;
|
||||
return (source, type, id, severity, length, message, userParam) -> {
|
||||
try {
|
||||
invoke.invoke(callback, source, type, id, severity, length, message, userParam);
|
||||
} catch (Throwable t) {
|
||||
GLStateManager.LOGGER.warn("Debug message callback threw", t);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static synchronized MethodHandle resolveKhrCallbackInvoke(Class<?> callbackClass) {
|
||||
if (!khrCallbackInvokeResolved) {
|
||||
khrCallbackInvokeResolved = true;
|
||||
try {
|
||||
khrCallbackInvoke = MethodHandles.publicLookup().findVirtual(callbackClass, "invoke", MethodType.methodType(void.class, int.class, int.class, int.class, int.class, int.class, long.class, long.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
GLStateManager.LOGGER.warn("{} has no public invoke(int,int,int,int,int,long,long); debug messages registered through it will not be delivered.", callbackClass.getName());
|
||||
}
|
||||
}
|
||||
return khrCallbackInvoke;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
public final class GLESCaps {
|
||||
|
||||
private GLESCaps() {}
|
||||
|
||||
public static boolean isCapAllowed(int cap, boolean isGLES, boolean hasClipCullDistance) {
|
||||
if (!isGLES) return true;
|
||||
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + GLStateManager.MAX_CLIP_PLANES) {
|
||||
return hasClipCullDistance;
|
||||
}
|
||||
return switch (cap) {
|
||||
case GL11.GL_LINE_SMOOTH,
|
||||
GL11.GL_POLYGON_SMOOTH,
|
||||
GL11.GL_COLOR_LOGIC_OP,
|
||||
GL11.GL_POLYGON_OFFSET_POINT,
|
||||
GL11.GL_POLYGON_OFFSET_LINE,
|
||||
GL13.GL_MULTISAMPLE,
|
||||
GL13.GL_SAMPLE_ALPHA_TO_ONE,
|
||||
GL20.GL_VERTEX_PROGRAM_POINT_SIZE,
|
||||
GL30.GL_FRAMEBUFFER_SRGB,
|
||||
GL31.GL_PRIMITIVE_RESTART,
|
||||
GL32.GL_DEPTH_CLAMP -> false;
|
||||
default -> true;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
public final class GLESFormatRemap {
|
||||
|
||||
private GLESFormatRemap() {}
|
||||
|
||||
public record Result(int internalFormat, int format, int type) {}
|
||||
|
||||
public static Result apply(int internalformat, int format, int type, boolean isGLES) {
|
||||
internalformat = promoteAlphaFormat(internalformat);
|
||||
if (isGLES) {
|
||||
internalformat = remapInternalFormat(internalformat);
|
||||
if (type == GL12.GL_UNSIGNED_INT_8_8_8_8_REV && (format == GL12.GL_BGRA || format == GL11.GL_RGBA)) {
|
||||
type = GL11.GL_UNSIGNED_BYTE;
|
||||
}
|
||||
if (isGenericPixelType(type)) {
|
||||
type = typeForInternalFormatES32(internalformat, type);
|
||||
}
|
||||
}
|
||||
return new Result(internalformat, format, type);
|
||||
}
|
||||
|
||||
public static int promoteAlphaFormat(int internalformat) {
|
||||
return switch (internalformat) {
|
||||
case GL11.GL_ALPHA4 -> GL11.GL_RGBA4;
|
||||
case GL11.GL_ALPHA8 -> GL11.GL_RGBA8;
|
||||
case GL11.GL_ALPHA12 -> GL11.GL_RGBA12;
|
||||
case GL11.GL_ALPHA16 -> GL11.GL_RGBA16;
|
||||
default -> internalformat;
|
||||
};
|
||||
}
|
||||
|
||||
public static int remapInternalFormat(int internalformat) {
|
||||
return switch (internalformat) {
|
||||
case GL11.GL_RGB16, GL11.GL_RGB12 -> GL30.GL_RGB16F;
|
||||
case GL11.GL_RGBA16, GL11.GL_RGBA12 -> GL30.GL_RGBA16F;
|
||||
case GL30.GL_R16 -> GL30.GL_R16F;
|
||||
case GL30.GL_RG16 -> GL30.GL_RG16F;
|
||||
case GL11.GL_RGB10, GL11.GL_R3_G3_B2 -> GL11.GL_RGB8;
|
||||
default -> internalformat;
|
||||
};
|
||||
}
|
||||
|
||||
public static int remapPixelType(int format, int type) {
|
||||
if (type == GL12.GL_UNSIGNED_INT_8_8_8_8_REV && (format == GL12.GL_BGRA || format == GL11.GL_RGBA)) {
|
||||
return GL11.GL_UNSIGNED_BYTE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static int typeForInternalFormatES32(int internalformat, int type) {
|
||||
return switch (internalformat) {
|
||||
case GL30.GL_R16F, GL30.GL_RG16F, GL30.GL_RGB16F, GL30.GL_RGBA16F -> GL30.GL_HALF_FLOAT;
|
||||
case GL30.GL_R32F, GL30.GL_RG32F, GL30.GL_RGB32F, GL30.GL_RGBA32F -> GL11.GL_FLOAT;
|
||||
case GL31.GL_R8_SNORM, GL31.GL_RG8_SNORM, GL31.GL_RGB8_SNORM, GL31.GL_RGBA8_SNORM -> GL11.GL_BYTE;
|
||||
case GL30.GL_R11F_G11F_B10F, GL30.GL_RGB9_E5 -> GL30.GL_HALF_FLOAT;
|
||||
case GL11.GL_RGB10_A2 -> GL12.GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
case GL30.GL_R8I, GL30.GL_RG8I, GL30.GL_RGB8I, GL30.GL_RGBA8I,
|
||||
GL30.GL_R16I, GL30.GL_RG16I, GL30.GL_RGB16I, GL30.GL_RGBA16I,
|
||||
GL30.GL_R32I, GL30.GL_RG32I, GL30.GL_RGB32I, GL30.GL_RGBA32I -> GL11.GL_INT;
|
||||
case GL30.GL_R8UI, GL30.GL_RG8UI, GL30.GL_RGB8UI, GL30.GL_RGBA8UI,
|
||||
GL30.GL_R16UI, GL30.GL_RG16UI, GL30.GL_RGB16UI, GL30.GL_RGBA16UI,
|
||||
GL30.GL_R32UI, GL30.GL_RG32UI, GL30.GL_RGB32UI, GL30.GL_RGBA32UI -> GL11.GL_UNSIGNED_INT;
|
||||
case GL14.GL_DEPTH_COMPONENT24 -> GL11.GL_UNSIGNED_INT;
|
||||
case GL30.GL_DEPTH_COMPONENT32F -> GL11.GL_FLOAT;
|
||||
case GL30.GL_DEPTH24_STENCIL8 -> GL30.GL_UNSIGNED_INT_24_8;
|
||||
case GL30.GL_DEPTH32F_STENCIL8 -> GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||
default -> type;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isGenericPixelType(int type) {
|
||||
return type == GL11.GL_UNSIGNED_BYTE
|
||||
|| type == GL11.GL_UNSIGNED_SHORT
|
||||
|| type == GL11.GL_UNSIGNED_INT
|
||||
|| type == GL11.GL_BYTE
|
||||
|| type == GL11.GL_SHORT
|
||||
|| type == GL11.GL_INT;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,57 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
public final class GLTypes {
|
||||
|
||||
private GLTypes() {}
|
||||
|
||||
public static int sizeBytes(int glType) {
|
||||
return switch (glType) {
|
||||
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE -> 1;
|
||||
case GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT, GL30.GL_HALF_FLOAT -> 2;
|
||||
case GL11.GL_INT, GL11.GL_UNSIGNED_INT, GL11.GL_FLOAT -> 4;
|
||||
case GL11.GL_DOUBLE -> 8;
|
||||
default -> throw new IllegalArgumentException("Unknown GL type: 0x" + Integer.toHexString(glType));
|
||||
};
|
||||
}
|
||||
|
||||
public static int packedTexelBytes(int glType) {
|
||||
return switch (glType) {
|
||||
case GL12.GL_UNSIGNED_BYTE_3_3_2, GL12.GL_UNSIGNED_BYTE_2_3_3_REV -> 1;
|
||||
case GL12.GL_UNSIGNED_SHORT_5_6_5, GL12.GL_UNSIGNED_SHORT_5_6_5_REV,
|
||||
GL12.GL_UNSIGNED_SHORT_4_4_4_4, GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV,
|
||||
GL12.GL_UNSIGNED_SHORT_5_5_5_1, GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV -> 2;
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8, GL12.GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
GL12.GL_UNSIGNED_INT_10_10_10_2, GL12.GL_UNSIGNED_INT_2_10_10_10_REV,
|
||||
GL30.GL_UNSIGNED_INT_24_8, GL30.GL_UNSIGNED_INT_10F_11F_11F_REV,
|
||||
GL30.GL_UNSIGNED_INT_5_9_9_9_REV -> 4;
|
||||
case GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV -> 8;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static String name(int glType) {
|
||||
return switch (glType) {
|
||||
case GL11.GL_BYTE -> "BYTE";
|
||||
case GL11.GL_UNSIGNED_BYTE -> "UNSIGNED_BYTE";
|
||||
case GL11.GL_SHORT -> "SHORT";
|
||||
case GL11.GL_UNSIGNED_SHORT -> "UNSIGNED_SHORT";
|
||||
case GL11.GL_INT -> "INT";
|
||||
case GL11.GL_UNSIGNED_INT -> "UNSIGNED_INT";
|
||||
case GL11.GL_FLOAT -> "FLOAT";
|
||||
case GL11.GL_DOUBLE -> "DOUBLE";
|
||||
case GL30.GL_HALF_FLOAT -> "HALF_FLOAT";
|
||||
case GL12.GL_UNSIGNED_BYTE_3_3_2 -> "UNSIGNED_BYTE_3_3_2";
|
||||
case GL12.GL_UNSIGNED_SHORT_4_4_4_4 -> "UNSIGNED_SHORT_4_4_4_4";
|
||||
case GL12.GL_UNSIGNED_SHORT_5_5_5_1 -> "UNSIGNED_SHORT_5_5_5_1";
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8 -> "UNSIGNED_INT_8_8_8_8";
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8_REV -> "UNSIGNED_INT_8_8_8_8_REV";
|
||||
case GL12.GL_UNSIGNED_INT_10_10_10_2 -> "UNSIGNED_INT_10_10_10_2";
|
||||
case GL12.GL_UNSIGNED_INT_2_10_10_10_REV -> "UNSIGNED_INT_2_10_10_10_REV";
|
||||
default -> String.format("0x%X", glType);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.antlr.v4.runtime.BufferedTokenStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonToken;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.WritableToken;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import org.taumc.glsl.grammar.GLSLPreParser;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Shared GLSL shader transformation utilities.
|
||||
*/
|
||||
public class GlslTransformUtils {
|
||||
|
||||
private static final String RENAMED_PREFIX = "angelica_renamed_";
|
||||
|
||||
/** Texture function renames */
|
||||
public static final Map<String, String> TEXTURE_RENAMES = Map.ofEntries(
|
||||
Map.entry("texture2D", "texture"),
|
||||
Map.entry("texture3D", "texture"),
|
||||
Map.entry("texture2DLod", "textureLod"),
|
||||
Map.entry("texture3DLod", "textureLod"),
|
||||
Map.entry("texture2DProj", "textureProj"),
|
||||
Map.entry("texture3DProj", "textureProj"),
|
||||
Map.entry("texture2DGrad", "textureGrad"),
|
||||
Map.entry("texture2DGradARB", "textureGrad"),
|
||||
Map.entry("texture3DGrad", "textureGrad"),
|
||||
Map.entry("texelFetch2D", "texelFetch"),
|
||||
Map.entry("texelFetch3D", "texelFetch"),
|
||||
Map.entry("textureSize2D", "textureSize")
|
||||
);
|
||||
|
||||
private static final Pattern TEXTURE_PATTERN = Pattern.compile("\\btexture\\s*\\(|(\\btexture\\b)");
|
||||
|
||||
/** Reserved words added in later GLSL versions that may appear as identifiers in older shaders. */
|
||||
private record ReservedWordRename(Pattern pattern, String replacement) {}
|
||||
private static final Map<Integer, List<ReservedWordRename>> VERSIONED_RESERVED_WORDS = Map.of(
|
||||
// Some reserved words to always rename
|
||||
0, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample"),
|
||||
new ReservedWordRename(Pattern.compile("\\bnew\\b"), RENAMED_PREFIX + "new")
|
||||
),
|
||||
400, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsampler\\b(?!\\d)"), RENAMED_PREFIX + "sampler")
|
||||
)
|
||||
);
|
||||
|
||||
public static String replaceTexture(String input) {
|
||||
final Matcher matcher = TEXTURE_PATTERN.matcher(input);
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
while (matcher.find()) {
|
||||
if (matcher.group(1) != null) {
|
||||
matcher.appendReplacement(builder, RENAMED_PREFIX + "texture");
|
||||
} else {
|
||||
matcher.appendReplacement(builder, Matcher.quoteReplacement(matcher.group(0)));
|
||||
}
|
||||
}
|
||||
matcher.appendTail(builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String renameReservedWords(String source, int targetVersion) {
|
||||
for (var entry : VERSIONED_RESERVED_WORDS.entrySet()) {
|
||||
// Rename identifiers that become reserved words at or above this GLSL version
|
||||
if (targetVersion >= entry.getKey()) {
|
||||
for (var rename : entry.getValue()) {
|
||||
source = rename.pattern().matcher(source).replaceAll(rename.replacement());
|
||||
}
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
public static String restoreReservedWords(String source) {
|
||||
source = source.replace(RENAMED_PREFIX + "texture", "texture");
|
||||
source = source.replace(RENAMED_PREFIX + "sampler", "sampler");
|
||||
source = source.replace(RENAMED_PREFIX + "sample", "sample");
|
||||
return source;
|
||||
}
|
||||
|
||||
public static void restoreReservedWordsInTree(ParseTree tree) {
|
||||
if (tree instanceof TerminalNode terminal) {
|
||||
final Token token = terminal.getSymbol();
|
||||
final String text = token.getText();
|
||||
if (text != null && text.startsWith(RENAMED_PREFIX) && token instanceof WritableToken writable) {
|
||||
final String rest = text.substring(RENAMED_PREFIX.length());
|
||||
if (rest.startsWith("texture") || rest.startsWith("sample")) {
|
||||
writable.setText(rest);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < tree.getChildCount(); i++) {
|
||||
restoreReservedWordsInTree(tree.getChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
public record QuietParse(GLSLParser.Translation_unitContext full, GLSLPreParser.Translation_unitContext pre) {}
|
||||
|
||||
public static GLSLParser.Translation_unitContext parseFullQuiet(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final GLSLParser parser = new GLSLParser(new CommonTokenStream(lexer));
|
||||
parser.removeErrorListeners();
|
||||
parser.setBuildParseTree(true);
|
||||
return parser.translation_unit();
|
||||
}
|
||||
|
||||
public static GLSLPreParser.Translation_unitContext parsePreQuiet(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final GLSLPreParser preParser = new GLSLPreParser(new CommonTokenStream(lexer, GLSLLexer.DIRECTIVES));
|
||||
preParser.removeErrorListeners();
|
||||
preParser.setBuildParseTree(true);
|
||||
return preParser.translation_unit();
|
||||
}
|
||||
|
||||
public static QuietParse parseBothQuiet(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final GLSLPreParser preParser = new GLSLPreParser(new BufferedTokenStream(lexer));
|
||||
preParser.removeErrorListeners();
|
||||
preParser.setBuildParseTree(true);
|
||||
final GLSLPreParser.Translation_unitContext pre = preParser.translation_unit();
|
||||
lexer.reset();
|
||||
final GLSLParser parser = new GLSLParser(new CommonTokenStream(lexer));
|
||||
parser.removeErrorListeners();
|
||||
parser.setBuildParseTree(true);
|
||||
return new QuietParse(parser.translation_unit(), pre);
|
||||
}
|
||||
|
||||
public static Set<String> identifiersInDirectiveText(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final BufferedTokenStream tokens = new BufferedTokenStream(lexer);
|
||||
tokens.fill();
|
||||
|
||||
final Set<String> names = new HashSet<>();
|
||||
for (Token token : tokens.getTokens()) {
|
||||
switch (token.getType()) {
|
||||
case GLSLLexer.PROGRAM_TEXT, GLSLLexer.MACRO_TEXT, GLSLLexer.CONSTANT_EXPRESSION -> collectIdentifiers(token.getText(), names);
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private static void collectIdentifiers(String text, Set<String> out) {
|
||||
if (text == null) return;
|
||||
final int len = text.length();
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
final char c = text.charAt(i);
|
||||
if (c != '_' && !Character.isLetter(c)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
int end = i + 1;
|
||||
while (end < len) {
|
||||
final char n = text.charAt(end);
|
||||
if (n != '_' && !Character.isLetterOrDigit(n)) break;
|
||||
end++;
|
||||
}
|
||||
out.add(text.substring(i, end));
|
||||
i = end;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFormattedShader(ParseTree tree, String header) {
|
||||
final StringBuilder sb = new StringBuilder(header + "\n");
|
||||
final String[] tabHolder = {""};
|
||||
getFormattedShader(tree, sb, tabHolder, false);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getFormattedShaderRebased(ParseTree tree, String header) {
|
||||
final StringBuilder sb = new StringBuilder(header + "\n");
|
||||
final String[] tabHolder = {""};
|
||||
getFormattedShader(tree, sb, tabHolder, true);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void getFormattedShader(ParseTree tree, StringBuilder stringBuilder, String[] tabHolder, boolean rebaseOffsets) {
|
||||
if (tree instanceof TerminalNode terminal) {
|
||||
final String text = tree.getText();
|
||||
if (text.equals("<EOF>")) {
|
||||
return;
|
||||
}
|
||||
if (text.equals("#")) {
|
||||
stringBuilder.append("\n#");
|
||||
rebaseAt(terminal, stringBuilder.length() - 1, text, rebaseOffsets);
|
||||
return;
|
||||
}
|
||||
stringBuilder.append(text);
|
||||
if (!text.equals("}")) {
|
||||
rebaseAt(terminal, stringBuilder.length() - text.length(), text, rebaseOffsets);
|
||||
}
|
||||
if (text.equals("{")) {
|
||||
stringBuilder.append(" \n\t");
|
||||
tabHolder[0] = "\t";
|
||||
}
|
||||
|
||||
if (text.equals("}")) {
|
||||
if (stringBuilder.length() >= 2) {
|
||||
stringBuilder.deleteCharAt(stringBuilder.length() - 2);
|
||||
}
|
||||
rebaseAt(terminal, stringBuilder.length() - 1, text, rebaseOffsets);
|
||||
tabHolder[0] = "";
|
||||
stringBuilder.append(" \n");
|
||||
} else {
|
||||
stringBuilder.append(text.equals(";") ? " \n" + tabHolder[0] : " ");
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < tree.getChildCount(); ++i) {
|
||||
getFormattedShader(tree.getChild(i), stringBuilder, tabHolder, rebaseOffsets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void rebaseAt(TerminalNode terminal, int startIndex, String text, boolean rebaseOffsets) {
|
||||
if (!rebaseOffsets) return;
|
||||
if (terminal.getSymbol() instanceof CommonToken common) {
|
||||
common.setText(text);
|
||||
common.setStartIndex(startIndex);
|
||||
common.setStopIndex(startIndex + text.length() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class LTWWorkaround {
|
||||
|
||||
private LTWWorkaround() {}
|
||||
|
||||
private static ByteBuffer oneTexel;
|
||||
|
||||
public static boolean isLtwVersionString(String glVersion) {
|
||||
return glVersion != null && glVersion.contains("LTW");
|
||||
}
|
||||
|
||||
public static void onTexImage2D(int target, int level, int format, boolean hasPixels) {
|
||||
if (target != GL11.GL_TEXTURE_2D || level != 0 || format != GL12.GL_BGRA || hasPixels) return;
|
||||
if (oneTexel == null) {
|
||||
oneTexel = BufferUtils.createByteBuffer(4);
|
||||
}
|
||||
oneTexel.clear();
|
||||
GLStateManager.forcePixelUnpackState(PixelUnpackState.DEFAULT);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texSubImage2D(target, level, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, oneTexel);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLStateManager.restorePixelUnpackState(PixelUnpackState.DEFAULT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
|
||||
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.
|
||||
*
|
||||
* <p>Maintains a single growable element buffer with pre-computed indices mapping
|
||||
* GL_QUADS vertex order to GL_TRIANGLES: {@code (0,1,3, 1,2,3)} per quad.
|
||||
* Uses {@code GL_LAST_VERTEX_CONVENTION} winding (Mesa {@code do_quad} LAST path)
|
||||
* so both emitted triangles share the quad's provoking vertex (v3).
|
||||
*/
|
||||
public final class QuadConverter {
|
||||
public static final int INDEX_TYPE = GL11.GL_UNSIGNED_INT;
|
||||
|
||||
private static int eboId;
|
||||
private static int maxQuads;
|
||||
|
||||
/** Scratch EBO for converted quad-element draws (drawQuadElementsAsTriangles). */
|
||||
private static int scratchEboId;
|
||||
private static int scratchEboCapacity; // in bytes
|
||||
|
||||
private QuadConverter() {}
|
||||
|
||||
private static void ensureCapacity(int quadCount) {
|
||||
if (quadCount <= maxQuads) return;
|
||||
|
||||
// Round up to power of 2 for amortized growth
|
||||
int newMaxQuads = Math.max(256, maxQuads);
|
||||
while (newMaxQuads < quadCount) {
|
||||
newMaxQuads *= 2;
|
||||
}
|
||||
|
||||
if (eboId == 0) {
|
||||
eboId = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
// Generate index data: 6 ints per quad
|
||||
final ByteBuffer indexData = memAlloc(newMaxQuads * 6 * 4);
|
||||
long ptr = memAddress0(indexData);
|
||||
for (int i = 0; i < newMaxQuads; i++) {
|
||||
final int base = i * 4;
|
||||
// GL_LAST_VERTEX_CONVENTION: both triangles share v3 as provoking vertex
|
||||
// Triangle 1: 0, 1, 3
|
||||
memPutInt(ptr, base);
|
||||
memPutInt(ptr + 4, base + 1);
|
||||
memPutInt(ptr + 8, base + 3);
|
||||
// Triangle 2: 1, 2, 3
|
||||
memPutInt(ptr + 12, base + 1);
|
||||
memPutInt(ptr + 16, base + 2);
|
||||
memPutInt(ptr + 20, base + 3);
|
||||
ptr += 24;
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indexData, GL15.GL_STATIC_DRAW);
|
||||
|
||||
memFree(indexData);
|
||||
maxQuads = newMaxQuads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a GL_QUADS glDrawArrays call to indexed GL_TRIANGLES.
|
||||
* Binds the shared EBO to the current VAO and issues glDrawElements.
|
||||
*
|
||||
* @param first first vertex index (must be aligned to quad boundary, i.e. multiple of 4)
|
||||
* @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 int misalignedDraws;
|
||||
|
||||
private static void drawSharedQuadEbo(int first, int vertexCount, int primcount, boolean instanced) {
|
||||
final int quadCount = vertexCount / 4;
|
||||
if ((first & 3) != 0) {
|
||||
misalignedDraws++;
|
||||
if ((misalignedDraws & (misalignedDraws - 1)) == 0) {
|
||||
GLStateManager.LOGGER.warn("QuadConverter: quad draw with unaligned first={} count={} (#{}) -- using scratch indices", first, vertexCount, misalignedDraws);
|
||||
}
|
||||
final ByteBuffer dst = memAlloc(quadCount * 6 * 4);
|
||||
long ptr = memAddress0(dst);
|
||||
for (int i = 0; i < quadCount; i++) {
|
||||
final int base = first + i * 4;
|
||||
memPutInt(ptr, base);
|
||||
memPutInt(ptr + 4, base + 1);
|
||||
memPutInt(ptr + 8, base + 3);
|
||||
memPutInt(ptr + 12, base + 1);
|
||||
memPutInt(ptr + 16, base + 2);
|
||||
memPutInt(ptr + 20, base + 3);
|
||||
ptr += 24;
|
||||
}
|
||||
uploadAndDraw(dst, quadCount * 6, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
return;
|
||||
}
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
GLStateManager.preDraw();
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
ensureCapacity(first / 4 + quadCount);
|
||||
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;
|
||||
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);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload converted triangle indices to the scratch EBO and draw.
|
||||
* Saves/restores the caller's EBO binding so subsequent indexed draws
|
||||
* (or glGetBufferSubData reads) still see the original EBO.
|
||||
*
|
||||
* @param dst off-heap buffer with triangle index data (freed by this method)
|
||||
* @param triIndexCount number of triangle indices
|
||||
* @param indexType GL_UNSIGNED_INT or GL_UNSIGNED_SHORT
|
||||
* @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) {
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
GLStateManager.preDraw();
|
||||
final int needed = triIndexCount * bytesPerIndex;
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
|
||||
if (scratchEboId == 0) {
|
||||
scratchEboId = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
|
||||
|
||||
if (needed > scratchEboCapacity) {
|
||||
// Power-of-2 growth -- allocate full capacity, upload actual data
|
||||
int newCap = Math.max(4096, scratchEboCapacity);
|
||||
while (newCap < needed) newCap *= 2;
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
|
||||
scratchEboCapacity = newCap;
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
|
||||
|
||||
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);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
memFree(dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an indexed GL_QUADS draw to GL_TRIANGLES by reading the source EBO,
|
||||
* building a triangle index buffer, and issuing the converted draw.
|
||||
*
|
||||
* @param indexCount number of quad indices (must be multiple of 4)
|
||||
* @param type GL_UNSIGNED_INT, GL_UNSIGNED_SHORT, or GL_UNSIGNED_BYTE
|
||||
* @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 = GLTypes.sizeBytes(type);
|
||||
|
||||
final ByteBuffer src = memAlloc(indexCount * bytesPerIndex);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, src);
|
||||
|
||||
final ByteBuffer dst = memAlloc(triIndexCount * 4);
|
||||
triangulateQuads(memAddress0(src), type, memAddress0(dst), GL11.GL_UNSIGNED_INT, quadCount);
|
||||
|
||||
memFree(src);
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a client-side IntBuffer of quad indices to triangles.
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(IntBuffer indices) {
|
||||
final int indexCount = indices.remaining();
|
||||
if (indexCount == 0) return;
|
||||
assert indexCount % 4 == 0;
|
||||
final int quadCount = indexCount / 4;
|
||||
final int triIndexCount = quadCount * 6;
|
||||
|
||||
final ByteBuffer dst = memAlloc(triIndexCount * 4);
|
||||
final long srcAddr = memAddress0(indices) + (long) indices.position() * 4;
|
||||
triangulateQuads(srcAddr, GL11.GL_UNSIGNED_INT, memAddress0(dst), GL11.GL_UNSIGNED_INT, quadCount);
|
||||
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a client-side ShortBuffer of quad indices to triangles.
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(ShortBuffer indices) {
|
||||
final int indexCount = indices.remaining();
|
||||
if (indexCount == 0) return;
|
||||
assert indexCount % 4 == 0;
|
||||
final int quadCount = indexCount / 4;
|
||||
final int triIndexCount = quadCount * 6;
|
||||
|
||||
final ByteBuffer dst = memAlloc(triIndexCount * 2);
|
||||
final long srcAddr = memAddress0(indices) + (long) indices.position() * 2;
|
||||
triangulateQuads(srcAddr, GL11.GL_UNSIGNED_SHORT, memAddress0(dst), GL11.GL_UNSIGNED_SHORT, quadCount);
|
||||
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_SHORT, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a client-side ByteBuffer of quad indices (type-agnostic) to triangles.
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(int count, int type, ByteBuffer indices) {
|
||||
if (count == 0) return;
|
||||
assert count % 4 == 0;
|
||||
final int quadCount = count / 4;
|
||||
final int triIndexCount = quadCount * 6;
|
||||
final long srcAddr = memAddress0(indices) + indices.position();
|
||||
|
||||
final ByteBuffer dst = memAlloc(triIndexCount * 4);
|
||||
triangulateQuads(srcAddr, type, memAddress0(dst), GL11.GL_UNSIGNED_INT, quadCount);
|
||||
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the shared EBO.
|
||||
*/
|
||||
public static void destroy() {
|
||||
if (eboId != 0) {
|
||||
GLStateManager.glDeleteBuffers(eboId);
|
||||
eboId = 0;
|
||||
maxQuads = 0;
|
||||
}
|
||||
if (scratchEboId != 0) {
|
||||
GLStateManager.glDeleteBuffers(scratchEboId);
|
||||
scratchEboId = 0;
|
||||
scratchEboCapacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one index at {@code srcAddr + i * sizeof(srcType)} and return its
|
||||
* zero-extended unsigned value.
|
||||
*/
|
||||
private static int readIndex(long srcAddr, int srcType, int i) {
|
||||
return switch (srcType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> memGetByte(srcAddr + i) & 0xFF;
|
||||
case GL11.GL_UNSIGNED_SHORT -> memGetShort(srcAddr + i * 2L) & 0xFFFF;
|
||||
case GL11.GL_UNSIGNED_INT -> memGetInt(srcAddr + i * 4L);
|
||||
default -> throw new IllegalArgumentException("unsupported index type 0x" + Integer.toHexString(srcType));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Write one index at {@code dstAddr + i * sizeof(dstType)}.
|
||||
*/
|
||||
private static void writeIndex(long dstAddr, int dstType, int i, int value) {
|
||||
switch (dstType) {
|
||||
case GL11.GL_UNSIGNED_SHORT -> memPutShort(dstAddr + i * 2L, (short) value);
|
||||
case GL11.GL_UNSIGNED_INT -> memPutInt(dstAddr + i * 4L, value);
|
||||
default -> throw new IllegalArgumentException("unsupported index type 0x" + Integer.toHexString(dstType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code quadCount} quads from {@code srcAddr} and emit 6 triangle indices
|
||||
* per quad at {@code dstAddr} using {@code GL_LAST_VERTEX_CONVENTION}:
|
||||
* {@code (a,b,d), (b,c,d)}. v3 is the provoking vertex for both triangles;
|
||||
* diagonal runs b→d. Matches Mesa's {@code do_quad} LAST path.
|
||||
*
|
||||
* <p>Caller allocates dst of size {@code quadCount * 6 * sizeof(dstType)}.
|
||||
*
|
||||
* <p>Supported src types: {@code GL_UNSIGNED_BYTE} / {@code GL_UNSIGNED_SHORT}
|
||||
* / {@code GL_UNSIGNED_INT}. Supported dst types: {@code GL_UNSIGNED_SHORT} /
|
||||
* {@code GL_UNSIGNED_INT}. Widening only.
|
||||
*/
|
||||
public static void triangulateQuads(long srcAddr, int srcType, long dstAddr, int dstType, int quadCount) {
|
||||
triangulateQuads(srcAddr, srcType, dstAddr, dstType, quadCount, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #triangulateQuads(long, int, long, int, int)} but subtracts
|
||||
* {@code minVtx} from every emitted index, rebasing to a 0-based buffer in
|
||||
* one pass.
|
||||
*/
|
||||
public static void triangulateQuads(long srcAddr, int srcType, long dstAddr, int dstType, int quadCount, int minVtx) {
|
||||
for (int i = 0; i < quadCount; i++) {
|
||||
final int a = readIndex(srcAddr, srcType, i * 4) - minVtx;
|
||||
final int b = readIndex(srcAddr, srcType, i * 4 + 1) - minVtx;
|
||||
final int c = readIndex(srcAddr, srcType, i * 4 + 2) - minVtx;
|
||||
final int d = readIndex(srcAddr, srcType, i * 4 + 3) - minVtx;
|
||||
final int o = i * 6;
|
||||
writeIndex(dstAddr, dstType, o, a);
|
||||
writeIndex(dstAddr, dstType, o + 1, b);
|
||||
writeIndex(dstAddr, dstType, o + 2, d);
|
||||
writeIndex(dstAddr, dstType, o + 3, b);
|
||||
writeIndex(dstAddr, dstType, o + 4, c);
|
||||
writeIndex(dstAddr, dstType, o + 5, d);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy {@code count} indices from {@code srcAddr} to {@code dstAddr}, converting
|
||||
* from {@code srcType} to {@code dstType}.
|
||||
*
|
||||
* <p>Supported src types: {@code GL_UNSIGNED_BYTE} / {@code GL_UNSIGNED_SHORT} /
|
||||
* {@code GL_UNSIGNED_INT}. Supported dst types: {@code GL_UNSIGNED_SHORT} /
|
||||
* {@code GL_UNSIGNED_INT}. Widening only; narrowing is rejected by
|
||||
* {@link #writeIndex(long, int, int, int) writeIndex}.
|
||||
*/
|
||||
public static void widenIndices(long srcAddr, int srcType, long dstAddr, int dstType, int count) {
|
||||
widenIndices(srcAddr, srcType, dstAddr, dstType, count, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #widenIndices(long, int, long, int, int)} but subtracts
|
||||
* {@code minVtx} from every written index in one pass.
|
||||
*/
|
||||
public static void widenIndices(long srcAddr, int srcType, long dstAddr, int dstType, int count, int minVtx) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeIndex(dstAddr, dstType, i, readIndex(srcAddr, srcType, i) - minVtx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan {@code count} indices at {@code srcAddr} of {@code srcType}. Returns
|
||||
* {@code (max << 32) | (min & 0xFFFFFFFFL)} or {@code -1L} for an unsupported
|
||||
* type or empty range.
|
||||
*/
|
||||
public static long scanMinMaxIndex(long srcAddr, int srcType, int count) {
|
||||
if (count <= 0) return -1L;
|
||||
long min = Long.MAX_VALUE;
|
||||
long max = Long.MIN_VALUE;
|
||||
switch (srcType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> {
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int v = memGetByte(srcAddr + i) & 0xFF;
|
||||
if (v < min) min = v;
|
||||
if (v > max) max = v;
|
||||
}
|
||||
}
|
||||
case GL11.GL_UNSIGNED_SHORT -> {
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int v = memGetShort(srcAddr + i * 2L) & 0xFFFF;
|
||||
if (v < min) min = v;
|
||||
if (v > max) max = v;
|
||||
}
|
||||
}
|
||||
case GL11.GL_UNSIGNED_INT -> {
|
||||
for (int i = 0; i < count; i++) {
|
||||
final long v = memGetInt(srcAddr + i * 4L) & 0xFFFFFFFFL;
|
||||
if (v < min) min = v;
|
||||
if (v > max) max = v;
|
||||
}
|
||||
// readIndex / writeIndex / rebase all route UINT through signed int.
|
||||
// Reject the > 2^31 slice so the caller bails rather than silently
|
||||
// emitting negative indices. MC never hits this in practice.
|
||||
if (max > Integer.MAX_VALUE) return -1L;
|
||||
}
|
||||
default -> {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
return (max << 32) | (min & 0xFFFFFFFFL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,695 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.dsa.DSAARB;
|
||||
import com.gtnewhorizons.angelica.glsm.dsa.DSAAccess;
|
||||
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.texture.TextureInfoCache;
|
||||
import com.mitchej123.lwjgl.LWJGLServiceProvider;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3i;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.ARBShaderImageLoadStore;
|
||||
import org.lwjgl.opengl.ARBShaderStorageBufferObject;
|
||||
import org.lwjgl.opengl.EXTShaderImageLoadStore;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.opengl.NVXGpuMemoryInfo;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* This class is responsible for abstracting calls to OpenGL and asserting that calls are run on the render thread.
|
||||
*/
|
||||
public class RenderSystem {
|
||||
private static DSAAccess dsaState;
|
||||
private static boolean supportsCompute;
|
||||
private static boolean supportsImageLoadStore;
|
||||
private static boolean supportsSSBO;
|
||||
private static boolean supportsMultiDrawIndirect;
|
||||
private static boolean supportsBufferStorage;
|
||||
private static boolean supportsClearTexture;
|
||||
private static boolean supportsTesselation;
|
||||
private static boolean supportsSamplerObjects;
|
||||
private static int maxImageUnits;
|
||||
private static int maxSSBOBindings;
|
||||
private static int maxGlslVersion;
|
||||
private static boolean supportsGpuShader4;
|
||||
@Getter private static int glMajor;
|
||||
@Getter private static int glMinor;
|
||||
@Getter private static boolean coreProfile;
|
||||
|
||||
private static volatile boolean isGLES;
|
||||
private static volatile int glesVersion;
|
||||
private static volatile boolean glesDetected;
|
||||
private static volatile boolean hasClipCullDistance;
|
||||
|
||||
private static volatile boolean isLTW;
|
||||
private static volatile boolean ltwDetected;
|
||||
|
||||
// Sampler object state tracking (null if unsupported)
|
||||
private static int[] samplers;
|
||||
|
||||
private static final Pattern SEMVER_PATTERN = Pattern.compile("(?<major>\\d+)\\.(?<minor>\\d+)\\.*(?<bugfix>\\d*)(.*)");
|
||||
|
||||
private RenderSystem() {}
|
||||
|
||||
private static boolean rendererInitialized;
|
||||
|
||||
public static void initRenderer() {
|
||||
if (rendererInitialized) return;
|
||||
rendererInitialized = true;
|
||||
|
||||
final boolean wasDetected = glesDetected;
|
||||
if (isGLES() && !wasDetected) {
|
||||
GLStateManager.LOGGER.info("OpenGL ES {}.{} detected", glesVersion / 100, (glesVersion / 10) % 10);
|
||||
}
|
||||
|
||||
if (!isGLES) {
|
||||
try {
|
||||
if (GLStateManager.vendorIsIntel() && GLStateManager.isWindows()) {
|
||||
dsaState = new DSAUnsupported();
|
||||
GLStateManager.LOGGER.info("Detected Intel drivers on Windows, disabling DSA.");
|
||||
} else if (!GLStateManager.getInitConfig().isDSAEnabled()) {
|
||||
dsaState = new DSAUnsupported();
|
||||
GLStateManager.LOGGER.info("enableDSA is set to false, disabling DSA.");
|
||||
} else if (GLStateManager.capabilities.OpenGL45) {
|
||||
dsaState = (Runtime.version().feature() > 8 && GLStateManager.capabilities.GL_EXT_direct_state_access) ? new DSAEXT() : new DSACore();
|
||||
GLStateManager.LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
|
||||
}
|
||||
|
||||
} catch (NoSuchFieldError ignored) {
|
||||
}
|
||||
try {
|
||||
if (dsaState == null && GLStateManager.capabilities.GL_ARB_direct_state_access) {
|
||||
dsaState = new DSAARB();
|
||||
GLStateManager.LOGGER.info("ARB_direct_state_access detected, enabling DSA.");
|
||||
}
|
||||
} catch (NoSuchFieldError ignored) {
|
||||
}
|
||||
}
|
||||
if (dsaState == null) {
|
||||
dsaState = new DSAUnsupported();
|
||||
GLStateManager.LOGGER.info("{}", isGLES ? "GLES context, using DSAUnsupported (bind-based) fallback." : "No DSA support detected, falling back to legacy OpenGL.");
|
||||
}
|
||||
|
||||
BackendManager.init();
|
||||
|
||||
try {
|
||||
// Force init on client thread
|
||||
GLStateManager.LOGGER.info("Celeritas LWJGL service: {}", LWJGLServiceProvider.LWJGL.getClass().getName());
|
||||
} catch (Throwable t) {
|
||||
GLStateManager.LOGGER.debug("Could not resolve the celeritas LWJGL service: {}", t.toString());
|
||||
}
|
||||
|
||||
supportsCompute = supportsCompute();
|
||||
supportsTesselation = GLStateManager.capabilities.GL_ARB_tessellation_shader || GLStateManager.capabilities.OpenGL40;
|
||||
|
||||
supportsImageLoadStore = GLStateManager.capabilities.OpenGL42
|
||||
|| GLStateManager.capabilities.GL_ARB_shader_image_load_store
|
||||
|| GLStateManager.capabilities.GL_EXT_shader_image_load_store;
|
||||
supportsSSBO = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_shader_storage_buffer_object;
|
||||
supportsMultiDrawIndirect = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_multi_draw_indirect;
|
||||
supportsBufferStorage = !isGLES && (GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_buffer_storage);
|
||||
supportsClearTexture = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_clear_texture;
|
||||
hasClipCullDistance = !isGLES || hasExtension("GL_EXT_clip_cull_distance");
|
||||
|
||||
// Cache maximum image units
|
||||
if (supportsImageLoadStore) {
|
||||
if (GLStateManager.capabilities.OpenGL42) {
|
||||
maxImageUnits = RENDER_BACKEND.getInteger(GL42.GL_MAX_IMAGE_UNITS);
|
||||
} else if (GLStateManager.capabilities.GL_ARB_shader_image_load_store) {
|
||||
maxImageUnits = RENDER_BACKEND.getInteger(ARBShaderImageLoadStore.GL_MAX_IMAGE_UNITS);
|
||||
} else {
|
||||
maxImageUnits = RENDER_BACKEND.getInteger(EXTShaderImageLoadStore.GL_MAX_IMAGE_UNITS_EXT);
|
||||
}
|
||||
} else {
|
||||
maxImageUnits = 0;
|
||||
}
|
||||
|
||||
// Cache maximum SSBO bindings
|
||||
if (supportsSSBO) {
|
||||
if (GLStateManager.capabilities.OpenGL43) {
|
||||
maxSSBOBindings = RENDER_BACKEND.getInteger(GL43.GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
|
||||
} else {
|
||||
maxSSBOBindings = RENDER_BACKEND.getInteger(ARBShaderStorageBufferObject.GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
|
||||
}
|
||||
} else {
|
||||
maxSSBOBindings = 0;
|
||||
}
|
||||
|
||||
// Check for sampler objects support (GL 3.3+ or ARB extension)
|
||||
supportsSamplerObjects = GLStateManager.capabilities.OpenGL33 || GLStateManager.capabilities.GL_ARB_sampler_objects;
|
||||
if (supportsSamplerObjects) {
|
||||
samplers = new int[RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)];
|
||||
}
|
||||
|
||||
maxGlslVersion = Integer.parseInt(parseGlVersionString(RENDER_BACKEND.getString(GL20.GL_SHADING_LANGUAGE_VERSION)));
|
||||
supportsGpuShader4 = GLStateManager.capabilities.GL_EXT_gpu_shader4;
|
||||
|
||||
GLStateManager.LOGGER.info("Max GLSL version: {}, GPU Shader4: {}", maxGlslVersion, supportsGpuShader4);
|
||||
GLStateManager.LOGGER.info("Image Load/Store: {}, Max Image Units: {}", supportsImageLoadStore, maxImageUnits);
|
||||
GLStateManager.LOGGER.info("SSBO: {}, Max SSBO Bindings: {}", supportsSSBO, maxSSBOBindings);
|
||||
GLStateManager.LOGGER.info("Buffer Storage: {}, Clear Texture: {}, Sampler Objects: {}", supportsBufferStorage, supportsClearTexture, supportsSamplerObjects);
|
||||
|
||||
if (isGLES) {
|
||||
GLStateManager.LOGGER.info("GL ES context detected, enabling shader transformer.");
|
||||
ShaderManager.getInstance().enable();
|
||||
} else if (GLStateManager.capabilities.OpenGL32) {
|
||||
final int profileMask = RENDER_BACKEND.getInteger(GL32.GL_CONTEXT_PROFILE_MASK);
|
||||
if ((profileMask & GL32.GL_CONTEXT_CORE_PROFILE_BIT) != 0) {
|
||||
coreProfile = true;
|
||||
GLStateManager.LOGGER.info("GL 3.3 core profile detected, enabling FFP shader emulation.");
|
||||
ShaderManager.getInstance().enable();
|
||||
} else {
|
||||
throw new IllegalStateException("Non-core GL context (profile mask 0x" + Integer.toHexString(profileMask)
|
||||
+ "); FFP emulation requires a core profile and nothing would render. Context creation should have rejected this.");
|
||||
}
|
||||
}
|
||||
|
||||
final Matcher glVerMatcher = SEMVER_PATTERN.matcher(RENDER_BACKEND.getString(GL11.GL_VERSION));
|
||||
if (glVerMatcher.matches()) {
|
||||
glMajor = Integer.parseInt(glVerMatcher.group("major"));
|
||||
glMinor = Integer.parseInt(glVerMatcher.group("minor"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void generateMipmaps(int texture, int mipmapTarget) {
|
||||
dsaState.generateMipmaps(texture, mipmapTarget);
|
||||
}
|
||||
|
||||
public static void bindAttributeLocation(int program, int index, CharSequence name) {
|
||||
RENDER_BACKEND.bindAttribLocation(program, index, name);
|
||||
}
|
||||
|
||||
public static void texImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) {
|
||||
final GLStateManager.GLESTexImageRemap remap = GLStateManager.remapTexImageForGLES(internalformat, format, type);
|
||||
internalformat = remap.internalFormat();
|
||||
format = remap.format();
|
||||
type = remap.type();
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
if (target == GL11.GL_TEXTURE_2D && level == 0) {
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
public static void uniformMatrix4fv(int location, boolean transpose, FloatBuffer matrix) {
|
||||
RENDER_BACKEND.uniformMatrix4(location, transpose, matrix);
|
||||
}
|
||||
|
||||
public static void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) {
|
||||
GLStateManager.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
|
||||
}
|
||||
|
||||
public static void uniform1f(int location, float v0) {
|
||||
RENDER_BACKEND.uniform1f(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform1i(int location, int v0) {
|
||||
RENDER_BACKEND.uniform1i(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform2f(int location, float v0, float v1) {
|
||||
RENDER_BACKEND.uniform2f(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform2i(int location, int v0, int v1) {
|
||||
RENDER_BACKEND.uniform2i(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform3f(int location, float v0, float v1, float v2) {
|
||||
RENDER_BACKEND.uniform3f(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) {RENDER_BACKEND.uniform3i(location, v0, v1, v2);}
|
||||
|
||||
public static void uniform4f(int location, float v0, float v1, float v2, float v3) {
|
||||
RENDER_BACKEND.uniform4f(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void uniform4i(int location, int v0, int v1, int v2, int v3) {
|
||||
RENDER_BACKEND.uniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static int getAttribLocation(int programId, String name) {
|
||||
return RENDER_BACKEND.getAttribLocation(programId, name);
|
||||
}
|
||||
|
||||
public static int getUniformLocation(int programId, String name) {
|
||||
return RENDER_BACKEND.getUniformLocation(programId, name);
|
||||
}
|
||||
|
||||
public static void texParameteriv(int texture, int target, int pname, IntBuffer params) {
|
||||
dsaState.texParameteriv(texture, target, pname, params);
|
||||
}
|
||||
|
||||
public static void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
dsaState.copyTexSubImage2D(destTexture, target, i, i1, i2, i3, i4, width, height);
|
||||
}
|
||||
|
||||
public static void texParameteri(int texture, int target, int pname, int param) {
|
||||
dsaState.texParameteri(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void texParameterf(int texture, int target, int pname, float param) {
|
||||
dsaState.texParameterf(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void texImage3D(int texture, int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static String getProgramInfoLog(int program) {
|
||||
return RENDER_BACKEND.getProgramInfoLog(program, RENDER_BACKEND.getProgrami(program, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static String getShaderInfoLog(int shader) {
|
||||
return RENDER_BACKEND.getShaderInfoLog(shader, RENDER_BACKEND.getShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static void drawBuffers(int framebuffer, IntBuffer buffers) {
|
||||
dsaState.drawBuffers(framebuffer, buffers);
|
||||
}
|
||||
|
||||
public static void readBuffer(int framebuffer, int buffer) {
|
||||
dsaState.readBuffer(framebuffer, buffer);
|
||||
}
|
||||
|
||||
public static String getActiveUniform(int program, int index, int maxLength, IntBuffer sizeType) {
|
||||
return RENDER_BACKEND.getActiveUniform(program, index, maxLength, sizeType);
|
||||
}
|
||||
|
||||
public static void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
GLStateManager.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelPackBuffer();
|
||||
}
|
||||
|
||||
public static void bufferData(int target, FloatBuffer data, int usage) {
|
||||
GLStateManager.glBufferData(target, data, usage);
|
||||
}
|
||||
|
||||
public static int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
return dsaState.bufferStorage(target, data, usage);
|
||||
}
|
||||
|
||||
public static void vertexAttrib4f(int index, float v0, float v1, float v2, float v3) {
|
||||
RENDER_BACKEND.vertexAttrib4f(index, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void detachShader(int program, int shader) {
|
||||
RENDER_BACKEND.detachShader(program, shader);
|
||||
}
|
||||
|
||||
public static void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
dsaState.framebufferTexture2D(fb, fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
public static int getTexParameteri(int texture, int target, int pname) {
|
||||
return dsaState.getTexParameteri(texture, target, pname);
|
||||
}
|
||||
|
||||
public static float getTexParameterf(int texture, int target, int pname) {
|
||||
return dsaState.getTexParameterf(texture, target, pname);
|
||||
}
|
||||
|
||||
public static int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
return dsaState.getTexLevelParameteri(texture, level, pname);
|
||||
}
|
||||
|
||||
public static void bindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format) {
|
||||
GLStateManager.glBindImageTexture(unit, texture, level, layered, layer, access, format);
|
||||
}
|
||||
|
||||
public static int getMaxImageUnits() {
|
||||
return maxImageUnits;
|
||||
}
|
||||
|
||||
public static boolean supportsImageLoadStore() {
|
||||
if (RENDER_BACKEND.isSDLGPU()) return true;
|
||||
return supportsImageLoadStore;
|
||||
}
|
||||
|
||||
public static boolean hasClipCullDistance() {
|
||||
return hasClipCullDistance;
|
||||
}
|
||||
|
||||
private static boolean hasExtension(String name) {
|
||||
final int count = GLStateManager.glGetInteger(GL30.GL_NUM_EXTENSIONS);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (name.equals(GLStateManager.glGetStringi(GL11.GL_EXTENSIONS, i))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isGLES() {
|
||||
if (!glesDetected) detectGLES();
|
||||
return isGLES;
|
||||
}
|
||||
|
||||
private static synchronized void detectGLES() {
|
||||
if (glesDetected) return;
|
||||
try {
|
||||
final String v = RENDER_BACKEND.getString(GL11.GL_VERSION);
|
||||
if (v != null && v.startsWith("OpenGL ES ")) {
|
||||
isGLES = true;
|
||||
glesVersion = Integer.parseInt(parseGlVersionString(v));
|
||||
}
|
||||
glesDetected = true;
|
||||
} catch (Throwable ignored) {
|
||||
// No GL context on this thread yet (splash); retry on next call.
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLTW() {
|
||||
if (!ltwDetected) detectLTW();
|
||||
return isLTW;
|
||||
}
|
||||
|
||||
private static synchronized void detectLTW() {
|
||||
if (ltwDetected) return;
|
||||
if (SystemProperties.DISABLE_LTW_WORKAROUND) {
|
||||
ltwDetected = true;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final String v = RENDER_BACKEND.getString(GL11.GL_VERSION);
|
||||
if (v == null) return;
|
||||
isLTW = LTWWorkaround.isLtwVersionString(v);
|
||||
ltwDetected = true;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean supportsSSBO() {
|
||||
return supportsSSBO;
|
||||
}
|
||||
|
||||
public static boolean supportsMultiDrawIndirect() {
|
||||
return supportsMultiDrawIndirect;
|
||||
}
|
||||
|
||||
public static boolean supportsBufferStorage() {
|
||||
return supportsBufferStorage;
|
||||
}
|
||||
|
||||
public static boolean supportsClearTexture() {
|
||||
return supportsClearTexture;
|
||||
}
|
||||
|
||||
public static int getMaxSSBOBindings() {
|
||||
return maxSSBOBindings;
|
||||
}
|
||||
|
||||
public static void getProgramiv(int program, int value, IntBuffer storage) {
|
||||
RENDER_BACKEND.getProgramiv(program, value, storage);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(int workX, int workY, int workZ) {
|
||||
RENDER_BACKEND.dispatchCompute(workX, workY, workZ);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(Vector3i workGroups) {
|
||||
RENDER_BACKEND.dispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
}
|
||||
|
||||
public static void dispatchComputeIndirect(long offset) {
|
||||
RENDER_BACKEND.dispatchComputeIndirect(offset);
|
||||
}
|
||||
|
||||
public static void bindBuffer(int target, int buffer) {
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
}
|
||||
|
||||
public static void memoryBarrier(int barriers) {
|
||||
if (supportsCompute) {
|
||||
RENDER_BACKEND.memoryBarrier(barriers);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean supportsBufferBlending() {
|
||||
return GLStateManager.capabilities.GL_ARB_draw_buffers_blend || GLStateManager.capabilities.OpenGL40;
|
||||
}
|
||||
|
||||
public static void disableBufferBlend(int buffer) {
|
||||
RENDER_BACKEND.disablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
}
|
||||
|
||||
public static void enableBufferBlend(int buffer) {
|
||||
RENDER_BACKEND.enablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
}
|
||||
|
||||
public static void blendFuncSeparatei(int buffer, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
RENDER_BACKEND.blendFuncSeparatei(buffer, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int texture) {
|
||||
dsaState.bindTextureToUnit(unit, texture);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int target, int unit, int texture) {
|
||||
dsaState.bindTextureToUnit(target, unit, texture);
|
||||
}
|
||||
|
||||
public static final FloatBuffer PROJECTION_MATRIX_BUFFER = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
public static void setupProjectionMatrix(Matrix4f matrix) {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPushMatrix();
|
||||
matrix.get(0, PROJECTION_MATRIX_BUFFER);
|
||||
GLStateManager.glLoadMatrix(PROJECTION_MATRIX_BUFFER);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
public static void restoreProjectionMatrix() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
public static void blitFramebuffer(int source, int dest, int offsetX, int offsetY, int width, int height, int offsetX2, int offsetY2, int width2, int height2, int bufferChoice, int filter) {
|
||||
final boolean scissor = GLStateManager.glIsEnabled(GL11.GL_SCISSOR_TEST);
|
||||
if (scissor) GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
try {
|
||||
dsaState.blitFramebuffer(source, dest, offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
|
||||
} finally {
|
||||
if (scissor) GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
public static int createFramebuffer() {
|
||||
return dsaState.createFramebuffer();
|
||||
}
|
||||
|
||||
public static int createTexture(int target) {
|
||||
return dsaState.createTexture(target);
|
||||
}
|
||||
|
||||
// TODO: Proper notification of compute support
|
||||
public static boolean supportsCompute() {
|
||||
try {
|
||||
return GLStateManager.capabilities.GL_ARB_compute_shader || GLStateManager.capabilities.OpenGL43;
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int createBuffers() {
|
||||
return GLStateManager.glGenBuffers();
|
||||
}
|
||||
|
||||
public static int getIndexedBufferBinding(int target, int index) {
|
||||
return RENDER_BACKEND.getIndexedBufferBinding(target, index);
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
GLStateManager.glBindBufferBase(target, index, buffer);
|
||||
}
|
||||
|
||||
public static void bufferStorage(int target, long size, int flags) {
|
||||
GLStateManager.glBufferStorage(target, size, flags);
|
||||
}
|
||||
|
||||
public static void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, int[] data) {
|
||||
final ByteBuffer buf = BufferUtils.createByteBuffer(data.length * 4);
|
||||
buf.asIntBuffer().put(data);
|
||||
GLStateManager.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
|
||||
}
|
||||
|
||||
public static void deleteBuffers(int buffer) {
|
||||
GLStateManager.glDeleteBuffers(buffer);
|
||||
}
|
||||
|
||||
public static long getVRAM() {
|
||||
if (GLStateManager.capabilities.GL_NVX_gpu_memory_info) {
|
||||
return RENDER_BACKEND.getInteger(NVXGpuMemoryInfo.GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX) * 1024L;
|
||||
} else {
|
||||
// Default to 4GB if we can't query VRAM
|
||||
return 4294967296L;
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearTexImage(int texture, int target, int level, int format, int type) {
|
||||
RENDER_BACKEND.clearTexImage(texture, level, format, type);
|
||||
}
|
||||
|
||||
public static void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
|
||||
dsaState.textureStorage1D(texture, target, levels, internalFormat, width);
|
||||
}
|
||||
|
||||
public static void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
|
||||
dsaState.textureStorage2D(texture, target, levels, internalFormat, width, height);
|
||||
}
|
||||
|
||||
public static void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
|
||||
dsaState.textureStorage3D(texture, target, levels, internalFormat, width, height, depth);
|
||||
}
|
||||
|
||||
public static int getMaxGlslVersion() {
|
||||
return maxGlslVersion;
|
||||
}
|
||||
|
||||
public static boolean supportsGpuShader4() {
|
||||
return supportsGpuShader4;
|
||||
}
|
||||
|
||||
public static boolean supportsSnormFormats() {
|
||||
if (isGLES()) return false;
|
||||
return GLStateManager.capabilities.OpenGL31;
|
||||
}
|
||||
|
||||
public static boolean supportsPackedFloatRenderable() {
|
||||
return GLStateManager.capabilities.OpenGL30 && maxGlslVersion >= 130;
|
||||
}
|
||||
|
||||
public static boolean supportsTesselation() {
|
||||
return supportsTesselation;
|
||||
}
|
||||
|
||||
public static boolean supportsSamplerObjects() {
|
||||
return supportsSamplerObjects;
|
||||
}
|
||||
|
||||
public static int genSampler() {
|
||||
if (!supportsSamplerObjects) return 0;
|
||||
return RENDER_BACKEND.genSamplers();
|
||||
}
|
||||
|
||||
public static void destroySampler(int sampler) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GLStateManager.glDeleteSamplers(sampler);
|
||||
}
|
||||
|
||||
public static void samplerParameteri(int sampler, int pname, int param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
RENDER_BACKEND.samplerParameteri(sampler, pname, param);
|
||||
}
|
||||
|
||||
public static void bindSamplerToUnit(int unit, int sampler) {
|
||||
if (!supportsSamplerObjects) return;
|
||||
if (samplers[unit] == sampler) return;
|
||||
RENDER_BACKEND.bindSampler(unit, sampler);
|
||||
samplers[unit] = sampler;
|
||||
}
|
||||
|
||||
public static void unbindAllSamplers() {
|
||||
if (!supportsSamplerObjects) return;
|
||||
for (int i = 0; i < samplers.length; i++) {
|
||||
if (samplers[i] != 0) {
|
||||
RENDER_BACKEND.bindSampler(i, 0);
|
||||
samplers[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460". Strips
|
||||
* "OpenGL ES " / "OpenGL ES GLSL ES " prefixes so ES contexts parse too. */
|
||||
public static String parseGlVersionString(String info) {
|
||||
Objects.requireNonNull(info);
|
||||
String stripped = info;
|
||||
if (stripped.startsWith("OpenGL ES GLSL ES ")) {
|
||||
stripped = stripped.substring("OpenGL ES GLSL ES ".length());
|
||||
} else if (stripped.startsWith("OpenGL ES ")) {
|
||||
stripped = stripped.substring("OpenGL ES ".length());
|
||||
}
|
||||
final Matcher matcher = SEMVER_PATTERN.matcher(stripped);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalStateException("Could not parse GL version from \"" + info + "\"");
|
||||
}
|
||||
final String major = matcher.group("major");
|
||||
final String minor = matcher.group("minor");
|
||||
String bugfix = matcher.group("bugfix");
|
||||
if (bugfix == null || bugfix.isEmpty()) bugfix = "0";
|
||||
return major + minor + bugfix;
|
||||
}
|
||||
|
||||
/** Parse a GL version string into packed major*10+minor (e.g. "4.3.0 - Build ..." -> 43), or -1 if unparseable. */
|
||||
public static int parseMajorMinor(String glVersion) {
|
||||
if (glVersion == null) return -1;
|
||||
final Matcher matcher = SEMVER_PATTERN.matcher(glVersion.trim());
|
||||
if (!matcher.matches()) return -1;
|
||||
return Integer.parseInt(matcher.group("major")) * 10 + Integer.parseInt(matcher.group("minor"));
|
||||
}
|
||||
|
||||
public static boolean isContextValid(int requestedMajor, int requestedMinor, int actualVersion, boolean coreBitSet) {
|
||||
if (!coreBitSet) return false;
|
||||
if (actualVersion < 0) return true;
|
||||
return actualVersion >= requestedMajor * 10 + requestedMinor;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,53 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
public final class BackendManager {
|
||||
private static final Logger LOGGER = LogManager.getLogger("BackendManager");
|
||||
|
||||
public static final RenderBackend RENDER_BACKEND = loadService();
|
||||
|
||||
private BackendManager() {}
|
||||
|
||||
public static void init() {
|
||||
RENDER_BACKEND.init();
|
||||
LOGGER.info("Initialized backend: {} (priority {})", RENDER_BACKEND.getName(), RENDER_BACKEND.getPriority());
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
RENDER_BACKEND.shutdown();
|
||||
}
|
||||
|
||||
private static RenderBackend loadService() {
|
||||
final ServiceLoader<RenderBackend> loader = ServiceLoader.load(RenderBackend.class, RenderBackend.class.getClassLoader());
|
||||
|
||||
RenderBackend best = null;
|
||||
final Iterator<RenderBackend> iterator = loader.iterator();
|
||||
int attempts = 0;
|
||||
|
||||
while (attempts < 16) {
|
||||
try {
|
||||
attempts++;
|
||||
if (!iterator.hasNext()) break;
|
||||
final RenderBackend backend = iterator.next();
|
||||
LOGGER.info("Found backend: {} (priority {})", backend.getName(), backend.getPriority());
|
||||
if (backend.isAvailable() && (best == null || backend.getPriority() > best.getPriority())) {
|
||||
best = backend;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LOGGER.debug("Skipping unavailable backend: {}", t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (best == null) {
|
||||
throw new IllegalStateException("No available render backend!");
|
||||
}
|
||||
|
||||
LOGGER.info("Selected backend: {} (priority {})", best.getName(), best.getPriority());
|
||||
return best;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DebugMessageHandler {
|
||||
void handleMessage(int source, int type, int id, int severity, String message);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
public interface GLDebugMessageListener {
|
||||
void onMessage(int source, int type, int id, int severity, int length, long message, long userParam);
|
||||
}
|
||||
+1746
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,532 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Abstract rendering backend.
|
||||
*/
|
||||
public abstract class RenderBackend {
|
||||
|
||||
public void onPostWindowCreate(long window) {}
|
||||
|
||||
public abstract void init();
|
||||
public abstract void shutdown();
|
||||
public abstract boolean isAvailable();
|
||||
public abstract String getName();
|
||||
|
||||
/** Returns true if the current thread has a valid render context. */
|
||||
public abstract boolean hasContext();
|
||||
|
||||
/** Returns true if the calling thread owns the "GL Context" */
|
||||
public abstract boolean isCurrent();
|
||||
|
||||
/** Higher priority backends are preferred. */
|
||||
public int getPriority() { return 0; }
|
||||
|
||||
public boolean supportsFileDrop() { return false; }
|
||||
|
||||
public void startFileDrop() {}
|
||||
|
||||
public void stopFileDrop() {}
|
||||
|
||||
public List<String> pollDroppedFiles() { return Collections.emptyList(); }
|
||||
|
||||
public boolean isIndirectRequired() { return false; }
|
||||
public boolean supportsGeometryShaders() { return true; }
|
||||
public boolean isSDLGPU() { return false; }
|
||||
|
||||
public boolean framebufferCompletenessIsMeaningful() { return true; }
|
||||
public void onPersistentBufferWrite(int glId, long offset, long size) {}
|
||||
|
||||
public boolean isAnisotropicSupported() { return true; }
|
||||
|
||||
public boolean supportsGpuDrivenCulling() { return RenderSystem.supportsCompute(); }
|
||||
|
||||
public void beginComputeDispatchBatch() {}
|
||||
|
||||
public void endComputeDispatchBatch() {}
|
||||
|
||||
public abstract int getMinGLSLVersion();
|
||||
|
||||
public abstract void flush();
|
||||
public abstract void finish();
|
||||
|
||||
private VSyncMode effectiveVSyncMode = VSyncMode.ON;
|
||||
private VSyncMode preferredTearFreeMode;
|
||||
|
||||
public final void setVSyncEnabled(boolean enabled) {
|
||||
setVSyncMode(enabled ? preferredTearFreeMode() : VSyncMode.OFF);
|
||||
}
|
||||
|
||||
public final void setVSyncMode(VSyncMode preferred) {
|
||||
setVSyncPreference(preferred);
|
||||
publishVSyncMode(applyVSyncMode(preferred));
|
||||
}
|
||||
|
||||
public final void setVSyncPreference(VSyncMode preferred) {
|
||||
if (preferred.tearFree()) preferredTearFreeMode = preferred;
|
||||
}
|
||||
|
||||
public final VSyncMode preferredTearFreeMode() {
|
||||
return resolveTearFreeMode(preferredTearFreeMode, this::supportsVSyncMode);
|
||||
}
|
||||
|
||||
static VSyncMode resolveTearFreeMode(VSyncMode preferred, Predicate<VSyncMode> supported) {
|
||||
if (preferred != null && preferred.tearFree() && supported.test(preferred)) return preferred;
|
||||
return supported.test(VSyncMode.MAILBOX) ? VSyncMode.MAILBOX : VSyncMode.ON;
|
||||
}
|
||||
|
||||
protected abstract VSyncMode applyVSyncMode(VSyncMode preferred);
|
||||
|
||||
public boolean supportsVSyncMode(VSyncMode mode) { return mode != VSyncMode.MAILBOX; }
|
||||
|
||||
protected final void publishVSyncMode(VSyncMode mode) { effectiveVSyncMode = mode; }
|
||||
|
||||
public final VSyncMode getEffectiveVSyncMode() { return effectiveVSyncMode; }
|
||||
|
||||
private static final long REFRESH_POLL_NANOS = 1_000_000_000L;
|
||||
|
||||
private long refreshPollNanos;
|
||||
private int cachedRefreshHz;
|
||||
|
||||
public final int getDisplayRefreshRateHz() {
|
||||
final long now = System.nanoTime();
|
||||
if (refreshPollNanos == 0L || now - refreshPollNanos >= REFRESH_POLL_NANOS) {
|
||||
refreshPollNanos = now;
|
||||
cachedRefreshHz = queryDisplayRefreshRateHz();
|
||||
}
|
||||
return cachedRefreshHz;
|
||||
}
|
||||
|
||||
protected int queryDisplayRefreshRateHz() { return 0; }
|
||||
|
||||
public static int refreshHzFrom(int numerator, int denominator, float fallbackHz) {
|
||||
if (numerator > 0 && denominator > 0) return (numerator + denominator - 1) / denominator;
|
||||
return fallbackHz > 0.0f ? (int) Math.ceil(fallbackHz) : 0;
|
||||
}
|
||||
|
||||
public boolean hasSwapchainBackpressure() { return false; }
|
||||
|
||||
private long frameGateNanos;
|
||||
private long frameGateEndNanos;
|
||||
|
||||
public boolean gateAnchorsNextFrameStart() { return false; }
|
||||
|
||||
public boolean wantsDisplayUpdateGateTiming() { return true; }
|
||||
|
||||
public void recordFrameGate(long durationNanos, long endNanos) {
|
||||
frameGateNanos = durationNanos;
|
||||
frameGateEndNanos = endNanos;
|
||||
}
|
||||
|
||||
public long lastFrameGateNanos() { return frameGateNanos; }
|
||||
|
||||
public long lastFrameGateEndNanos() { return frameGateEndNanos; }
|
||||
|
||||
public void onFrameBegin() {
|
||||
if (GLStateManager.takeStateSeedPending()) {
|
||||
GLStateManager.replayStateToBackend();
|
||||
}
|
||||
}
|
||||
|
||||
public void onFrameEnd() {}
|
||||
|
||||
/** Fired by lwjgl3ify Display before a mutation that recreates the swapchain. */
|
||||
public void onPreSwapchainInvalidatingChange(Object change) {}
|
||||
|
||||
public boolean handleMakeCurrent(Object drawable) { return false; }
|
||||
public boolean handleReleaseContext(Object drawable) { return false; }
|
||||
public boolean handleSwapBuffers() { return false; }
|
||||
public void onRenderThreadReleased(Thread thread) {}
|
||||
|
||||
public String getTransferDebugInfo() { return null; }
|
||||
|
||||
public boolean bindVoxelizationRegion(int ssboBinding, int vertexBufferGlId, long openPass, float x, float y, float z) { return false; }
|
||||
public long beginVoxelizationBatch(int ssboBinding) { return 0L; }
|
||||
public void voxelizeRange(long pass, int vertexOffset, int vertexCount) {}
|
||||
public void endVoxelizationBatch(long pass) {}
|
||||
|
||||
public abstract void enable(int cap);
|
||||
public abstract void enablei(int cap, int index);
|
||||
public abstract void disable(int cap);
|
||||
public abstract void disablei(int cap, int index);
|
||||
public abstract void blendFuncSeparatei(int buf, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);
|
||||
public abstract void blendFunc(int sfactor, int dfactor);
|
||||
public abstract void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);
|
||||
public abstract void blendEquation(int mode);
|
||||
public abstract void blendEquationSeparate(int modeRGB, int modeAlpha);
|
||||
public abstract void blendColor(float red, float green, float blue, float alpha);
|
||||
public abstract void depthFunc(int func);
|
||||
public abstract void depthMask(boolean flag);
|
||||
public abstract void colorMask(boolean red, boolean green, boolean blue, boolean alpha);
|
||||
public abstract void cullFace(int mode);
|
||||
public abstract void frontFace(int mode);
|
||||
public abstract void polygonMode(int face, int mode);
|
||||
public abstract void polygonOffset(float factor, float units);
|
||||
public void polygonOffsetClamp(float factor, float units, float clamp) { throw new UnsupportedOperationException(getName() + ": polygon offset clamp unsupported"); }
|
||||
public abstract void stencilFunc(int func, int ref, int mask);
|
||||
public abstract void stencilOp(int sfail, int dpfail, int dppass);
|
||||
public abstract void stencilMask(int mask);
|
||||
public abstract void stencilFuncSeparate(int face, int func, int ref, int mask);
|
||||
public abstract void stencilOpSeparate(int face, int sfail, int dpfail, int dppass);
|
||||
public abstract void stencilMaskSeparate(int face, int mask);
|
||||
public abstract void viewport(int x, int y, int width, int height);
|
||||
public abstract void depthRange(double nearVal, double farVal);
|
||||
public abstract void scissor(int x, int y, int width, int height);
|
||||
public abstract void clearColor(float red, float green, float blue, float alpha);
|
||||
public abstract void clearDepth(double depth);
|
||||
public abstract void clearStencil(int s);
|
||||
public abstract void clear(int mask);
|
||||
public abstract void lineWidth(float width);
|
||||
public abstract void pointSize(float size);
|
||||
public abstract void logicOp(int opcode);
|
||||
public abstract void hint(int target, int mode);
|
||||
|
||||
public abstract void drawArrays(int mode, int first, int count);
|
||||
public abstract void drawElements(int mode, int count, int type, long indices);
|
||||
public void drawElements(int mode, ByteBuffer indices) {}
|
||||
public void drawElements(int mode, IntBuffer indices) {}
|
||||
public void drawElements(int mode, ShortBuffer indices) {}
|
||||
public void drawElements(int mode, int count, int type, ByteBuffer indices) {}
|
||||
public abstract void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride);
|
||||
public void multiDrawArraysIndirectCount(int mode, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
|
||||
public void multiDrawElementsIndirectCount(int mode, int type, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
|
||||
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 void drawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
||||
drawElements(mode, count, type, indices);
|
||||
}
|
||||
public void drawRangeElementsBaseVertex(int mode, int start, int end, int count, int type, long indices, int baseVertex) {
|
||||
drawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
public abstract void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts);
|
||||
public abstract void primitiveRestartIndex(int index);
|
||||
public abstract void pointParameterf(int pname, float param);
|
||||
public abstract void pointParameteri(int pname, int param);
|
||||
public abstract void drawBuffer(int mode);
|
||||
public abstract void dispatchCompute(int numGroupsX, int numGroupsY, int numGroupsZ);
|
||||
public abstract void dispatchComputeIndirect(long offset);
|
||||
|
||||
public abstract int genTextures();
|
||||
public abstract void genTextures(IntBuffer textures);
|
||||
public abstract void deleteTextures(int texture);
|
||||
public abstract void bindTexture(int target, int texture);
|
||||
public abstract void activeTexture(int texture);
|
||||
public abstract void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer pixels);
|
||||
public abstract void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, DoubleBuffer pixels);
|
||||
public abstract void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, FloatBuffer pixels);
|
||||
public abstract void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntBuffer pixels);
|
||||
public abstract void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, long pixelBufferOffset);
|
||||
public abstract void texSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels);
|
||||
public abstract void texSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels);
|
||||
public abstract void copyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, int width, int height);
|
||||
public void texImage1D(int target, int level, int internalFormat, int width, int border, int format, int type, ByteBuffer pixels) {}
|
||||
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {}
|
||||
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {}
|
||||
public void texSubImage1D(int target, int level, int xoffset, int width, int format, int type, ByteBuffer pixels) {}
|
||||
public void texSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, long pboOffset) {}
|
||||
public void texSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, int format, int type, ByteBuffer pixels) {}
|
||||
public void copyTexImage1D(int target, int level, int internalFormat, int x, int y, int width, int border) {}
|
||||
public void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) {}
|
||||
public void copyTexSubImage1D(int target, int level, int xoffset, int x, int y, int width) {}
|
||||
public void copyTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height) {}
|
||||
public abstract void texParameteri(int target, int pname, int param);
|
||||
public abstract void texParameterf(int target, int pname, float param);
|
||||
public abstract void texParameteriv(int target, int pname, IntBuffer params);
|
||||
public abstract void texParameterfv(int target, int pname, FloatBuffer params);
|
||||
public abstract int getTexParameteri(int target, int pname);
|
||||
public abstract float getTexParameterf(int target, int pname);
|
||||
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 sampleCoverage(float value, boolean invert) {}
|
||||
|
||||
|
||||
public abstract int genSamplers();
|
||||
public abstract void deleteSamplers(int sampler);
|
||||
public abstract void bindSampler(int unit, int sampler);
|
||||
public abstract void samplerParameteri(int sampler, int pname, int param);
|
||||
public abstract void samplerParameterf(int sampler, int pname, float param);
|
||||
|
||||
public abstract int genFramebuffers();
|
||||
public abstract void deleteFramebuffers(int framebuffer);
|
||||
public abstract void bindFramebuffer(int target, int framebuffer);
|
||||
public abstract void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level);
|
||||
public abstract void framebufferTexture(int target, int attachment, int texture, int level);
|
||||
public abstract int genRenderbuffers();
|
||||
public abstract void deleteRenderbuffers(int renderbuffer);
|
||||
public abstract void bindRenderbuffer(int target, int renderbuffer);
|
||||
public abstract void renderbufferStorage(int target, int internalformat, int width, int height);
|
||||
public abstract void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height);
|
||||
public abstract void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer);
|
||||
public abstract int checkFramebufferStatus(int target);
|
||||
public abstract void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
|
||||
public abstract void drawBuffers(int buffer);
|
||||
public abstract void drawBuffers(IntBuffer bufs);
|
||||
public abstract void readBuffer(int mode);
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels);
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels);
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, ByteBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, IntBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, long pixelBufferOffset);
|
||||
public abstract int getFramebufferAttachmentParameteri(int target, int attachment, int pname);
|
||||
|
||||
public abstract int createShader(int type);
|
||||
public abstract void deleteShader(int shader);
|
||||
public abstract void shaderSource(int shader, CharSequence source);
|
||||
public abstract void compileShader(int shader);
|
||||
public void specializeShader(int shader, CharSequence entryPoint, IntBuffer constantIndex, IntBuffer constantValue) { throw new UnsupportedOperationException(getName() + ": externally supplied SPIR-V binaries unsupported"); }
|
||||
public abstract int createProgram();
|
||||
public abstract void deleteProgram(int program);
|
||||
public abstract void attachShader(int program, int shader);
|
||||
public abstract void detachShader(int program, int shader);
|
||||
public abstract void linkProgram(int program);
|
||||
public abstract void useProgram(int program);
|
||||
public abstract String getShaderInfoLog(int shader, int maxLength);
|
||||
public String getShaderInfoLog(int shader) {
|
||||
return getShaderInfoLog(shader, getShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
public abstract void getShaderInfoLog(int shader, IntBuffer length, ByteBuffer infoLog);
|
||||
public abstract String getProgramInfoLog(int program, int maxLength);
|
||||
public String getProgramInfoLog(int program) {
|
||||
return getProgramInfoLog(program, getProgrami(program, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
public abstract void getProgramInfoLog(int program, IntBuffer length, ByteBuffer infoLog);
|
||||
public abstract int getShaderi(int shader, int pname);
|
||||
public abstract int getProgrami(int program, int pname);
|
||||
public abstract void getProgramiv(int program, int pname, IntBuffer params);
|
||||
public abstract String getActiveUniform(int program, int index, int maxLength, IntBuffer sizeType);
|
||||
public abstract void getActiveUniform(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name);
|
||||
public abstract void getActiveAttrib(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name);
|
||||
public abstract String getActiveAttrib(int program, int index, int maxLength, IntBuffer sizeType);
|
||||
public abstract void bindAttribLocation(int program, int index, CharSequence name);
|
||||
public void bindFragDataLocation(int program, int colorNumber, CharSequence name) {}
|
||||
public abstract int getAttribLocation(int program, CharSequence name);
|
||||
public abstract int getAttribLocation(int program, ByteBuffer name);
|
||||
public abstract int getUniformLocation(int program, CharSequence name);
|
||||
public abstract int getUniformLocation(int program, ByteBuffer name);
|
||||
public boolean isShader(int obj) { return false; }
|
||||
public boolean isProgram(int obj) { return false; }
|
||||
public void validateProgram(int program) {}
|
||||
public void getAttachedShaders(int program, IntBuffer count, IntBuffer shaders) {
|
||||
if (count != null) count.put(0, 0);
|
||||
}
|
||||
public String getShaderSource(int shader, int maxLength) { return ""; }
|
||||
public abstract void getShaderSource(int shader, IntBuffer length, ByteBuffer source);
|
||||
|
||||
public abstract void uniform1i(int location, int v0);
|
||||
public abstract void uniform1f(int location, float v0);
|
||||
public void uniform1fv(int location, FloatBuffer values) {}
|
||||
public void uniform3fv(int location, float[] values) {}
|
||||
public void uniform4fv(int location, float[] values) {}
|
||||
public abstract void uniform2f(int location, float v0, float v1);
|
||||
public abstract void uniform2i(int location, int v0, int v1);
|
||||
public abstract void uniform3f(int location, float v0, float v1, float v2);
|
||||
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) {}
|
||||
public void getUniformfv(int program, int location, FloatBuffer params) {}
|
||||
public void getUniformiv(int program, int location, IntBuffer params) {}
|
||||
|
||||
public abstract void vertexAttrib2f(int index, float v0, float v1);
|
||||
public abstract void vertexAttrib3f(int index, float v0, float v1, float v2);
|
||||
public abstract void vertexAttrib4f(int index, float v0, float v1, float v2, float v3);
|
||||
|
||||
public abstract int genBuffers();
|
||||
public abstract void deleteBuffers(int buffer);
|
||||
public abstract void deleteBuffers(IntBuffer buffers);
|
||||
public abstract void bindBuffer(int target, int buffer);
|
||||
public abstract void bindBufferBase(int target, int index, int buffer);
|
||||
public abstract void bindBufferRange(int target, int index, int buffer, long offset, long size);
|
||||
public abstract int getUniformBlockIndex(int program, CharSequence name);
|
||||
public abstract void uniformBlockBinding(int program, int blockIndex, int binding);
|
||||
public abstract int getIndexedBufferBinding(int target, int index);
|
||||
public abstract int getIntegerIndexed(int pname, int index);
|
||||
public abstract void bufferData(int target, long size, int usage);
|
||||
public abstract void bufferData(int target, ByteBuffer data, int usage);
|
||||
public abstract void bufferData(int target, FloatBuffer data, int usage);
|
||||
public abstract void bufferData(int target, ShortBuffer data, int usage);
|
||||
public abstract void bufferData(int target, IntBuffer data, int usage);
|
||||
public abstract void bufferData(int target, DoubleBuffer data, int usage);
|
||||
public abstract void bufferData(int target, int[] data, int usage);
|
||||
public abstract void bufferData(int target, float[] data, int usage);
|
||||
public abstract void bufferSubData(int target, long offset, ByteBuffer data);
|
||||
public abstract void bufferSubData(int target, long offset, ShortBuffer data);
|
||||
public abstract void bufferSubData(int target, long offset, IntBuffer data);
|
||||
public abstract void bufferSubData(int target, long offset, FloatBuffer data);
|
||||
public abstract void bufferSubData(int target, long offset, DoubleBuffer data);
|
||||
public abstract ByteBuffer mapBuffer(int target, int access);
|
||||
public abstract ByteBuffer mapBuffer(int target, int access, long length, ByteBuffer old_buffer);
|
||||
public abstract boolean unmapBuffer(int target);
|
||||
public abstract void bufferStorage(int target, ByteBuffer data, int flags);
|
||||
public abstract void bufferStorage(int target, long size, int flags);
|
||||
public abstract void getBufferSubData(int target, long offset, ByteBuffer data);
|
||||
public abstract void getBufferSubData(int target, long offset, ShortBuffer data);
|
||||
public abstract void getBufferSubData(int target, long offset, IntBuffer data);
|
||||
public abstract void getBufferSubData(int target, long offset, FloatBuffer data);
|
||||
public abstract void getBufferSubData(int target, long offset, DoubleBuffer data);
|
||||
public abstract int getBufferParameteri(int target, int pname);
|
||||
public abstract boolean isBuffer(int buffer);
|
||||
public abstract ByteBuffer mapBufferRange(int target, long offset, long length, int access);
|
||||
public abstract void flushMappedBufferRange(int target, long offset, long length);
|
||||
public long mapBufferRangeAddress(int target, long offset, long length, int access) {
|
||||
final ByteBuffer buf = mapBufferRange(target, offset, length, access);
|
||||
return buf == null ? 0L : MemoryUtilities.memAddress0(buf);
|
||||
}
|
||||
|
||||
public abstract boolean isTexture(int texture);
|
||||
public abstract boolean isFramebuffer(int framebuffer);
|
||||
public abstract boolean isRenderbuffer(int renderbuffer);
|
||||
public abstract boolean isSampler(int sampler);
|
||||
public abstract boolean isQuery(int query);
|
||||
|
||||
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));
|
||||
}
|
||||
public void vertexAttribIPointer(int index, int size, int type, int stride, ByteBuffer pointer) {
|
||||
vertexAttribIPointer(index, size, type, stride, MemoryUtilities.memAddress0(pointer));
|
||||
}
|
||||
public abstract void vertexAttribIPointer(int index, int size, int type, int stride, long pointer);
|
||||
public abstract void enableVertexAttribArray(int index);
|
||||
public abstract void disableVertexAttribArray(int index);
|
||||
public abstract void vertexAttribDivisor(int index, int divisor);
|
||||
public abstract void bindVertexBuffer(int bindingindex, int buffer, long offset, int stride);
|
||||
public abstract void vertexAttribFormat(int attribindex, int size, int type, boolean normalized, int relativeoffset);
|
||||
public abstract void vertexAttribIFormat(int attribindex, int size, int type, int relativeoffset);
|
||||
public abstract void vertexAttribBinding(int attribindex, int bindingindex);
|
||||
|
||||
public abstract int createTextures(int target);
|
||||
public abstract void bindTextureUnit(int unit, int texture);
|
||||
public abstract void textureParameteri(int texture, int target, int pname, int param);
|
||||
public abstract void textureParameterf(int texture, int target, int pname, float param);
|
||||
public abstract void textureParameteriv(int texture, int target, int pname, IntBuffer params);
|
||||
public abstract void texStorage1D(int target, int levels, int internalFormat, int width);
|
||||
public abstract void texStorage2D(int target, int levels, int internalFormat, int width, int height);
|
||||
public abstract void texStorage3D(int target, int levels, int internalFormat, int width, int height, int depth);
|
||||
public abstract void textureStorage1D(int texture, int levels, int internalFormat, int width);
|
||||
public abstract void textureStorage2D(int texture, int levels, int internalFormat, int width, int height);
|
||||
public abstract void textureStorage3D(int texture, int levels, int internalFormat, int width, int height, int depth);
|
||||
public abstract void generateTextureMipmap(int texture);
|
||||
public abstract void textureImage2DEXT(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels);
|
||||
public abstract void textureImage2DEXT(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels);
|
||||
public abstract void textureSubImage2D(int texture, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels);
|
||||
public abstract void textureSubImage2D(int texture, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels);
|
||||
public abstract int createFramebuffers();
|
||||
public abstract void namedFramebufferTexture(int framebuffer, int attachment, int texture, int level);
|
||||
public abstract void namedFramebufferReadBuffer(int framebuffer, int mode);
|
||||
public abstract void namedFramebufferDrawBuffers(int framebuffer, IntBuffer bufs);
|
||||
public abstract void blitNamedFramebuffer(int readFramebuffer, int drawFramebuffer, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
|
||||
public abstract int createBuffers();
|
||||
public abstract void namedBufferData(int buffer, long size, int usage);
|
||||
public abstract void namedBufferData(int buffer, ByteBuffer data, int usage);
|
||||
public abstract void namedBufferData(int buffer, FloatBuffer data, int usage);
|
||||
public abstract void namedBufferSubData(int buffer, long offset, ByteBuffer data);
|
||||
public abstract void copyTextureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int x, int y, int width, int height);
|
||||
public abstract int getTextureParameteri(int texture, int target, int pname);
|
||||
public abstract float getTextureParameterf(int texture, int target, int pname);
|
||||
public abstract int getTextureLevelParameteri(int texture, int level, int pname);
|
||||
|
||||
public abstract int getInteger(int pname);
|
||||
public abstract void getInteger(int pname, IntBuffer params);
|
||||
public void getInteger(int pname, int[] params) { if (params.length > 0) params[0] = getInteger(pname); }
|
||||
public abstract float getFloat(int pname);
|
||||
public abstract void getFloat(int pname, FloatBuffer params);
|
||||
public double getDouble(int pname) { return getFloat(pname); }
|
||||
public void getDouble(int pname, DoubleBuffer params) { if (params.remaining() > 0) params.put(params.position(), getDouble(pname)); }
|
||||
public abstract boolean getBoolean(int pname);
|
||||
public abstract void getBoolean(int pname, ByteBuffer params);
|
||||
public abstract String getString(int pname);
|
||||
public abstract String getStringi(int name, int index);
|
||||
public abstract int getError();
|
||||
|
||||
public abstract long fenceSync(int condition, int flags);
|
||||
public abstract int clientWaitSync(long sync, int flags, long timeout);
|
||||
public abstract void deleteSync(long sync);
|
||||
public void waitSync(long sync, int flags, long timeout) { throw new UnsupportedOperationException("sync objects unsupported"); }
|
||||
public int getSynci(long sync, int pname, IntBuffer length) { throw new UnsupportedOperationException("sync objects unsupported"); }
|
||||
|
||||
public void genQueries(IntBuffer ids) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
|
||||
public int genQueries() { throw new UnsupportedOperationException("occlusion queries unsupported"); }
|
||||
public void deleteQueries(int id) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
|
||||
public void beginQuery(int target, int id) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
|
||||
public void endQuery(int target) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
|
||||
public void getQueryObjectui(int id, int pname, IntBuffer params) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
|
||||
public int getQueryObjecti(int id, int pname) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
|
||||
|
||||
public void queryCounter(int id, int target) { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
public long getQueryObjectui64(int id, int pname) { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
|
||||
public boolean supportsGpuProfiling() { return false; }
|
||||
public int genQuery() { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
public void deleteQuery(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
public void queryCounter(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
public boolean isQueryResultAvailable(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
public long getQueryResult64(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
public long getGpuTimestamp() { throw new UnsupportedOperationException("timer queries unsupported"); }
|
||||
|
||||
public abstract void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data);
|
||||
public abstract void clearBufferData(int target, int internalformat, int format, int type, ByteBuffer data);
|
||||
public abstract void clearTexImage(int texture, int level, int format, int type);
|
||||
|
||||
public abstract void bindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format);
|
||||
public abstract void memoryBarrier(int barriers);
|
||||
|
||||
public abstract void copyImageSubData(int srcName, int srcTarget, int srcLevel, int srcX, int srcY, int srcZ,
|
||||
int dstName, int dstTarget, int dstLevel, int dstX, int dstY, int dstZ,
|
||||
int srcWidth, int srcHeight, int srcDepth);
|
||||
|
||||
// Debug output
|
||||
public boolean supportsDebugOutput() { return false; }
|
||||
public int setupDebugOutput(DebugMessageHandler handler) { return 0; }
|
||||
public int disableDebugOutput() { return 0; }
|
||||
|
||||
// Object labeling
|
||||
public void objectLabel(int identifier, int name, CharSequence label) {}
|
||||
public String getObjectLabel(int identifier, int name) { return ""; }
|
||||
|
||||
// Debug groups
|
||||
public void pushDebugGroup(int source, int id, CharSequence message) {}
|
||||
public void popDebugGroup() {}
|
||||
|
||||
// Debug message insertion
|
||||
public void debugMessageInsert(int source, int type, int id, int severity, CharSequence message) {}
|
||||
|
||||
public void debugMessageControl(int source, int type, int severity, IntBuffer ids, boolean enabled) {}
|
||||
|
||||
public void debugMessageCallback(GLDebugMessageListener listener, long userParam) {}
|
||||
|
||||
public int getDebugMessageLog(int count, IntBuffer sources, IntBuffer types, IntBuffer ids, IntBuffer severities, IntBuffer lengths, ByteBuffer messageLog) { return 0; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
public enum VSyncMode {
|
||||
OFF,
|
||||
ON,
|
||||
MAILBOX;
|
||||
|
||||
public boolean tearFree() {
|
||||
return this != OFF;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-2
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.util.glu.PartialDisk;
|
||||
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
import static org.lwjgl.opengl.GL11.GL_LINE_STRIP;
|
||||
import static org.lwjgl.opengl.GL11.GL_POINTS;
|
||||
import static org.lwjgl.opengl.GL11.GL_TRIANGLE_FAN;
|
||||
@@ -35,7 +34,7 @@ public class AngelicaPartialDisk extends PartialDisk {
|
||||
|
||||
if (slices >= CACHE_SIZE) slices = CACHE_SIZE - 1;
|
||||
if (slices < 2 || loops < 1 || outerRadius <= 0.0f || innerRadius < 0.0f || innerRadius > outerRadius) {
|
||||
LOGGER.warn("PartialDisk: GLU_INVALID_VALUE (slices={}, loops={}, innerRadius={}, outerRadius={})", slices, loops, innerRadius, outerRadius);
|
||||
GLStateManager.LOGGER.warn("PartialDisk: GLU_INVALID_VALUE (slices={}, loops={}, innerRadius={}, outerRadius={})", slices, loops, innerRadius, outerRadius);
|
||||
return;
|
||||
}
|
||||
|
||||
File renamed without changes.
+38
-31
@@ -1,18 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.glsm.dsa;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.ARBDirectStateAccess;
|
||||
import org.lwjgl.opengl.GL45;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public class DSAARB extends DSAUnsupported {
|
||||
|
||||
@Override
|
||||
public void generateMipmaps(int texture, int target) {
|
||||
ARBDirectStateAccess.glGenerateTextureMipmap(texture);
|
||||
RENDER_BACKEND.generateTextureMipmap(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,12 +31,12 @@ public class DSAARB extends DSAUnsupported {
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
ARBDirectStateAccess.glTextureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
RENDER_BACKEND.textureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
ARBDirectStateAccess.glTextureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
RENDER_BACKEND.textureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,7 +44,7 @@ public class DSAARB extends DSAUnsupported {
|
||||
param = GLStateManager.remapTexClamp(pname, param);
|
||||
if(!GLStateManager.updateTexParameteriCache(target, texture, pname, param)) return;
|
||||
|
||||
ARBDirectStateAccess.glTextureParameteri(texture, pname, param);
|
||||
RENDER_BACKEND.textureParameteri(texture, target, pname, param);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,97 +52,103 @@ public class DSAARB extends DSAUnsupported {
|
||||
param = GLStateManager.remapTexClamp(pname, param);
|
||||
if(!GLStateManager.updateTexParameterfCache(target, texture, pname, param)) return;
|
||||
|
||||
ARBDirectStateAccess.glTextureParameterf(texture, pname, param);
|
||||
RENDER_BACKEND.textureParameterf(texture, target, pname, param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texParameteriv(int texture, int target, int pname, IntBuffer params) {
|
||||
GLStateManager.remapTexClampBuffer(pname, params);
|
||||
ARBDirectStateAccess.glTextureParameter(texture, pname, params);
|
||||
RENDER_BACKEND.textureParameteriv(texture, target, pname, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readBuffer(int framebuffer, int buffer) {
|
||||
ARBDirectStateAccess.glNamedFramebufferReadBuffer(framebuffer, buffer);
|
||||
RENDER_BACKEND.namedFramebufferReadBuffer(framebuffer, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBuffers(int framebuffer, IntBuffer buffers) {
|
||||
ARBDirectStateAccess.glNamedFramebufferDrawBuffers(framebuffer, buffers);
|
||||
RENDER_BACKEND.namedFramebufferDrawBuffers(framebuffer, buffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTexParameteri(int texture, int target, int pname) {
|
||||
return GLStateManager.getTexParameterOrDefault(texture, pname, () -> ARBDirectStateAccess.glGetTextureParameteri(texture, pname));
|
||||
return GLStateManager.getTexParameterOrDefault(texture, pname, () -> RENDER_BACKEND.getTextureParameteri(texture, target, pname));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTexParameterf(int texture, int target, int pname) {
|
||||
return RENDER_BACKEND.getTextureParameterf(texture, target, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
return ARBDirectStateAccess.glGetTextureLevelParameteri(texture, level, pname);
|
||||
return RENDER_BACKEND.getTextureLevelParameteri(texture, level, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
ARBDirectStateAccess.glCopyTextureSubImage2D(destTexture, i, i1, i2, i3, i4, width, height);
|
||||
RENDER_BACKEND.copyTextureSubImage2D(destTexture, target, i, i1, i2, i3, i4, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTextureToUnit(int unit, int texture) {
|
||||
if(GLStateManager.getBoundTextureForServerState(unit) == texture) return;
|
||||
|
||||
if (texture == 0) {
|
||||
super.bindTextureToUnit(unit, texture);
|
||||
} else {
|
||||
ARBDirectStateAccess.glBindTextureUnit(unit, texture);
|
||||
GLStateManager.getTextures().getTextureUnitBindings(unit).setBinding(texture);
|
||||
}
|
||||
bindTextureToUnit(0, unit, texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTextureToUnit(int target, int unit, int texture) {
|
||||
bindTextureToUnit(unit, texture);
|
||||
if(GLStateManager.getBoundTextureForServerState(unit) == texture) return;
|
||||
|
||||
if (texture == 0) {
|
||||
super.bindTextureToUnit(target != 0 ? target : GL11.GL_TEXTURE_2D, unit, texture);
|
||||
} else {
|
||||
RENDER_BACKEND.bindTextureUnit(unit, texture);
|
||||
GLStateManager.getTextures().getTextureUnitBindings(unit).setBinding(texture, target);
|
||||
GLStateManager.trackMaxBoundTextureUnit(unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
final int buffer = GL45.glCreateBuffers();
|
||||
GL45.glNamedBufferData(buffer, data, usage);
|
||||
final int buffer = GLStateManager.glCreateBuffers();
|
||||
GLStateManager.glNamedBufferData(buffer, data, usage);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blitFramebuffer(int source, int dest, int offsetX, int offsetY, int width, int height, int offsetX2, int offsetY2, int width2, int height2,
|
||||
int bufferChoice, int filter) {
|
||||
ARBDirectStateAccess.glBlitNamedFramebuffer(source, dest, offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
|
||||
RENDER_BACKEND.blitNamedFramebuffer(source, dest, offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
ARBDirectStateAccess.glNamedFramebufferTexture(fb, attachment, texture, levels);
|
||||
RENDER_BACKEND.namedFramebufferTexture(fb, attachment, texture, levels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int createFramebuffer() {
|
||||
return ARBDirectStateAccess.glCreateFramebuffers();
|
||||
return RENDER_BACKEND.createFramebuffers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int createTexture(int target) {
|
||||
return ARBDirectStateAccess.glCreateTextures(target);
|
||||
return RENDER_BACKEND.createTextures(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
|
||||
ARBDirectStateAccess.glTextureStorage1D(texture, levels, internalFormat, width);
|
||||
RENDER_BACKEND.textureStorage1D(texture, levels, internalFormat, width);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
|
||||
ARBDirectStateAccess.glTextureStorage2D(texture, levels, internalFormat, width, height);
|
||||
RENDER_BACKEND.textureStorage2D(texture, levels, internalFormat, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
|
||||
ARBDirectStateAccess.glTextureStorage3D(texture, levels, internalFormat, width, height, depth);
|
||||
RENDER_BACKEND.textureStorage3D(texture, levels, internalFormat, width, height, depth);
|
||||
}
|
||||
}
|
||||
+2
@@ -23,6 +23,8 @@ public interface DSAAccess {
|
||||
|
||||
int getTexParameteri(int texture, int target, int pname);
|
||||
|
||||
float getTexParameterf(int texture, int target, int pname);
|
||||
|
||||
int getTexLevelParameteri(int texture, int level, int pname);
|
||||
|
||||
void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height);
|
||||
File renamed without changes.
+4
-4
@@ -1,18 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.glsm.dsa;
|
||||
|
||||
import org.lwjgl.opengl.EXTDirectStateAccess;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public class DSAEXT extends DSACore {
|
||||
@Override
|
||||
public void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
EXTDirectStateAccess.glTextureImage2DEXT(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
RENDER_BACKEND.textureImage2DEXT(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
EXTDirectStateAccess.glTextureImage2DEXT(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
RENDER_BACKEND.textureImage2DEXT(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
}
|
||||
+44
-40
@@ -1,15 +1,13 @@
|
||||
package com.gtnewhorizons.angelica.glsm.dsa;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -20,40 +18,40 @@ public class DSAUnsupported implements DSAAccess {
|
||||
@Override
|
||||
public void generateMipmaps(int texture, int target) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GL30.glGenerateMipmap(target);
|
||||
RENDER_BACKEND.generateMipmap(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
// Get what cache thinks is bound - we'll restore to this to keep cache/GL in sync
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding); // Restore to cache value
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
RENDER_BACKEND.bindTexture(target, cachedBinding); // Restore to cache value
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
RENDER_BACKEND.bindTexture(target, cachedBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
RENDER_BACKEND.bindTexture(target, cachedBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
RENDER_BACKEND.bindTexture(target, cachedBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,13 +75,13 @@ public class DSAUnsupported implements DSAAccess {
|
||||
@Override
|
||||
public void readBuffer(int framebuffer, int buffer) {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
GL11.glReadBuffer(buffer);
|
||||
RENDER_BACKEND.readBuffer(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBuffers(int framebuffer, IntBuffer buffers) {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
GL20.glDrawBuffers(buffers);
|
||||
RENDER_BACKEND.drawBuffers(buffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,12 +90,18 @@ public class DSAUnsupported implements DSAAccess {
|
||||
return GLStateManager.glGetTexParameteri(target, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTexParameterf(int texture, int target, int pname) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
return GLStateManager.glGetTexParameterf(target, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
final int previous = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
final int result = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, pname);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
final int result = RENDER_BACKEND.getTexLevelParameteri(GL11.GL_TEXTURE_2D, level, pname);
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -105,7 +109,7 @@ public class DSAUnsupported implements DSAAccess {
|
||||
public void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
final int previous = GLStateManager.getBoundTextureForServerState();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, destTexture);
|
||||
GL11.glCopyTexSubImage2D(target, i, i1, i2, i3, i4, width, height);
|
||||
RENDER_BACKEND.copyTexSubImage2D(target, i, i1, i2, i3, i4, width, height);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
}
|
||||
|
||||
@@ -123,9 +127,9 @@ public class DSAUnsupported implements DSAAccess {
|
||||
|
||||
@Override
|
||||
public int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
final int buffer = GL15.glGenBuffers();
|
||||
final int buffer = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
RenderSystem.bufferData(target, data, usage);
|
||||
GLStateManager.glBufferData(target, data, usage);
|
||||
GLStateManager.glBindBuffer(target, 0);
|
||||
|
||||
return buffer;
|
||||
@@ -136,13 +140,13 @@ public class DSAUnsupported implements DSAAccess {
|
||||
int bufferChoice, int filter) {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, source);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, dest);
|
||||
GL30.glBlitFramebuffer(offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
|
||||
RENDER_BACKEND.blitFramebuffer(offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
GLStateManager.glBindFramebuffer(fbtarget, fb);
|
||||
GL30.glFramebufferTexture2D(fbtarget, attachment, target, texture, levels);
|
||||
RENDER_BACKEND.framebufferTexture2D(fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,32 +158,32 @@ public class DSAUnsupported implements DSAAccess {
|
||||
|
||||
@Override
|
||||
public int createTexture(int target) {
|
||||
final int texture = GL11.glGenTextures();
|
||||
final int texture = RENDER_BACKEND.genTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
|
||||
final int previous = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_1D);
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL42.glTexStorage1D(target, levels, internalFormat, width);
|
||||
GL11.glBindTexture(target, previous);
|
||||
final int previous = RENDER_BACKEND.getInteger(GL11.GL_TEXTURE_BINDING_1D);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
RENDER_BACKEND.texStorage1D(target, levels, internalFormat, width);
|
||||
RENDER_BACKEND.bindTexture(target, previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
|
||||
final int previous = (target == GL11.GL_TEXTURE_2D) ? GLStateManager.getBoundTextureForServerState() : GL11.glGetInteger(GL31.GL_TEXTURE_BINDING_RECTANGLE);
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL42.glTexStorage2D(target, levels, internalFormat, width, height);
|
||||
GL11.glBindTexture(target, previous);
|
||||
final int previous = (target == GL11.GL_TEXTURE_2D) ? GLStateManager.getBoundTextureForServerState() : RENDER_BACKEND.getInteger(GL31.GL_TEXTURE_BINDING_RECTANGLE);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
RENDER_BACKEND.texStorage2D(target, levels, internalFormat, width, height);
|
||||
RENDER_BACKEND.bindTexture(target, previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
|
||||
final int previous = GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D);
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL42.glTexStorage3D(target, levels, internalFormat, width, height, depth);
|
||||
GL11.glBindTexture(target, previous);
|
||||
final int previous = RENDER_BACKEND.getInteger(GL12.GL_TEXTURE_BINDING_3D);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
RENDER_BACKEND.texStorage3D(target, levels, internalFormat, width, height, depth);
|
||||
RENDER_BACKEND.bindTexture(target, previous);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
|
||||
public final class FFPUniformBlock {
|
||||
|
||||
public static final String BLOCK_NAME = "FFPUniformBlock";
|
||||
public static final int BINDING_POINT = 0;
|
||||
|
||||
private static final StringBuilder DECL = new StringBuilder(2048);
|
||||
private static int cursor = 0;
|
||||
static final Object2IntMap<String> MEMBER_OFFSETS = new Object2IntLinkedOpenHashMap<>();
|
||||
|
||||
private static int align(int alignment) {
|
||||
cursor = (cursor + alignment - 1) & -alignment;
|
||||
return cursor;
|
||||
}
|
||||
|
||||
private static int member(String type, String name, int alignment, int size) {
|
||||
DECL.append(" ").append(type).append(' ').append(name).append(";\n");
|
||||
final int offset = align(alignment);
|
||||
cursor += size;
|
||||
MEMBER_OFFSETS.put(name, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int mat4(String name) { return member("mat4", name, 16, 64); }
|
||||
private static int mat3(String name) { return member("mat3", name, 16, 48); }
|
||||
private static int vec4(String name) { return member("vec4", name, 16, 16); }
|
||||
private static int vec3(String name) { return member("vec3", name, 16, 12); }
|
||||
private static int vec2(String name) { return member("vec2", name, 8, 8); }
|
||||
private static int flt(String name) { return member("float", name, 4, 4); }
|
||||
private static int integer(String name) { return member("int", name, 4, 4); }
|
||||
private static int vec4Array(String name, int count) {
|
||||
DECL.append(" vec4 ").append(name).append('[').append(count).append("];\n");
|
||||
final int offset = align(16);
|
||||
cursor += 16 * count;
|
||||
MEMBER_OFFSETS.put(name + "[0]", offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static { DECL.append("layout(std140) uniform ").append(BLOCK_NAME).append(" {\n"); }
|
||||
|
||||
public static final int MODEL_VIEW_MATRIX = mat4("u_ModelViewMatrix");
|
||||
public static final int PROJECTION_MATRIX = mat4("u_ProjectionMatrix");
|
||||
public static final int MVP_MATRIX = mat4("u_MVPMatrix");
|
||||
public static final int NORMAL_MATRIX = mat3("u_NormalMatrix");
|
||||
public static final int TEXTURE_MATRIX_0 = mat4("u_TextureMatrix0");
|
||||
public static final int TEXTURE_MATRIX_2 = mat4("u_TextureMatrix2");
|
||||
public static final int TEXTURE_MATRIX_3 = mat4("u_TextureMatrix3");
|
||||
public static final int LIGHTMAP_TEXTURE_MATRIX = mat4("u_LightmapTextureMatrix");
|
||||
|
||||
public static final int CURRENT_COLOR = vec4("u_CurrentColor");
|
||||
public static final int CURRENT_TEX_COORD_0 = vec4("u_CurrentTexCoord0");
|
||||
public static final int CURRENT_TEX_COORD_2 = vec4("u_CurrentTexCoord2");
|
||||
public static final int CURRENT_TEX_COORD_3 = vec4("u_CurrentTexCoord3");
|
||||
public static final int VIEWPORT = vec4("u_Viewport");
|
||||
public static final int CURRENT_NORMAL = vec3("u_CurrentNormal");
|
||||
public static final int CURRENT_LIGHTMAP_COORD = vec2("u_CurrentLightmapCoord");
|
||||
public static final int VIEWPORT_SIZE = vec2("u_ViewportSize");
|
||||
public static final int NORMAL_SCALE = flt("u_NormalScale");
|
||||
public static final int MATERIAL_SHININESS = flt("u_MaterialShininess");
|
||||
public static final int ALPHA_REF = flt("u_AlphaRef");
|
||||
public static final int LINE_WIDTH = flt("u_LineWidth");
|
||||
public static final int LINE_STIPPLE = integer("u_LineStipple");
|
||||
|
||||
public static final int TEX_GEN_OBJ_PLANE_S = vec4("u_TexGenObjPlaneS");
|
||||
public static final int TEX_GEN_OBJ_PLANE_T = vec4("u_TexGenObjPlaneT");
|
||||
public static final int TEX_GEN_OBJ_PLANE_R = vec4("u_TexGenObjPlaneR");
|
||||
public static final int TEX_GEN_OBJ_PLANE_Q = vec4("u_TexGenObjPlaneQ");
|
||||
public static final int TEX_GEN_EYE_PLANE_S = vec4("u_TexGenEyePlaneS");
|
||||
public static final int TEX_GEN_EYE_PLANE_T = vec4("u_TexGenEyePlaneT");
|
||||
public static final int TEX_GEN_EYE_PLANE_R = vec4("u_TexGenEyePlaneR");
|
||||
public static final int TEX_GEN_EYE_PLANE_Q = vec4("u_TexGenEyePlaneQ");
|
||||
|
||||
public static final int CLIP_PLANES = vec4Array("u_ClipPlane", 8);
|
||||
|
||||
public static final int LIGHT_MODEL_AMBIENT = vec4("u_LightModelAmbient");
|
||||
public static final int MATERIAL_EMISSION = vec4("u_MaterialEmission");
|
||||
public static final int MATERIAL_AMBIENT = vec4("u_MaterialAmbient");
|
||||
public static final int MATERIAL_DIFFUSE = vec4("u_MaterialDiffuse");
|
||||
public static final int MATERIAL_SPECULAR = vec4("u_MaterialSpecular");
|
||||
public static final int LIGHT0_AMBIENT = vec4("u_Light0Ambient");
|
||||
public static final int LIGHT0_DIFFUSE = vec4("u_Light0Diffuse");
|
||||
public static final int LIGHT0_SPECULAR = vec4("u_Light0Specular");
|
||||
public static final int LIGHT0_POSITION = vec4("u_Light0Position");
|
||||
public static final int LIGHT1_AMBIENT = vec4("u_Light1Ambient");
|
||||
public static final int LIGHT1_DIFFUSE = vec4("u_Light1Diffuse");
|
||||
public static final int LIGHT1_SPECULAR = vec4("u_Light1Specular");
|
||||
public static final int LIGHT1_POSITION = vec4("u_Light1Position");
|
||||
|
||||
public static final int SCENE_COLOR = vec4("u_SceneColor");
|
||||
public static final int LIGHT_PROD0_AMBIENT = vec3("u_LightProd0Ambient");
|
||||
public static final int LIGHT_PROD0_DIFFUSE = vec3("u_LightProd0Diffuse");
|
||||
public static final int LIGHT_PROD0_SPECULAR = vec3("u_LightProd0Specular");
|
||||
public static final int LIGHT_PROD1_AMBIENT = vec3("u_LightProd1Ambient");
|
||||
public static final int LIGHT_PROD1_DIFFUSE = vec3("u_LightProd1Diffuse");
|
||||
public static final int LIGHT_PROD1_SPECULAR = vec3("u_LightProd1Specular");
|
||||
|
||||
public static final int TEX_ENV_COLOR_0 = vec4("u_TexEnvColor0");
|
||||
public static final int TEX_ENV_COLOR_1 = vec4("u_TexEnvColor1");
|
||||
public static final int TEX_ENV_COLOR_2 = vec4("u_TexEnvColor2");
|
||||
public static final int TEX_ENV_COLOR_3 = vec4("u_TexEnvColor3");
|
||||
public static final int OVERLAY_COLOR = vec4("u_OverlayColor");
|
||||
public static final int SECONDARY_COLOR = vec3("u_SecondaryColor");
|
||||
public static final int FOG_PARAMS = vec4("u_FogParams");
|
||||
public static final int FOG_COLOR = vec4("u_FogColor");
|
||||
|
||||
public static final int SIZE;
|
||||
public static final String GLSL_DECL;
|
||||
|
||||
static {
|
||||
SIZE = (cursor + 15) & -16;
|
||||
DECL.append("};\n");
|
||||
GLSL_DECL = DECL.toString();
|
||||
}
|
||||
|
||||
private FFPUniformBlock() {}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
public final class FFPVertexLighting {
|
||||
|
||||
private static final Vector3f scratchNormal = new Vector3f();
|
||||
private static final Vector3f scratchLightVec = new Vector3f();
|
||||
private static final Vector4f scratchEyePos = new Vector4f();
|
||||
private static final Matrix3f scratchNormalMat = new Matrix3f();
|
||||
private static final Vector3f scratchVec = new Vector3f();
|
||||
|
||||
private FFPVertexLighting() {}
|
||||
|
||||
public static boolean modulatesVertexColor(Vector3f destFactor) {
|
||||
destFactor.set(1.0f);
|
||||
|
||||
if (!GLStateManager.getLightingState().isEnabled()) return false;
|
||||
if (!GLStateManager.getColorMaterial().isEnabled()) return false;
|
||||
if (VertexKey.encodeColorMaterialMode(GLStateManager.getColorMaterialParameter().getValue()) != VertexKey.CM_AMBIENT_AND_DIFFUSE) return false;
|
||||
|
||||
final MaterialState material = GLStateManager.getFrontMaterial();
|
||||
if (material.emission.x != 0.0f || material.emission.y != 0.0f || material.emission.z != 0.0f)
|
||||
return false;
|
||||
|
||||
final boolean materialHasSpecular = material.shininess > 0.0f && (material.specular.x != 0.0f || material.specular.y != 0.0f || material.specular.z != 0.0f);
|
||||
|
||||
final Vector4f lightModelAmbient = GLStateManager.getLightModel().ambient;
|
||||
float r = lightModelAmbient.x;
|
||||
float g = lightModelAmbient.y;
|
||||
float b = lightModelAmbient.z;
|
||||
|
||||
final var lightEnables = GLStateManager.getLightStates();
|
||||
final var lightData = GLStateManager.getLightDataStates();
|
||||
boolean normalReady = false;
|
||||
|
||||
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
|
||||
if (!lightEnables[i].isEnabled()) continue;
|
||||
final LightState light = lightData[i];
|
||||
if (materialHasSpecular && (light.specular.x != 0.0f || light.specular.y != 0.0f || light.specular.z != 0.0f))
|
||||
return false;
|
||||
if (!normalReady) {
|
||||
transformCurrentNormal();
|
||||
normalReady = true;
|
||||
}
|
||||
final float nDotVP = Math.max(scratchNormal.dot(lightDirection(light)), 0.0f);
|
||||
r += light.ambient.x + nDotVP * light.diffuse.x;
|
||||
g += light.ambient.y + nDotVP * light.diffuse.y;
|
||||
b += light.ambient.z + nDotVP * light.diffuse.z;
|
||||
}
|
||||
|
||||
if (r == 1.0f && g == 1.0f && b == 1.0f) return false;
|
||||
if (!Float.isFinite(r + g + b)) return false;
|
||||
destFactor.set(r, g, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void transformCurrentNormal() {
|
||||
GLStateManager.getModelViewMatrix().normal(scratchNormalMat);
|
||||
scratchNormal.set(ShaderManager.getCurrentNormal());
|
||||
scratchNormalMat.transform(scratchNormal);
|
||||
if (GLStateManager.getNormalizeState().isEnabled()) {
|
||||
scratchNormal.normalize();
|
||||
} else if (GLStateManager.getRescaleNormalState().isEnabled()) {
|
||||
scratchNormal.mul(Uniforms.rescaleFactor(scratchNormalMat, scratchVec));
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector3f lightDirection(LightState light) {
|
||||
if (light.position.w == 0.0f) {
|
||||
scratchLightVec.set(light.position.x, light.position.y, light.position.z);
|
||||
} else {
|
||||
scratchEyePos.set(0.0f, 0.0f, 0.0f, 1.0f).mul(GLStateManager.getModelViewMatrix());
|
||||
scratchLightVec.set(light.position.x - scratchEyePos.x, light.position.y - scratchEyePos.y, light.position.z - scratchEyePos.z);
|
||||
}
|
||||
return scratchLightVec.normalize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetShort;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Supplies {@code mc_midTexCoord}/{@code at_tangent} for mod geometry drawn through the FFPclient-array path
|
||||
* ({@code glVertexPointer}/{@code glTexCoordPointer} + {@code glDrawArrays}) out of a VBO, which bypasses the
|
||||
* Tessellator and display-list ext-injection paths.
|
||||
*
|
||||
* <p>Tangents are reconstructed from the flat face normal (handler's {@code normalOffset < 0} path).
|
||||
*/
|
||||
public final class FfpExtendedAttribs {
|
||||
|
||||
private FfpExtendedAttribs() {}
|
||||
|
||||
private static final int POSITION_LOC = Usage.POSITION.getAttributeLocation();
|
||||
private static final int UV_LOC = Usage.PRIMARY_UV.getAttributeLocation();
|
||||
private static final int NORMAL_LOC = Usage.NORMAL.getAttributeLocation();
|
||||
private static final int SRC_TEX_OFFSET = 12;
|
||||
private static final int SRC_NORMAL_OFFSET = 20;
|
||||
private static final int SRC_STRIDE_FLAT = 20;
|
||||
private static final int SRC_STRIDE_SMOOTH = 24;
|
||||
private static final int MAX_CACHE = 1024;
|
||||
private static final int MAX_EXT_VERTS = 1 << 20;
|
||||
private static final int MAX_RESPECS = 8;
|
||||
private static final int RESPEC_DECAY_FRAMES = 2;
|
||||
private static final int MIN_STREAM_SPAN_FRAMES = 4;
|
||||
private static final int STREAM_EXPIRE_FRAMES = 60;
|
||||
|
||||
private static final HashMap<Key, ExtBuffer> cache = new HashMap<>();
|
||||
private static final Key lookup = new Key();
|
||||
private static int internalGlDepth = 0;
|
||||
|
||||
private static final HashMap<EboKey, Boolean> eboPatternCache = new HashMap<>();
|
||||
private static final EboKey eboLookup = new EboKey();
|
||||
|
||||
private static final Int2ObjectMap<ObjectSet<Key>> keysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<ObjectSet<EboKey>> eboKeysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2IntMap respecCounts = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecFrames = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecRunStart = new Int2IntOpenHashMap();
|
||||
private static final IntSet streamingSources = new IntOpenHashSet();
|
||||
private static final IntSet persistentSources = new IntOpenHashSet();
|
||||
private static int frame;
|
||||
|
||||
public static void endFrame() { frame++; }
|
||||
|
||||
public static boolean isEmpty() {
|
||||
return cache.isEmpty() && eboPatternCache.isEmpty();
|
||||
}
|
||||
|
||||
private static int internalDrawDepth = 0;
|
||||
|
||||
public static void beginInternalDraw() { internalDrawDepth++; }
|
||||
|
||||
public static void endInternalDraw() { if (internalDrawDepth > 0) internalDrawDepth--; }
|
||||
|
||||
private static void beginInternalGl() { internalGlDepth++; }
|
||||
|
||||
private static void endInternalGl() { if (internalGlDepth > 0) internalGlDepth--; }
|
||||
|
||||
private static boolean isNotConventionalTexturedArray(VAOManager.Attrib a) {
|
||||
return a == null || !a.enabled || a.genericPointer || a.vboId == 0 || a.size < 2;
|
||||
}
|
||||
|
||||
private static boolean isStreaming(int vboId) {
|
||||
if (vboId == 0 || !streamingSources.contains(vboId)) return false;
|
||||
if (persistentSources.contains(vboId)) return true;
|
||||
if (frame - respecFrames.get(vboId) > STREAM_EXPIRE_FRAMES) {
|
||||
streamingSources.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean maybeBind(int mode, int first, int count) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(mode, count);
|
||||
if (extPrim == 0 || first < 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId)) return false;
|
||||
|
||||
VAOManager.Attrib normal = extPrim == 3 ? VAOManager.get(NORMAL_LOC) : null;
|
||||
if (normal != null && (!normal.enabled || normal.genericPointer || normal.vboId == 0 || normal.size < 3
|
||||
|| isStreaming(normal.vboId))) {
|
||||
normal = null;
|
||||
}
|
||||
|
||||
return bindExt(h, mode, first, count, extPrim, pos, uv, normal);
|
||||
}
|
||||
|
||||
public static boolean maybeBindIndexed(int mode, int indexCount, int indexType, long indicesOffset) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
if (mode != GL11.GL_TRIANGLES || indexCount <= 0 || indexCount % 6 != 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId) || isStreaming(VAOManager.boundEBO)) return false;
|
||||
|
||||
final int indexSize = indexTypeBytes(indexType);
|
||||
if (indexSize == 0) return false;
|
||||
if (indicesOffset % (6L * indexSize) != 0) return false;
|
||||
final int first = (int) (indicesOffset / indexSize) / 6 * 4;
|
||||
final int count = indexCount / 6 * 4;
|
||||
|
||||
if (!isQuadPatternEbo(VAOManager.boundEBO, indicesOffset, indexCount, indexType, indexSize, first)) return false;
|
||||
|
||||
// Build/bind as GL_QUADS (the real primitive): face-normal tangents, 4-vertex tiles, no per-vertex normal.
|
||||
return bindExt(h, GL11.GL_QUADS, first, count, 4, pos, uv, null);
|
||||
}
|
||||
|
||||
private static boolean isQuadPatternEbo(int ebo, long offset, int indexCount, int indexType, int indexSize, int firstVertex) {
|
||||
if (ebo == 0) return false;
|
||||
eboLookup.set(ebo, offset, indexCount, indexType);
|
||||
final Boolean cached = eboPatternCache.get(eboLookup);
|
||||
if (cached != null) return cached;
|
||||
if (eboPatternCache.size() >= MAX_CACHE) {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
}
|
||||
|
||||
final ByteBuffer buf = memCalloc(indexCount, indexSize);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, buf);
|
||||
final long addr = memAddress0(buf);
|
||||
boolean match = true;
|
||||
for (int q = 0, quadCount = indexCount / 6; q < quadCount; q++) {
|
||||
final int base = firstVertex + q * 4;
|
||||
final long p = addr + (long) q * 6 * indexSize;
|
||||
if (readIndex(p, 0, indexSize) != base
|
||||
|| readIndex(p, 1, indexSize) != base + 1
|
||||
|| readIndex(p, 2, indexSize) != base + 2
|
||||
|| readIndex(p, 3, indexSize) != base + 2
|
||||
|| readIndex(p, 4, indexSize) != base + 3
|
||||
|| readIndex(p, 5, indexSize) != base) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
memFree(buf);
|
||||
final EboKey stored = eboLookup.copy();
|
||||
eboPatternCache.put(stored, match);
|
||||
ObjectSet<EboKey> ebos = eboKeysBySource.get(stored.ebo);
|
||||
if (ebos == null) eboKeysBySource.put(stored.ebo, ebos = new ObjectOpenHashSet<>());
|
||||
ebos.add(stored);
|
||||
return match;
|
||||
}
|
||||
|
||||
private static long readIndex(long base, int i, int indexSize) {
|
||||
return switch (indexSize) {
|
||||
case 1 -> memGetByte(base + i) & 0xFFL;
|
||||
case 2 -> memGetShort(base + (long) i * 2) & 0xFFFFL;
|
||||
default -> memGetInt(base + (long) i * 4) & 0xFFFFFFFFL;
|
||||
};
|
||||
}
|
||||
|
||||
private static int indexTypeBytes(int indexType) {
|
||||
return switch (indexType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> 1;
|
||||
case GL11.GL_UNSIGNED_SHORT -> 2;
|
||||
case GL11.GL_UNSIGNED_INT -> 4;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean bindExt(ImmediateExtendedAttribHandler h, int mode, int first, int count, int extPrim,
|
||||
VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal) {
|
||||
final int extVbo = getOrBuild(h, pos, uv, normal, first, count, extPrim, mode);
|
||||
if (extVbo == 0) return false;
|
||||
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void unbind() {
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
|
||||
setNeutralCurrentValues();
|
||||
}
|
||||
|
||||
public static void setNeutralCurrentValues() {
|
||||
GLStateManager.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
|
||||
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
dropDerivedFrom(vboId);
|
||||
respecCounts.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
streamingSources.remove(vboId);
|
||||
persistentSources.remove(vboId);
|
||||
}
|
||||
|
||||
public static void onBufferRespecified(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
if (streamingSources.contains(vboId)) {
|
||||
respecFrames.put(vboId, frame);
|
||||
return;
|
||||
}
|
||||
if (!dropDerivedFrom(vboId)) return;
|
||||
final boolean sameRun = respecCounts.containsKey(vboId) && frame - respecFrames.get(vboId) <= RESPEC_DECAY_FRAMES;
|
||||
if (!sameRun) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
final int respecs = respecCounts.get(vboId) + 1;
|
||||
final int span = frame - respecRunStart.get(vboId);
|
||||
if (respecs >= MAX_RESPECS && span >= MIN_STREAM_SPAN_FRAMES && span <= respecs) {
|
||||
markStreaming(vboId);
|
||||
return;
|
||||
}
|
||||
if (span > respecs) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
respecCounts.put(vboId, respecs);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
private static void startRespecRun(int vboId) {
|
||||
respecCounts.put(vboId, 1);
|
||||
respecRunStart.put(vboId, frame);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
public static void markStreaming(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
respecCounts.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
respecFrames.put(vboId, frame);
|
||||
if (streamingSources.add(vboId)) dropDerivedFrom(vboId);
|
||||
}
|
||||
|
||||
public static void markPersistent(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
markStreaming(vboId);
|
||||
persistentSources.add(vboId);
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
invalidateAll();
|
||||
respecCounts.clear();
|
||||
respecFrames.clear();
|
||||
respecRunStart.clear();
|
||||
streamingSources.clear();
|
||||
persistentSources.clear();
|
||||
}
|
||||
|
||||
private static boolean dropDerivedFrom(int srcId) {
|
||||
final ObjectSet<EboKey> ebos = eboKeysBySource.remove(srcId);
|
||||
if (ebos != null) {
|
||||
for (EboKey k : ebos) eboPatternCache.remove(k);
|
||||
}
|
||||
final ObjectSet<Key> keys = keysBySource.get(srcId);
|
||||
if (keys == null || keys.isEmpty()) return ebos != null;
|
||||
IntArrayList toFree = null;
|
||||
for (Key k : keys.toArray(new Key[0])) {
|
||||
final ExtBuffer buf = cache.remove(k);
|
||||
if (buf != null && buf.vbo != 0) {
|
||||
if (toFree == null) toFree = new IntArrayList();
|
||||
toFree.add(buf.vbo);
|
||||
}
|
||||
unindexKey(k);
|
||||
}
|
||||
freeExtVbos(toFree);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void indexKey(Key k) {
|
||||
addToBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) addToBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) addToBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void unindexKey(Key k) {
|
||||
removeFromBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) removeFromBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) removeFromBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void addToBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set == null) keysBySource.put(src, set = new ObjectOpenHashSet<>());
|
||||
set.add(k);
|
||||
}
|
||||
|
||||
private static void removeFromBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
final ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set != null && set.remove(k) && set.isEmpty()) keysBySource.remove(src);
|
||||
}
|
||||
|
||||
private static int getOrBuild(ImmediateExtendedAttribHandler h, VAOManager.Attrib pos, VAOManager.Attrib uv,
|
||||
VAOManager.Attrib normal, int first, int count, int extPrim, int mode) {
|
||||
if (first + count > MAX_EXT_VERTS) return 0;
|
||||
lookup.set(pos, uv, normal, mode, first % extPrim);
|
||||
ExtBuffer buf = cache.get(lookup);
|
||||
if (buf == null) {
|
||||
if (cache.size() >= MAX_CACHE) invalidateExtBuffers();
|
||||
buf = new ExtBuffer();
|
||||
final Key stored = lookup.copy();
|
||||
cache.put(stored, buf);
|
||||
indexKey(stored);
|
||||
}
|
||||
|
||||
final long range = (long) first << 32 | (count & 0xFFFFFFFFL);
|
||||
if (buf.vbo != 0 && buf.builtRanges.contains(range)) return buf.vbo;
|
||||
if (!ensureCapacity(buf, first + count)) return 0;
|
||||
buildRange(h, buf.vbo, pos, uv, normal, first, count, extPrim);
|
||||
buf.builtRanges.add(range);
|
||||
return buf.vbo;
|
||||
}
|
||||
|
||||
private static boolean ensureCapacity(ExtBuffer buf, int neededVerts) {
|
||||
if (buf.vbo != 0 && buf.capacityVerts >= neededVerts) return true;
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int newCap = Math.min(MAX_EXT_VERTS, Math.max(neededVerts, buf.capacityVerts * 2));
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int vbo = GLStateManager.glGenBuffers();
|
||||
if (vbo == 0) return false;
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) newCap * extStride, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
if (buf.vbo != 0) {
|
||||
final int savedRead = GLStateManager.getBoundCopyReadBuffer();
|
||||
final int savedWrite = GLStateManager.getBoundCopyWriteBuffer();
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, buf.vbo);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, vbo);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L,
|
||||
(long) buf.capacityVerts * extStride);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, savedRead);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, savedWrite);
|
||||
GLStateManager.glDeleteBuffers(buf.vbo);
|
||||
}
|
||||
buf.vbo = vbo;
|
||||
buf.capacityVerts = newCap;
|
||||
return true;
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildRange(ImmediateExtendedAttribHandler h, int extVbo, VAOManager.Attrib pos,
|
||||
VAOManager.Attrib uv, VAOManager.Attrib normal, int first, int count, int extPrim) {
|
||||
final int posStride = pos.effectiveStride();
|
||||
final int uvStride = uv.effectiveStride();
|
||||
final boolean smooth = normal != null;
|
||||
final int normalStride = smooth ? normal.effectiveStride() : 0;
|
||||
final int srcStride = smooth ? SRC_STRIDE_SMOOTH : SRC_STRIDE_FLAT;
|
||||
final int srcNormalOffset = smooth ? SRC_NORMAL_OFFSET : -1;
|
||||
|
||||
final ByteBuffer posBuf = readRange(pos, first, count, posStride);
|
||||
final ByteBuffer uvBuf = readRange(uv, first, count, uvStride);
|
||||
final ByteBuffer normalBuf = smooth ? readRange(normal, first, count, normalStride) : null;
|
||||
|
||||
final ByteBuffer src = memAlloc(count * srcStride).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int pb = i * posStride, ub = i * uvStride, d = i * srcStride;
|
||||
src.putFloat(d, pos.readComponent(posBuf, pb, 0));
|
||||
src.putFloat(d + 4, pos.readComponent(posBuf, pb, 1));
|
||||
src.putFloat(d + 8, pos.size >= 3 ? pos.readComponent(posBuf, pb, 2) : 0f);
|
||||
src.putFloat(d + SRC_TEX_OFFSET, uv.readComponent(uvBuf, ub, 0));
|
||||
src.putFloat(d + SRC_TEX_OFFSET + 4, uv.readComponent(uvBuf, ub, 1));
|
||||
if (smooth) {
|
||||
final int nb = i * normalStride;
|
||||
src.putInt(d + SRC_NORMAL_OFFSET, NormI8.pack(
|
||||
normal.readComponent(normalBuf, nb, 0),
|
||||
normal.readComponent(normalBuf, nb, 1),
|
||||
normal.readComponent(normalBuf, nb, 2)));
|
||||
}
|
||||
}
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final ByteBuffer ext = memCalloc(count, extStride);
|
||||
h.buildPacked(memAddress0(src), srcStride, 0, SRC_TEX_OFFSET, srcNormalOffset, count, extPrim, memAddress0(ext), extStride);
|
||||
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(count * extStride);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * extStride, ext);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
|
||||
memFree(posBuf);
|
||||
memFree(uvBuf);
|
||||
if (normalBuf != null) memFree(normalBuf);
|
||||
memFree(src);
|
||||
memFree(ext);
|
||||
}
|
||||
|
||||
private static ByteBuffer readRange(VAOManager.Attrib a, int first, int count, int stride) {
|
||||
final int elementBytes = a.size * a.typeSizeBytes();
|
||||
final int bytes = (count - 1) * stride + elementBytes;
|
||||
final ByteBuffer dst = memAlloc(bytes).order(ByteOrder.nativeOrder());
|
||||
dst.position(0).limit(bytes);
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, a.offset + (long) first * stride, dst);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
dst.position(0).limit(bytes);
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static void invalidateAll() {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
invalidateExtBuffers();
|
||||
}
|
||||
|
||||
private static void invalidateExtBuffers() {
|
||||
if (cache.isEmpty()) return;
|
||||
final IntArrayList toFree = new IntArrayList(cache.size());
|
||||
for (ExtBuffer buf : cache.values()) {
|
||||
if (buf.vbo != 0) toFree.add(buf.vbo);
|
||||
}
|
||||
cache.clear();
|
||||
keysBySource.clear();
|
||||
freeExtVbos(toFree);
|
||||
}
|
||||
|
||||
private static void freeExtVbos(IntArrayList vbos) {
|
||||
if (vbos == null || vbos.isEmpty()) return;
|
||||
beginInternalGl();
|
||||
try {
|
||||
for (int i = 0; i < vbos.size(); i++) {
|
||||
GLStateManager.glDeleteBuffers(vbos.getInt(i));
|
||||
}
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ExtBuffer {
|
||||
int vbo;
|
||||
int capacityVerts;
|
||||
final LongOpenHashSet builtRanges = new LongOpenHashSet();
|
||||
}
|
||||
|
||||
private static final class EboKey {
|
||||
int ebo, count, type;
|
||||
long offset;
|
||||
|
||||
void set(int ebo, long offset, int count, int type) {
|
||||
this.ebo = ebo;
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
EboKey copy() {
|
||||
final EboKey k = new EboKey();
|
||||
k.set(ebo, offset, count, type);
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof EboKey k)) return false;
|
||||
return ebo == k.ebo && count == k.count && type == k.type && offset == k.offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = ebo;
|
||||
h = h * 31 + count;
|
||||
h = h * 31 + type;
|
||||
h = h * 31 + Long.hashCode(offset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Key {
|
||||
int posVBO, uvVBO, normalVBO, posStride, uvStride, normalStride, posFmt, uvFmt, normalFmt, mode, phase;
|
||||
long posOffset, uvOffset, normalOffset;
|
||||
|
||||
void set(VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal, int mode, int phase) {
|
||||
posVBO = pos.vboId; posOffset = pos.offset; posStride = pos.effectiveStride(); posFmt = fmt(pos);
|
||||
uvVBO = uv.vboId; uvOffset = uv.offset; uvStride = uv.effectiveStride(); uvFmt = fmt(uv);
|
||||
if (normal != null) {
|
||||
normalVBO = normal.vboId; normalOffset = normal.offset;
|
||||
normalStride = normal.effectiveStride(); normalFmt = fmt(normal);
|
||||
} else {
|
||||
normalVBO = 0; normalOffset = 0; normalStride = 0; normalFmt = 0;
|
||||
}
|
||||
this.mode = mode;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
private static int fmt(VAOManager.Attrib a) {
|
||||
return (a.size & 0x7F) | (a.normalized ? 0x80 : 0) | (a.type << 8);
|
||||
}
|
||||
|
||||
Key copy() {
|
||||
final Key k = new Key();
|
||||
k.posVBO = posVBO; k.posOffset = posOffset; k.posStride = posStride; k.posFmt = posFmt;
|
||||
k.uvVBO = uvVBO; k.uvOffset = uvOffset; k.uvStride = uvStride; k.uvFmt = uvFmt;
|
||||
k.normalVBO = normalVBO; k.normalOffset = normalOffset; k.normalStride = normalStride; k.normalFmt = normalFmt;
|
||||
k.mode = mode; k.phase = phase;
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Key k)) return false;
|
||||
return posVBO == k.posVBO && uvVBO == k.uvVBO && normalVBO == k.normalVBO
|
||||
&& posStride == k.posStride && uvStride == k.uvStride && normalStride == k.normalStride
|
||||
&& posFmt == k.posFmt && uvFmt == k.uvFmt && normalFmt == k.normalFmt
|
||||
&& mode == k.mode && phase == k.phase
|
||||
&& posOffset == k.posOffset && uvOffset == k.uvOffset && normalOffset == k.normalOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = posVBO;
|
||||
h = h * 31 + uvVBO;
|
||||
h = h * 31 + normalVBO;
|
||||
h = h * 31 + posStride;
|
||||
h = h * 31 + uvStride;
|
||||
h = h * 31 + normalStride;
|
||||
h = h * 31 + posFmt;
|
||||
h = h * 31 + uvFmt;
|
||||
h = h * 31 + normalFmt;
|
||||
h = h * 31 + mode;
|
||||
h = h * 31 + phase;
|
||||
h = h * 31 + Long.hashCode(posOffset);
|
||||
h = h * 31 + Long.hashCode(uvOffset);
|
||||
h = h * 31 + Long.hashCode(normalOffset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
-11
@@ -4,8 +4,6 @@ import java.util.Arrays;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
@@ -14,7 +12,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (10) + unit 0 (47 bits at offset 10)
|
||||
* long[0]: global bits (13) + unit 0 (47 bits at offset 13)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
@@ -34,8 +32,6 @@ import org.lwjgl.opengl.GL13;
|
||||
* 42-46: arg2 Alpha
|
||||
*/
|
||||
public final class FragmentKey {
|
||||
private static final Logger LOGGER = LogManager.getLogger("FragmentKey");
|
||||
|
||||
public static final int TEX_ENV_MODULATE = 0;
|
||||
public static final int TEX_ENV_REPLACE = 1;
|
||||
public static final int TEX_ENV_ADD = 2;
|
||||
@@ -67,12 +63,15 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 10;
|
||||
private static final int GLOBAL_BITS = 13;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
private static final int BIT_SEPARATE_SPECULAR = 6; // 1 bit
|
||||
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
|
||||
private static final int BIT_COLOR_SUM = 12; // 1 bit
|
||||
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
@@ -123,17 +122,29 @@ public final class FragmentKey {
|
||||
global |= ((long) (encodeAlphaFunc(GLStateManager.getAlphaState().getFunction()) & 0x7)) << BIT_ALPHA_FUNC;
|
||||
}
|
||||
|
||||
// Separate specular
|
||||
// Damage overlay
|
||||
if (GLStateManager.getOverlayA() != 0.0f) {
|
||||
global |= (1L << BIT_OVERLAY_ENABLED);
|
||||
}
|
||||
|
||||
if (GLStateManager.lineStippleActive) {
|
||||
global |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
// Separate specular / color sum
|
||||
if (GLStateManager.getLightingState().isEnabled()
|
||||
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
global |= (1L << BIT_SEPARATE_SPECULAR);
|
||||
} else if (GLStateManager.getColorSumState().isEnabled()) {
|
||||
global |= (1L << BIT_COLOR_SUM);
|
||||
}
|
||||
|
||||
// Per-unit state
|
||||
int highestEnabled = -1;
|
||||
long unit0Bits = 0;
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled();
|
||||
// Per Mesa - an enabled unit with no complete texture bound counts as disabled.
|
||||
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled() && GLStateManager.getTextures().getTextureUnitBindings(i).getBinding() != 0;
|
||||
if (texEnabled) highestEnabled = i;
|
||||
|
||||
final long unitBits = packUnit(i, texEnabled);
|
||||
@@ -230,7 +241,10 @@ public final class FragmentKey {
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
|
||||
public boolean colorSum() { return ((packed[0] >> BIT_COLOR_SUM) & 1) != 0; }
|
||||
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
|
||||
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
|
||||
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
|
||||
|
||||
private long unitBits(int i) {
|
||||
return (i == 0) ? (packed[0] >>> GLOBAL_BITS) : packed[i];
|
||||
@@ -256,6 +270,28 @@ public final class FragmentKey {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Per-unit enable bits as a 4-bit mask (bit i = unit i enabled). */
|
||||
public int enabledUnitMask() {
|
||||
int mask = 0;
|
||||
final int n = Math.min(nrEnabledUnits(), MAX_UNITS);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (unitEnabled(i)) mask |= (1 << i);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
/** Per-unit enable bits from a packed scratch as a 4-bit mask (bit i = unit i enabled). */
|
||||
public static int unitMaskFromPacked(long[] packed, int len) {
|
||||
int mask = 0;
|
||||
if (len < 1) return 0;
|
||||
if (((packed[0] >>> GLOBAL_BITS) & 1L) != 0) mask |= 1;
|
||||
final int n = Math.min(len, MAX_UNITS);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if ((packed[i] & 1L) != 0) mask |= (1 << i);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
public boolean lightmapEnabled() {
|
||||
return nrEnabledUnits() > 1 && unitEnabled(1);
|
||||
}
|
||||
@@ -297,7 +333,7 @@ public final class FragmentKey {
|
||||
case GL13.GL_PRIMARY_COLOR -> SRC_PRIMARY_COLOR;
|
||||
case GL13.GL_PREVIOUS -> SRC_PREVIOUS;
|
||||
default -> {
|
||||
LOGGER.debug("Unknown TexEnv source 0x{}, falling back to SRC_PREVIOUS", Integer.toHexString(glSource));
|
||||
GLStateManager.LOGGER.debug("Unknown TexEnv source 0x{}, falling back to SRC_PREVIOUS", Integer.toHexString(glSource));
|
||||
yield SRC_PREVIOUS;
|
||||
}
|
||||
};
|
||||
@@ -342,10 +378,10 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b units=%d",
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d",
|
||||
fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-",
|
||||
separateSpecular(), nrEnabledUnits()));
|
||||
separateSpecular(), colorSum(), overlayEnabled(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
+55
-34
@@ -20,6 +20,7 @@ public final class FragmentShaderGenerator {
|
||||
sb.append("out vec4 fragColor;\n\n");
|
||||
|
||||
sb.append("void main() {\n");
|
||||
emitLineStipple(sb, key);
|
||||
emitTextureSampling(sb, key);
|
||||
emitTexEnvChain(sb, key);
|
||||
emitSpecularAdd(sb, key);
|
||||
@@ -37,64 +38,64 @@ public final class FragmentShaderGenerator {
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("in vec3 v_SpecularColor;\n");
|
||||
}
|
||||
if (key.textureEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord0;\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord1;\n");
|
||||
// Per-unit texcoord inputs. Each enabled unit sources from its own varying
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (key.unitEnabled(i)) {
|
||||
sb.append("in vec4 v_TexCoord").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
if (key.fogMode() != FragmentKey.FOG_NONE) {
|
||||
sb.append("in float v_FogCoord;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat in vec2 v_LineStart;\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitLineStipple(StringBuilder sb, FragmentKey key) {
|
||||
if (!key.lineStipple()) return;
|
||||
sb.append(" vec2 stippleDelta = abs(gl_FragCoord.xy - v_LineStart);\n");
|
||||
sb.append(" int stippleBit = int(floor(max(stippleDelta.x, stippleDelta.y) / float(u_LineStipple >> 16))) & 15;\n");
|
||||
sb.append(" if (((u_LineStipple >> stippleBit) & 1) == 0) discard;\n\n");
|
||||
}
|
||||
|
||||
private static void emitUniforms(StringBuilder sb, FragmentKey key) {
|
||||
// Samplers for each enabled unit
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (key.unitEnabled(i)) {
|
||||
sb.append("uniform sampler2D u_Sampler").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
if (key.alphaTestEnabled()) {
|
||||
sb.append("uniform float u_AlphaRef;\n");
|
||||
}
|
||||
// Per-unit texEnvColor uniforms
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (key.unitEnabled(i) && key.unitNeedsEnvColor(i)) {
|
||||
sb.append("uniform vec4 u_TexEnvColor").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
if (key.fogMode() != FragmentKey.FOG_NONE) {
|
||||
sb.append("uniform vec4 u_FogParams;\n");
|
||||
sb.append("uniform vec4 u_FogColor;\n");
|
||||
}
|
||||
sb.append(FFPUniformBlock.GLSL_DECL);
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
// Unit 1 is the lightmap — uses v_TexCoord1.
|
||||
// Units 2-3 intentionally share unit 0's texture coordinates since
|
||||
// the vertex shader only provides 2 tex coord varyings (unit 0 and unit 1/lightmap).
|
||||
final String texCoord = (i == 1) ? "v_TexCoord1.st" : "v_TexCoord0.st";
|
||||
sb.append(" vec4 tex").append(i).append("Color = texture(u_Sampler").append(i).append(", ").append(texCoord).append(");\n");
|
||||
sb.append(" vec4 tex").append(i).append("Color = textureProj(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitTexEnvChain(StringBuilder sb, FragmentKey key) {
|
||||
if (!key.textureEnabled()) {
|
||||
sb.append(" // No texture — vertex color only\n");
|
||||
sb.append(" // No texture -- vertex color only\n");
|
||||
sb.append(" vec4 color = v_Color;\n");
|
||||
emitOverlay(sb, key);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process units in order; "previous" starts as vertex color
|
||||
// Process units in order; "previous" starts as vertex color.
|
||||
boolean firstUnit = true;
|
||||
boolean overlayInjected = false;
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
|
||||
if (!firstUnit && !overlayInjected) {
|
||||
emitOverlay(sb, key);
|
||||
overlayInjected = true;
|
||||
}
|
||||
|
||||
final String texVar = "tex" + i + "Color";
|
||||
final String envColorVar = "u_TexEnvColor" + i;
|
||||
final String prevVar = firstUnit ? "v_Color" : "color";
|
||||
@@ -107,6 +108,16 @@ public final class FragmentShaderGenerator {
|
||||
}
|
||||
firstUnit = false;
|
||||
}
|
||||
|
||||
if (!overlayInjected) emitOverlay(sb, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the modern-style damage overlay mix.
|
||||
*/
|
||||
private static void emitOverlay(StringBuilder sb, FragmentKey key) {
|
||||
if (!key.overlayEnabled()) return;
|
||||
sb.append(" color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);\n");
|
||||
}
|
||||
|
||||
private static void emitSimpleUnit(StringBuilder sb, FragmentKey key, int unit, String texVar, String envColorVar, String prevVar, String assign) {
|
||||
@@ -265,6 +276,10 @@ public final class FragmentShaderGenerator {
|
||||
sb.append(" // Add separate specular\n");
|
||||
sb.append(" color.rgb += v_SpecularColor;\n");
|
||||
}
|
||||
if (key.colorSum()) {
|
||||
sb.append(" // Color sum - GL_COLOR_SUM with the current secondary color\n");
|
||||
sb.append(" color.rgb += u_SecondaryColor;\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitAlphaTest(StringBuilder sb, FragmentKey key) {
|
||||
@@ -273,21 +288,27 @@ public final class FragmentShaderGenerator {
|
||||
sb.append(" // Alpha test\n");
|
||||
final int func = FragmentKey.decodeAlphaFunc(key.alphaTestFunc());
|
||||
|
||||
|
||||
sb.append(" float qa = round(color.a * 255.0);\n");
|
||||
switch (func) {
|
||||
case GL11.GL_NEVER ->
|
||||
sb.append(" discard;\n");
|
||||
case GL11.GL_LESS ->
|
||||
sb.append(" if (color.a >= u_AlphaRef) discard;\n");
|
||||
case GL11.GL_EQUAL ->
|
||||
sb.append(" if (color.a != u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa >= u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_EQUAL -> {
|
||||
sb.append(" float qr = round(u_AlphaRef * 255.0);\n");
|
||||
sb.append(" if (qa != qr) discard;\n");
|
||||
}
|
||||
case GL11.GL_LEQUAL ->
|
||||
sb.append(" if (color.a > u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa > u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_GREATER ->
|
||||
sb.append(" if (color.a <= u_AlphaRef) discard;\n");
|
||||
case GL11.GL_NOTEQUAL ->
|
||||
sb.append(" if (color.a == u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa <= u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_NOTEQUAL -> {
|
||||
sb.append(" float qr = round(u_AlphaRef * 255.0);\n");
|
||||
sb.append(" if (qa == qr) discard;\n");
|
||||
}
|
||||
case GL11.GL_GEQUAL ->
|
||||
sb.append(" if (color.a < u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa < u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_ALWAYS -> {}
|
||||
}
|
||||
}
|
||||
+23
-20
@@ -14,38 +14,40 @@ public final class GeometryShaderGenerator {
|
||||
sb.append("layout(lines) in;\n");
|
||||
sb.append("layout(triangle_strip, max_vertices = 4) out;\n\n");
|
||||
|
||||
sb.append("uniform vec2 u_ViewportSize;\n");
|
||||
sb.append("uniform float u_LineWidth;\n\n");
|
||||
sb.append(FFPUniformBlock.GLSL_DECL);
|
||||
sb.append('\n');
|
||||
|
||||
sb.append("// Pass-through varyings\n");
|
||||
sb.append("in vec4 v_Color_gs[];\n");
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("in vec3 v_SpecularColor_gs[];\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord0_gs[];\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord1_gs[];\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append("in vec4 v_TexCoord0_gs[];\n");
|
||||
if (key.lightmapEnabled()) sb.append("in vec4 v_TexCoord1_gs[];\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("in vec4 v_TexCoord2_gs[];\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("in vec4 v_TexCoord3_gs[];\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("in float v_FogCoord_gs[];\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat in vec2 v_LineStart_gs[];\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
|
||||
sb.append("out vec4 v_Color;\n");
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("out vec3 v_SpecularColor;\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord0;\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord1;\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append("out vec4 v_TexCoord0;\n");
|
||||
if (key.lightmapEnabled()) sb.append("out vec4 v_TexCoord1;\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("out vec4 v_TexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("out vec4 v_TexCoord3;\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("out float v_FogCoord;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
|
||||
sb.append("void main() {\n");
|
||||
@@ -82,15 +84,16 @@ public final class GeometryShaderGenerator {
|
||||
if (key.separateSpecular()) {
|
||||
sb.append(" v_SpecularColor = v_SpecularColor_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
sb.append(" v_TexCoord0 = v_TexCoord0_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append(" v_TexCoord1 = v_TexCoord1_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = v_TexCoord0_gs").append(idx).append(";\n");
|
||||
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = v_TexCoord1_gs").append(idx).append(";\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = v_TexCoord2_gs").append(idx).append(";\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = v_TexCoord3_gs").append(idx).append(";\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append(" v_FogCoord = v_FogCoord_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append(" v_LineStart = v_LineStart_gs[0];\n");
|
||||
}
|
||||
|
||||
if (key.clipPlanesEnabled()) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
public final class InstancedAttribs {
|
||||
|
||||
public static final int LOC_MATRIX_COL0 = 5;
|
||||
public static final int LOC_MATRIX_COL1 = 6;
|
||||
public static final int LOC_MATRIX_COL2 = 7;
|
||||
public static final int LOC_MATRIX_COL3 = 8;
|
||||
public static final int LOC_COLOR = 9;
|
||||
public static final int LOC_LIGHTMAP = 10;
|
||||
|
||||
public static final int OFFSET_MATRIX = 0;
|
||||
public static final int OFFSET_COLOR = 64;
|
||||
public static final int OFFSET_LIGHTMAP = 68;
|
||||
public static final int STRIDE = 80;
|
||||
|
||||
private InstancedAttribs() {}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CompatShaderTransformer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import lombok.Getter;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.opengl.KHRDebug;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* A compiled and linked FFP emulation program (vertex + fragment + optional geometry shaders). Owns the GL program handle and caches uniform locations.
|
||||
*/
|
||||
public class Program {
|
||||
|
||||
private static final AtomicInteger PROGRAM_COUNTER = new AtomicInteger();
|
||||
|
||||
@Getter private final int programId;
|
||||
@Getter private final VertexKey vertexKey;
|
||||
@Getter private final FragmentKey fragmentKey;
|
||||
|
||||
public final int[] locSampler = { -1, -1, -1, -1 };
|
||||
|
||||
Program(int programId, VertexKey vertexKey, FragmentKey fragmentKey) {
|
||||
this.programId = programId;
|
||||
this.vertexKey = vertexKey;
|
||||
this.fragmentKey = fragmentKey;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
locSampler[i] = RENDER_BACKEND.getUniformLocation(programId, "u_Sampler" + i);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
GLStateManager.glDeleteProgram(programId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile vertex + fragment + optional geometry shaders, link into a program, and return the FFPProgram.
|
||||
*/
|
||||
static Program create(VertexKey vk, FragmentKey fk, String vertSrc, String fragSrc, String geomSrc) {
|
||||
final RenderBackend backend = RENDER_BACKEND;
|
||||
final int id = PROGRAM_COUNTER.getAndIncrement();
|
||||
final String vkHex = Long.toHexString(vk.pack());
|
||||
final boolean hasGeom = geomSrc != null;
|
||||
|
||||
int shaderCount = 0;
|
||||
final int[] shaders = new int[5]; // vertex, fragment, geometry, tess control, tess evaluation
|
||||
int program = 0;
|
||||
try {
|
||||
shaders[shaderCount++] = compileShader(GL20.GL_VERTEX_SHADER, vertSrc, "ffp_v_" + vkHex);
|
||||
shaders[shaderCount++] = compileShader(GL20.GL_FRAGMENT_SHADER, fragSrc, "ffp_f_" + id);
|
||||
if (geomSrc != null) {
|
||||
shaders[shaderCount++] = compileShader(GL32.GL_GEOMETRY_SHADER, geomSrc, "ffp_g_" + id);
|
||||
}
|
||||
|
||||
program = backend.createProgram();
|
||||
for (int i = 0; i < shaderCount; i++) backend.attachShader(program, shaders[i]);
|
||||
backend.linkProgram(program);
|
||||
|
||||
final String log = backend.getProgramInfoLog(program, backend.getProgrami(program, GL20.GL_INFO_LOG_LENGTH));
|
||||
if (!log.isEmpty()) {
|
||||
GLStateManager.LOGGER.warn("FFP program link log (vk=0x{}, fk={}): {}", vkHex, fk, log);
|
||||
}
|
||||
|
||||
if (backend.getProgrami(program, GL20.GL_LINK_STATUS) != GL11.GL_TRUE) {
|
||||
throw new RuntimeException("FFP shader link failed (vk=0x" + vkHex + ", fk=" + fk + "): " + log);
|
||||
}
|
||||
|
||||
final String debugName = "FFP(v=0x" + vkHex + ",f=" + id + (hasGeom ? ",g" : "") + ")";
|
||||
GLDebug.nameObject(KHRDebug.GL_PROGRAM, program, debugName);
|
||||
|
||||
// Detach and delete individual shaders - they're linked into the program
|
||||
for (int i = 0; i < shaderCount; i++) {
|
||||
backend.detachShader(program, shaders[i]);
|
||||
backend.deleteShader(shaders[i]);
|
||||
}
|
||||
shaderCount = 0;
|
||||
|
||||
final Program ffpProgram = new Program(program, vk, fk);
|
||||
|
||||
final int blockIndex = backend.getUniformBlockIndex(program, FFPUniformBlock.BLOCK_NAME);
|
||||
if (blockIndex != GL31.GL_INVALID_INDEX) {
|
||||
backend.uniformBlockBinding(program, blockIndex, FFPUniformBlock.BINDING_POINT);
|
||||
}
|
||||
|
||||
final int previousProgram = GLStateManager.getActiveProgram();
|
||||
backend.useProgram(program);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ffpProgram.locSampler[i] != -1) backend.uniform1i(ffpProgram.locSampler[i], i);
|
||||
}
|
||||
backend.useProgram(previousProgram);
|
||||
|
||||
return ffpProgram;
|
||||
} catch (RuntimeException e) {
|
||||
if (program != 0) backend.deleteProgram(program);
|
||||
for (int i = 0; i < shaderCount; i++) backend.deleteShader(shaders[i]);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static int compileShader(int type, String src, String name) {
|
||||
final RenderBackend backend = RENDER_BACKEND;
|
||||
final int shader = backend.createShader(type);
|
||||
if (RenderSystem.isGLES()) {
|
||||
src = CompatShaderTransformer.toGLES(src, type, type == GL20.GL_FRAGMENT_SHADER);
|
||||
dumpTranslatedFFPShader(type, src, name);
|
||||
}
|
||||
backend.shaderSource(shader, src);
|
||||
backend.compileShader(shader);
|
||||
|
||||
final String typeName = switch (type) {
|
||||
case GL20.GL_VERTEX_SHADER -> "vertex";
|
||||
case GL20.GL_FRAGMENT_SHADER -> "fragment";
|
||||
case GL32.GL_GEOMETRY_SHADER -> "geometry";
|
||||
case GL40.GL_TESS_CONTROL_SHADER -> "tess_control";
|
||||
case GL40.GL_TESS_EVALUATION_SHADER -> "tess_evaluation";
|
||||
case GL43.GL_COMPUTE_SHADER -> "compute";
|
||||
default -> "unknown(0x" + Integer.toHexString(type) + ")";
|
||||
};
|
||||
GLDebug.nameObject(KHRDebug.GL_SHADER, shader, name + "(" + typeName + ")");
|
||||
|
||||
final String log = backend.getShaderInfoLog(shader, backend.getShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
|
||||
if (!log.isEmpty()) {
|
||||
GLStateManager.LOGGER.warn("FFP {} shader compilation log for {}: {}", typeName, name, log);
|
||||
}
|
||||
|
||||
final int result = backend.getShaderi(shader, GL20.GL_COMPILE_STATUS);
|
||||
if (result != GL11.GL_TRUE) {
|
||||
GLStateManager.LOGGER.error("FFP {} shader source:\n{}", typeName, src);
|
||||
backend.deleteShader(shader);
|
||||
throw new RuntimeException("FFP " + typeName + " shader compilation failed for " + name + ": " + log);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
// Post-translation GLSL ES dump, paired with ShaderCache's pre-translation 330 core dump.
|
||||
private static void dumpTranslatedFFPShader(int type, String translatedSrc, String name) {
|
||||
final Path dir = SystemProperties.shaderDumpDir("ffp");
|
||||
if (dir == null) return;
|
||||
final String suffix = switch (type) {
|
||||
case GL20.GL_VERTEX_SHADER -> ".vert.glsl";
|
||||
case GL20.GL_FRAGMENT_SHADER -> ".frag.glsl";
|
||||
case GL32.GL_GEOMETRY_SHADER -> ".geom.glsl";
|
||||
default -> ".glsl";
|
||||
};
|
||||
try {
|
||||
Files.createDirectories(dir);
|
||||
Files.writeString(dir.resolve(name + ".gles" + suffix), translatedSrc);
|
||||
} catch (IOException e) {
|
||||
GLStateManager.LOGGER.warn("Failed to dump translated FFP shader: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-17
@@ -2,8 +2,8 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import lombok.Setter;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -26,7 +26,8 @@ import java.util.IdentityHashMap;
|
||||
* </pre>
|
||||
*/
|
||||
public class ShaderCache {
|
||||
private static final Logger LOGGER = LogManager.getLogger("FFPShaderCache");
|
||||
private static final Tracy.ZoneId Z_FFP_SHADER_GEN = Tracy.zoneId("ffpShaderGen", Tracy.COLOR_FFP);
|
||||
|
||||
private static final int SLOT_STRIDE = 6;
|
||||
private static final int INITIAL_CAPACITY = 64; // power of 2
|
||||
private static final long EMPTY_MARKER = 0L; // slot [1] == 0 means empty (fkLen >= 1 guarantees nonzero)
|
||||
@@ -100,18 +101,25 @@ public class ShaderCache {
|
||||
}
|
||||
|
||||
final VertexKey vk = VertexKey.fromPacked(vkPacked);
|
||||
final String vertSrc = getOrGenerateVertex(vk);
|
||||
|
||||
final FragmentKey fk = FragmentKey.fromPacked(fkPacked, fkLen);
|
||||
String fragSrc = findFragSource(fkPacked, fkLen);
|
||||
if (fragSrc == null) {
|
||||
fragSrc = FragmentShaderGenerator.generate(fk);
|
||||
uniqueFragCount++;
|
||||
final String vertSrc;
|
||||
String fragSrc;
|
||||
final String geomSrc;
|
||||
final Program program;
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_FFP_SHADER_GEN);
|
||||
try {
|
||||
vertSrc = getOrGenerateVertex(vk);
|
||||
fragSrc = findFragSource(fkPacked, fkLen);
|
||||
if (fragSrc == null) {
|
||||
fragSrc = FragmentShaderGenerator.generate(fk);
|
||||
uniqueFragCount++;
|
||||
}
|
||||
geomSrc = vk.wideLineEmulation() ? getOrGenerateGeometry(vk) : null;
|
||||
program = Program.create(vk, fk, vertSrc, fragSrc, geomSrc);
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
|
||||
final String geomSrc = vk.wideLineEmulation() ? getOrGenerateGeometry(vk) : null;
|
||||
final Program program = Program.create(vk, fk, vertSrc, fragSrc, geomSrc);
|
||||
|
||||
final int insertIdx = findEmptySlot(hash);
|
||||
final int base = insertIdx * SLOT_STRIDE;
|
||||
keys[base] = vkPacked;
|
||||
@@ -123,7 +131,7 @@ public class ShaderCache {
|
||||
|
||||
setLastUsed(vkPacked, fkPacked, fkLen, program);
|
||||
|
||||
LOGGER.debug("FFP variant compiled: {} + {} (total: {} programs)", vk, fk, count);
|
||||
GLStateManager.LOGGER.debug("FFP variant compiled: {} + {} (total: {} programs)", vk, fk, count);
|
||||
|
||||
if (dumpDir != null) {
|
||||
dumpShader(Long.toHexString(vkPacked), ".vert.glsl", vertSrc);
|
||||
@@ -234,7 +242,7 @@ public class ShaderCache {
|
||||
Files.writeString(file, source, StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Failed to dump FFP shader to {}: {}", dumpDir, e.getMessage());
|
||||
GLStateManager.LOGGER.warn("Failed to dump FFP shader to {}: {}", dumpDir, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,10 +264,10 @@ public class ShaderCache {
|
||||
for (var entry : geometrySourceCache.entrySet()) {
|
||||
dumpShader(Long.toHexString(entry.getKey()), ".geom.glsl", entry.getValue());
|
||||
}
|
||||
LOGGER.info("Dumped {} vertex, {} geometry, and {} fragment FFP shader sources to {}",
|
||||
GLStateManager.LOGGER.info("Dumped {} vertex, {} geometry, and {} fragment FFP shader sources to {}",
|
||||
vertexSourceCache.size(), geometrySourceCache.size(), seen.size(), outputDir);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Failed to dump FFP shaders: {}", e.getMessage());
|
||||
GLStateManager.LOGGER.warn("Failed to dump FFP shaders: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +287,7 @@ public class ShaderCache {
|
||||
geometrySourceCache.clear();
|
||||
lastProgram = null;
|
||||
lastVK = Long.MIN_VALUE;
|
||||
LOGGER.debug("FFP shader cache cleared");
|
||||
GLStateManager.LOGGER.debug("FFP shader cache cleared");
|
||||
}
|
||||
|
||||
public int getProgramCount() {
|
||||
@@ -0,0 +1,239 @@
|
||||
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.config.SystemProperties;
|
||||
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.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Vec3fStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import lombok.Getter;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* FFP shader manager
|
||||
*/
|
||||
public final class ShaderManager {
|
||||
|
||||
private static final class Holder {
|
||||
|
||||
static final ShaderManager INSTANCE = new ShaderManager();
|
||||
}
|
||||
|
||||
private final ShaderCache cache = new ShaderCache();
|
||||
private final Uniforms uniforms = new Uniforms();
|
||||
|
||||
@Getter
|
||||
private boolean active = false;
|
||||
private Program currentProgram = null;
|
||||
private int lastBoundProgramId = -1;
|
||||
private long currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
private final long[] currentFKScratch = new long[FragmentKey.MAX_UNITS];
|
||||
private final long[] currentFKPacked = new long[FragmentKey.MAX_UNITS];
|
||||
private int currentFKLen = 0;
|
||||
|
||||
@Getter private static final Vector3f currentNormal = new Vector3f(0.0f, 0.0f, 1.0f);
|
||||
private static final Vector4f[] currentTexCoords = {
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
};
|
||||
@Getter private static final Vec3fStack normalStack = new Vec3fStack(currentNormal);
|
||||
@Getter private static final Vec4fStack texCoordStack = new Vec4fStack(currentTexCoords[0]);
|
||||
@Getter private static int normalGeneration;
|
||||
@Getter private static int texCoordGeneration;
|
||||
|
||||
private int preDrawCalls;
|
||||
private int lastFramePreDrawCalls;
|
||||
|
||||
public static long variantSwitches;
|
||||
|
||||
public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; }
|
||||
public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; }
|
||||
@Getter private boolean enabled = false;
|
||||
|
||||
private ShaderManager() {
|
||||
cache.setDumpDir(SystemProperties.shaderDumpDir("ffp"));
|
||||
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
|
||||
VAOManager.setCurrentVertexFlags(format.getVertexFlags());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public static ShaderManager getInstance() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
warmUp();
|
||||
enabled = true;
|
||||
|
||||
if (GLStateManager.getActiveProgram() == 0) {
|
||||
activate();
|
||||
}
|
||||
|
||||
GLStateManager.LOGGER.info("FFP shader emulation enabled");
|
||||
}
|
||||
|
||||
// Force loading of classes before the SplashThread kicks off
|
||||
// Ensure it's GL free
|
||||
private static void warmUp() {
|
||||
try {
|
||||
final long[] fkScratch = new long[FragmentKey.MAX_UNITS];
|
||||
final int fkLen = FragmentKey.packFromState(fkScratch);
|
||||
final int fragMask = FragmentKey.unitMaskFromPacked(fkScratch, fkLen);
|
||||
final long vkPacked = VertexKey.packFromState(true, true, true, true, fragMask);
|
||||
final VertexKey vk = VertexKey.fromPacked(vkPacked);
|
||||
VertexShaderGenerator.generate(vk);
|
||||
FragmentShaderGenerator.generate(FragmentKey.fromPacked(fkScratch, fkLen));
|
||||
GeometryShaderGenerator.generate(vk);
|
||||
final Class<?>[] touched = { Program.class, ShaderCache.class, TessellatorStreamingDrawer.class, QuadConverter.class };
|
||||
for (Class<?> c : touched) {
|
||||
c.getName();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
GLStateManager.LOGGER.warn("FFP warmup failed; draw-path classes will resolve lazily", t);
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
active = true;
|
||||
lastBoundProgramId = GLStateManager.getActiveProgram();
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
active = false;
|
||||
currentProgram = null;
|
||||
lastBoundProgramId = -1;
|
||||
currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
currentFKLen = 0;
|
||||
GLStateManager.forceAttribDefaultsDirty();
|
||||
}
|
||||
|
||||
public void preDraw() {
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
|
||||
// Handle FFP & Iris uniforms
|
||||
final int currentProgramId = GLStateManager.getActiveProgram();
|
||||
if (currentProgramId != 0) {
|
||||
if (!CompatUniformManager.refreshCompatUniforms(currentProgramId)) {
|
||||
return; // Don't emulate FFP on non-iris core shaders
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
preDrawCalls++;
|
||||
final int fkLen = FragmentKey.packFromState(currentFKScratch);
|
||||
final int fragMask = FragmentKey.unitMaskFromPacked(currentFKScratch, fkLen);
|
||||
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragMask);
|
||||
|
||||
if (vkPacked != currentVertexKeyPacked || !Arrays.equals(currentFKScratch, 0, fkLen, currentFKPacked, 0, currentFKLen)) {
|
||||
commitVariant(vkPacked, fkLen);
|
||||
}
|
||||
|
||||
uploadUniforms();
|
||||
}
|
||||
|
||||
private void commitVariant(long vkPacked, int fkLen) {
|
||||
currentVertexKeyPacked = vkPacked;
|
||||
System.arraycopy(currentFKScratch, 0, currentFKPacked, 0, fkLen);
|
||||
currentFKLen = fkLen;
|
||||
currentProgram = cache.getOrCreate(vkPacked, currentFKPacked, currentFKLen);
|
||||
final int programId = currentProgram.getProgramId();
|
||||
if (programId != lastBoundProgramId) {
|
||||
if (Tracy.ENABLED) variantSwitches++;
|
||||
RENDER_BACKEND.useProgram(programId);
|
||||
lastBoundProgramId = programId;
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadUniforms() {
|
||||
if (currentProgram != null) {
|
||||
uniforms.upload();
|
||||
}
|
||||
}
|
||||
|
||||
public static void endFrame() {
|
||||
final ShaderManager sm = Holder.INSTANCE;
|
||||
sm.lastFramePreDrawCalls = sm.preDrawCalls;
|
||||
sm.preDrawCalls = 0;
|
||||
sm.uniforms.endFrame();
|
||||
FfpExtendedAttribs.endFrame();
|
||||
}
|
||||
|
||||
public static void setCurrentNormal(float x, float y, float z) {
|
||||
currentNormal.set(x, y, z);
|
||||
normalGeneration++;
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(float s, float t, float r, float q) {
|
||||
currentTexCoords[0].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(int unit, float s, float t, float r, float q) {
|
||||
currentTexCoords[unit].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public static void bumpNormalGeneration() { normalGeneration++; }
|
||||
public static void bumpTexCoordGeneration() { texCoordGeneration++; }
|
||||
|
||||
public void destroy() {
|
||||
cache.destroy();
|
||||
uniforms.destroy();
|
||||
final GLSMInitConfig config = GLStateManager.getInitConfig();
|
||||
if (config != null && config.getStreamingDrawerDestroy() != null) config.getStreamingDrawerDestroy().run();
|
||||
QuadConverter.destroy();
|
||||
active = false;
|
||||
currentProgram = null;
|
||||
}
|
||||
|
||||
public String getDebugInfo() {
|
||||
return String.format("FFP: %d programs (%d vert, %d frag variants) | UBO: %d/%d draws wrote (mat %d, light %d, frag %d, misc %d)",
|
||||
cache.getProgramCount(), cache.getVertexVariantCount(), cache.getFragmentVariantCount(),
|
||||
uniforms.lastFrameBlockWrites, lastFramePreDrawCalls,
|
||||
uniforms.lastFrameStagedMatrices, uniforms.lastFrameStagedLighting,
|
||||
uniforms.lastFrameStagedFragment, uniforms.lastFrameStagedMisc);
|
||||
}
|
||||
|
||||
public int statLastFramePreDrawCalls() { return lastFramePreDrawCalls; }
|
||||
public int statLastFrameBlockWrites() { return uniforms.lastFrameBlockWrites; }
|
||||
public int statLastFrameBlockSkips() { return uniforms.lastFrameBlockSkips; }
|
||||
public int statLastFrameStagedMatrices() { return uniforms.lastFrameStagedMatrices; }
|
||||
public int statLastFrameStagedLighting() { return uniforms.lastFrameStagedLighting; }
|
||||
public int statLastFrameStagedFragment() { return uniforms.lastFrameStagedFragment; }
|
||||
public int statLastFrameStagedColor() { return uniforms.lastFrameStagedColor; }
|
||||
public int statLastFrameStagedNormal() { return uniforms.lastFrameStagedNormal; }
|
||||
public int statLastFrameStagedTexCoord() { return uniforms.lastFrameStagedTexCoord; }
|
||||
public int statLastFrameStagedLightmap() { return uniforms.lastFrameStagedLightmap; }
|
||||
public int statLastFrameStagedTexGen() { return uniforms.lastFrameStagedTexGen; }
|
||||
public int statLastFrameStagedClipPlanes() { return uniforms.lastFrameStagedClipPlanes; }
|
||||
public int statLastFrameStagedMisc() { return uniforms.lastFrameStagedMisc; }
|
||||
public int statProgramCount() { return cache.getProgramCount(); }
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LineState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.UniformRingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import org.joml.Math;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAllocFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
/**
|
||||
* Maintains the shared {@link FFPUniformBlock} image from GLSM cached state and streams it to the UBO ring.
|
||||
*/
|
||||
public class Uniforms {
|
||||
|
||||
private static final int RING_CAPACITY = 16 * 1024 * 1024;
|
||||
|
||||
private final ByteBuffer staging = memCalloc(FFPUniformBlock.SIZE);
|
||||
private final long stagingAddress = memAddress0(staging);
|
||||
private UniformRingBuffer ring;
|
||||
private boolean bound;
|
||||
private boolean contentChanged;
|
||||
|
||||
private int mvGen = -1, mvLinearGen = -1, projGen = -1, texMatGen = -1;
|
||||
private int lightingGen = -1, fragmentGen = -1, colorGen = -1, normalGen = -1, texCoordGen = -1;
|
||||
private int texGenGen = -1, clipPlaneGen = -1;
|
||||
private float lightmapX = Float.NaN, lightmapY = Float.NaN, lineWidth = Float.NaN;
|
||||
private int viewportX = Integer.MIN_VALUE, viewportY = Integer.MIN_VALUE, viewportWidth = -1, viewportHeight = -1;
|
||||
private int lineStipple = -1;
|
||||
|
||||
int blockWrites;
|
||||
int blockSkips;
|
||||
int stagedMatrices;
|
||||
int stagedLighting;
|
||||
int stagedFragment;
|
||||
int stagedColor;
|
||||
int stagedNormal;
|
||||
int stagedTexCoord;
|
||||
int stagedLightmap;
|
||||
int stagedTexGen;
|
||||
int stagedClipPlanes;
|
||||
int stagedMisc;
|
||||
int lastFrameBlockWrites;
|
||||
int lastFrameBlockSkips;
|
||||
int lastFrameStagedMatrices;
|
||||
int lastFrameStagedLighting;
|
||||
int lastFrameStagedFragment;
|
||||
int lastFrameStagedColor;
|
||||
int lastFrameStagedNormal;
|
||||
int lastFrameStagedTexCoord;
|
||||
int lastFrameStagedLightmap;
|
||||
int lastFrameStagedTexGen;
|
||||
int lastFrameStagedClipPlanes;
|
||||
int lastFrameStagedMisc;
|
||||
|
||||
private final Matrix4f mvpMatrix = new Matrix4f();
|
||||
private final Matrix3f normalMatrix = new Matrix3f();
|
||||
private final Vector3f tempVec3 = new Vector3f();
|
||||
private final FloatBuffer clipPlaneBuf = memAllocFloat(32); // 8 planes * vec4
|
||||
|
||||
private static final double LN2 = java.lang.Math.log(2.0);
|
||||
private static final double SQRT_LN2 = java.lang.Math.sqrt(LN2);
|
||||
|
||||
public void upload() {
|
||||
boolean dirty = false;
|
||||
contentChanged = false;
|
||||
|
||||
final int mvG = GLStateManager.getMvGeneration();
|
||||
final int projG = GLStateManager.getProjGeneration();
|
||||
final boolean mvChanged = mvG != mvGen;
|
||||
final boolean projChanged = projG != projGen;
|
||||
if (mvChanged || projChanged) {
|
||||
stageMatrices(mvChanged, projChanged);
|
||||
mvGen = mvG;
|
||||
projGen = projG;
|
||||
stagedMatrices++;
|
||||
dirty = true;
|
||||
}
|
||||
final int mvLinG = GLStateManager.getMvLinearGeneration();
|
||||
if (mvLinG != mvLinearGen) {
|
||||
stageNormalMatrix();
|
||||
mvLinearGen = mvLinG;
|
||||
dirty = true;
|
||||
}
|
||||
final int texMatG = GLStateManager.getTexMatrixGeneration();
|
||||
if (texMatG != texMatGen) {
|
||||
stageTextureMatrices();
|
||||
texMatGen = texMatG;
|
||||
dirty = true;
|
||||
}
|
||||
final int litG = GLStateManager.getLightingGeneration();
|
||||
if (litG != lightingGen) {
|
||||
stageLighting();
|
||||
lightingGen = litG;
|
||||
stagedLighting++;
|
||||
dirty = true;
|
||||
}
|
||||
final int colG = GLStateManager.getColorGeneration();
|
||||
if (colG != colorGen) {
|
||||
stageCurrentColor();
|
||||
colorGen = colG;
|
||||
stagedColor++;
|
||||
dirty = true;
|
||||
}
|
||||
final int normG = ShaderManager.getNormalGeneration();
|
||||
if (normG != normalGen) {
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
putVec3(FFPUniformBlock.CURRENT_NORMAL, normal.x, normal.y, normal.z);
|
||||
normalGen = normG;
|
||||
stagedNormal++;
|
||||
dirty = true;
|
||||
}
|
||||
final int tcG = ShaderManager.getTexCoordGeneration();
|
||||
if (tcG != texCoordGen) {
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_0, ShaderManager.getCurrentTexCoord(0));
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_2, ShaderManager.getCurrentTexCoord(2));
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_3, ShaderManager.getCurrentTexCoord(3));
|
||||
texCoordGen = tcG;
|
||||
stagedTexCoord++;
|
||||
dirty = true;
|
||||
}
|
||||
final float brightX = GLSMConfig.lastBrightnessX;
|
||||
final float brightY = GLSMConfig.lastBrightnessY;
|
||||
if (brightX != lightmapX || brightY != lightmapY) {
|
||||
putFloat(FFPUniformBlock.CURRENT_LIGHTMAP_COORD, brightX);
|
||||
putFloat(FFPUniformBlock.CURRENT_LIGHTMAP_COORD + 4, brightY);
|
||||
lightmapX = brightX;
|
||||
lightmapY = brightY;
|
||||
stagedLightmap++;
|
||||
dirty = true;
|
||||
}
|
||||
final int tgG = GLStateManager.getTexGenGeneration();
|
||||
if (tgG != texGenGen) {
|
||||
stageTexGen();
|
||||
texGenGen = tgG;
|
||||
stagedTexGen++;
|
||||
dirty = true;
|
||||
}
|
||||
final int cpG = GLStateManager.getClipPlaneGeneration();
|
||||
if (cpG != clipPlaneGen) {
|
||||
stageClipPlanes();
|
||||
clipPlaneGen = cpG;
|
||||
stagedClipPlanes++;
|
||||
dirty = true;
|
||||
}
|
||||
final int fragG = GLStateManager.getFragmentGeneration();
|
||||
if (fragG != fragmentGen) {
|
||||
stageFragment();
|
||||
fragmentGen = fragG;
|
||||
stagedFragment++;
|
||||
dirty = true;
|
||||
}
|
||||
final float lw = GLStateManager.getLineState().getWidth();
|
||||
if (lw != lineWidth) {
|
||||
putFloat(FFPUniformBlock.LINE_WIDTH, lw);
|
||||
lineWidth = lw;
|
||||
stagedMisc++;
|
||||
dirty = true;
|
||||
}
|
||||
final ViewportState vp = GLStateManager.getViewportState();
|
||||
if (vp.x != viewportX || vp.y != viewportY || vp.width != viewportWidth || vp.height != viewportHeight) {
|
||||
putFloat(FFPUniformBlock.VIEWPORT_SIZE, vp.width);
|
||||
putFloat(FFPUniformBlock.VIEWPORT_SIZE + 4, vp.height);
|
||||
putVec4(FFPUniformBlock.VIEWPORT, vp.x, vp.y, vp.width, vp.height);
|
||||
viewportX = vp.x;
|
||||
viewportY = vp.y;
|
||||
viewportWidth = vp.width;
|
||||
viewportHeight = vp.height;
|
||||
stagedMisc++;
|
||||
dirty = true;
|
||||
}
|
||||
final LineState line = GLStateManager.getLineState();
|
||||
final int stippleFactor = line.getStippleFactor() < 1 ? 1 : line.getStippleFactor();
|
||||
final int stipplePacked = (line.getStipplePattern() & 0xFFFF) | (stippleFactor << 16);
|
||||
if (stipplePacked != lineStipple) {
|
||||
putInt(FFPUniformBlock.LINE_STIPPLE, stipplePacked);
|
||||
lineStipple = stipplePacked;
|
||||
stagedMisc++;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (dirty || !bound) {
|
||||
if (!contentChanged && bound) {
|
||||
blockSkips++;
|
||||
return;
|
||||
}
|
||||
if (ring == null) {
|
||||
ring = new UniformRingBuffer(RING_CAPACITY, FFPUniformBlock.SIZE);
|
||||
}
|
||||
final int offset = ring.writeBlock(staging);
|
||||
GLStateManager.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
|
||||
ring.getBufferId(), offset, FFPUniformBlock.SIZE);
|
||||
bound = true;
|
||||
blockWrites++;
|
||||
}
|
||||
}
|
||||
|
||||
private void stageMatrices(boolean mvChanged, boolean projChanged) {
|
||||
final Matrix4f mv = GLStateManager.getModelViewMatrix();
|
||||
final Matrix4f proj = GLStateManager.getProjectionMatrix();
|
||||
if (mvChanged) {
|
||||
putMat4(FFPUniformBlock.MODEL_VIEW_MATRIX, mv);
|
||||
}
|
||||
if (projChanged) {
|
||||
putMat4(FFPUniformBlock.PROJECTION_MATRIX, proj);
|
||||
}
|
||||
proj.mul(mv, mvpMatrix);
|
||||
putMat4(FFPUniformBlock.MVP_MATRIX, mvpMatrix);
|
||||
}
|
||||
|
||||
private void stageNormalMatrix() {
|
||||
GLStateManager.getModelViewMatrix().normal(normalMatrix);
|
||||
putMat3(FFPUniformBlock.NORMAL_MATRIX, normalMatrix);
|
||||
putFloat(FFPUniformBlock.NORMAL_SCALE, rescaleFactor(normalMatrix, tempVec3));
|
||||
}
|
||||
|
||||
static float rescaleFactor(Matrix3f normalMatrix, Vector3f scratch) {
|
||||
final float f = normalMatrix.getColumn(2, scratch).lengthSquared();
|
||||
return f > 1.0e-12f ? 1.0f / (float) Math.sqrt(f) : 1.0f;
|
||||
}
|
||||
|
||||
private void stageTextureMatrices() {
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_0, GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_2, GLStateManager.getTextures().getTextureUnitMatrix(2));
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_3, GLStateManager.getTextures().getTextureUnitMatrix(3));
|
||||
putMat4(FFPUniformBlock.LIGHTMAP_TEXTURE_MATRIX, GLStateManager.getTextures().getTextureUnitMatrix(1));
|
||||
}
|
||||
|
||||
private void stageLighting() {
|
||||
final MaterialState mat = GLStateManager.getFrontMaterial();
|
||||
final Vector4f lmAmbient = GLStateManager.getLightModel().ambient;
|
||||
final LightState light0 = GLStateManager.getLightDataStates()[0];
|
||||
final LightState light1 = GLStateManager.getLightDataStates()[1];
|
||||
|
||||
putVec4(FFPUniformBlock.LIGHT_MODEL_AMBIENT, lmAmbient);
|
||||
putVec4(FFPUniformBlock.MATERIAL_EMISSION, mat.emission);
|
||||
putVec4(FFPUniformBlock.MATERIAL_AMBIENT, mat.ambient);
|
||||
putVec4(FFPUniformBlock.MATERIAL_DIFFUSE, mat.diffuse);
|
||||
putVec4(FFPUniformBlock.MATERIAL_SPECULAR, mat.specular);
|
||||
putFloat(FFPUniformBlock.MATERIAL_SHININESS, mat.shininess);
|
||||
putVec4(FFPUniformBlock.LIGHT0_AMBIENT, light0.ambient);
|
||||
putVec4(FFPUniformBlock.LIGHT0_DIFFUSE, light0.diffuse);
|
||||
putVec4(FFPUniformBlock.LIGHT0_SPECULAR, light0.specular);
|
||||
putVec4(FFPUniformBlock.LIGHT0_POSITION, light0.position);
|
||||
putVec4(FFPUniformBlock.LIGHT1_AMBIENT, light1.ambient);
|
||||
putVec4(FFPUniformBlock.LIGHT1_DIFFUSE, light1.diffuse);
|
||||
putVec4(FFPUniformBlock.LIGHT1_SPECULAR, light1.specular);
|
||||
putVec4(FFPUniformBlock.LIGHT1_POSITION, light1.position);
|
||||
|
||||
putVec4(FFPUniformBlock.SCENE_COLOR,
|
||||
mat.emission.x + mat.ambient.x * lmAmbient.x,
|
||||
mat.emission.y + mat.ambient.y * lmAmbient.y,
|
||||
mat.emission.z + mat.ambient.z * lmAmbient.z,
|
||||
mat.diffuse.w);
|
||||
putLightProduct(FFPUniformBlock.LIGHT_PROD0_AMBIENT, light0.ambient, mat.ambient);
|
||||
putLightProduct(FFPUniformBlock.LIGHT_PROD0_DIFFUSE, light0.diffuse, mat.diffuse);
|
||||
putLightProduct(FFPUniformBlock.LIGHT_PROD0_SPECULAR, light0.specular, mat.specular);
|
||||
putLightProduct(FFPUniformBlock.LIGHT_PROD1_AMBIENT, light1.ambient, mat.ambient);
|
||||
putLightProduct(FFPUniformBlock.LIGHT_PROD1_DIFFUSE, light1.diffuse, mat.diffuse);
|
||||
putLightProduct(FFPUniformBlock.LIGHT_PROD1_SPECULAR, light1.specular, mat.specular);
|
||||
}
|
||||
|
||||
private void putLightProduct(int offset, Vector4f lightVal, Vector4f materialVal) {
|
||||
putVec3(offset, lightVal.x * materialVal.x, lightVal.y * materialVal.y, lightVal.z * materialVal.z);
|
||||
}
|
||||
|
||||
private void stageCurrentColor() {
|
||||
final var color = GLStateManager.getColor();
|
||||
putVec4(FFPUniformBlock.CURRENT_COLOR,
|
||||
Math.clamp(0f, 1f, color.getRed()),
|
||||
Math.clamp(0f, 1f, color.getGreen()),
|
||||
Math.clamp(0f, 1f, color.getBlue()),
|
||||
Math.clamp(0f, 1f, color.getAlpha()));
|
||||
}
|
||||
|
||||
private void stageTexGen() {
|
||||
final TexGenState tg = GLStateManager.getTextures().getTexGenState(0);
|
||||
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_S, tg.getObjectPlane(GL11.GL_S));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_T, tg.getObjectPlane(GL11.GL_T));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_R, tg.getObjectPlane(GL11.GL_R));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_Q, tg.getObjectPlane(GL11.GL_Q));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_S, tg.getEyePlane(GL11.GL_S));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_T, tg.getEyePlane(GL11.GL_T));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_R, tg.getEyePlane(GL11.GL_R));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_Q, tg.getEyePlane(GL11.GL_Q));
|
||||
}
|
||||
|
||||
private void stageClipPlanes() {
|
||||
final ClipPlaneState cps = GLStateManager.getClipPlaneState();
|
||||
clipPlaneBuf.clear();
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
cps.putEyePlane(i, clipPlaneBuf);
|
||||
}
|
||||
final long src = memAddress0(clipPlaneBuf);
|
||||
for (int i = 0; i < 32 * 4; i += 4) {
|
||||
putInt(FFPUniformBlock.CLIP_PLANES + i, memGetInt(src + i));
|
||||
}
|
||||
}
|
||||
|
||||
private void stageFragment() {
|
||||
putFloat(FFPUniformBlock.ALPHA_REF, GLStateManager.getAlphaState().getReference());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final var envState = GLStateManager.getTextures().getTexEnvState(i);
|
||||
putVec4(FFPUniformBlock.TEX_ENV_COLOR_0 + i * 16,
|
||||
envState.envColorR, envState.envColorG, envState.envColorB, envState.envColorA);
|
||||
}
|
||||
|
||||
putVec4(FFPUniformBlock.OVERLAY_COLOR,
|
||||
GLStateManager.getOverlayR(), GLStateManager.getOverlayG(),
|
||||
GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
|
||||
final var secondary = GLStateManager.getSecondaryColor();
|
||||
putVec3(FFPUniformBlock.SECONDARY_COLOR, secondary.getRed(), secondary.getGreen(), secondary.getBlue());
|
||||
|
||||
// Mesa STATE_FOG_PARAMS_OPTIMIZED
|
||||
final FogState fog = GLStateManager.getFogState();
|
||||
final float start = fog.getStart();
|
||||
final float end = fog.getEnd();
|
||||
final float density = fog.getDensity();
|
||||
final float range = end - start;
|
||||
putVec4(FFPUniformBlock.FOG_PARAMS,
|
||||
range != 0.0f ? -1.0f / range : 0.0f,
|
||||
range != 0.0f ? end / range : 1.0f,
|
||||
(float) (density / LN2),
|
||||
(float) (density / SQRT_LN2));
|
||||
putVec4(FFPUniformBlock.FOG_COLOR,
|
||||
(float) fog.getFogColor().x, (float) fog.getFogColor().y, (float) fog.getFogColor().z,
|
||||
fog.getFogAlpha());
|
||||
}
|
||||
|
||||
private void putFloat(int offset, float v) {
|
||||
final long a = stagingAddress + offset;
|
||||
final int bits = Float.floatToRawIntBits(v);
|
||||
if (memGetInt(a) != bits) contentChanged = true;
|
||||
memPutInt(a, bits);
|
||||
}
|
||||
|
||||
private void putInt(int offset, int v) {
|
||||
final long a = stagingAddress + offset;
|
||||
if (memGetInt(a) != v) contentChanged = true;
|
||||
memPutInt(a, v);
|
||||
}
|
||||
|
||||
private void putMat4(int offset, Matrix4f m) {
|
||||
putVec4(offset, m.m00(), m.m01(), m.m02(), m.m03());
|
||||
putVec4(offset + 16, m.m10(), m.m11(), m.m12(), m.m13());
|
||||
putVec4(offset + 32, m.m20(), m.m21(), m.m22(), m.m23());
|
||||
putVec4(offset + 48, m.m30(), m.m31(), m.m32(), m.m33());
|
||||
}
|
||||
|
||||
/** std140 mat3: three vec4-aligned columns. */
|
||||
private void putMat3(int offset, Matrix3f m) {
|
||||
putVec3(offset, m.m00(), m.m01(), m.m02());
|
||||
putVec3(offset + 16, m.m10(), m.m11(), m.m12());
|
||||
putVec3(offset + 32, m.m20(), m.m21(), m.m22());
|
||||
}
|
||||
|
||||
private void putVec4(int offset, Vector4f v) {
|
||||
putVec4(offset, v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
private void putVec4(int offset, float x, float y, float z, float w) {
|
||||
putFloat(offset, x);
|
||||
putFloat(offset + 4, y);
|
||||
putFloat(offset + 8, z);
|
||||
putFloat(offset + 12, w);
|
||||
}
|
||||
|
||||
private void putVec3(int offset, float x, float y, float z) {
|
||||
putFloat(offset, x);
|
||||
putFloat(offset + 4, y);
|
||||
putFloat(offset + 8, z);
|
||||
}
|
||||
|
||||
private void putPlane(int offset, float[] plane) {
|
||||
putVec4(offset, plane[0], plane[1], plane[2], plane[3]);
|
||||
}
|
||||
|
||||
public void endFrame() {
|
||||
if (ring != null) {
|
||||
ring.endFrame();
|
||||
}
|
||||
lastFrameBlockWrites = blockWrites;
|
||||
lastFrameBlockSkips = blockSkips;
|
||||
lastFrameStagedMatrices = stagedMatrices;
|
||||
lastFrameStagedLighting = stagedLighting;
|
||||
lastFrameStagedFragment = stagedFragment;
|
||||
lastFrameStagedColor = stagedColor;
|
||||
lastFrameStagedNormal = stagedNormal;
|
||||
lastFrameStagedTexCoord = stagedTexCoord;
|
||||
lastFrameStagedLightmap = stagedLightmap;
|
||||
lastFrameStagedTexGen = stagedTexGen;
|
||||
lastFrameStagedClipPlanes = stagedClipPlanes;
|
||||
lastFrameStagedMisc = stagedMisc;
|
||||
blockWrites = 0;
|
||||
blockSkips = 0;
|
||||
stagedMatrices = 0;
|
||||
stagedLighting = 0;
|
||||
stagedFragment = 0;
|
||||
stagedColor = 0;
|
||||
stagedNormal = 0;
|
||||
stagedTexCoord = 0;
|
||||
stagedLightmap = 0;
|
||||
stagedTexGen = 0;
|
||||
stagedClipPlanes = 0;
|
||||
stagedMisc = 0;
|
||||
}
|
||||
|
||||
UniformRingBuffer getRing() {
|
||||
return ring;
|
||||
}
|
||||
|
||||
ByteBuffer getStaging() {
|
||||
return staging;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
memFree(staging);
|
||||
memFree(clipPlaneBuf);
|
||||
if (ring != null) {
|
||||
ring.destroy();
|
||||
ring = null;
|
||||
}
|
||||
bound = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
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<>();
|
||||
|
||||
static final Int2ObjectOpenHashMap<VAOData> eboOwners = 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;
|
||||
if (current.ebo != boundEBO) {
|
||||
removeEboOwner(current.ebo, current);
|
||||
current.ebo = boundEBO;
|
||||
addEboOwner(current.ebo, current);
|
||||
}
|
||||
current.attribs = currentAttribs;
|
||||
current.clientSideEnabledCount = clientSideEnabledCount;
|
||||
}
|
||||
|
||||
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;
|
||||
clientSideEnabledCount = data.clientSideEnabledCount;
|
||||
}
|
||||
|
||||
public static void onDeleteVertexArray(int vaoId) {
|
||||
final VAOData data = vaoMap.remove(vaoId);
|
||||
if (data == null) return;
|
||||
removeEboOwner(data.ebo, data);
|
||||
if (data == current) current = null;
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int buffer) {
|
||||
if (buffer == 0) return;
|
||||
VAOData owner = eboOwners.remove(buffer);
|
||||
while (owner != null) {
|
||||
final VAOData next = owner.nextEboOwner;
|
||||
owner.nextEboOwner = null;
|
||||
owner.ebo = 0;
|
||||
owner = next;
|
||||
}
|
||||
}
|
||||
|
||||
private static void addEboOwner(int ebo, VAOData data) {
|
||||
if (ebo == 0) return;
|
||||
data.nextEboOwner = eboOwners.put(ebo, data);
|
||||
}
|
||||
|
||||
private static void removeEboOwner(int ebo, VAOData data) {
|
||||
if (ebo == 0) return;
|
||||
VAOData node = eboOwners.get(ebo);
|
||||
if (node == data) {
|
||||
final VAOData next = data.nextEboOwner;
|
||||
if (next == null) eboOwners.remove(ebo); else eboOwners.put(ebo, next);
|
||||
data.nextEboOwner = null;
|
||||
return;
|
||||
}
|
||||
while (node != null) {
|
||||
final VAOData next = node.nextEboOwner;
|
||||
if (next == data) {
|
||||
node.nextEboOwner = data.nextEboOwner;
|
||||
data.nextEboOwner = null;
|
||||
return;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
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 int clientSideEnabledCount;
|
||||
private VAOData nextEboOwner;
|
||||
|
||||
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;
|
||||
a.genericPointer = true;
|
||||
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;
|
||||
a.genericPointer = true;
|
||||
if (!wasClient && a.enabled) {
|
||||
clientSideEnabledCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isGenericPointerEnabled(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return false;
|
||||
final Attrib a = currentAttribs[index];
|
||||
return a != null && a.genericPointer && a.enabled;
|
||||
}
|
||||
|
||||
public static void markConventional(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = currentAttribs[index];
|
||||
if (a != null) a.genericPointer = false;
|
||||
}
|
||||
|
||||
public static void setEnabled(int index, boolean enabled) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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 genericPointer;
|
||||
|
||||
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;
|
||||
genericPointer = false;
|
||||
}
|
||||
|
||||
public int effectiveStride() {
|
||||
return (stride != 0) ? stride : size * typeSizeBytes();
|
||||
}
|
||||
|
||||
public int typeSizeBytes() {
|
||||
return GLTypes.sizeBytes(type);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+82
-81
@@ -1,8 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.MatrixHelper;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -10,39 +12,44 @@ import org.lwjgl.opengl.GL12;
|
||||
/**
|
||||
* Compact key capturing the vertex shader permutation axes for FFP emulation.
|
||||
* Packed into a single long for fast hash/compare.
|
||||
*
|
||||
* Per-unit fields are 4-bit base-indexed: bit (BASE + i) for unit i in [0..3].
|
||||
* Unit 1 is the lightmap; texcoord enable for unit 1 also gates the lightmap-specific code paths.
|
||||
*/
|
||||
public final class VertexKey {
|
||||
|
||||
public static final int MAX_UNITS = 4;
|
||||
|
||||
public static final int FFP_LIGHT_COUNT = 2;
|
||||
|
||||
private final long packed;
|
||||
|
||||
private static final int BIT_LIGHTING = 0;
|
||||
private static final int BIT_LIGHT0 = 1;
|
||||
private static final int BIT_LIGHT1 = 2;
|
||||
private static final int BIT_LIGHT0_DIR = 3;
|
||||
private static final int BIT_LIGHT1_DIR = 4;
|
||||
private static final int BIT_COLOR_MATERIAL = 5;
|
||||
private static final int BIT_SEPARATE_SPECULAR = 6;
|
||||
private static final int BIT_FOG = 7;
|
||||
private static final int BIT_FOG_DIST_MODE = 8;
|
||||
private static final int BIT_NORMALIZE = 10;
|
||||
private static final int BIT_RESCALE_NORMAL = 11;
|
||||
private static final int BIT_TEXTURE = 12;
|
||||
private static final int BIT_LIGHTMAP = 13;
|
||||
private static final int BIT_TEX_MATRIX = 14;
|
||||
private static final int BIT_HAS_VERTEX_COLOR = 15;
|
||||
private static final int BIT_HAS_VERTEX_NORMAL = 16;
|
||||
private static final int BIT_HAS_VERTEX_TEX = 17;
|
||||
private static final int BIT_HAS_VERTEX_LIGHTMAP = 18;
|
||||
private static final int BIT_COLOR_MAT_MODE = 19;
|
||||
// bits 19-21: colorMaterialMode (3 bits)
|
||||
// TexGen modes: 3 bits each (0=NONE, 1=OBJ_LINEAR, 2=EYE_LINEAR)
|
||||
private static final int BIT_TEXGEN_S = 22;
|
||||
private static final int BIT_TEXGEN_T = 25;
|
||||
private static final int BIT_TEXGEN_R = 28;
|
||||
private static final int BIT_TEXGEN_Q = 31;
|
||||
// bits 31-33: texGenModeQ (3 bits)
|
||||
private static final int BIT_CLIP_PLANES = 34;
|
||||
private static final int BIT_WIDE_LINE = 35;
|
||||
private static final int BIT_LIGHT_BASE = BIT_LIGHTING + 1;
|
||||
private static final int BIT_LIGHT_DIR_BASE = BIT_LIGHT_BASE + FFP_LIGHT_COUNT;
|
||||
private static final int BIT_COLOR_MATERIAL = BIT_LIGHT_DIR_BASE + FFP_LIGHT_COUNT;
|
||||
private static final int BIT_SEPARATE_SPECULAR = BIT_COLOR_MATERIAL + 1;
|
||||
private static final int BIT_FOG = BIT_SEPARATE_SPECULAR + 1;
|
||||
private static final int BIT_FOG_DIST_MODE = BIT_FOG + 1;
|
||||
private static final int BIT_NORMALIZE = BIT_FOG_DIST_MODE + 2;
|
||||
private static final int BIT_RESCALE_NORMAL = BIT_NORMALIZE + 1;
|
||||
private static final int BIT_UNIT_TEX_BASE = BIT_RESCALE_NORMAL + 1;
|
||||
private static final int BIT_HAS_VERTEX_COLOR = BIT_UNIT_TEX_BASE + MAX_UNITS;
|
||||
private static final int BIT_HAS_VERTEX_NORMAL = BIT_HAS_VERTEX_COLOR + 1;
|
||||
private static final int BIT_HAS_VERTEX_TEX = BIT_HAS_VERTEX_NORMAL + 1;
|
||||
private static final int BIT_HAS_VERTEX_LIGHTMAP = BIT_HAS_VERTEX_TEX + 1;
|
||||
private static final int BIT_COLOR_MAT_MODE = BIT_HAS_VERTEX_LIGHTMAP + 1;
|
||||
// TexGen modes are unit 0 only, for now.
|
||||
private static final int BIT_TEXGEN_S = BIT_COLOR_MAT_MODE + 3;
|
||||
private static final int BIT_TEXGEN_T = BIT_TEXGEN_S + 3;
|
||||
private static final int BIT_TEXGEN_R = BIT_TEXGEN_T + 3;
|
||||
private static final int BIT_TEXGEN_Q = BIT_TEXGEN_R + 3;
|
||||
private static final int BIT_CLIP_PLANES = BIT_TEXGEN_Q + 3;
|
||||
private static final int BIT_WIDE_LINE = BIT_CLIP_PLANES + 1;
|
||||
private static final int BIT_UNIT_TEXMAT_BASE = BIT_WIDE_LINE + 1;
|
||||
private static final int BIT_UNIT23_UV_FROM_UNIT0 = BIT_UNIT_TEXMAT_BASE + MAX_UNITS;
|
||||
private static final int BIT_LINE_STIPPLE = BIT_UNIT23_UV_FROM_UNIT0 + 1;
|
||||
private static final int BIT_INSTANCED = BIT_LINE_STIPPLE + 1;
|
||||
|
||||
public static final int TG_NONE = 0;
|
||||
public static final int TG_OBJ_LINEAR = 1;
|
||||
@@ -61,48 +68,40 @@ public final class VertexKey {
|
||||
public long pack() { return packed; }
|
||||
|
||||
public boolean lightingEnabled() { return bit(BIT_LIGHTING); }
|
||||
public boolean light0Enabled() { return bit(BIT_LIGHT0); }
|
||||
public boolean light1Enabled() { return bit(BIT_LIGHT1); }
|
||||
public boolean light0Directional() { return bit(BIT_LIGHT0_DIR); }
|
||||
public boolean light1Directional() { return bit(BIT_LIGHT1_DIR); }
|
||||
public boolean lightEnabled(int light) { return bit(BIT_LIGHT_BASE + light); }
|
||||
public boolean lightDirectional(int light) { return bit(BIT_LIGHT_DIR_BASE + light); }
|
||||
public boolean colorMaterialEnabled() { return bit(BIT_COLOR_MATERIAL); }
|
||||
public boolean separateSpecular() { return bit(BIT_SEPARATE_SPECULAR); }
|
||||
public boolean fogEnabled() { return bit(BIT_FOG); }
|
||||
public int fogDistanceMode() { return (int)((packed >> BIT_FOG_DIST_MODE) & 0x3); }
|
||||
public boolean normalizeEnabled() { return bit(BIT_NORMALIZE); }
|
||||
public boolean rescaleNormalsEnabled() { return bit(BIT_RESCALE_NORMAL); }
|
||||
public boolean textureEnabled() { return bit(BIT_TEXTURE); }
|
||||
public boolean lightmapEnabled() { return bit(BIT_LIGHTMAP); }
|
||||
public boolean textureMatrixEnabled() { return bit(BIT_TEX_MATRIX); }
|
||||
public boolean unitTexCoordEnabled(int unit) { return bit(BIT_UNIT_TEX_BASE + unit); }
|
||||
public boolean unitTexMatEnabled(int unit) { return bit(BIT_UNIT_TEXMAT_BASE + unit); }
|
||||
public boolean anyUnitTexCoordEnabled() { return ((packed >> BIT_UNIT_TEX_BASE) & 0xF) != 0; }
|
||||
public boolean lightmapEnabled() { return unitTexCoordEnabled(1); }
|
||||
public boolean hasVertexColor() { return bit(BIT_HAS_VERTEX_COLOR); }
|
||||
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); }
|
||||
|
||||
/** TexGen mode for S coordinate (TG_NONE, TG_OBJ_LINEAR, TG_EYE_LINEAR). */
|
||||
/** TexGen mode for S coordinate (TG_NONE, TG_OBJ_LINEAR, TG_EYE_LINEAR). Unit 0 only; see TODO in packFromState. */
|
||||
public int texGenModeS() { return (int)((packed >> BIT_TEXGEN_S) & 0x7); }
|
||||
/** TexGen mode for T coordinate. */
|
||||
public int texGenModeT() { return (int)((packed >> BIT_TEXGEN_T) & 0x7); }
|
||||
/** TexGen mode for R coordinate. */
|
||||
public int texGenModeR() { return (int)((packed >> BIT_TEXGEN_R) & 0x7); }
|
||||
/** TexGen mode for Q coordinate. */
|
||||
public int texGenModeQ() { return (int)((packed >> BIT_TEXGEN_Q) & 0x7); }
|
||||
/** Whether any texgen coordinate is enabled. */
|
||||
public boolean texGenEnabled() { return texGenModeS() != TG_NONE || texGenModeT() != TG_NONE || texGenModeR() != TG_NONE || texGenModeQ() != TG_NONE; }
|
||||
/** Whether any clip plane is enabled. */
|
||||
public boolean clipPlanesEnabled() { return bit(BIT_CLIP_PLANES); }
|
||||
/** Whether wide line GS emulation is active for this variant. */
|
||||
public boolean wideLineEmulation() { return bit(BIT_WIDE_LINE); }
|
||||
public boolean lineStipple() { return bit(BIT_LINE_STIPPLE); }
|
||||
public boolean instancedDraw() { return bit(BIT_INSTANCED); }
|
||||
|
||||
/** Whether vertex color replaces material ambient in this variant. */
|
||||
public boolean cmReplacesAmbient() { final int m = colorMaterialMode(); return m == CM_AMBIENT || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
/** Whether vertex color replaces material diffuse in this variant. */
|
||||
public boolean cmReplacesDiffuse() { final int m = colorMaterialMode(); return m == CM_DIFFUSE || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
/** Whether vertex color replaces material specular in this variant. */
|
||||
public boolean cmReplacesSpecular() { return colorMaterialMode() == CM_SPECULAR; }
|
||||
/** Whether vertex color replaces material emission in this variant. */
|
||||
public boolean cmReplacesEmission() { return colorMaterialMode() == CM_EMISSION; }
|
||||
|
||||
private boolean bit(int pos) { return ((packed >> pos) & 1) != 0; }
|
||||
@@ -126,24 +125,19 @@ public final class VertexKey {
|
||||
};
|
||||
}
|
||||
|
||||
public static long packFromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
public static long packFromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask) {
|
||||
long bits = 0;
|
||||
|
||||
final boolean lighting = GLStateManager.getLightingState().isEnabled();
|
||||
if (lighting) {
|
||||
bits |= (1L << BIT_LIGHTING);
|
||||
|
||||
if (GLStateManager.getLightStates()[0].isEnabled()) {
|
||||
bits |= (1L << BIT_LIGHT0);
|
||||
for (int i = 0; i < FFP_LIGHT_COUNT; i++) {
|
||||
if (!GLStateManager.getLightStates()[i].isEnabled()) continue;
|
||||
bits |= (1L << (BIT_LIGHT_BASE + i));
|
||||
// position.w == 0 means directional
|
||||
if (GLStateManager.getLightDataStates()[0].position.w == 0.0f) {
|
||||
bits |= (1L << BIT_LIGHT0_DIR);
|
||||
}
|
||||
}
|
||||
if (GLStateManager.getLightStates()[1].isEnabled()) {
|
||||
bits |= (1L << BIT_LIGHT1);
|
||||
if (GLStateManager.getLightDataStates()[1].position.w == 0.0f) {
|
||||
bits |= (1L << BIT_LIGHT1_DIR);
|
||||
if (GLStateManager.getLightDataStates()[i].position.w == 0.0f) {
|
||||
bits |= (1L << (BIT_LIGHT_DIR_BASE + i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,23 +165,18 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_RESCALE_NORMAL);
|
||||
}
|
||||
|
||||
// Texture unit 0 (base texture)
|
||||
if (GLStateManager.getTextures().getTextureUnitStates(0).isEnabled()) {
|
||||
bits |= (1L << BIT_TEXTURE);
|
||||
}
|
||||
// Texture unit 1 (lightmap)
|
||||
if (GLStateManager.getTextures().getTextureUnitStates(1).isEnabled()) {
|
||||
bits |= (1L << BIT_LIGHTMAP);
|
||||
final TextureUnitArray texUnit = GLStateManager.getTextures();
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
if ((fragUnitMask & (1 << i)) != 0) {
|
||||
bits |= (1L << (BIT_UNIT_TEX_BASE + i));
|
||||
}
|
||||
if (!MatrixHelper.isIdentity(texUnit.getTextureUnitMatrix(i))) {
|
||||
bits |= (1L << (BIT_UNIT_TEXMAT_BASE + i));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if texture matrix unit 0 is non-identity
|
||||
final Matrix4f texMat = GLStateManager.getTextures().getTextureUnitMatrix(0);
|
||||
if (!DisplayListManager.isIdentity(texMat)) {
|
||||
bits |= (1L << BIT_TEX_MATRIX);
|
||||
}
|
||||
|
||||
// TexGen (unit 0 only) — encode per-coordinate mode if enabled
|
||||
final var texUnit = GLStateManager.getTextures();
|
||||
// TexGen (unit 0 only) - per-coordinate mode if enabled.
|
||||
// TODO: per-unit texgen for units 2/3. Deferred - no MC code path uses glTexGen on those.
|
||||
final TexGenState tg = texUnit.getTexGenState(0);
|
||||
boolean anyTexGen = false;
|
||||
if (texUnit.getTexGenSStates(0).isEnabled() && tg.getMode(GL11.GL_S) != 0) {
|
||||
@@ -210,9 +199,9 @@ public final class VertexKey {
|
||||
bits |= ((long) m & 0x7) << BIT_TEXGEN_Q;
|
||||
if (m != TG_NONE) anyTexGen = true;
|
||||
}
|
||||
// Force texture matrix on when texgen is active (end portal pattern always uses it)
|
||||
// Force unit-0 texture matrix on when texgen is active (end portal pattern always uses it).
|
||||
if (anyTexGen) {
|
||||
bits |= (1L << BIT_TEX_MATRIX);
|
||||
bits |= (1L << BIT_UNIT_TEXMAT_BASE);
|
||||
}
|
||||
|
||||
// Vertex format flags
|
||||
@@ -221,21 +210,31 @@ public final class VertexKey {
|
||||
if (hasTexCoord) bits |= (1L << BIT_HAS_VERTEX_TEX);
|
||||
if (hasLightmap) bits |= (1L << BIT_HAS_VERTEX_LIGHTMAP);
|
||||
|
||||
// Clip planes
|
||||
if (GLStateManager.consumeUnit23TexCoordSetDuringDraw() && hasTexCoord) {
|
||||
bits |= (1L << BIT_UNIT23_UV_FROM_UNIT0);
|
||||
}
|
||||
|
||||
if (GLStateManager.anyClipPlaneEnabled()) {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
}
|
||||
|
||||
// Wide line GS emulation
|
||||
if (GLStateManager.wideLineEmulationActive) {
|
||||
bits |= (1L << BIT_WIDE_LINE);
|
||||
}
|
||||
|
||||
if (GLStateManager.lineStippleActive) {
|
||||
bits |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
if (GLStateManager.instancedFfpDrawActive) {
|
||||
bits |= (1L << BIT_INSTANCED);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
public static VertexKey fromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
return new VertexKey(packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap));
|
||||
public static VertexKey fromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask) {
|
||||
return new VertexKey(packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragUnitMask));
|
||||
}
|
||||
|
||||
static VertexKey fromPacked(long packed) {
|
||||
@@ -256,10 +255,12 @@ public final class VertexKey {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("FFPVertexKey[0x%09X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%b lm=%b col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b]",
|
||||
packed, lightingEnabled(), light0Enabled(), light1Enabled(),
|
||||
colorMaterialEnabled(), fogEnabled(), textureEnabled(), lightmapEnabled(),
|
||||
return String.format("FFPVertexKey[0x%011X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b inst=%b]",
|
||||
packed, lightingEnabled(), lightEnabled(0), lightEnabled(1),
|
||||
colorMaterialEnabled(), fogEnabled(),
|
||||
unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0,
|
||||
unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0,
|
||||
hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(),
|
||||
texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation());
|
||||
texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation(), instancedDraw());
|
||||
}
|
||||
}
|
||||
+80
-135
@@ -29,6 +29,9 @@ public final class VertexShaderGenerator {
|
||||
emitColorPassthrough(sb, key);
|
||||
}
|
||||
emitTexCoordPassthrough(sb, key);
|
||||
if (key.lineStipple()) {
|
||||
emitLineStart(sb);
|
||||
}
|
||||
if (key.fogEnabled()) {
|
||||
emitFogDistance(sb, key);
|
||||
}
|
||||
@@ -42,7 +45,10 @@ public final class VertexShaderGenerator {
|
||||
source = source.replace("v_SpecularColor", "v_SpecularColor_gs")
|
||||
.replace("v_TexCoord0", "v_TexCoord0_gs")
|
||||
.replace("v_TexCoord1", "v_TexCoord1_gs")
|
||||
.replace("v_TexCoord2", "v_TexCoord2_gs")
|
||||
.replace("v_TexCoord3", "v_TexCoord3_gs")
|
||||
.replace("v_FogCoord", "v_FogCoord_gs")
|
||||
.replace("v_LineStart", "v_LineStart_gs")
|
||||
.replace("v_Color", "v_Color_gs");
|
||||
}
|
||||
return source;
|
||||
@@ -63,127 +69,45 @@ public final class VertexShaderGenerator {
|
||||
if (key.hasVertexNormal()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.NORMAL.getAttributeLocation()).append(") in vec3 a_Normal;\n");
|
||||
}
|
||||
if (key.instancedDraw()) {
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL0).append(") in vec4 a_InstCol0;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL1).append(") in vec4 a_InstCol1;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL2).append(") in vec4 a_InstCol2;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL3).append(") in vec4 a_InstCol3;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_COLOR).append(") in vec4 a_InstColor;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_LIGHTMAP).append(") in vec2 a_InstLightmap;\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitLineStart(StringBuilder sb) {
|
||||
sb.append(" vec2 ndc = gl_Position.xy / gl_Position.w;\n");
|
||||
sb.append(" v_LineStart = u_Viewport.xy + (ndc * 0.5 + 0.5) * u_Viewport.zw;\n\n");
|
||||
}
|
||||
|
||||
private static void emitUniforms(StringBuilder sb, VertexKey key) {
|
||||
sb.append("// Matrices\n");
|
||||
sb.append("uniform mat4 u_ModelViewMatrix;\n");
|
||||
sb.append("uniform mat4 u_ProjectionMatrix;\n");
|
||||
sb.append("uniform mat4 u_MVPMatrix;\n");
|
||||
|
||||
if (key.lightingEnabled()) {
|
||||
sb.append("uniform mat3 u_NormalMatrix;\n");
|
||||
}
|
||||
|
||||
if (key.textureMatrixEnabled()) {
|
||||
sb.append("uniform mat4 u_TextureMatrix0;\n");
|
||||
}
|
||||
|
||||
// TexGen plane uniforms
|
||||
if (key.texGenModeS() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneS;\n");
|
||||
if (key.texGenModeS() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneS;\n");
|
||||
if (key.texGenModeT() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneT;\n");
|
||||
if (key.texGenModeT() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneT;\n");
|
||||
if (key.texGenModeR() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneR;\n");
|
||||
if (key.texGenModeR() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneR;\n");
|
||||
if (key.texGenModeQ() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneQ;\n");
|
||||
if (key.texGenModeQ() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneQ;\n");
|
||||
|
||||
if (!key.hasVertexNormal() && key.lightingEnabled()) {
|
||||
sb.append("uniform vec3 u_CurrentNormal;\n");
|
||||
}
|
||||
|
||||
if (!key.hasVertexColor()) {
|
||||
sb.append("uniform vec4 u_CurrentColor;\n");
|
||||
}
|
||||
|
||||
if (!key.hasVertexTexCoord() && key.textureEnabled()) {
|
||||
sb.append("uniform vec4 u_CurrentTexCoord;\n");
|
||||
}
|
||||
|
||||
if (key.lightmapEnabled() && !key.hasVertexLightmap()) {
|
||||
sb.append("uniform vec2 u_CurrentLightmapCoord;\n");
|
||||
}
|
||||
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("uniform mat4 u_LightmapTextureMatrix;\n");
|
||||
}
|
||||
|
||||
if (key.lightingEnabled()) {
|
||||
emitLightingUniforms(sb, key);
|
||||
}
|
||||
|
||||
if (key.lightingEnabled() && (key.normalizeEnabled() || key.rescaleNormalsEnabled())) {
|
||||
// normalize is done in shader; rescale uses normalScale
|
||||
if (key.rescaleNormalsEnabled() && !key.normalizeEnabled()) {
|
||||
sb.append("uniform float u_NormalScale;\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (key.clipPlanesEnabled()) {
|
||||
sb.append("uniform vec4 u_ClipPlane[8];\n");
|
||||
}
|
||||
|
||||
sb.append(FFPUniformBlock.GLSL_DECL);
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitLightingUniforms(StringBuilder sb, VertexKey key) {
|
||||
sb.append("\n// Lighting\n");
|
||||
|
||||
if (key.colorMaterialEnabled()) {
|
||||
// When color material is active, we can't pre-compute light products so we upload raw light and material values
|
||||
sb.append("uniform vec4 u_LightModelAmbient;\n");
|
||||
sb.append("uniform vec4 u_MaterialEmission;\n");
|
||||
sb.append("uniform vec4 u_MaterialAmbient;\n");
|
||||
sb.append("uniform vec4 u_MaterialDiffuse;\n");
|
||||
sb.append("uniform vec4 u_MaterialSpecular;\n");
|
||||
sb.append("uniform float u_MaterialShininess;\n");
|
||||
|
||||
if (key.light0Enabled()) {
|
||||
sb.append("uniform vec4 u_Light0Ambient;\n");
|
||||
sb.append("uniform vec4 u_Light0Diffuse;\n");
|
||||
sb.append("uniform vec4 u_Light0Specular;\n");
|
||||
sb.append("uniform vec4 u_Light0Position;\n");
|
||||
}
|
||||
if (key.light1Enabled()) {
|
||||
sb.append("uniform vec4 u_Light1Ambient;\n");
|
||||
sb.append("uniform vec4 u_Light1Diffuse;\n");
|
||||
sb.append("uniform vec4 u_Light1Specular;\n");
|
||||
sb.append("uniform vec4 u_Light1Position;\n");
|
||||
}
|
||||
} else {
|
||||
// Pre-computed: sceneColor + lightProducts
|
||||
sb.append("uniform vec4 u_SceneColor;\n");
|
||||
|
||||
if (key.light0Enabled()) {
|
||||
sb.append("uniform vec4 u_Light0Position;\n");
|
||||
sb.append("uniform vec3 u_LightProd0Ambient;\n");
|
||||
sb.append("uniform vec3 u_LightProd0Diffuse;\n");
|
||||
sb.append("uniform vec3 u_LightProd0Specular;\n");
|
||||
}
|
||||
if (key.light1Enabled()) {
|
||||
sb.append("uniform vec4 u_Light1Position;\n");
|
||||
sb.append("uniform vec3 u_LightProd1Ambient;\n");
|
||||
sb.append("uniform vec3 u_LightProd1Diffuse;\n");
|
||||
sb.append("uniform vec3 u_LightProd1Specular;\n");
|
||||
}
|
||||
sb.append("uniform float u_MaterialShininess;\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitOutputs(StringBuilder sb, VertexKey key) {
|
||||
sb.append("// Outputs\n");
|
||||
sb.append("out vec4 v_Color;\n");
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
}
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("out vec3 v_SpecularColor;\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
// Per-unit varying outputs. Each enabled texture unit (and texgen, which targets unit 0) gets its own v_TexCoord<i>.
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord0;\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord1;\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("out vec4 v_TexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("out vec4 v_TexCoord3;\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("out float v_FogCoord;\n");
|
||||
}
|
||||
@@ -193,22 +117,30 @@ public final class VertexShaderGenerator {
|
||||
private static void emitPositionTransform(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Position transform\n");
|
||||
sb.append(" vec4 pos4 = vec4(a_Position, 1.0);\n");
|
||||
sb.append(" gl_Position = u_MVPMatrix * pos4;\n");
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" mat4 instMV = mat4(a_InstCol0, a_InstCol1, a_InstCol2, a_InstCol3);\n");
|
||||
sb.append(" vec4 eyePos = instMV * pos4;\n");
|
||||
sb.append(" gl_Position = u_ProjectionMatrix * eyePos;\n");
|
||||
} else {
|
||||
sb.append(" gl_Position = u_MVPMatrix * pos4;\n");
|
||||
|
||||
// Eye position needed for lighting, fog, EYE_LINEAR texgen, and clip planes
|
||||
if (key.lightingEnabled() || key.fogEnabled() || texGenNeedsEyePos(key) || key.clipPlanesEnabled()) {
|
||||
sb.append(" vec4 eyePos = u_ModelViewMatrix * pos4;\n");
|
||||
// Eye position needed for lighting, fog, EYE_LINEAR texgen, and clip planes
|
||||
if (key.lightingEnabled() || key.fogEnabled() || texGenNeedsEyePos(key) || key.clipPlanesEnabled()) {
|
||||
sb.append(" vec4 eyePos = u_ModelViewMatrix * pos4;\n");
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitNormalTransform(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Normal transform\n");
|
||||
if (key.hasVertexNormal()) {
|
||||
sb.append(" vec3 normal = u_NormalMatrix * a_Normal;\n");
|
||||
} else {
|
||||
sb.append(" vec3 normal = u_NormalMatrix * u_CurrentNormal;\n");
|
||||
final String srcNormal = key.hasVertexNormal() ? "a_Normal" : "u_CurrentNormal";
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" vec3 normal = normalize(mat3(instMV) * ").append(srcNormal).append(");\n");
|
||||
sb.append('\n');
|
||||
return;
|
||||
}
|
||||
sb.append(" vec3 normal = u_NormalMatrix * ").append(srcNormal).append(";\n");
|
||||
|
||||
if (key.normalizeEnabled()) {
|
||||
sb.append(" normal = normalize(normal);\n");
|
||||
@@ -225,6 +157,9 @@ public final class VertexShaderGenerator {
|
||||
// Color material: vertex color replaces specific material properties per glColorMaterial mode (Mesa ffvertex_prog.c get_material() bitmask approach)
|
||||
sb.append(" vec4 matColor = ");
|
||||
sb.append(key.hasVertexColor() ? "a_Color" : "u_CurrentColor");
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" * a_InstColor");
|
||||
}
|
||||
sb.append(";\n");
|
||||
|
||||
// Emission source: vertex color for CM_EMISSION, else material uniform
|
||||
@@ -247,11 +182,10 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
|
||||
// Per-light accumulation
|
||||
if (key.light0Enabled()) {
|
||||
emitLightContribution(sb, key, 0);
|
||||
}
|
||||
if (key.light1Enabled()) {
|
||||
emitLightContribution(sb, key, 1);
|
||||
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
|
||||
if (key.lightEnabled(i)) {
|
||||
emitLightContribution(sb, key, i);
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" v_Color = vec4(clamp(color0, 0.0, 1.0), outAlpha);\n");
|
||||
@@ -263,7 +197,7 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitLightContribution(StringBuilder sb, VertexKey key, int lightIndex) {
|
||||
final String li = String.valueOf(lightIndex);
|
||||
final boolean directional = (lightIndex == 0) ? key.light0Directional() : key.light1Directional();
|
||||
final boolean directional = key.lightDirectional(lightIndex);
|
||||
final String posUniform = "u_Light" + li + "Position";
|
||||
|
||||
sb.append(" { // Light ").append(li).append('\n');
|
||||
@@ -320,10 +254,11 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitColorPassthrough(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Color passthrough (no lighting)\n");
|
||||
if (key.hasVertexColor()) {
|
||||
sb.append(" v_Color = a_Color;\n");
|
||||
final String src = key.hasVertexColor() ? "a_Color" : "u_CurrentColor";
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" v_Color = ").append(src).append(" * a_InstColor;\n");
|
||||
} else {
|
||||
sb.append(" v_Color = u_CurrentColor;\n");
|
||||
sb.append(" v_Color = ").append(src).append(";\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
@@ -331,29 +266,39 @@ public final class VertexShaderGenerator {
|
||||
private static void emitTexCoordPassthrough(StringBuilder sb, VertexKey key) {
|
||||
if (key.texGenEnabled()) {
|
||||
emitTexGenCoordGeneration(sb, key);
|
||||
} else if (key.textureEnabled() || key.hasVertexTexCoord()) {
|
||||
sb.append(" // Texture coordinates\n");
|
||||
if (key.hasVertexTexCoord()) {
|
||||
if (key.textureMatrixEnabled()) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * vec4(a_TexCoord0, 0.0, 1.0);\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord0 = vec4(a_TexCoord0, 0.0, 1.0);\n");
|
||||
}
|
||||
} else if (key.unitTexCoordEnabled(0)) {
|
||||
sb.append(" // Texture coordinates - unit 0\n");
|
||||
final String src = key.hasVertexTexCoord()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord0";
|
||||
if (key.unitTexMatEnabled(0)) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * ").append(src).append(";\n");
|
||||
} else {
|
||||
if (key.textureMatrixEnabled()) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * u_CurrentTexCoord;\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord0 = u_CurrentTexCoord;\n");
|
||||
}
|
||||
sb.append(" v_TexCoord0 = ").append(src).append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (key.lightmapEnabled()) {
|
||||
if (key.hasVertexLightmap()) {
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(a_InstLightmap, 0.0, 1.0);\n");
|
||||
} else if (key.hasVertexLightmap()) {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(a_TexCoord1, 0.0, 1.0);\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(u_CurrentLightmapCoord, 0.0, 1.0);\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 2; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (!key.unitTexCoordEnabled(i)) continue;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,7 +312,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)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredAlphaHandler {
|
||||
boolean isAlphaTestLocked();
|
||||
void deferAlphaTestToggle(boolean enabled);
|
||||
void deferAlphaFunc(int function, float reference);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredBlendHandler {
|
||||
boolean isBlendLocked();
|
||||
boolean isOverrideHeld();
|
||||
void deferBlendModeToggle(boolean enabled);
|
||||
void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha);
|
||||
void flushDeferredBlend();
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredDepthColorHandler {
|
||||
boolean isDepthColorLocked();
|
||||
boolean isOverrideHeld();
|
||||
void deferDepthEnable(boolean enabled);
|
||||
void deferColorMask(boolean r, boolean g, boolean b, boolean a);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public final class GLSMConfig {
|
||||
|
||||
// Lightmap (replaces OpenGlHelper.lastBrightnessX/Y)
|
||||
public static float lastBrightnessX;
|
||||
public static float lastBrightnessY;
|
||||
|
||||
public static int packBrightness(float x, float y) {
|
||||
return ((int) y << 16) | ((int) x & 0xFFFF);
|
||||
}
|
||||
|
||||
public static int packedLastBrightness() {
|
||||
return packBrightness(lastBrightnessX, lastBrightnessY);
|
||||
}
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
|
||||
public static boolean extendedAttribsExpected;
|
||||
public static volatile boolean expandVertexFormats;
|
||||
|
||||
private GLSMConfig() {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.events.*;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
|
||||
public final class GLSMHooks {
|
||||
|
||||
public static DeferredBlendHandler blendHandler;
|
||||
public static DeferredAlphaHandler alphaHandler;
|
||||
public static DeferredDepthColorHandler depthColorHandler;
|
||||
public static GlintColorHandler glintColorHandler;
|
||||
public static ImmediateExtendedAttribHandler immediateExtendedHandler;
|
||||
public static ShaderWorkSubmitter shaderWorkSubmitter;
|
||||
public static ShaderTransformPostProcessor postTransformProcessor;
|
||||
public static PerFrameUniformBlock perFrameUniformBlock;
|
||||
public static PerFrameUniformBlock perPassUniformBlock;
|
||||
|
||||
// EventBus instances
|
||||
public static final EventBus<TextureBindEvent> TEXTURE_BIND = EventBus.create(TextureBindEvent.class);
|
||||
public static final EventBus<TextureDeleteEvent> TEXTURE_DELETE = EventBus.create(TextureDeleteEvent.class);
|
||||
public static final EventBus<TextureUnitStateEvent> TEXTURE_UNIT_STATE = EventBus.create(TextureUnitStateEvent.class);
|
||||
public static final EventBus<ProgramChangeEvent> PROGRAM_CHANGE = EventBus.create(ProgramChangeEvent.class);
|
||||
public static final EventBus<ProgramDeleteEvent> PROGRAM_DELETE = EventBus.create(ProgramDeleteEvent.class);
|
||||
public static final EventBus<BlendFuncChangeEvent> BLEND_FUNC_CHANGE = EventBus.create(BlendFuncChangeEvent.class);
|
||||
public static final EventBus<FogStateChangeEvent> FOG_STATE_CHANGE = EventBus.create(FogStateChangeEvent.class);
|
||||
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
|
||||
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
|
||||
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
|
||||
public static final EventBus<ForeignDrawEndEvent> FOREIGN_DRAW_END = EventBus.create(ForeignDrawEndEvent.class);
|
||||
public static final EventBus<VanillaBlendChangeEvent> VANILLA_BLEND_CHANGE = EventBus.create(VanillaBlendChangeEvent.class);
|
||||
public static final EventBus<LoadingCheckpointEvent> LOADING_CHECKPOINT = EventBus.create(LoadingCheckpointEvent.class);
|
||||
|
||||
// Reusable event instances
|
||||
public static final TextureBindEvent textureBindEvent = new TextureBindEvent();
|
||||
public static final TextureDeleteEvent textureDeleteEvent = new TextureDeleteEvent();
|
||||
public static final TextureUnitStateEvent textureUnitStateEvent = new TextureUnitStateEvent();
|
||||
public static final ProgramChangeEvent programChangeEvent = new ProgramChangeEvent();
|
||||
public static final ProgramDeleteEvent programDeleteEvent = new ProgramDeleteEvent();
|
||||
public static final BlendFuncChangeEvent blendFuncChangeEvent = new BlendFuncChangeEvent();
|
||||
public static final FogStateChangeEvent fogStateChangeEvent = new FogStateChangeEvent();
|
||||
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
|
||||
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
|
||||
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
|
||||
public static final ForeignDrawEndEvent foreignDrawEndEvent = new ForeignDrawEndEvent();
|
||||
public static final VanillaBlendChangeEvent vanillaBlendChangeEvent = new VanillaBlendChangeEvent();
|
||||
public static final LoadingCheckpointEvent loadingCheckpointEvent = new LoadingCheckpointEvent();
|
||||
|
||||
private GLSMHooks() {}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class GLSMInitConfig {
|
||||
@Getter private final boolean lwjglDebug;
|
||||
@Getter private final StreamingUploader.UploadStrategy streamingUploadStrategy;
|
||||
@Getter private final Consumer<DirectTessellator> directDrawer;
|
||||
@Getter private final Runnable streamingDrawerDestroy;
|
||||
@Getter private final int displayWidth;
|
||||
@Getter private final int displayHeight;
|
||||
@Getter private final Runnable postInitCallback;
|
||||
@Getter private final boolean DSAEnabled;
|
||||
@Getter private final boolean noErrorChecks;
|
||||
|
||||
private GLSMInitConfig(Builder builder) {
|
||||
this.lwjglDebug = builder.lwjglDebug;
|
||||
this.streamingUploadStrategy = builder.streamingUploadStrategy;
|
||||
this.noErrorChecks = builder.noErrorChecks;
|
||||
this.directDrawer = builder.directDrawer;
|
||||
this.streamingDrawerDestroy = builder.streamingDrawerDestroy;
|
||||
this.displayWidth = builder.displayWidth;
|
||||
this.displayHeight = builder.displayHeight;
|
||||
this.postInitCallback = builder.postInitCallback;
|
||||
this.DSAEnabled = builder.enableDSA;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private boolean lwjglDebug = false;
|
||||
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
|
||||
private Consumer<DirectTessellator> directDrawer = null;
|
||||
private Runnable streamingDrawerDestroy = null;
|
||||
private int displayWidth = 0;
|
||||
private int displayHeight = 0;
|
||||
private Runnable postInitCallback = null;
|
||||
private boolean enableDSA = false;
|
||||
private boolean noErrorChecks;
|
||||
|
||||
private Builder() {}
|
||||
|
||||
public Builder lwjglDebug(boolean lwjglDebug) {
|
||||
this.lwjglDebug = lwjglDebug;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder streamingUploadStrategy(StreamingUploader.UploadStrategy strategy) {
|
||||
this.streamingUploadStrategy = strategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder noErrorChecks(boolean noErrorChecks) {
|
||||
this.noErrorChecks = noErrorChecks;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder directDrawer(Consumer<DirectTessellator> directDrawer) {
|
||||
this.directDrawer = directDrawer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder streamingDrawerDestroy(Runnable streamingDrawerDestroy) {
|
||||
this.streamingDrawerDestroy = streamingDrawerDestroy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder displaySize(int width, int height) {
|
||||
this.displayWidth = width;
|
||||
this.displayHeight = height;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder postInitCallback(Runnable callback) {
|
||||
this.postInitCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enableDSA(boolean enableDSA) {
|
||||
this.enableDSA = enableDSA;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GLSMInitConfig build() {
|
||||
return new GLSMInitConfig(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface GlintColorHandler {
|
||||
void onColorChanged(float red, float green, float blue, float alpha);
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public interface ImmediateExtendedAttribHandler {
|
||||
|
||||
int EXT_STRIDE = 12;
|
||||
int LOC_MC_ENTITY = 11;
|
||||
int LOC_MID_TEX = 12;
|
||||
int LOC_TANGENT = 13;
|
||||
int LOC_MIDBLOCK = 14;
|
||||
|
||||
int RAW_NORMAL_INDEX = 6;
|
||||
|
||||
boolean wantsExtended();
|
||||
|
||||
boolean wantsTangent();
|
||||
|
||||
boolean wantsExtendedCapture();
|
||||
|
||||
void build(int[] rawBuffer, int vertexCount, int vertsPerPrim, int normalIntIndex, long dstAddr, int dstStride);
|
||||
|
||||
void buildPacked(long srcBase, int stride, int posOffset, int texOffset, int normalOffset,
|
||||
int vertexCount, int vertsPerPrim, long dstAddr, int dstStride);
|
||||
|
||||
static int extPrimVerts(int drawMode) {
|
||||
if (drawMode == GL11.GL_QUADS) return 4;
|
||||
if (drawMode == GL11.GL_TRIANGLES) return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extPrimVerts(int drawMode, int vertexCount) {
|
||||
final int p = extPrimVerts(drawMode);
|
||||
return (p != 0 && vertexCount > 0 && vertexCount % p == 0) ? p : 0;
|
||||
}
|
||||
|
||||
static int texOffset(VertexFormat format) {
|
||||
int offset = 0;
|
||||
for (VertexFormatElement element : format.elementsArray) {
|
||||
if (element.getUsage() == VertexFormatElement.Usage.PRIMARY_UV) return offset;
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int normalOffset(VertexFormat format) {
|
||||
int offset = 0;
|
||||
for (VertexFormatElement element : format.elementsArray) {
|
||||
if (element.getUsage() == VertexFormatElement.Usage.NORMAL) return offset;
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setupExtAttribPointers(long baseOffset, int stride) {
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_MID_TEX);
|
||||
GLStateManager.glVertexAttribPointer(LOC_MID_TEX, 2, GL11.GL_FLOAT, false, stride, baseOffset);
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_TANGENT);
|
||||
GLStateManager.glVertexAttribPointer(LOC_TANGENT, 4, GL11.GL_BYTE, true, stride, baseOffset + 8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.UniformType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shared per-frame uniform block for SDL GPU. Produced by Iris once per shader pack load. Every shader declares the
|
||||
* whole block regardless of which members it reads.
|
||||
*/
|
||||
public record PerFrameUniformBlock(List<Member> members) {
|
||||
|
||||
public record Member(String name, UniformType type) {}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return members.isEmpty();
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ShaderTransformPostProcessor {
|
||||
void onTransformed(String transformedSource, ShaderType shaderType);
|
||||
|
||||
default void onTransformed(String transformedSource, GLSLParser.Translation_unitContext bodyTree, int headerLen, ShaderType shaderType) {
|
||||
onTransformed(transformedSource, shaderType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface ShaderWorkSubmitter {
|
||||
<T> CompletableFuture<T> submit(Supplier<T> work);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface VanillaBooleanLayer {
|
||||
|
||||
boolean isOverrideHeld();
|
||||
boolean getVanilla();
|
||||
void setVanilla(boolean enabled);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface VanillaStateLayer<T> {
|
||||
|
||||
boolean isOverrideHeld();
|
||||
void readVanilla(T into);
|
||||
void writeVanilla(T from);
|
||||
|
||||
static boolean isHeld(VanillaStateLayer<?> layer) {
|
||||
return layer != null && layer.isOverrideHeld();
|
||||
}
|
||||
|
||||
static <T> void capture(VanillaStateLayer<T> layer, T slot) {
|
||||
if (isHeld(layer)) layer.readVanilla(slot);
|
||||
}
|
||||
|
||||
static <T> boolean restore(VanillaStateLayer<T> layer, T saved) {
|
||||
if (!isHeld(layer)) return false;
|
||||
layer.writeVanilla(saved);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
/**
|
||||
* AlphaTest changed - Listeners should query GLSM alpha state directly if they care what changed.
|
||||
*/
|
||||
public final class AlphaStateChangeEvent extends MutableEvent {
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class BlendFuncChangeEvent extends MutableEvent {
|
||||
public int srcRgb;
|
||||
public int dstRgb;
|
||||
public int srcAlpha;
|
||||
public int dstAlpha;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
/** FogState changed - Listeners should query GLSM fog state directly if they care what changed. */
|
||||
public final class FogStateChangeEvent extends MutableEvent {}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ForeignDrawEndEvent extends MutableEvent {
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class LightmapCoordsEvent extends MutableEvent {
|
||||
public float x;
|
||||
public float y;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
/** Fired at loading-phase boundaries */
|
||||
public final class LoadingCheckpointEvent extends MutableEvent {
|
||||
public boolean requiresSync;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ProgramChangeEvent extends MutableEvent {
|
||||
public int previousProgram;
|
||||
public int newProgram;
|
||||
public boolean postBind;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ProgramDeleteEvent extends MutableEvent {
|
||||
public int program;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ShaderColorChangeEvent extends MutableEvent {
|
||||
public float red;
|
||||
public float green;
|
||||
public float blue;
|
||||
public float alpha;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class TextureBindEvent extends MutableEvent {
|
||||
public int textureId;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class TextureDeleteEvent extends MutableEvent {
|
||||
public int textureId;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class TextureUnitStateEvent extends MutableEvent {
|
||||
public int unit;
|
||||
public boolean enabled;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class VanillaBlendChangeEvent extends MutableEvent {
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Verifies required classes/resources are present on the classpath at load time.
|
||||
* Fails fast with a descriptive error message if a dependency is missing.
|
||||
*/
|
||||
public final class DependencyVerifier {
|
||||
|
||||
/**
|
||||
* A single dependency check: a classpath resource that must exist.
|
||||
* @param resourcePath Resource path to check (e.g. "/com/example/SomeClass.class")
|
||||
* @param errorMessage Human-readable error message if missing (include download URL)
|
||||
*/
|
||||
public record Check(String resourcePath, String errorMessage) {}
|
||||
|
||||
/**
|
||||
* Verify all checks pass. Throws RuntimeException on first failure.
|
||||
* @param anchor Class whose classloader to use for resource lookups
|
||||
* @param checks Dependency checks to run
|
||||
*/
|
||||
public static void verify(Class<?> anchor, List<Check> checks) {
|
||||
for (Check check : checks) {
|
||||
if (anchor.getResource(check.resourcePath()) == null) {
|
||||
throw new RuntimeException(check.errorMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DependencyVerifier() {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class EarlyRedirectorCore {
|
||||
|
||||
private final GLSMRedirector core;
|
||||
private final String[] targets;
|
||||
private final String[] exclusions;
|
||||
|
||||
public EarlyRedirectorCore(String... extraExclusions) {
|
||||
this.core = new GLSMRedirector();
|
||||
this.targets = EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS.clone();
|
||||
|
||||
final List<String> excl = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
|
||||
if (extraExclusions != null && extraExclusions.length > 0) {
|
||||
Collections.addAll(excl, extraExclusions);
|
||||
}
|
||||
this.exclusions = excl.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public byte[] transform(String transformedName, byte[] basicClass) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
if (!startsWithAny(transformedName, targets)) return basicClass;
|
||||
if (startsWithAny(transformedName, exclusions)) return basicClass;
|
||||
if (!core.shouldTransform(basicClass)) return basicClass;
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
if (core.transformClassNode(transformedName, cn)) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
private static boolean startsWithAny(String name, String[] prefixes) {
|
||||
for (String p : prefixes) {
|
||||
if (name.startsWith(p)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.loading.TransformerNarrower.NarrowRule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class EcosystemNarrowRules {
|
||||
|
||||
public static final List<NarrowRule> ALL = List.of(
|
||||
new NarrowRule("DragonAPI", "Reika.DragonAPI.ASM", List.of(
|
||||
"Reika.DragonAPI.ASM.Patchers",
|
||||
"Reika.DragonAPI.ASM.Profiling",
|
||||
"Reika.DragonAPI.ASM.APIStripper",
|
||||
"Reika.DragonAPI.ASM.ClassReparenter",
|
||||
"Reika.DragonAPI.ASM.DependentMethodStripper",
|
||||
"Reika.DragonAPI.ASM.DragonAPIClassTransformer",
|
||||
"Reika.DragonAPI.ASM.FMLItemBlockPatch",
|
||||
"Reika.DragonAPI.ASM.FluidNamePatch",
|
||||
"Reika.DragonAPI.ASM.InterfaceInjector",
|
||||
"Reika.DragonAPI.ASM.SpecialBiomePlacement",
|
||||
"Reika.DragonAPI.ASM.StructureLootHooks"
|
||||
)),
|
||||
|
||||
new NarrowRule("Xaeros", "xaero.common.core", List.of(
|
||||
"xaero.common.core.transformer"
|
||||
)),
|
||||
|
||||
new NarrowRule("Xaeros", "xaero.map.core", List.of(
|
||||
"xaero.map.core.transformer"
|
||||
)),
|
||||
|
||||
new NarrowRule("AdvancedLightsabers", "com.fiskmods.lightsabers.asm", List.of(
|
||||
"com.fiskmods.lightsabers.asm.ALLoadingPlugin",
|
||||
"com.fiskmods.lightsabers.asm.ASMHooks",
|
||||
"com.fiskmods.lightsabers.asm.transformers"
|
||||
)),
|
||||
|
||||
new NarrowRule("FiskHeroes", "com.fiskmods.heroes.asm", List.of(
|
||||
"com.fiskmods.heroes.asm.SHLoadingPlugin",
|
||||
"com.fiskmods.heroes.asm.transformers"
|
||||
)),
|
||||
|
||||
new NarrowRule("Ears", "com.unascribed.ears", List.of(
|
||||
"com.unascribed.ears.asm",
|
||||
"com.unascribed.ears.common.agent"
|
||||
)),
|
||||
|
||||
new NarrowRule("FoamFix", "pl.asie.foamfix", List.of(
|
||||
"pl.asie.foamfix.bugfixmod",
|
||||
"pl.asie.foamfix.coremod",
|
||||
"pl.asie.foamfix.forkage",
|
||||
"pl.asie.foamfix.ghostbuster",
|
||||
"pl.asie.foamfix.FoamFixMod",
|
||||
"pl.asie.foamfix.HackyMessageFactoryWrapper",
|
||||
"pl.asie.foamfix.LaunchWrapperRuntimeFix",
|
||||
"pl.asie.foamfix.ProxyClient",
|
||||
"pl.asie.foamfix.ProxyCommon",
|
||||
"pl.asie.foamfix.repack.com.unascribed.ears.asm",
|
||||
"pl.asie.foamfix.repack.com.unascribed.ears.common.agent"
|
||||
)),
|
||||
|
||||
new NarrowRule("Alfheim", "alfheim.common.core.asm.hook", List.of(
|
||||
"alfheim.common.core.asm.hook.AlfheimFieldHookHandler",
|
||||
"alfheim.common.core.asm.hook.AlfheimHPHooks",
|
||||
"alfheim.common.core.asm.hook.Botania18AndUpBackport",
|
||||
"alfheim.common.core.asm.hook.ElementalDamageAdapter",
|
||||
"alfheim.common.core.asm.hook.integration",
|
||||
"alfheim.common.core.asm.hook.extender.FurnaceExtender",
|
||||
"alfheim.common.core.asm.hook.extender.ItemAuraRingExtender",
|
||||
"alfheim.common.core.asm.hook.extender.ItemLensExtender",
|
||||
"alfheim.common.core.asm.hook.extender.ItemTwigWandExtender",
|
||||
"alfheim.common.core.asm.hook.extender.LightRelayExtender",
|
||||
"alfheim.common.core.asm.hook.extender.PureDaisyExtender",
|
||||
"alfheim.common.core.asm.hook.extender.QuartzExtender",
|
||||
"alfheim.common.core.asm.hook.extender.RelicHooks",
|
||||
"alfheim.common.core.asm.hook.extender.SparkExtender",
|
||||
"alfheim.common.core.asm.hook.fixes.BotaniaGlowingRenderFixes",
|
||||
"alfheim.common.core.asm.hook.fixes.CorporeaInputFix",
|
||||
"alfheim.common.core.asm.hook.fixes.GodAttributesHooks",
|
||||
"alfheim.common.core.asm.hook.fixes.RecipeAncientWillsFix"
|
||||
)),
|
||||
|
||||
new NarrowRule("LegendsMod", "com.tihyo.legends.management.asm", List.of(
|
||||
"com.tihyo.legends.management.asm.LegendsLoadingPlugin",
|
||||
"com.tihyo.legends.management.asm.AbstractTransformer",
|
||||
"com.tihyo.legends.management.asm.ASMHelper",
|
||||
"com.tihyo.legends.management.asm.Transformer"
|
||||
))
|
||||
);
|
||||
|
||||
public static final String[] LWJGL3IFY_EXCLUSIONS_SHARED = {
|
||||
"org.embeddedt.embeddium",
|
||||
"org.taumc.celeritas",
|
||||
"com.mitchej123.lwjgl.lwjgl3",
|
||||
"com.mitchej123.glsm",
|
||||
"com.gtnewhorizons.angelica.lwjgl3",
|
||||
};
|
||||
|
||||
public static final String[] EARLY_REDIRECTOR_TARGETS = {
|
||||
"cn.tesseract.mycelium.",
|
||||
};
|
||||
|
||||
private EcosystemNarrowRules() {}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Narrows overly-broad transformer exclusions that prevent the GLSM redirector from
|
||||
* transforming GL calls in mod code. Some coremods register exclusions covering their
|
||||
* entire package tree, which inadvertently shields rendering code from redirection.
|
||||
* <p>
|
||||
* This utility removes the broad exclusion and re-adds narrower ones that cover only
|
||||
* the coremod's own transformer/ASM classes, leaving rendering code exposed.
|
||||
* <p>
|
||||
* Operates on a {@code Set<String>} (the transformer exceptions set from LaunchClassLoader)
|
||||
* and a {@code Map<String, Object>} (the Launch blackboard). Callers extract these from
|
||||
* the Forge/launchwrapper infrastructure.
|
||||
*/
|
||||
public final class TransformerNarrower {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("GLSM");
|
||||
|
||||
/**
|
||||
* A rule that replaces a broad transformer exclusion with narrower ones.
|
||||
* @param name Blackboard/config key suffix. (e.g. "DragonAPI").
|
||||
* @param broad The broad exclusion to remove (e.g. "Reika.DragonAPI.ASM")
|
||||
* @param narrow Narrower exclusions to add in its place
|
||||
*/
|
||||
public record NarrowRule(String name, String broad, List<String> narrow) {}
|
||||
|
||||
/**
|
||||
* Apply narrowing rules to the transformer exceptions set.
|
||||
*
|
||||
* @param exceptions The mutable Set from LaunchClassLoader.transformerExceptions
|
||||
* @param blackboard The Launch.blackboard map (checked for disable flags)
|
||||
* @param keyPrefix Blackboard key prefix for per-rule opt-out (e.g. "angelica" or "umbra").
|
||||
* Rule "DragonAPI" checks key "{prefix}.narrow.DragonAPI". If explicitly
|
||||
* set to Boolean.FALSE, the rule is skipped.
|
||||
* @param callerName Name for log messages (e.g. "Angelica" or "Umbra")
|
||||
* @param rules The narrowing rules to apply
|
||||
* @return number of rules that were applied
|
||||
*/
|
||||
public static int narrow(Set<String> exceptions, Map<String, Object> blackboard,
|
||||
String keyPrefix, String callerName, List<NarrowRule> rules) {
|
||||
int applied = 0;
|
||||
for (NarrowRule rule : rules) {
|
||||
final String key = keyPrefix + ".narrow." + rule.name();
|
||||
if (Boolean.FALSE.equals(blackboard.get(key))) {
|
||||
continue;
|
||||
}
|
||||
if (exceptions.remove(rule.broad())) {
|
||||
for (String narrow : rule.narrow()) {
|
||||
exceptions.add(narrow);
|
||||
}
|
||||
LOGGER.info("[{}] Narrowed {} transformer exclusion to allow GL redirection", callerName, rule.broad());
|
||||
applied++;
|
||||
}
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
private TransformerNarrower() {}
|
||||
}
|
||||
Loaded 100 of 1481 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user