mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 09:44:52 +00:00
Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
580b72829d | ||
|
|
f31161fcc7 | ||
|
|
6e6d90aceb | ||
|
|
d96c10d52f | ||
|
|
ef3f500b1c | ||
|
|
be2f76e23e | ||
|
|
6ef0c77848 | ||
|
|
67656e6342 | ||
|
|
09f1cc7f01 | ||
|
|
c8619a5e27 | ||
|
|
bca80d5518 | ||
|
|
4c0c0970aa | ||
|
|
e1d150f4d5 | ||
|
|
28f62ca04b | ||
|
|
75c5b0f26f | ||
|
|
a62fae73c3 | ||
|
|
16a0e1b8a4 | ||
|
|
91a45c2b69 | ||
|
|
3deec73d3e | ||
|
|
c0ea254d62 | ||
|
|
dea84d4279 | ||
|
|
7a2286f3d5 | ||
|
|
c4c44bd14e | ||
|
|
936d6f82b3 | ||
|
|
6225fcd344 |
No files matched your search
@@ -1,14 +0,0 @@
|
||||
# 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>
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/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"
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,81 +0,0 @@
|
||||
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: [ '*', '!tracy-v*' ]
|
||||
tags: [ '*' ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -12,22 +12,3 @@ 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
|
||||
+2
-10
@@ -1,11 +1,10 @@
|
||||
.gradle
|
||||
.kotlin/
|
||||
.settings
|
||||
/.idea/
|
||||
/.vs/
|
||||
/.vscode/
|
||||
run/
|
||||
build/
|
||||
/run/
|
||||
/build/
|
||||
/eclipse/
|
||||
.classpath
|
||||
.project
|
||||
@@ -13,7 +12,6 @@ build/
|
||||
/config/
|
||||
/crash-reports/
|
||||
/logs/
|
||||
tmp/
|
||||
options.txt
|
||||
/saves/
|
||||
usernamecache.json
|
||||
@@ -39,9 +37,3 @@ 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/
|
||||
@@ -1,55 +0,0 @@
|
||||
# AI Usage Policy
|
||||
|
||||
This project has strict rules for AI usage:
|
||||
|
||||
* **The human-in-the-loop must fully understand all code.** If you
|
||||
can't explain what your changes do and how they interact with the
|
||||
greater system without the aid of AI tools, do not contribute
|
||||
those changes.
|
||||
|
||||
* **Issues and discussions can use AI assistance but must have a full
|
||||
human-in-the-loop.** This means that any content generated with AI
|
||||
must have been reviewed *and edited* by a human before submission.
|
||||
The author must do their research and trim this down.
|
||||
|
||||
* **No AI-generated media is allowed (textures, videos, audio, etc.).**
|
||||
Text and code are the only acceptable AI-generated content, per the
|
||||
other rules in this policy.
|
||||
|
||||
* **Commit messages, PR descriptions must read in your own voice.**
|
||||
No AI-authored or AI co-authored commits, including Co-authored-by AI tags.
|
||||
|
||||
* **Only contribute fixes for bugs you have personally hit in-game.**
|
||||
If you haven’t experienced or reproduced a bug firsthand, and don’t
|
||||
already know the relevant code well, don’t throw AI at it and forward it to us.
|
||||
Patches written for problems the author never actually experienced themselves
|
||||
tend to fix the wrong thing and/or produce another.
|
||||
|
||||
* **The voice and intent of written content must be yours.**
|
||||
For documentation and especially quests, the tone and the point you're trying to make
|
||||
has to come from you, not an AI. Use AI to help with phrasing or cleanup if you like,
|
||||
but if the substance and voice aren't yours, don't contribute it.
|
||||
|
||||
* **Bad AI use may be blocked.** People who produce bad contributions
|
||||
that are clearly AI (slop) may be blocked from making future contributions.
|
||||
We love to help junior developers learn and grow, but if you're interested
|
||||
in that, don't use AI and we'll help you. We're sorry that bad AI
|
||||
users have ruined this for you.
|
||||
|
||||
|
||||
## There are Humans Here
|
||||
|
||||
Please remember that this project is maintained by humans.
|
||||
|
||||
Every discussion, issue, and pull request is read and reviewed by
|
||||
humans (and sometimes machines, too). It is a boundary point at which
|
||||
people interact with each other and work is done. It is rude and
|
||||
disrespectful to approach this boundary with low-effort, unqualified
|
||||
work, since it puts the burden of validation on the maintainer.
|
||||
|
||||
In a perfect world, AI would produce high-quality, accurate work
|
||||
every time. But today, that reality depends on the user of the AI.
|
||||
And today, many users of AI do not use it well. So, until that
|
||||
changes, we need to have strict rules to protect maintainers.
|
||||
|
||||
Based on https://github.com/ghostty-org/ghostty/blob/main/AI_POLICY.md
|
||||
@@ -1,17 +0,0 @@
|
||||
# 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
|
||||
@@ -23,7 +23,7 @@ Targets GL 3.3+ core profile
|
||||
|
||||
# Required Dependencies
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.9.0
|
||||
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
|
||||
|
||||
# Known (temporary) Incompatibilities
|
||||
@@ -50,7 +50,6 @@ 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
|
||||
|
||||
+23
-222
@@ -1,129 +1,33 @@
|
||||
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")
|
||||
}
|
||||
|
||||
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()
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dangelica.enableTestBlocks=true")
|
||||
extraRunJvmArguments.add("-Dangelica.dumpClass=true")
|
||||
// extraRunJvmArguments.addAll("-Dlegacy.debugClassLoadingSave=true")
|
||||
// extraRunJvmArguments.addAll("-Drfb.dumpLoadedClasses=true", "-Drfb.dumpLoadedClassesPerTransformer=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.debug.redirectorLogspam=true")
|
||||
|
||||
//extraRunJvmArguments.add("-Dangelica.redirectorLogspam=true")
|
||||
}
|
||||
|
||||
// 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>") }
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
dependsOn(tasks.extractNatives2)
|
||||
jvmArgs("-Djava.library.path=${tasks.extractNatives2.get().destinationFolder.asFile.get().path}")
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
@@ -142,125 +46,22 @@ 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 {
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
|
||||
dependsOn(downgradeEmbedOnlyForTest)
|
||||
classpath = classpath
|
||||
.minus(embedOnly)
|
||||
.plus(files(downgradeEmbedOnlyForTest.map { it.outputCollection }))
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") { from(files("../gradle/libs.versions.toml")) }
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.10: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"))
|
||||
|
||||
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:CoreTweaks:0.3.3.6-GTNH")
|
||||
// For testing alternative splash screens, can be sourced from https://github.com/MalTeeez/ModernSplash/releases
|
||||
//runtimeOnly(rfg.deobf(files("dependencies/modernsplash-1.7.10-1.2.2-dev.jar")))
|
||||
|
||||
// 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")
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
@@ -1,334 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,669 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
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;
|
||||
|
||||
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.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.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Compat transformation for mod shaders running under core profile.
|
||||
*
|
||||
* <p>Handles:
|
||||
* <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>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>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[ \\t]+(\\d+)(?:[ \\t]+(\\w+))?");
|
||||
|
||||
private static final Pattern DEFINE_PATTERN = Pattern.compile("^\\s*#\\s*define\\s+.+$", Pattern.MULTILINE);
|
||||
|
||||
/** 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",
|
||||
"gl_Vertex", "gl_MultiTexCoord", "gl_TexCoord", "gl_Normal", "ftransform",
|
||||
"texture2D", "texture3D", "texelFetch2D", "texelFetch3D", "textureSize2D",
|
||||
"shadow2D", "gl_FrontLightModelProduct",
|
||||
"gl_LightSource", "gl_FrontMaterial"
|
||||
);
|
||||
|
||||
private static final Pattern NEEDS_TRANSFORM_PATTERN = Pattern.compile(
|
||||
String.join("|", COMPAT_BUILTINS) + "|\\b(?:attribute|varying)\\b"
|
||||
);
|
||||
|
||||
private static final Map<String, String> MATRIX_RENAMES = Map.of(
|
||||
"gl_ModelViewMatrix", "angelica_ModelViewMatrix",
|
||||
"gl_ModelViewMatrixInverse", "angelica_ModelViewMatrixInverse",
|
||||
"gl_ProjectionMatrix", "angelica_ProjectionMatrix",
|
||||
"gl_ProjectionMatrixInverse", "angelica_ProjectionMatrixInverse",
|
||||
"gl_NormalMatrix", "angelica_NormalMatrix"
|
||||
);
|
||||
|
||||
private static final AtomicInteger dumpCounter = new AtomicInteger(0);
|
||||
|
||||
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<>(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();
|
||||
}
|
||||
|
||||
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) {
|
||||
result = fixupVersion(source);
|
||||
} else {
|
||||
final CacheKey key = new CacheKey(source, isFragment);
|
||||
final String cached = cache.get(key);
|
||||
if (cached != null) {
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
public static boolean isCoreShader(String source) {
|
||||
final Matcher vm = VERSION_PATTERN.matcher(source);
|
||||
if (vm.find()) {
|
||||
final int version = Integer.parseInt(vm.group(1));
|
||||
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();
|
||||
}
|
||||
|
||||
private static String transformInternal(String source, boolean isFragment) {
|
||||
final Matcher versionMatcher = VERSION_PATTERN.matcher(source);
|
||||
int declaredVersion = 110;
|
||||
if (versionMatcher.find()) {
|
||||
declaredVersion = Integer.parseInt(versionMatcher.group(1));
|
||||
}
|
||||
|
||||
final int targetVersion = Math.max(declaredVersion, RENDER_BACKEND != null ? RENDER_BACKEND.getMinGLSLVersion() : 330);
|
||||
|
||||
// Pre-parse reserved word renaming - prevents ANTLR parse failures
|
||||
source = GlslTransformUtils.replaceTexture(source);
|
||||
source = GlslTransformUtils.renameReservedWords(source, targetVersion);
|
||||
|
||||
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
|
||||
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)
|
||||
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("uniform angelica_LightSourceParameters angelica_LightSource[2];");
|
||||
transformer.rename("gl_LightSource", "angelica_LightSource");
|
||||
}
|
||||
|
||||
// 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("uniform angelica_MaterialParameters angelica_FrontMaterial;");
|
||||
transformer.rename("gl_FrontMaterial", "angelica_FrontMaterial");
|
||||
}
|
||||
|
||||
// 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) {
|
||||
transformer.injectVariable("out vec4 angelica_FrontColor;");
|
||||
transformer.rename("gl_FrontColor", "angelica_FrontColor");
|
||||
transformer.prependMain("angelica_FrontColor = vec4(1.0);");
|
||||
|
||||
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;");
|
||||
transformer.rename("gl_Color", "angelica_FrontColor");
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
final String qualifier = isFragment ? "in" : "out";
|
||||
transformer.injectVariable(qualifier + " vec4 angelica_TexCoord" + i + ";");
|
||||
}
|
||||
|
||||
// Fragment output handling + alpha test discard
|
||||
if (isFragment) {
|
||||
transformFragmentOutputs(transformer);
|
||||
}
|
||||
|
||||
// texture-as-variable collision handling
|
||||
if (transformer.containsCall("texture") && transformer.hasVariable("texture")) {
|
||||
transformer.rename("texture", "gtexture");
|
||||
}
|
||||
if (transformer.hasVariable("angelica_renamed_texture")) {
|
||||
transformer.rename("angelica_renamed_texture", "gtexture");
|
||||
}
|
||||
|
||||
transformer.renameFunctionCall(GlslTransformUtils.TEXTURE_RENAMES);
|
||||
|
||||
transformer.renameAndWrapShadow("shadow2D", "texture");
|
||||
transformer.renameAndWrapShadow("shadow2DLod", "textureLod");
|
||||
|
||||
final String versionDirective = "#version " + targetVersion + " core\n";
|
||||
final String extensions = VERSION_PATTERN.matcher(GlslTransformUtils.getFormattedShader(parsed.pre(), "")).replaceFirst("").trim();
|
||||
|
||||
// Preserve #define directives
|
||||
final StringBuilder defines = new StringBuilder();
|
||||
final Matcher defineMatcher = DEFINE_PATTERN.matcher(source);
|
||||
while (defineMatcher.find()) {
|
||||
defines.append(defineMatcher.group().trim()).append('\n');
|
||||
}
|
||||
|
||||
final String header = versionDirective + (extensions.isEmpty() ? "" : "\n" + extensions) + (defines.isEmpty() ? "" : "\n" + defines);
|
||||
final StringBuilder result = new StringBuilder();
|
||||
transformer.mutateTree(tree -> result.append(GlslTransformUtils.getFormattedShader(tree, header)));
|
||||
|
||||
// Restore pre-parse renames
|
||||
String output = GlslTransformUtils.restoreReservedWords(result.toString());
|
||||
|
||||
// Core profile: attribute -> in, varying -> out (vertex) / in (fragment)
|
||||
output = fixupQualifiers(output, isFragment);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject matrix uniforms and rename compat builtins.
|
||||
*/
|
||||
private static void injectMatrixUniforms(Transformer transformer) {
|
||||
transformer.injectVariable("uniform mat4 angelica_ModelViewMatrix;");
|
||||
transformer.injectVariable("uniform mat4 angelica_ModelViewMatrixInverse;");
|
||||
transformer.injectVariable("uniform mat4 angelica_ProjectionMatrix;");
|
||||
transformer.injectVariable("uniform mat4 angelica_ProjectionMatrixInverse;");
|
||||
transformer.injectVariable("uniform mat3 angelica_NormalMatrix;");
|
||||
transformer.injectVariable("uniform mat4 angelica_LightmapTextureMatrix;");
|
||||
|
||||
transformer.rename(MATRIX_RENAMES);
|
||||
|
||||
// Expression replacements
|
||||
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))");
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform fragment outputs for core profile.
|
||||
*/
|
||||
private static void transformFragmentOutputs(Transformer transformer) {
|
||||
if (transformer.containsCall("gl_FragColor")) {
|
||||
transformer.replaceExpression("gl_FragColor", "gl_FragData[0]");
|
||||
}
|
||||
|
||||
final Set<Integer> found = new HashSet<>();
|
||||
transformer.renameArray("gl_FragData", "angelica_FragData", found);
|
||||
|
||||
for (Integer i : found) {
|
||||
transformer.injectVariable("layout (location = " + i + ") out vec4 angelica_FragData" + i + ";");
|
||||
}
|
||||
|
||||
// Core profile: GL_ALPHA_TEST is removed - inject runtime discard using GLSM-tracked alpha reference (uploaded by CompatUniformManager).
|
||||
if (found.contains(0)) {
|
||||
transformer.injectVariable("uniform float angelica_currentAlphaTest;");
|
||||
transformer.appendMain("if (angelica_FragData0.a <= angelica_currentAlphaTest) discard;");
|
||||
}
|
||||
}
|
||||
|
||||
private static void transformFog(Transformer transformer, boolean isFragment, String source) {
|
||||
// Vertex side is unconditional: fragment may read gl_FogFragCoord without vertex writing it
|
||||
transformer.rename("gl_FogFragCoord", "angelica_FogFragCoord");
|
||||
if (!isFragment) {
|
||||
transformer.injectVariable("out float angelica_FogFragCoord;");
|
||||
transformer.prependMain("angelica_FogFragCoord = 0.0;");
|
||||
} else {
|
||||
if (source.contains("gl_FogFragCoord")) {
|
||||
transformer.injectVariable("in float angelica_FogFragCoord;");
|
||||
}
|
||||
}
|
||||
|
||||
transformer.rename("gl_Fog", "angelica_Fog");
|
||||
transformer.injectVariable("uniform float angelica_FogDensity;");
|
||||
transformer.injectVariable("uniform float angelica_FogStart;");
|
||||
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;");
|
||||
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, 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 (ffp.color()) {
|
||||
transformer.injectVariable("layout(location = 1) in vec4 angelica_Color;");
|
||||
transformer.rename("gl_Color", "angelica_Color");
|
||||
}
|
||||
if (ffp.uv0()) {
|
||||
transformer.injectVariable("layout(location = 2) in vec4 angelica_MultiTexCoord0;");
|
||||
transformer.rename("gl_MultiTexCoord0", "angelica_MultiTexCoord0");
|
||||
}
|
||||
if (ffp.uv1()) {
|
||||
transformer.injectVariable("layout(location = 3) in vec4 angelica_MultiTexCoord1;");
|
||||
transformer.rename("gl_MultiTexCoord1", "angelica_MultiTexCoord1");
|
||||
}
|
||||
if (ffp.normal()) {
|
||||
transformer.injectVariable("layout(location = 4) in vec3 angelica_Normal;");
|
||||
transformer.rename("gl_Normal", "angelica_Normal");
|
||||
}
|
||||
if (source.contains("ftransform")) {
|
||||
transformer.replaceExpression("ftransform()", "(angelica_ProjectionMatrix * angelica_ModelViewMatrix * angelica_Vertex)");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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(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(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) {
|
||||
GLStateManager.LOGGER.warn("Failed to dump compat shader: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static String identifyCaller() {
|
||||
for (StackTraceElement frame : Thread.currentThread().getStackTrace()) {
|
||||
final String cls = frame.getClassName();
|
||||
if (!cls.startsWith("com.gtnewhorizons.angelica.glsm.")
|
||||
&& !cls.startsWith("java.")
|
||||
&& !cls.equals("org.lwjgl.opengl.GL20")) {
|
||||
return cls + "." + frame.getMethodName() + ":" + frame.getLineNumber();
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("\\battribute\\b");
|
||||
private static final Pattern VARYING_PATTERN = Pattern.compile("\\bvarying\\b");
|
||||
|
||||
/** Replace legacy storage qualifiers removed in core profile. Safe on AST-serialized output (no comments). */
|
||||
public static String fixupQualifiers(String source, boolean isFragment) {
|
||||
source = ATTRIBUTE_PATTERN.matcher(source).replaceAll("in");
|
||||
source = VARYING_PATTERN.matcher(source).replaceAll(isFragment ? "in" : "out");
|
||||
return source;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param fragmentSource the transformed fragment shader source
|
||||
* @return a complete vertex shader source string
|
||||
*/
|
||||
public static String generatePassthroughVertexShader(String fragmentSource) {
|
||||
final StringBuilder sb = new StringBuilder(512);
|
||||
sb.append("#version 330 core\n\n");
|
||||
|
||||
// Always have position
|
||||
sb.append("layout(location = 0) in vec4 a_Position;\n");
|
||||
|
||||
// Matrix uniforms for position transform
|
||||
sb.append("uniform mat4 angelica_ModelViewMatrix;\n");
|
||||
sb.append("uniform mat4 angelica_ProjectionMatrix;\n");
|
||||
|
||||
final StringBuilder varyings = new StringBuilder();
|
||||
final StringBuilder assignments = new StringBuilder();
|
||||
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 Map<Integer, String> texCoordTypes = new HashMap<>();
|
||||
while (texCoordMatcher.find()) {
|
||||
texCoordTypes.putIfAbsent(Integer.parseInt(texCoordMatcher.group(2)), texCoordMatcher.group(1));
|
||||
}
|
||||
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 ").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");
|
||||
}
|
||||
|
||||
if (FRAG_IN_FOGCOORD.matcher(fragmentSource).find()) {
|
||||
varyings.append("out float angelica_FogFragCoord;\n");
|
||||
assignments.append(" angelica_FogFragCoord = abs((angelica_ModelViewMatrix * a_Position).z);\n");
|
||||
}
|
||||
|
||||
sb.append(varyings);
|
||||
sb.append("\nvoid main() {\n");
|
||||
sb.append(" gl_Position = angelica_ProjectionMatrix * angelica_ModelViewMatrix * a_Position;\n");
|
||||
sb.append(assignments);
|
||||
sb.append("}\n");
|
||||
|
||||
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);
|
||||
if (!m.find()) {
|
||||
return "#version 330 core\n" + source;
|
||||
}
|
||||
|
||||
final int version = Integer.parseInt(m.group(1));
|
||||
final String profile = m.group(2);
|
||||
|
||||
if (version >= 330 && !"compatibility".equals(profile)) {
|
||||
if ("core".equals(profile)) return source;
|
||||
return m.replaceFirst("#version " + version + " core");
|
||||
}
|
||||
|
||||
final int targetVersion = Math.max(version, 330);
|
||||
return m.replaceFirst("#version " + targetVersion + " core");
|
||||
}
|
||||
}
|
||||
@@ -1,452 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.FogStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.LightStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
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 java.nio.FloatBuffer;
|
||||
|
||||
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.
|
||||
*/
|
||||
public class CompatUniformManager {
|
||||
|
||||
// Uniform location indices within the cached array (package-private for tests)
|
||||
static final int LOC_MODELVIEW = 0;
|
||||
static final int LOC_MODELVIEW_INVERSE = 1;
|
||||
static final int LOC_PROJECTION = 2;
|
||||
static final int LOC_PROJECTION_INVERSE = 3;
|
||||
static final int LOC_NORMAL = 4;
|
||||
static final int LOC_LIGHTMAP_TEXTURE_MATRIX = 5;
|
||||
static final int LOC_FOG_DENSITY = 6;
|
||||
static final int LOC_FOG_START = 7;
|
||||
static final int LOC_FOG_END = 8;
|
||||
static final int LOC_FOG_COLOR = 9;
|
||||
static final int LOC_IRIS_MODELVIEW = 10;
|
||||
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_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;
|
||||
static final int LF_AMBIENT = 0, LF_DIFFUSE = 1, LF_SPECULAR = 2, LF_POSITION = 3;
|
||||
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 = 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; // 43
|
||||
|
||||
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",
|
||||
"spotDirection", "spotExponent", "spotCutoff", "spotCosCutoff",
|
||||
"constantAttenuation", "linearAttenuation", "quadraticAttenuation",
|
||||
};
|
||||
private static final String[] MAT_FIELD_NAMES = {
|
||||
"emission", "ambient", "diffuse", "specular", "shininess",
|
||||
};
|
||||
|
||||
private static final String[] UNIFORM_NAMES;
|
||||
|
||||
static {
|
||||
UNIFORM_NAMES = new String[LOC_COUNT];
|
||||
UNIFORM_NAMES[LOC_MODELVIEW] = "angelica_ModelViewMatrix";
|
||||
UNIFORM_NAMES[LOC_MODELVIEW_INVERSE] = "angelica_ModelViewMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_PROJECTION] = "angelica_ProjectionMatrix";
|
||||
UNIFORM_NAMES[LOC_PROJECTION_INVERSE] = "angelica_ProjectionMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_NORMAL] = "angelica_NormalMatrix";
|
||||
UNIFORM_NAMES[LOC_LIGHTMAP_TEXTURE_MATRIX] = "angelica_LightmapTextureMatrix";
|
||||
UNIFORM_NAMES[LOC_FOG_DENSITY] = "angelica_FogDensity";
|
||||
UNIFORM_NAMES[LOC_FOG_START] = "angelica_FogStart";
|
||||
UNIFORM_NAMES[LOC_FOG_END] = "angelica_FogEnd";
|
||||
UNIFORM_NAMES[LOC_FOG_COLOR] = "angelica_FogColor";
|
||||
UNIFORM_NAMES[LOC_IRIS_MODELVIEW] = "iris_ModelViewMatrix";
|
||||
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];
|
||||
}
|
||||
}
|
||||
for (int fi = 0; fi < MAT_FIELDS; fi++) {
|
||||
UNIFORM_NAMES[LOC_MAT_BASE + fi] = "angelica_FrontMaterial." + MAT_FIELD_NAMES[fi];
|
||||
}
|
||||
}
|
||||
|
||||
/** 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);
|
||||
private static final FloatBuffer mat3Buf = BufferUtils.createFloatBuffer(9);
|
||||
private static final FloatBuffer vec4Buf = BufferUtils.createFloatBuffer(4);
|
||||
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);
|
||||
}
|
||||
|
||||
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] = RENDER_BACKEND.getUniformLocation(program, UNIFORM_NAMES[i]);
|
||||
if (locs[i] != -1) hasAny = true;
|
||||
}
|
||||
|
||||
if (hasAny) {
|
||||
programUniforms.put(program, new ProgramUniforms(locs));
|
||||
GLStateManager.LOGGER.debug("CompatUniformManager: program {} has compat uniforms", program);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// 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, 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.getFragmentGeneration();
|
||||
if (fragGen != pu.lastFragmentGen) {
|
||||
pu.lastFragmentGen = fragGen;
|
||||
uploadFragmentUniforms(locs);
|
||||
}
|
||||
|
||||
// Lighting-derived uniforms (scene color, light sources, material)
|
||||
final int litGen = GLStateManager.getLightingGeneration();
|
||||
if (litGen != pu.lastLightingGen) {
|
||||
pu.lastLightingGen = litGen;
|
||||
if (locs[LOC_SCENE_COLOR] != -1) uploadSceneColor(locs);
|
||||
uploadLightSources(locs);
|
||||
uploadMaterial(locs);
|
||||
}
|
||||
|
||||
// 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.getClipPlaneGeneration();
|
||||
if (cpGen != pu.lastClipPlaneGen) {
|
||||
pu.lastClipPlaneGen = cpGen;
|
||||
uploadClipPlanes(locs);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void uploadMatrices(int[] locs, boolean mvChanged, boolean mvLinearChanged, boolean projChanged, boolean texMatChanged) {
|
||||
final Matrix4f mv = GLStateManager.getModelViewMatrix();
|
||||
final Matrix4f proj = GLStateManager.getProjectionMatrix();
|
||||
|
||||
if (mvChanged) {
|
||||
// ModelView
|
||||
if (locs[LOC_MODELVIEW] != -1 || locs[LOC_IRIS_MODELVIEW] != -1) {
|
||||
mv.get(mat4Buf);
|
||||
if (locs[LOC_MODELVIEW] != -1) {
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW], false, mat4Buf);
|
||||
}
|
||||
if (locs[LOC_IRIS_MODELVIEW] != -1) {
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_MODELVIEW], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
|
||||
// ModelView Inverse
|
||||
if (locs[LOC_MODELVIEW_INVERSE] != -1 || locs[LOC_IRIS_MODELVIEW_INVERSE] != -1) {
|
||||
mv.invert(scratchMatrix);
|
||||
scratchMatrix.get(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);
|
||||
if (locs[LOC_NORMAL] != -1) {
|
||||
normalMatrix.get(mat3Buf);
|
||||
RENDER_BACKEND.uniformMatrix3(locs[LOC_NORMAL], false, mat3Buf);
|
||||
}
|
||||
// Column-normalized to unit magnitude
|
||||
if (locs[LOC_IRIS_NORMAL] != -1) {
|
||||
normalizeColumnsInPlace(normalMatrix);
|
||||
normalMatrix.get(mat3Buf);
|
||||
RENDER_BACKEND.uniformMatrix3(locs[LOC_IRIS_NORMAL], false, mat3Buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (projChanged) {
|
||||
// Projection
|
||||
if (locs[LOC_PROJECTION] != -1 || locs[LOC_IRIS_PROJECTION] != -1) {
|
||||
proj.get(mat4Buf);
|
||||
if (locs[LOC_PROJECTION] != -1) {
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_PROJECTION], false, mat4Buf);
|
||||
}
|
||||
if (locs[LOC_IRIS_PROJECTION] != -1) {
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_PROJECTION], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
|
||||
// Projection Inverse
|
||||
if (locs[LOC_PROJECTION_INVERSE] != -1 || locs[LOC_IRIS_PROJECTION_INVERSE] != -1) {
|
||||
proj.invert(scratchMatrix);
|
||||
scratchMatrix.get(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) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadFragmentUniforms(int[] locs) {
|
||||
// Fog uniforms
|
||||
FogStateStack fog = GLStateManager.getFogState();
|
||||
if (locs[LOC_FOG_DENSITY] != -1) {
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_DENSITY], Math.max(0.0f, fog.getDensity()));
|
||||
}
|
||||
if (locs[LOC_FOG_START] != -1) {
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_START], fog.getStart());
|
||||
}
|
||||
if (locs[LOC_FOG_END] != -1) {
|
||||
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.flip();
|
||||
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) {
|
||||
ref = -1.0f;
|
||||
} else {
|
||||
ref = GLStateManager.getAlphaState().getReference();
|
||||
}
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_ALPHA_TEST_REF], ref);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
final LightModelState lm = GLStateManager.getLightModel();
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put(mat.emission.x + mat.ambient.x * lm.ambient.x)
|
||||
.put(mat.emission.y + mat.ambient.y * lm.ambient.y)
|
||||
.put(mat.emission.z + mat.ambient.z * lm.ambient.z)
|
||||
.put(mat.diffuse.w);
|
||||
vec4Buf.flip();
|
||||
RENDER_BACKEND.uniform4(locs[LOC_SCENE_COLOR], vec4Buf);
|
||||
}
|
||||
|
||||
private static void uploadLightSources(int[] locs) {
|
||||
// Early exit: if the first light field isn't present, no lighting uniforms exist
|
||||
if (locs[LOC_LIGHT_BASE] == -1) return;
|
||||
final LightStateStack[] lights = GLStateManager.getLightDataStates();
|
||||
for (int li = 0; li < 2; li++) {
|
||||
final int base = LOC_LIGHT_BASE + li * LIGHT_FIELDS;
|
||||
final LightState light = lights[li];
|
||||
|
||||
if (locs[base + LF_AMBIENT] != -1)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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) {
|
||||
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;
|
||||
hz = light.position.z / len + 1.0f;
|
||||
} else {
|
||||
hx = 0; hy = 0; hz = 1;
|
||||
}
|
||||
} else {
|
||||
hx = 0; hy = 0; hz = 1;
|
||||
}
|
||||
final float hlen = (float) Math.sqrt(hx * hx + hy * hy + hz * hz);
|
||||
if (hlen > 0) { hx /= hlen; hy /= hlen; hz /= hlen; }
|
||||
RENDER_BACKEND.uniform4f(locs[base + LF_HALF_VECTOR], hx, hy, hz, 0.0f);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadMaterial(int[] locs) {
|
||||
// Early exit: if the first material field isn't present, no material uniforms exist
|
||||
if (locs[LOC_MAT_BASE] == -1) return;
|
||||
final MaterialState mat = GLStateManager.getFrontMaterial();
|
||||
if (locs[LOC_MAT_BASE + MF_EMISSION] != -1)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_MAT_BASE + MF_SHININESS], mat.shininess);
|
||||
}
|
||||
|
||||
private static void uploadClipPlanes(int[] locs) {
|
||||
if (locs[LOC_CLIP_PLANES_ENABLED] != -1) {
|
||||
RENDER_BACKEND.uniform1i(locs[LOC_CLIP_PLANES_ENABLED], GLStateManager.anyClipPlaneEnabled() ? 1 : 0);
|
||||
}
|
||||
if (locs[LOC_CLIP_PLANES] != -1) {
|
||||
final ClipPlaneState cps = GLStateManager.getClipPlaneState();
|
||||
clipPlaneBuf.clear();
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
cps.putEyePlane(i, clipPlaneBuf);
|
||||
}
|
||||
clipPlaneBuf.flip();
|
||||
RENDER_BACKEND.uniform4(locs[LOC_CLIP_PLANES], clipPlaneBuf);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onDeleteProgram(int program) {
|
||||
programUniforms.remove(program);
|
||||
}
|
||||
|
||||
public static boolean hasProgram(int program) {
|
||||
return programUniforms.containsKey(program);
|
||||
}
|
||||
|
||||
public static int[] getLocations(int program) {
|
||||
final ProgramUniforms pu = programUniforms.get(program);
|
||||
return pu == null ? null : pu.locs;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,397 +0,0 @@
|
||||
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.ViewportState;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Software emulation of GL feedback mode
|
||||
*/
|
||||
public class FeedbackManager {
|
||||
|
||||
private static final int FB_3D = 0x01;
|
||||
private static final int FB_4D = 0x02;
|
||||
private static final int FB_COLOR = 0x04;
|
||||
private static final int FB_TEXTURE = 0x08;
|
||||
|
||||
private static int renderMode = GL11.GL_RENDER;
|
||||
private static FloatBuffer feedbackBuffer;
|
||||
private static int feedbackBufferSize;
|
||||
private static int feedbackMask;
|
||||
private static int feedbackCount;
|
||||
|
||||
private static final Matrix4f mvpMatrix = new Matrix4f();
|
||||
private static final Vector4f clipVec = new Vector4f();
|
||||
|
||||
private static ByteBuffer readbackBuf = ByteBuffer.allocateDirect(256 * 1024).order(ByteOrder.nativeOrder());
|
||||
private static ByteBuffer indexReadbackBuf = ByteBuffer.allocateDirect(4 * 1024).order(ByteOrder.nativeOrder());
|
||||
|
||||
@FunctionalInterface
|
||||
private interface IndexReader {
|
||||
|
||||
int read(int i);
|
||||
}
|
||||
|
||||
public static boolean isFeedbackMode() {
|
||||
return renderMode == GL11.GL_FEEDBACK;
|
||||
}
|
||||
|
||||
public static void glFeedbackBuffer(int type, FloatBuffer buffer) {
|
||||
if (renderMode == GL11.GL_FEEDBACK) {
|
||||
GLStateManager.LOGGER.error("glFeedbackBuffer called while already in feedback mode");
|
||||
return;
|
||||
}
|
||||
feedbackBuffer = buffer;
|
||||
feedbackBufferSize = buffer.remaining();
|
||||
feedbackMask = computeMask(type);
|
||||
feedbackCount = 0;
|
||||
}
|
||||
|
||||
public static int glRenderMode(int mode) {
|
||||
int result = switch (renderMode) {
|
||||
case GL11.GL_FEEDBACK -> (feedbackCount > feedbackBufferSize) ? -1 : feedbackCount;
|
||||
case GL11.GL_RENDER, GL11.GL_SELECT -> 0;
|
||||
default -> 0;
|
||||
};
|
||||
|
||||
if (mode == GL11.GL_SELECT) {
|
||||
GLStateManager.LOGGER.warn("glRenderMode(GL_SELECT): selection mode not emulated");
|
||||
}
|
||||
|
||||
feedbackCount = 0;
|
||||
renderMode = mode;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void glPassThrough(float token) {
|
||||
if (renderMode == GL11.GL_FEEDBACK) {
|
||||
feedbackToken(GL11.GL_PASS_THROUGH_TOKEN);
|
||||
feedbackToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
private static void feedbackToken(float token) {
|
||||
if (feedbackCount < feedbackBufferSize) {
|
||||
feedbackBuffer.put(feedbackBuffer.position() + feedbackCount, token);
|
||||
}
|
||||
feedbackCount++;
|
||||
}
|
||||
|
||||
private static void feedbackVertex(float winX, float winY, float winZ, float winW) {
|
||||
feedbackToken(winX);
|
||||
feedbackToken(winY);
|
||||
if ((feedbackMask & FB_3D) != 0) feedbackToken(winZ);
|
||||
if ((feedbackMask & FB_4D) != 0) feedbackToken(winW);
|
||||
if ((feedbackMask & FB_COLOR) != 0) {
|
||||
feedbackToken(1f);
|
||||
feedbackToken(1f);
|
||||
feedbackToken(1f);
|
||||
feedbackToken(1f);
|
||||
}
|
||||
if ((feedbackMask & FB_TEXTURE) != 0) {
|
||||
feedbackToken(0f);
|
||||
feedbackToken(0f);
|
||||
feedbackToken(0f);
|
||||
feedbackToken(1f);
|
||||
}
|
||||
}
|
||||
|
||||
private static void transformAndEmitVertex(float x, float y, float z) {
|
||||
clipVec.set(x, y, z, 1.0f);
|
||||
mvpMatrix.transform(clipVec);
|
||||
|
||||
final float w = clipVec.w;
|
||||
final float ndcX = clipVec.x / w;
|
||||
final float ndcY = clipVec.y / w;
|
||||
final float ndcZ = clipVec.z / w;
|
||||
|
||||
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 winW = 1.0f / w;
|
||||
|
||||
feedbackVertex(winX, winY, winZ, winW);
|
||||
}
|
||||
|
||||
private static int positionStride() {
|
||||
final int s = VAOManager.getAttribEffectiveStride(0);
|
||||
return (s != 0) ? s : 12;
|
||||
}
|
||||
|
||||
private static long positionOffset() {
|
||||
return VAOManager.getAttribOffset(0);
|
||||
}
|
||||
|
||||
public static void processDrawArrays(int mode, int first, int count) {
|
||||
computeMVP();
|
||||
final int stride = positionStride();
|
||||
final long posOffset = positionOffset();
|
||||
final int byteCount = count * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
readPositionBuffer((long) first * stride + posOffset, readbackBuf);
|
||||
|
||||
emitAllPrimitives(mode, count, i -> i, stride);
|
||||
}
|
||||
|
||||
public static void processDrawElements(int mode, int indexCount, int type, long eboOffset) {
|
||||
computeMVP();
|
||||
final int stride = positionStride();
|
||||
|
||||
final int elementSize = GLTypes.sizeBytes(type);
|
||||
final int indexByteCount = indexCount * elementSize;
|
||||
ensureIndexReadbackCapacity(indexByteCount);
|
||||
indexReadbackBuf.clear().limit(indexByteCount);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, eboOffset, indexReadbackBuf);
|
||||
final ByteBuffer indexBuf = indexReadbackBuf;
|
||||
|
||||
int minIdx = Integer.MAX_VALUE, maxIdx = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < indexCount; i++) {
|
||||
final int idx = ImmediateModeRecorder.readIndex(indexBuf, type, 0, i);
|
||||
if (idx < minIdx) minIdx = idx;
|
||||
if (idx > maxIdx) maxIdx = idx;
|
||||
}
|
||||
|
||||
final long posOffset = positionOffset();
|
||||
final int vertexCount = maxIdx - minIdx + 1;
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf);
|
||||
|
||||
final int base = minIdx;
|
||||
emitAllPrimitives(mode, indexCount, i -> ImmediateModeRecorder.readIndex(indexBuf, type, 0, i) - base, stride);
|
||||
}
|
||||
|
||||
public static void processDrawElements(int mode, ByteBuffer indices) {
|
||||
final int basePos = indices.position();
|
||||
processDrawElementsImpl(mode, indices.remaining(), i -> indices.get(basePos + i) & 0xFF);
|
||||
}
|
||||
|
||||
public static void processDrawElements(int mode, ShortBuffer indices) {
|
||||
final int basePos = indices.position();
|
||||
processDrawElementsImpl(mode, indices.remaining(), i -> indices.get(basePos + i) & 0xFFFF);
|
||||
}
|
||||
|
||||
public static void processDrawElements(int mode, IntBuffer indices) {
|
||||
final int basePos = indices.position();
|
||||
processDrawElementsImpl(mode, indices.remaining(), i -> indices.get(basePos + i));
|
||||
}
|
||||
|
||||
public static void processDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
final int pos = indices.position();
|
||||
final IndexReader reader = switch (type) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> i -> indices.get(pos + i) & 0xFF;
|
||||
case GL11.GL_UNSIGNED_SHORT -> i -> indices.getShort(pos + i * 2) & 0xFFFF;
|
||||
case GL11.GL_UNSIGNED_INT -> i -> indices.getInt(pos + i * 4);
|
||||
default -> i -> 0;
|
||||
};
|
||||
processDrawElementsImpl(mode, count, reader);
|
||||
}
|
||||
|
||||
private static void processDrawElementsImpl(int mode, int count, IndexReader reader) {
|
||||
computeMVP();
|
||||
final int stride = positionStride();
|
||||
|
||||
int minIdx = Integer.MAX_VALUE, maxIdx = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int idx = reader.read(i);
|
||||
if (idx < minIdx) minIdx = idx;
|
||||
if (idx > maxIdx) maxIdx = idx;
|
||||
}
|
||||
|
||||
readVerticesFromVBO(minIdx, maxIdx, stride);
|
||||
final int base = minIdx;
|
||||
emitAllPrimitives(mode, count, i -> reader.read(i) - base, stride);
|
||||
}
|
||||
|
||||
private static void computeMVP() {
|
||||
mvpMatrix.set(GLStateManager.getProjectionMatrix()).mul(GLStateManager.getModelViewMatrix());
|
||||
}
|
||||
|
||||
private static int computeMask(int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_2D -> 0;
|
||||
case GL11.GL_3D -> FB_3D;
|
||||
case GL11.GL_3D_COLOR -> FB_3D | FB_COLOR;
|
||||
case GL11.GL_3D_COLOR_TEXTURE -> FB_3D | FB_COLOR | FB_TEXTURE;
|
||||
case GL11.GL_4D_COLOR_TEXTURE -> FB_3D | FB_4D | FB_COLOR | FB_TEXTURE;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private static void ensureReadbackCapacity(int bytes) {
|
||||
if (readbackBuf.capacity() < bytes) {
|
||||
readbackBuf = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureIndexReadbackCapacity(int bytes) {
|
||||
if (indexReadbackBuf.capacity() < bytes) {
|
||||
indexReadbackBuf = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
}
|
||||
|
||||
private static void readVerticesFromVBO(int minIdx, int maxIdx, int stride) {
|
||||
final long posOffset = positionOffset();
|
||||
final int vertexCount = maxIdx - minIdx + 1;
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
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) {
|
||||
final float x = readbackBuf.getFloat(vertexIndex * stride);
|
||||
final float y = readbackBuf.getFloat(vertexIndex * stride + 4);
|
||||
final float z = readbackBuf.getFloat(vertexIndex * stride + 8);
|
||||
transformAndEmitVertex(x, y, z);
|
||||
}
|
||||
|
||||
private static void emitAllPrimitives(int mode, int count, IndexReader reader, int stride) {
|
||||
switch (mode) {
|
||||
case GL11.GL_POINTS:
|
||||
for (int i = 0; i < count; i++) {
|
||||
feedbackToken(GL11.GL_POINT_TOKEN);
|
||||
emitVertex(reader.read(i), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_LINES:
|
||||
for (int i = 0; i + 1 < count; i += 2) {
|
||||
feedbackToken(i == 0 ? GL11.GL_LINE_RESET_TOKEN : GL11.GL_LINE_TOKEN);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_LINE_STRIP:
|
||||
for (int i = 0; i + 1 < count; i++) {
|
||||
feedbackToken(i == 0 ? GL11.GL_LINE_RESET_TOKEN : GL11.GL_LINE_TOKEN);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_LINE_LOOP:
|
||||
for (int i = 0; i + 1 < count; i++) {
|
||||
feedbackToken(i == 0 ? GL11.GL_LINE_RESET_TOKEN : GL11.GL_LINE_TOKEN);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
}
|
||||
if (count > 2) {
|
||||
feedbackToken(GL11.GL_LINE_TOKEN);
|
||||
emitVertex(reader.read(count - 1), stride);
|
||||
emitVertex(reader.read(0), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_TRIANGLES:
|
||||
for (int i = 0; i + 2 < count; i += 3) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(3);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
emitVertex(reader.read(i + 2), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_TRIANGLE_STRIP:
|
||||
for (int i = 0; i + 2 < count; i++) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(3);
|
||||
if ((i & 1) == 0) {
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
} else {
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
emitVertex(reader.read(i), stride);
|
||||
}
|
||||
emitVertex(reader.read(i + 2), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_TRIANGLE_FAN:
|
||||
for (int i = 1; i + 1 < count; i++) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(3);
|
||||
emitVertex(reader.read(0), stride);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_QUADS:
|
||||
for (int i = 0; i + 3 < count; i += 4) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(4);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
emitVertex(reader.read(i + 2), stride);
|
||||
emitVertex(reader.read(i + 3), stride);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
static void reset() {
|
||||
renderMode = GL11.GL_RENDER;
|
||||
feedbackBuffer = null;
|
||||
feedbackBufferSize = 0;
|
||||
feedbackMask = 0;
|
||||
feedbackCount = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
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
@@ -1,57 +0,0 @@
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,695 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DebugMessageHandler {
|
||||
void handleMessage(int source, int type, int id, int severity, String message);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
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
@@ -1,532 +0,0 @@
|
||||
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; }
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
public enum VSyncMode {
|
||||
OFF,
|
||||
ON,
|
||||
MAILBOX;
|
||||
|
||||
public boolean tearFree() {
|
||||
return this != OFF;
|
||||
}
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.util.glu.Cylinder;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_LINES;
|
||||
import static org.lwjgl.opengl.GL11.GL_LINE_LOOP;
|
||||
import static org.lwjgl.opengl.GL11.GL_POINTS;
|
||||
import static org.lwjgl.opengl.GL11.GL_TRIANGLE_STRIP;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_FILL;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_INSIDE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_LINE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_POINT;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_SILHOUETTE;
|
||||
|
||||
/**
|
||||
* Core-profile compatible replacement for {@link Cylinder}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AngelicaCylinder extends Cylinder {
|
||||
|
||||
private static final float PI = (float) Math.PI;
|
||||
|
||||
private void emitNormal(float x, float y, float z) {
|
||||
final float mag = (float) Math.sqrt(x * x + y * y + z * z);
|
||||
if (mag > 0.00001F) {
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
z /= mag;
|
||||
}
|
||||
GLStateManager.glNormal3f(x, y, z);
|
||||
}
|
||||
|
||||
private void emitTexCoord(float x, float y) {
|
||||
if (super.textureFlag) GLStateManager.glTexCoord2f(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(float baseRadius, float topRadius, float height, int slices, int stacks) {
|
||||
final float da, dr, dz, nz, nsign;
|
||||
float r, x, y, z;
|
||||
|
||||
nsign = (super.orientation == GLU_INSIDE) ? -1.0f : 1.0f;
|
||||
|
||||
da = 2.0f * PI / slices;
|
||||
dr = (topRadius - baseRadius) / stacks;
|
||||
dz = height / stacks;
|
||||
nz = (baseRadius - topRadius) / height;
|
||||
|
||||
if (super.drawStyle == GLU_POINT) {
|
||||
GLStateManager.glBegin(GL_POINTS);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
x = cos(i * da);
|
||||
y = sin(i * da);
|
||||
emitNormal(x * nsign, y * nsign, nz * nsign);
|
||||
|
||||
z = 0.0f;
|
||||
r = baseRadius;
|
||||
for (int j = 0; j <= stacks; j++) {
|
||||
GLStateManager.glVertex3f(x * r, y * r, z);
|
||||
z += dz;
|
||||
r += dr;
|
||||
}
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
} else if (super.drawStyle == GLU_LINE || super.drawStyle == GLU_SILHOUETTE) {
|
||||
// Draw rings
|
||||
if (super.drawStyle == GLU_LINE) {
|
||||
z = 0.0f;
|
||||
r = baseRadius;
|
||||
for (int j = 0; j <= stacks; j++) {
|
||||
GLStateManager.glBegin(GL_LINE_LOOP);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
x = cos(i * da);
|
||||
y = sin(i * da);
|
||||
emitNormal(x * nsign, y * nsign, nz * nsign);
|
||||
GLStateManager.glVertex3f(x * r, y * r, z);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
z += dz;
|
||||
r += dr;
|
||||
}
|
||||
} else {
|
||||
// draw one ring at each end
|
||||
if (baseRadius != 0.0) {
|
||||
GLStateManager.glBegin(GL_LINE_LOOP);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
x = cos(i * da);
|
||||
y = sin(i * da);
|
||||
emitNormal(x * nsign, y * nsign, nz * nsign);
|
||||
GLStateManager.glVertex3f(x * baseRadius, y * baseRadius, 0.0f);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
GLStateManager.glBegin(GL_LINE_LOOP);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
x = cos(i * da);
|
||||
y = sin(i * da);
|
||||
emitNormal(x * nsign, y * nsign, nz * nsign);
|
||||
GLStateManager.glVertex3f(x * topRadius, y * topRadius, height);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
}
|
||||
// draw length lines
|
||||
GLStateManager.glBegin(GL_LINES);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
x = cos(i * da);
|
||||
y = sin(i * da);
|
||||
emitNormal(x * nsign, y * nsign, nz * nsign);
|
||||
GLStateManager.glVertex3f(x * baseRadius, y * baseRadius, 0.0f);
|
||||
GLStateManager.glVertex3f(x * topRadius, y * topRadius, height);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
} else if (super.drawStyle == GLU_FILL) {
|
||||
final float ds = 1.0f / slices;
|
||||
final float dt = 1.0f / stacks;
|
||||
float t = 0.0f;
|
||||
z = 0.0f;
|
||||
r = baseRadius;
|
||||
for (int j = 0; j < stacks; j++) {
|
||||
float s = 0.0f;
|
||||
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
|
||||
for (int i = 0; i <= slices; i++) {
|
||||
final float angle = (i == slices) ? 0.0f : i * da;
|
||||
x = sin(angle);
|
||||
y = cos(angle);
|
||||
emitNormal(x * nsign, y * nsign, nz * nsign);
|
||||
emitTexCoord(s, t);
|
||||
GLStateManager.glVertex3f(x * r, y * r, z);
|
||||
emitNormal(x * nsign, y * nsign, nz * nsign);
|
||||
emitTexCoord(s, t + dt);
|
||||
GLStateManager.glVertex3f(x * (r + dr), y * (r + dr), z + dz);
|
||||
s += ds;
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
r += dr;
|
||||
t += dt;
|
||||
z += dz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.util.glu.Disk;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_LINE_LOOP;
|
||||
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_STRIP;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_FILL;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_LINE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_NONE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_OUTSIDE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_POINT;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_SILHOUETTE;
|
||||
|
||||
/**
|
||||
* Core-profile compatible replacement for {@link Disk}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AngelicaDisk extends Disk {
|
||||
|
||||
private static final float PI = (float) Math.PI;
|
||||
|
||||
private void emitTexCoord(float x, float y) {
|
||||
if (super.textureFlag) GLStateManager.glTexCoord2f(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(float innerRadius, float outerRadius, int slices, int loops) {
|
||||
final float da, dr;
|
||||
|
||||
if (super.normals != GLU_NONE) {
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, (super.orientation == GLU_OUTSIDE) ? 1.0f : -1.0f);
|
||||
}
|
||||
|
||||
da = 2.0f * PI / slices;
|
||||
dr = (outerRadius - innerRadius) / loops;
|
||||
|
||||
switch (super.drawStyle) {
|
||||
case GLU_FILL -> {
|
||||
final float dtc = 2.0f * outerRadius;
|
||||
float sa, ca;
|
||||
float r1 = innerRadius;
|
||||
for (int l = 0; l < loops; l++) {
|
||||
final float r2 = r1 + dr;
|
||||
if (super.orientation == GLU_OUTSIDE) {
|
||||
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
|
||||
for (int s = 0; s <= slices; s++) {
|
||||
final float a = (s == slices) ? 0.0f : s * da;
|
||||
sa = sin(a);
|
||||
ca = cos(a);
|
||||
emitTexCoord(0.5f + sa * r2 / dtc, 0.5f + ca * r2 / dtc);
|
||||
GLStateManager.glVertex2f(r2 * sa, r2 * ca);
|
||||
emitTexCoord(0.5f + sa * r1 / dtc, 0.5f + ca * r1 / dtc);
|
||||
GLStateManager.glVertex2f(r1 * sa, r1 * ca);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
} else {
|
||||
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
|
||||
for (int s = slices; s >= 0; s--) {
|
||||
final float a = (s == slices) ? 0.0f : s * da;
|
||||
sa = sin(a);
|
||||
ca = cos(a);
|
||||
emitTexCoord(0.5f - sa * r2 / dtc, 0.5f + ca * r2 / dtc);
|
||||
GLStateManager.glVertex2f(r2 * sa, r2 * ca);
|
||||
emitTexCoord(0.5f - sa * r1 / dtc, 0.5f + ca * r1 / dtc);
|
||||
GLStateManager.glVertex2f(r1 * sa, r1 * ca);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
r1 = r2;
|
||||
}
|
||||
}
|
||||
case GLU_LINE -> {
|
||||
/* draw loops */
|
||||
for (int l = 0; l <= loops; l++) {
|
||||
final float r = innerRadius + l * dr;
|
||||
GLStateManager.glBegin(GL_LINE_LOOP);
|
||||
for (int s = 0; s < slices; s++) {
|
||||
final float a = s * da;
|
||||
GLStateManager.glVertex2f(r * sin(a), r * cos(a));
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
/* draw spokes */
|
||||
for (int s = 0; s < slices; s++) {
|
||||
final float a = s * da;
|
||||
final float x = sin(a);
|
||||
final float y = cos(a);
|
||||
GLStateManager.glBegin(GL_LINE_STRIP);
|
||||
for (int l = 0; l <= loops; l++) {
|
||||
final float r = innerRadius + l * dr;
|
||||
GLStateManager.glVertex2f(r * x, r * y);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
}
|
||||
case GLU_POINT -> {
|
||||
GLStateManager.glBegin(GL_POINTS);
|
||||
for (int s = 0; s < slices; s++) {
|
||||
final float a = s * da;
|
||||
final float x = sin(a);
|
||||
final float y = cos(a);
|
||||
for (int l = 0; l <= loops; l++) {
|
||||
final float r = innerRadius * l * dr;
|
||||
GLStateManager.glVertex2f(r * x, r * y);
|
||||
}
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
case GLU_SILHOUETTE -> {
|
||||
if (innerRadius != 0.0) {
|
||||
GLStateManager.glBegin(GL_LINE_LOOP);
|
||||
for (float a = 0.0f; a < 2.0 * PI; a += da) {
|
||||
final float x = innerRadius * sin(a);
|
||||
final float y = innerRadius * cos(a);
|
||||
GLStateManager.glVertex2f(x, y);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
GLStateManager.glBegin(GL_LINE_LOOP);
|
||||
for (float a = 0; a < 2.0f * PI; a += da) {
|
||||
final float x = outerRadius * sin(a);
|
||||
final float y = outerRadius * cos(a);
|
||||
GLStateManager.glVertex2f(x, y);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-243
@@ -1,243 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.util.glu.PartialDisk;
|
||||
|
||||
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;
|
||||
import static org.lwjgl.opengl.GL11.GL_TRIANGLE_STRIP;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_FILL;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_FLAT;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_LINE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_OUTSIDE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_POINT;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_SILHOUETTE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_SMOOTH;
|
||||
|
||||
/**
|
||||
* Core-profile compatible replacement for {@link PartialDisk}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AngelicaPartialDisk extends PartialDisk {
|
||||
|
||||
private static final float PI = (float) Math.PI;
|
||||
private static final int CACHE_SIZE = 240;
|
||||
private final float[] sinCache = new float[CACHE_SIZE];
|
||||
private final float[] cosCache = new float[CACHE_SIZE];
|
||||
|
||||
@Override
|
||||
public void draw(float innerRadius, float outerRadius, int slices, int loops, float startAngle, float sweepAngle) {
|
||||
final float deltaRadius, angleOffset;
|
||||
float angle, sintemp, costemp, radiusLow, radiusHigh, texLow = 0, texHigh = 0;
|
||||
final int slices2, finish;
|
||||
|
||||
if (slices >= CACHE_SIZE) slices = CACHE_SIZE - 1;
|
||||
if (slices < 2 || loops < 1 || outerRadius <= 0.0f || innerRadius < 0.0f || innerRadius > outerRadius) {
|
||||
GLStateManager.LOGGER.warn("PartialDisk: GLU_INVALID_VALUE (slices={}, loops={}, innerRadius={}, outerRadius={})", slices, loops, innerRadius, outerRadius);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sweepAngle < -360.0f) sweepAngle = 360.0f;
|
||||
if (sweepAngle > 360.0f) sweepAngle = 360.0f;
|
||||
if (sweepAngle < 0) {
|
||||
startAngle += sweepAngle;
|
||||
sweepAngle = -sweepAngle;
|
||||
}
|
||||
|
||||
slices2 = (sweepAngle == 360.0f) ? slices : slices + 1;
|
||||
|
||||
/* Compute length (needed for normal calculations) */
|
||||
deltaRadius = outerRadius - innerRadius;
|
||||
|
||||
angleOffset = startAngle / 180.0f * PI;
|
||||
for (int i = 0; i <= slices; i++) {
|
||||
angle = angleOffset + ((PI * sweepAngle) / 180.0f) * i / slices;
|
||||
sinCache[i] = sin(angle);
|
||||
cosCache[i] = cos(angle);
|
||||
}
|
||||
|
||||
if (sweepAngle == 360.0f) {
|
||||
sinCache[slices] = sinCache[0];
|
||||
cosCache[slices] = cosCache[0];
|
||||
}
|
||||
|
||||
switch (super.normals) {
|
||||
case GLU_FLAT, GLU_SMOOTH -> GLStateManager.glNormal3f(0.0f, 0.0f, (super.orientation == GLU_OUTSIDE) ? 1.0f : -1.0f);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
switch (super.drawStyle) {
|
||||
case GLU_FILL -> {
|
||||
if (innerRadius == .0f) {
|
||||
finish = loops - 1;
|
||||
/* Triangle strip for inner polygons */
|
||||
GLStateManager.glBegin(GL_TRIANGLE_FAN);
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(0.5f, 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
radiusLow = outerRadius - deltaRadius * ((float) (loops - 1) / loops);
|
||||
if (super.textureFlag) {
|
||||
texLow = radiusLow / outerRadius / 2;
|
||||
}
|
||||
|
||||
if (super.orientation == GLU_OUTSIDE) {
|
||||
for (int i = slices; i >= 0; i--) {
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i <= slices; i++) {
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
|
||||
}
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
} else {
|
||||
finish = loops;
|
||||
}
|
||||
for (int j = 0; j < finish; j++) {
|
||||
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
|
||||
radiusHigh = outerRadius - deltaRadius * ((float) (j + 1) / loops);
|
||||
if (super.textureFlag) {
|
||||
texLow = radiusLow / outerRadius / 2;
|
||||
texHigh = radiusHigh / outerRadius / 2;
|
||||
}
|
||||
|
||||
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
|
||||
for (int i = 0; i <= slices; i++) {
|
||||
if (super.orientation == GLU_OUTSIDE) {
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
|
||||
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f);
|
||||
} else {
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f);
|
||||
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
|
||||
}
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
}
|
||||
case GLU_POINT -> {
|
||||
GLStateManager.glBegin(GL_POINTS);
|
||||
for (int i = 0; i < slices2; i++) {
|
||||
sintemp = sinCache[i];
|
||||
costemp = cosCache[i];
|
||||
for (int j = 0; j <= loops; j++) {
|
||||
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
|
||||
|
||||
if (super.textureFlag) {
|
||||
texLow = radiusLow / outerRadius / 2;
|
||||
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
|
||||
}
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
case GLU_LINE -> {
|
||||
if (innerRadius == outerRadius) {
|
||||
GLStateManager.glBegin(GL_LINE_STRIP);
|
||||
|
||||
for (int i = 0; i <= slices; i++) {
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(sinCache[i] / 2 + 0.5f, cosCache[i] / 2 + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(innerRadius * sinCache[i], innerRadius * cosCache[i], 0.0f);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
break;
|
||||
}
|
||||
for (int j = 0; j <= loops; j++) {
|
||||
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
|
||||
if (super.textureFlag) {
|
||||
texLow = radiusLow / outerRadius / 2;
|
||||
}
|
||||
|
||||
GLStateManager.glBegin(GL_LINE_STRIP);
|
||||
for (int i = 0; i <= slices; i++) {
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
for (int i = 0; i < slices2; i++) {
|
||||
sintemp = sinCache[i];
|
||||
costemp = cosCache[i];
|
||||
GLStateManager.glBegin(GL_LINE_STRIP);
|
||||
for (int j = 0; j <= loops; j++) {
|
||||
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
|
||||
if (super.textureFlag) {
|
||||
texLow = radiusLow / outerRadius / 2;
|
||||
}
|
||||
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
}
|
||||
case GLU_SILHOUETTE -> {
|
||||
if (sweepAngle < 360.0f) {
|
||||
for (int i = 0; i <= slices; i += slices) {
|
||||
sintemp = sinCache[i];
|
||||
costemp = cosCache[i];
|
||||
GLStateManager.glBegin(GL_LINE_STRIP);
|
||||
for (int j = 0; j <= loops; j++) {
|
||||
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
|
||||
|
||||
if (super.textureFlag) {
|
||||
texLow = radiusLow / outerRadius / 2;
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
}
|
||||
for (int j = 0; j <= loops; j += loops) {
|
||||
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
|
||||
if (super.textureFlag) {
|
||||
texLow = radiusLow / outerRadius / 2;
|
||||
}
|
||||
|
||||
GLStateManager.glBegin(GL_LINE_STRIP);
|
||||
for (int i = 0; i <= slices; i++) {
|
||||
if (super.textureFlag) {
|
||||
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
|
||||
}
|
||||
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
if (innerRadius == outerRadius) break;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.util.glu.Sphere;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_LINE_LOOP;
|
||||
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;
|
||||
import static org.lwjgl.opengl.GL11.GL_TRIANGLE_STRIP;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_FILL;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_INSIDE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_LINE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_NONE;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_POINT;
|
||||
import static org.lwjgl.util.glu.GLU.GLU_SILHOUETTE;
|
||||
|
||||
/**
|
||||
* Core-profile compatible replacement for {@link Sphere}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AngelicaSphere extends Sphere {
|
||||
|
||||
private static final float PI = (float) Math.PI;
|
||||
|
||||
private void emitNormal(float x, float y, float z) {
|
||||
final float mag = (float) Math.sqrt(x * x + y * y + z * z);
|
||||
if (mag > 0.00001F) {
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
z /= mag;
|
||||
}
|
||||
GLStateManager.glNormal3f(x, y, z);
|
||||
}
|
||||
|
||||
private void emitTexCoord(float x, float y) {
|
||||
if (super.textureFlag) GLStateManager.glTexCoord2f(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(float radius, int slices, int stacks) {
|
||||
final float drho, dtheta, ds, dt, nsign;
|
||||
float rho, theta, x, y, z, s, t;
|
||||
|
||||
final int imin, imax;
|
||||
final boolean normals;
|
||||
|
||||
normals = super.normals != GLU_NONE;
|
||||
nsign = (super.orientation == GLU_INSIDE) ? -1.0f : 1.0f;
|
||||
|
||||
drho = PI / stacks;
|
||||
dtheta = 2.0f * PI / slices;
|
||||
|
||||
if (super.drawStyle == GLU_FILL) {
|
||||
if (!super.textureFlag) {
|
||||
// draw +Z end as a triangle fan
|
||||
GLStateManager.glBegin(GL_TRIANGLE_FAN);
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, nsign * radius);
|
||||
for (int j = 0; j <= slices; j++) {
|
||||
theta = (j == slices) ? 0.0f : j * dtheta;
|
||||
x = -sin(theta) * sin(drho);
|
||||
y = cos(theta) * sin(drho);
|
||||
z = nsign * cos(drho);
|
||||
if (normals) {
|
||||
emitNormal(x * nsign, y * nsign, z * nsign);
|
||||
}
|
||||
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
|
||||
ds = 1.0f / slices;
|
||||
dt = 1.0f / stacks;
|
||||
t = 1.0f;
|
||||
if (super.textureFlag) {
|
||||
imin = 0;
|
||||
imax = stacks;
|
||||
} else {
|
||||
imin = 1;
|
||||
imax = stacks - 1;
|
||||
}
|
||||
|
||||
// draw intermediate stacks as triangle strips (GL_QUAD_STRIP removed in core profile)
|
||||
for (int i = imin; i < imax; i++) {
|
||||
rho = i * drho;
|
||||
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
|
||||
s = 0.0f;
|
||||
for (int j = 0; j <= slices; j++) {
|
||||
theta = (j == slices) ? 0.0f : j * dtheta;
|
||||
x = -sin(theta) * sin(rho);
|
||||
y = cos(theta) * sin(rho);
|
||||
z = nsign * cos(rho);
|
||||
if (normals) {
|
||||
emitNormal(x * nsign, y * nsign, z * nsign);
|
||||
}
|
||||
emitTexCoord(s, t);
|
||||
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
|
||||
x = -sin(theta) * sin(rho + drho);
|
||||
y = cos(theta) * sin(rho + drho);
|
||||
z = nsign * cos(rho + drho);
|
||||
if (normals) {
|
||||
emitNormal(x * nsign, y * nsign, z * nsign);
|
||||
}
|
||||
emitTexCoord(s, t - dt);
|
||||
s += ds;
|
||||
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
t -= dt;
|
||||
}
|
||||
|
||||
if (!super.textureFlag) {
|
||||
// draw -Z end as a triangle fan
|
||||
GLStateManager.glBegin(GL_TRIANGLE_FAN);
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, -1.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, -radius * nsign);
|
||||
rho = PI - drho;
|
||||
s = 1.0f;
|
||||
for (int j = slices; j >= 0; j--) {
|
||||
theta = (j == slices) ? 0.0f : j * dtheta;
|
||||
x = -sin(theta) * sin(rho);
|
||||
y = cos(theta) * sin(rho);
|
||||
z = nsign * cos(rho);
|
||||
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
|
||||
s -= ds;
|
||||
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
} else if (super.drawStyle == GLU_LINE || super.drawStyle == GLU_SILHOUETTE) {
|
||||
// draw stack lines
|
||||
for (int i = 1; i < stacks; i++) {
|
||||
rho = i * drho;
|
||||
GLStateManager.glBegin(GL_LINE_LOOP);
|
||||
for (int j = 0; j < slices; j++) {
|
||||
theta = j * dtheta;
|
||||
x = cos(theta) * sin(rho);
|
||||
y = sin(theta) * sin(rho);
|
||||
z = cos(rho);
|
||||
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
|
||||
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
// draw slice lines
|
||||
for (int j = 0; j < slices; j++) {
|
||||
theta = j * dtheta;
|
||||
GLStateManager.glBegin(GL_LINE_STRIP);
|
||||
for (int i = 0; i <= stacks; i++) {
|
||||
rho = i * drho;
|
||||
x = cos(theta) * sin(rho);
|
||||
y = sin(theta) * sin(rho);
|
||||
z = cos(rho);
|
||||
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
|
||||
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
} else if (super.drawStyle == GLU_POINT) {
|
||||
// top and bottom-most points
|
||||
GLStateManager.glBegin(GL_POINTS);
|
||||
if (normals) GLStateManager.glNormal3f(0.0f, 0.0f, nsign);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, radius);
|
||||
if (normals) GLStateManager.glNormal3f(0.0f, 0.0f, -nsign);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, -radius);
|
||||
|
||||
// loop over stacks
|
||||
for (int i = 1; i < stacks - 1; i++) {
|
||||
rho = i * drho;
|
||||
for (int j = 0; j < slices; j++) {
|
||||
theta = j * dtheta;
|
||||
x = cos(theta) * sin(rho);
|
||||
y = sin(theta) * sin(rho);
|
||||
z = cos(rho);
|
||||
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
|
||||
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
|
||||
}
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.dsa;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
RENDER_BACKEND.textureImage2DEXT(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,583 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
/**
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* 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.
|
||||
*
|
||||
* Per-unit packing (47 bits):
|
||||
* 0: enabled
|
||||
* 1-3: mode (MODULATE=0..COMBINE=5)
|
||||
* 4: needsEnvColor
|
||||
* 5-8: combineRgb (encoded 0-7)
|
||||
* 9-12: combineAlpha (encoded 0-7)
|
||||
* 13-14: scaleShiftRgb
|
||||
* 15-16: scaleShiftAlpha
|
||||
* 17-21: arg0 RGB (source:3 + operand:2)
|
||||
* 22-26: arg1 RGB
|
||||
* 27-31: arg2 RGB
|
||||
* 32-36: arg0 Alpha
|
||||
* 37-41: arg1 Alpha
|
||||
* 42-46: arg2 Alpha
|
||||
*/
|
||||
public final class 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;
|
||||
public static final int TEX_ENV_DECAL = 3;
|
||||
public static final int TEX_ENV_BLEND = 4;
|
||||
public static final int TEX_ENV_COMBINE = 5;
|
||||
|
||||
public static final int COMBINE_REPLACE = 0;
|
||||
public static final int COMBINE_MODULATE = 1;
|
||||
public static final int COMBINE_ADD = 2;
|
||||
public static final int COMBINE_ADD_SIGNED = 3;
|
||||
public static final int COMBINE_INTERPOLATE = 4;
|
||||
public static final int COMBINE_SUBTRACT = 5;
|
||||
public static final int COMBINE_DOT3_RGB = 6;
|
||||
public static final int COMBINE_DOT3_RGBA = 7;
|
||||
|
||||
public static final int SRC_TEXTURE = 0;
|
||||
public static final int SRC_CONSTANT = 1;
|
||||
public static final int SRC_PRIMARY_COLOR = 2;
|
||||
public static final int SRC_PREVIOUS = 3;
|
||||
|
||||
public static final int OP_SRC_COLOR = 0;
|
||||
public static final int OP_ONE_MINUS_SRC_COLOR = 1;
|
||||
public static final int OP_SRC_ALPHA = 2;
|
||||
public static final int OP_ONE_MINUS_SRC_ALPHA = 3;
|
||||
|
||||
public static final int FOG_NONE = 0;
|
||||
public static final int FOG_LINEAR = 1;
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
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
|
||||
private static final int U_NEEDS_ENV_COLOR = 4; // 1 bit
|
||||
private static final int U_COMBINE_RGB = 5; // 4 bits
|
||||
private static final int U_COMBINE_ALPHA = 9; // 4 bits
|
||||
private static final int U_SCALE_RGB = 13; // 2 bits
|
||||
private static final int U_SCALE_ALPHA = 15; // 2 bits
|
||||
private static final int U_ARG0_RGB = 17; // 5 bits (source:3 + operand:2)
|
||||
private static final int U_ARG1_RGB = 22; // 5 bits
|
||||
private static final int U_ARG2_RGB = 27; // 5 bits
|
||||
private static final int U_ARG0_ALPHA = 32; // 5 bits
|
||||
private static final int U_ARG1_ALPHA = 37; // 5 bits
|
||||
private static final int U_ARG2_ALPHA = 42; // 5 bits
|
||||
|
||||
static final int MAX_UNITS = 4;
|
||||
|
||||
private final long[] packed;
|
||||
private final int hash;
|
||||
|
||||
private FragmentKey(long[] packed, int hash) {
|
||||
this.packed = packed;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack current GLSM fragment state into pre-allocated output array.
|
||||
* @param out must be at least MAX_UNITS (4) longs
|
||||
* @return number of significant longs (1..4)
|
||||
*/
|
||||
public static int packFromState(long[] out) {
|
||||
long global = 0;
|
||||
|
||||
// Fog
|
||||
if (GLStateManager.getFogMode().isEnabled()) {
|
||||
final int fogMode = switch (GLStateManager.getFogState().getFogMode()) {
|
||||
case GL11.GL_LINEAR -> FOG_LINEAR;
|
||||
case GL11.GL_EXP -> FOG_EXP;
|
||||
case GL11.GL_EXP2 -> FOG_EXP2;
|
||||
default -> FOG_NONE;
|
||||
};
|
||||
global |= (long) fogMode;
|
||||
}
|
||||
|
||||
// Alpha test
|
||||
if (GLStateManager.getAlphaTest().isEnabled()) {
|
||||
global |= (1L << BIT_ALPHA_TEST);
|
||||
global |= ((long) (encodeAlphaFunc(GLStateManager.getAlphaState().getFunction()) & 0x7)) << BIT_ALPHA_FUNC;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
// 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);
|
||||
if (i == 0) {
|
||||
unit0Bits = unitBits;
|
||||
} else {
|
||||
out[i] = unitBits;
|
||||
}
|
||||
}
|
||||
|
||||
final int nrEnabled = highestEnabled + 1;
|
||||
global |= ((long) (nrEnabled & 0x7)) << BIT_NR_ENABLED_UNITS;
|
||||
global |= (unit0Bits << GLOBAL_BITS);
|
||||
out[0] = global;
|
||||
|
||||
return Math.max(1, nrEnabled);
|
||||
}
|
||||
|
||||
private static long packUnit(int unitIndex, boolean texEnabled) {
|
||||
if (!texEnabled) return 0;
|
||||
|
||||
final TexEnvState envState = GLStateManager.getTextures().getTexEnvState(unitIndex);
|
||||
final int mode = encodeTexEnvMode(envState.mode);
|
||||
|
||||
long bits = 1L; // enabled
|
||||
bits |= ((long) (mode & 0x7)) << U_MODE;
|
||||
|
||||
boolean needsEnvColor = (mode == TEX_ENV_BLEND);
|
||||
|
||||
if (mode == TEX_ENV_COMBINE) {
|
||||
final int combRgb = encodeCombineFunc(envState.combineRgb);
|
||||
final int combAlpha = encodeCombineFunc(envState.combineAlpha);
|
||||
bits |= ((long) (combRgb & 0xF)) << U_COMBINE_RGB;
|
||||
bits |= ((long) (combAlpha & 0xF)) << U_COMBINE_ALPHA;
|
||||
bits |= ((long) (encodeScale(envState.scaleRgb) & 0x3)) << U_SCALE_RGB;
|
||||
bits |= ((long) (encodeScale(envState.scaleAlpha) & 0x3)) << U_SCALE_ALPHA;
|
||||
|
||||
// Only pack args actually used by each channel's combine function.
|
||||
// Unused arg slots stay 0, preventing TexEnvState defaults from inflating the cache.
|
||||
final int numArgsRgb = combineNumArgs(combRgb);
|
||||
final int numArgsAlpha = combineNumArgs(combAlpha);
|
||||
final int maxArgs = Math.max(numArgsRgb, numArgsAlpha);
|
||||
|
||||
for (int a = 0; a < maxArgs; a++) {
|
||||
if (a < numArgsRgb) {
|
||||
final int srcR = encodeSource(envState.sourceRgb[a]);
|
||||
final int opR = encodeOperand(envState.operandRgb[a]);
|
||||
bits |= ((long) ((srcR & 0x7) | ((opR & 0x3) << 3))) << (U_ARG0_RGB + a * 5);
|
||||
if (srcR == SRC_CONSTANT) needsEnvColor = true;
|
||||
}
|
||||
if (a < numArgsAlpha) {
|
||||
final int srcA = encodeSource(envState.sourceAlpha[a]);
|
||||
final int opA = encodeOperand(envState.operandAlpha[a]);
|
||||
bits |= ((long) ((srcA & 0x7) | ((opA & 0x3) << 3))) << (U_ARG0_ALPHA + a * 5);
|
||||
if (srcA == SRC_CONSTANT) needsEnvColor = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsEnvColor) {
|
||||
bits |= (1L << U_NEEDS_ENV_COLOR);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
/** Create FragmentKey from packed scratch data. Only called on cache miss. */
|
||||
public static FragmentKey fromPacked(long[] scratch, int len) {
|
||||
final long[] copy = Arrays.copyOf(scratch, len);
|
||||
return new FragmentKey(copy, Arrays.hashCode(copy));
|
||||
}
|
||||
|
||||
/** Convenience: pack + create. Allocates. For infrequent paths. */
|
||||
public static FragmentKey fromState() {
|
||||
final long[] scratch = new long[MAX_UNITS];
|
||||
final int len = packFromState(scratch);
|
||||
return fromPacked(scratch, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof FragmentKey other)) return false;
|
||||
if (hash != other.hash) return false;
|
||||
return Arrays.equals(packed, other.packed);
|
||||
}
|
||||
|
||||
public int fogMode() { return (int) (packed[0] & 0x3); }
|
||||
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];
|
||||
}
|
||||
|
||||
public boolean unitEnabled(int i) { return (unitBits(i) & 1) != 0; }
|
||||
public int unitMode(int i) { return (int) ((unitBits(i) >> U_MODE) & 0x7); }
|
||||
public boolean unitNeedsEnvColor(int i) { return ((unitBits(i) >> U_NEEDS_ENV_COLOR) & 1) != 0; }
|
||||
public int unitCombineRgb(int i) { return (int) ((unitBits(i) >> U_COMBINE_RGB) & 0xF); }
|
||||
public int unitCombineAlpha(int i) { return (int) ((unitBits(i) >> U_COMBINE_ALPHA) & 0xF); }
|
||||
public int unitScaleShiftRgb(int i) { return (int) ((unitBits(i) >> U_SCALE_RGB) & 0x3); }
|
||||
public int unitScaleShiftAlpha(int i) { return (int) ((unitBits(i) >> U_SCALE_ALPHA) & 0x3); }
|
||||
|
||||
public int unitSourceRgb(int i, int arg) { return (int) ((unitBits(i) >> (U_ARG0_RGB + arg * 5)) & 0x7); }
|
||||
public int unitOperandRgb(int i, int arg) { return (int) ((unitBits(i) >> (U_ARG0_RGB + arg * 5 + 3)) & 0x3); }
|
||||
public int unitSourceAlpha(int i, int arg) { return (int) ((unitBits(i) >> (U_ARG0_ALPHA + arg * 5)) & 0x7); }
|
||||
public int unitOperandAlpha(int i, int arg) { return (int) ((unitBits(i) >> (U_ARG0_ALPHA + arg * 5 + 3)) & 0x3); }
|
||||
|
||||
public boolean textureEnabled() {
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (unitEnabled(i)) return true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
private static int encodeAlphaFunc(int glFunc) {
|
||||
return glFunc & 0x7;
|
||||
}
|
||||
|
||||
static int encodeTexEnvMode(int glMode) {
|
||||
return switch (glMode) {
|
||||
case GL11.GL_MODULATE -> TEX_ENV_MODULATE;
|
||||
case GL11.GL_REPLACE -> TEX_ENV_REPLACE;
|
||||
case GL11.GL_ADD -> TEX_ENV_ADD;
|
||||
case GL11.GL_DECAL -> TEX_ENV_DECAL;
|
||||
case GL11.GL_BLEND -> TEX_ENV_BLEND;
|
||||
case GL13.GL_COMBINE -> TEX_ENV_COMBINE;
|
||||
default -> TEX_ENV_MODULATE;
|
||||
};
|
||||
}
|
||||
|
||||
static int encodeCombineFunc(int glFunc) {
|
||||
return switch (glFunc) {
|
||||
case GL11.GL_REPLACE -> COMBINE_REPLACE;
|
||||
case GL11.GL_MODULATE -> COMBINE_MODULATE;
|
||||
case GL11.GL_ADD -> COMBINE_ADD;
|
||||
case GL13.GL_ADD_SIGNED -> COMBINE_ADD_SIGNED;
|
||||
case GL13.GL_INTERPOLATE -> COMBINE_INTERPOLATE;
|
||||
case GL13.GL_SUBTRACT -> COMBINE_SUBTRACT;
|
||||
case GL13.GL_DOT3_RGB -> COMBINE_DOT3_RGB;
|
||||
case GL13.GL_DOT3_RGBA -> COMBINE_DOT3_RGBA;
|
||||
default -> COMBINE_REPLACE;
|
||||
};
|
||||
}
|
||||
|
||||
static int encodeSource(int glSource) {
|
||||
return switch (glSource) {
|
||||
case GL11.GL_TEXTURE -> SRC_TEXTURE;
|
||||
case GL13.GL_CONSTANT -> SRC_CONSTANT;
|
||||
case GL13.GL_PRIMARY_COLOR -> SRC_PRIMARY_COLOR;
|
||||
case GL13.GL_PREVIOUS -> SRC_PREVIOUS;
|
||||
default -> {
|
||||
GLStateManager.LOGGER.debug("Unknown TexEnv source 0x{}, falling back to SRC_PREVIOUS", Integer.toHexString(glSource));
|
||||
yield SRC_PREVIOUS;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static int encodeOperand(int glOperand) {
|
||||
return switch (glOperand) {
|
||||
case GL11.GL_SRC_COLOR -> OP_SRC_COLOR;
|
||||
case GL11.GL_ONE_MINUS_SRC_COLOR -> OP_ONE_MINUS_SRC_COLOR;
|
||||
case GL11.GL_SRC_ALPHA -> OP_SRC_ALPHA;
|
||||
case GL11.GL_ONE_MINUS_SRC_ALPHA -> OP_ONE_MINUS_SRC_ALPHA;
|
||||
default -> OP_SRC_COLOR;
|
||||
};
|
||||
}
|
||||
|
||||
/** Number of source/operand args used by the given (encoded) combine function. */
|
||||
public static int combineNumArgs(int func) {
|
||||
return switch (func) {
|
||||
case COMBINE_REPLACE -> 1;
|
||||
case COMBINE_INTERPOLATE -> 3;
|
||||
default -> 2; // MODULATE, ADD, ADD_SIGNED, SUBTRACT, DOT3_*
|
||||
};
|
||||
}
|
||||
|
||||
private static int encodeScale(float scale) {
|
||||
if (scale >= 4.0f) return 2;
|
||||
if (scale >= 2.0f) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int decodeAlphaFunc(int encoded) {
|
||||
return 0x0200 | encoded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String fogName = switch (fogMode()) {
|
||||
case FOG_NONE -> "NONE";
|
||||
case FOG_LINEAR -> "LINEAR";
|
||||
case FOG_EXP -> "EXP";
|
||||
case FOG_EXP2 -> "EXP2";
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
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(), colorSum(), overlayEnabled(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
} else {
|
||||
final String modeName = switch (unitMode(i)) {
|
||||
case TEX_ENV_MODULATE -> "MOD";
|
||||
case TEX_ENV_REPLACE -> "REPL";
|
||||
case TEX_ENV_ADD -> "ADD";
|
||||
case TEX_ENV_DECAL -> "DEC";
|
||||
case TEX_ENV_BLEND -> "BL";
|
||||
case TEX_ENV_COMBINE -> "COMB";
|
||||
default -> "?";
|
||||
};
|
||||
sb.append(String.format(" u%d=%s", i, modeName));
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Generates GLSL 330 core fragment shaders for FFP emulation.
|
||||
* Supports per-unit texenv chain including GL_COMBINE mode.
|
||||
* Follows Mesa's ff_fragment_shader.c and st_nir_lower_fog.c.
|
||||
*/
|
||||
public final class FragmentShaderGenerator {
|
||||
|
||||
private FragmentShaderGenerator() {}
|
||||
|
||||
public static String generate(FragmentKey key) {
|
||||
final StringBuilder sb = new StringBuilder(2048);
|
||||
sb.append("#version 330 core\n\n");
|
||||
|
||||
emitInputs(sb, key);
|
||||
emitUniforms(sb, key);
|
||||
sb.append("out vec4 fragColor;\n\n");
|
||||
|
||||
sb.append("void main() {\n");
|
||||
emitLineStipple(sb, key);
|
||||
emitTextureSampling(sb, key);
|
||||
emitTexEnvChain(sb, key);
|
||||
emitSpecularAdd(sb, key);
|
||||
emitAlphaTest(sb, key);
|
||||
emitFog(sb, key);
|
||||
sb.append(" fragColor = color;\n");
|
||||
sb.append("}\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void emitInputs(StringBuilder sb, FragmentKey key) {
|
||||
sb.append("// Inputs from vertex shader\n");
|
||||
sb.append("in vec4 v_Color;\n");
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("in vec3 v_SpecularColor;\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) {
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (key.unitEnabled(i)) {
|
||||
sb.append("uniform sampler2D u_Sampler").append(i).append(";\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;
|
||||
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(" vec4 color = v_Color;\n");
|
||||
emitOverlay(sb, key);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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";
|
||||
final String assign = firstUnit ? " vec4 color = " : " color = ";
|
||||
|
||||
if (key.unitMode(i) == FragmentKey.TEX_ENV_COMBINE) {
|
||||
emitCombineUnit(sb, key, i, texVar, envColorVar, prevVar, assign);
|
||||
} else {
|
||||
emitSimpleUnit(sb, key, i, texVar, envColorVar, prevVar, assign);
|
||||
}
|
||||
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) {
|
||||
switch (key.unitMode(unit)) {
|
||||
case FragmentKey.TEX_ENV_REPLACE -> {
|
||||
sb.append(" // Unit ").append(unit).append(" (REPLACE)\n");
|
||||
sb.append(assign).append(texVar).append(";\n");
|
||||
}
|
||||
case FragmentKey.TEX_ENV_ADD -> {
|
||||
sb.append(" // Unit ").append(unit).append(" (ADD)\n");
|
||||
sb.append(assign).append("vec4(clamp(").append(prevVar).append(".rgb + ").append(texVar).append(".rgb, 0.0, 1.0), ")
|
||||
.append(prevVar).append(".a * ").append(texVar).append(".a);\n");
|
||||
}
|
||||
case FragmentKey.TEX_ENV_DECAL -> {
|
||||
sb.append(" // Unit ").append(unit).append(" (DECAL)\n");
|
||||
sb.append(assign).append("vec4(mix(").append(prevVar).append(".rgb, ").append(texVar).append(".rgb, ").append(texVar).append(".a), ")
|
||||
.append(prevVar).append(".a);\n");
|
||||
}
|
||||
case FragmentKey.TEX_ENV_BLEND -> {
|
||||
sb.append(" // Unit ").append(unit).append(" (BLEND)\n");
|
||||
sb.append(assign).append("vec4(").append(prevVar).append(".rgb * (vec3(1.0) - ").append(texVar).append(".rgb) + ")
|
||||
.append(envColorVar).append(".rgb * ").append(texVar).append(".rgb, ")
|
||||
.append(prevVar).append(".a * ").append(texVar).append(".a);\n");
|
||||
}
|
||||
default -> { // MODULATE
|
||||
sb.append(" // Unit ").append(unit).append(" (MODULATE)\n");
|
||||
sb.append(assign).append(prevVar).append(" * ").append(texVar).append(";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Emit a GL_COMBINE texenv unit */
|
||||
private static void emitCombineUnit(StringBuilder sb, FragmentKey key, int unit, String texVar, String envColorVar, String prevVar, String assign) {
|
||||
sb.append(" // Unit ").append(unit).append(" (COMBINE)\n");
|
||||
|
||||
final String rgbResult = "combRgb" + unit;
|
||||
final String alphaResult = "combAlpha" + unit;
|
||||
|
||||
if (key.unitCombineRgb(unit) == FragmentKey.COMBINE_DOT3_RGBA) {
|
||||
sb.append(" vec3 ").append(rgbResult).append(";\n");
|
||||
sb.append(" {\n");
|
||||
emitCombineChannel(sb, key, unit, texVar, envColorVar, prevVar, true);
|
||||
sb.append(" }\n");
|
||||
sb.append(assign).append("vec4(").append(rgbResult).append(", ").append(rgbResult).append(".x);\n");
|
||||
} else {
|
||||
sb.append(" vec3 ").append(rgbResult).append(";\n");
|
||||
sb.append(" float ").append(alphaResult).append(";\n");
|
||||
sb.append(" {\n");
|
||||
emitCombineChannel(sb, key, unit, texVar, envColorVar, prevVar, true);
|
||||
emitCombineChannel(sb, key, unit, texVar, envColorVar, prevVar, false);
|
||||
sb.append(" }\n");
|
||||
sb.append(assign).append("vec4(").append(rgbResult).append(", ").append(alphaResult).append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit one channel (RGB or Alpha) of a GL_COMBINE unit.
|
||||
*/
|
||||
private static void emitCombineChannel(StringBuilder sb, FragmentKey key, int unit, String texVar, String envColorVar, String prevVar, boolean isRgb) {
|
||||
final String suffix = isRgb ? "Rgb" : "Alpha";
|
||||
final String type = isRgb ? "vec3" : "float";
|
||||
final int combineFunc = isRgb ? key.unitCombineRgb(unit) : key.unitCombineAlpha(unit);
|
||||
final int scaleShift = isRgb ? key.unitScaleShiftRgb(unit) : key.unitScaleShiftAlpha(unit);
|
||||
|
||||
// Guard: DOT3 is invalid for alpha channel — fallback to REPLACE
|
||||
final int effectiveFunc;
|
||||
if (!isRgb && (combineFunc == FragmentKey.COMBINE_DOT3_RGB || combineFunc == FragmentKey.COMBINE_DOT3_RGBA)) {
|
||||
effectiveFunc = FragmentKey.COMBINE_REPLACE;
|
||||
} else {
|
||||
effectiveFunc = combineFunc;
|
||||
}
|
||||
|
||||
// Determine how many args we need
|
||||
final int numArgs = combineNumArgs(effectiveFunc);
|
||||
|
||||
// Emit argument resolution
|
||||
for (int a = 0; a < numArgs; a++) {
|
||||
final int source = isRgb ? key.unitSourceRgb(unit, a) : key.unitSourceAlpha(unit, a);
|
||||
final int operand = isRgb ? key.unitOperandRgb(unit, a) : key.unitOperandAlpha(unit, a);
|
||||
final String argName = "arg" + suffix + a + "_" + unit;
|
||||
final String sourceExpr = resolveSource(source, texVar, envColorVar, prevVar);
|
||||
final String operandExpr = applyOperand(sourceExpr, operand, isRgb);
|
||||
sb.append(" ").append(type).append(" ").append(argName).append(" = ").append(operandExpr).append(";\n");
|
||||
}
|
||||
|
||||
// Emit combine operation
|
||||
final String resultName = "comb" + suffix + unit;
|
||||
final String a0 = "arg" + suffix + "0_" + unit;
|
||||
final String a1 = "arg" + suffix + "1_" + unit;
|
||||
final String a2 = "arg" + suffix + "2_" + unit;
|
||||
|
||||
sb.append(" ").append(resultName).append(" = ");
|
||||
switch (effectiveFunc) {
|
||||
case FragmentKey.COMBINE_REPLACE -> sb.append(a0);
|
||||
case FragmentKey.COMBINE_MODULATE -> sb.append(a0).append(" * ").append(a1);
|
||||
case FragmentKey.COMBINE_ADD -> sb.append(a0).append(" + ").append(a1);
|
||||
case FragmentKey.COMBINE_ADD_SIGNED ->
|
||||
sb.append(a0).append(" + ").append(a1).append(" - ").append(isRgb ? "vec3(0.5)" : "0.5");
|
||||
case FragmentKey.COMBINE_SUBTRACT -> sb.append(a0).append(" - ").append(a1);
|
||||
case FragmentKey.COMBINE_INTERPOLATE ->
|
||||
sb.append("mix(").append(a1).append(", ").append(a0).append(", ").append(a2).append(")");
|
||||
case FragmentKey.COMBINE_DOT3_RGB, FragmentKey.COMBINE_DOT3_RGBA ->
|
||||
sb.append("vec3(4.0 * dot(").append(a0).append(" - vec3(0.5), ").append(a1).append(" - vec3(0.5)))");
|
||||
default -> sb.append(a0); // fallback to REPLACE
|
||||
}
|
||||
sb.append(";\n");
|
||||
|
||||
// Apply scale
|
||||
if (scaleShift > 0) {
|
||||
final String scaleFactor = scaleShift == 1 ? "2.0" : "4.0";
|
||||
sb.append(" ").append(resultName).append(" *= ").append(scaleFactor).append(";\n");
|
||||
}
|
||||
|
||||
// Clamp
|
||||
sb.append(" ").append(resultName).append(" = clamp(").append(resultName).append(", ")
|
||||
.append(isRgb ? "vec3(0.0), vec3(1.0)" : "0.0, 1.0").append(");\n");
|
||||
}
|
||||
|
||||
private static String resolveSource(int source, String texVar, String envColorVar, String prevVar) {
|
||||
return switch (source) {
|
||||
case FragmentKey.SRC_TEXTURE -> texVar;
|
||||
case FragmentKey.SRC_CONSTANT -> envColorVar;
|
||||
case FragmentKey.SRC_PRIMARY_COLOR -> "v_Color";
|
||||
case FragmentKey.SRC_PREVIOUS -> prevVar;
|
||||
default -> prevVar;
|
||||
};
|
||||
}
|
||||
|
||||
private static String applyOperand(String sourceExpr, int operand, boolean isRgb) {
|
||||
if (isRgb) {
|
||||
return switch (operand) {
|
||||
case FragmentKey.OP_SRC_COLOR -> sourceExpr + ".rgb";
|
||||
case FragmentKey.OP_ONE_MINUS_SRC_COLOR -> "(vec3(1.0) - " + sourceExpr + ".rgb)";
|
||||
case FragmentKey.OP_SRC_ALPHA -> "vec3(" + sourceExpr + ".a)";
|
||||
case FragmentKey.OP_ONE_MINUS_SRC_ALPHA -> "vec3(1.0 - " + sourceExpr + ".a)";
|
||||
default -> sourceExpr + ".rgb";
|
||||
};
|
||||
} else {
|
||||
return switch (operand) {
|
||||
case FragmentKey.OP_SRC_ALPHA -> sourceExpr + ".a";
|
||||
case FragmentKey.OP_ONE_MINUS_SRC_ALPHA -> "(1.0 - " + sourceExpr + ".a)";
|
||||
// Per GL spec, alpha channel operands are SRC_ALPHA or ONE_MINUS_SRC_ALPHA only, but handle color operands gracefully
|
||||
case FragmentKey.OP_SRC_COLOR -> sourceExpr + ".a";
|
||||
case FragmentKey.OP_ONE_MINUS_SRC_COLOR -> "(1.0 - " + sourceExpr + ".a)";
|
||||
default -> sourceExpr + ".a";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static int combineNumArgs(int func) {
|
||||
return FragmentKey.combineNumArgs(func);
|
||||
}
|
||||
|
||||
private static void emitSpecularAdd(StringBuilder sb, FragmentKey key) {
|
||||
if (key.separateSpecular()) {
|
||||
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) {
|
||||
if (!key.alphaTestEnabled()) return;
|
||||
|
||||
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 (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 (qa > u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_GREATER ->
|
||||
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 (qa < u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_ALWAYS -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitFog(StringBuilder sb, FragmentKey key) {
|
||||
final int fogMode = key.fogMode();
|
||||
if (fogMode == FragmentKey.FOG_NONE) return;
|
||||
|
||||
sb.append(" // Fog\n");
|
||||
sb.append(" float f;\n");
|
||||
|
||||
switch (fogMode) {
|
||||
case FragmentKey.FOG_LINEAR ->
|
||||
sb.append(" f = v_FogCoord * u_FogParams.x + u_FogParams.y;\n");
|
||||
case FragmentKey.FOG_EXP ->
|
||||
sb.append(" f = exp2(-(v_FogCoord * u_FogParams.z));\n");
|
||||
case FragmentKey.FOG_EXP2 -> {
|
||||
sb.append(" float fogTmp = v_FogCoord * u_FogParams.w;\n");
|
||||
sb.append(" f = exp2(-(fogTmp * fogTmp));\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" f = clamp(f, 0.0, 1.0);\n");
|
||||
sb.append(" color.rgb = mix(u_FogColor.rgb, color.rgb, f);\n");
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
/**
|
||||
* Generates GLSL 330 core geometry shaders for wide line emulation.
|
||||
*/
|
||||
public final class GeometryShaderGenerator {
|
||||
|
||||
private GeometryShaderGenerator() {}
|
||||
|
||||
public static String generate(VertexKey key) {
|
||||
final StringBuilder sb = new StringBuilder(1024);
|
||||
sb.append("#version 330 core\n\n");
|
||||
|
||||
sb.append("layout(lines) in;\n");
|
||||
sb.append("layout(triangle_strip, max_vertices = 4) out;\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.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.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");
|
||||
sb.append(" vec4 c0 = gl_in[0].gl_Position;\n");
|
||||
sb.append(" vec4 c1 = gl_in[1].gl_Position;\n\n");
|
||||
|
||||
sb.append(" // NDC positions\n");
|
||||
sb.append(" vec2 n0 = c0.xy / c0.w;\n");
|
||||
sb.append(" vec2 n1 = c1.xy / c1.w;\n\n");
|
||||
|
||||
sb.append(" // Screen-space direction and perpendicular\n");
|
||||
sb.append(" vec2 dir = normalize((n1 - n0) * u_ViewportSize);\n");
|
||||
sb.append(" vec2 offset = vec2(-dir.y, dir.x) * u_LineWidth / u_ViewportSize;\n\n");
|
||||
|
||||
emitVertex(sb, key, 0, "+offset");
|
||||
emitVertex(sb, key, 0, "-offset");
|
||||
emitVertex(sb, key, 1, "+offset");
|
||||
emitVertex(sb, key, 1, "-offset");
|
||||
|
||||
sb.append(" EndPrimitive();\n");
|
||||
sb.append("}\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void emitVertex(StringBuilder sb, VertexKey key, int endpointIdx, String offsetExpr) {
|
||||
final String ci = "c" + endpointIdx;
|
||||
final String ni = "n" + endpointIdx;
|
||||
final String idx = "[" + endpointIdx + "]";
|
||||
|
||||
sb.append(" // Endpoint ").append(endpointIdx).append(' ').append(offsetExpr).append('\n');
|
||||
|
||||
sb.append(" v_Color = v_Color_gs").append(idx).append(";\n");
|
||||
if (key.separateSpecular()) {
|
||||
sb.append(" v_SpecularColor = v_SpecularColor_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++) {
|
||||
sb.append(" gl_ClipDistance[").append(i).append("] = gl_in").append(idx).append(".gl_ClipDistance[").append(i).append("];\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" gl_Position = vec4((").append(ni).append(' ').append(offsetExpr).append(") * ").append(ci).append(".w, ").append(ci).append(".z, ").append(ci).append(".w);\n");
|
||||
sb.append(" EmitVertex();\n\n");
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import lombok.Setter;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.IdentityHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* Open-addressed cache for FFP shader program variants.
|
||||
* Keyed on (vertexKey, fragmentKey) pairs stored inline as raw longs — zero allocation on cache hits.
|
||||
* Converges on Mesa's prog_cache.c: hash+memcmp over packed bytes, open addressing.
|
||||
*
|
||||
* <p>Layout per slot in {@code keys[]}: {@link #SLOT_STRIDE}=6 longs
|
||||
* <pre>
|
||||
* [0]: vertexKeyPacked
|
||||
* [1]: (fkLen << 48) | (hash & 0x0000_FFFF_FFFF_FFFF) — never 0 for valid entries (fkLen >= 1)
|
||||
* [2..5]: fk0..fk3
|
||||
* </pre>
|
||||
*/
|
||||
public class ShaderCache {
|
||||
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)
|
||||
|
||||
private final Long2ObjectOpenHashMap<String> vertexSourceCache = new Long2ObjectOpenHashMap<>();
|
||||
private final Long2ObjectOpenHashMap<String> geometrySourceCache = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
// Open-addressed table — parallel arrays
|
||||
private long[] keys;
|
||||
private Program[] programs;
|
||||
private String[] fragSources; // fragment source per slot; reused across VK variants via findFragSource
|
||||
private int capacity;
|
||||
private int count;
|
||||
private int uniqueFragCount;
|
||||
|
||||
// Fast-path: last-used key (raw longs, no objects)
|
||||
private long lastVK = Long.MIN_VALUE;
|
||||
private int lastFKLen;
|
||||
private long lastFK0, lastFK1, lastFK2, lastFK3;
|
||||
private Program lastProgram;
|
||||
|
||||
private int fragmentDumpCounter;
|
||||
|
||||
@Setter private Path dumpDir = null;
|
||||
|
||||
public ShaderCache() {
|
||||
capacity = INITIAL_CAPACITY;
|
||||
keys = new long[capacity * SLOT_STRIDE];
|
||||
programs = new Program[capacity];
|
||||
fragSources = new String[capacity];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create an FFP program for the given state keys.
|
||||
*
|
||||
* @param vkPacked packed vertex key
|
||||
* @param fkPacked pre-allocated scratch array with packed fragment key longs
|
||||
* @param fkLen number of significant longs in fkPacked (1..4)
|
||||
*/
|
||||
public Program getOrCreate(long vkPacked, long[] fkPacked, int fkLen) {
|
||||
// Fast-path: compare raw longs inline
|
||||
if (vkPacked == lastVK && fkLen == lastFKLen && lastProgram != null
|
||||
&& fkPacked[0] == lastFK0
|
||||
&& (fkLen < 2 || fkPacked[1] == lastFK1)
|
||||
&& (fkLen < 3 || fkPacked[2] == lastFK2)
|
||||
&& (fkLen < 4 || fkPacked[3] == lastFK3)) {
|
||||
return lastProgram;
|
||||
}
|
||||
|
||||
final long hash = hashKey(vkPacked, fkPacked, fkLen);
|
||||
final long storedWord = ((long) fkLen << 48) | (hash & 0x0000_FFFF_FFFF_FFFFL);
|
||||
final int mask = capacity - 1;
|
||||
int idx = (int) (hash & mask);
|
||||
while (true) {
|
||||
final int base = idx * SLOT_STRIDE;
|
||||
final long slot1 = keys[base + 1];
|
||||
if (slot1 == EMPTY_MARKER) {
|
||||
break;
|
||||
}
|
||||
if (slot1 == storedWord && keys[base] == vkPacked && keysMatch(base + 2, fkPacked, fkLen)) {
|
||||
final Program p = programs[idx];
|
||||
setLastUsed(vkPacked, fkPacked, fkLen, p);
|
||||
return p;
|
||||
}
|
||||
idx = (idx + 1) & mask;
|
||||
}
|
||||
|
||||
if (count >= (capacity * 3) / 4) {
|
||||
grow();
|
||||
return getOrCreate(vkPacked, fkPacked, fkLen);
|
||||
}
|
||||
|
||||
final VertexKey vk = VertexKey.fromPacked(vkPacked);
|
||||
final FragmentKey fk = FragmentKey.fromPacked(fkPacked, fkLen);
|
||||
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 int insertIdx = findEmptySlot(hash);
|
||||
final int base = insertIdx * SLOT_STRIDE;
|
||||
keys[base] = vkPacked;
|
||||
keys[base + 1] = storedWord;
|
||||
for (int i = 0; i < fkLen; i++) keys[base + 2 + i] = fkPacked[i];
|
||||
programs[insertIdx] = program;
|
||||
fragSources[insertIdx] = fragSrc;
|
||||
count++;
|
||||
|
||||
setLastUsed(vkPacked, fkPacked, fkLen, program);
|
||||
|
||||
GLStateManager.LOGGER.debug("FFP variant compiled: {} + {} (total: {} programs)", vk, fk, count);
|
||||
|
||||
if (dumpDir != null) {
|
||||
dumpShader(Long.toHexString(vkPacked), ".vert.glsl", vertSrc);
|
||||
dumpShader("frag_" + fragmentDumpCounter++, ".frag.glsl", fragSrc);
|
||||
if (geomSrc != null) {
|
||||
dumpShader(Long.toHexString(vkPacked), ".geom.glsl", geomSrc);
|
||||
}
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
private String findFragSource(long[] fkPacked, int fkLen) {
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
final int base = i * SLOT_STRIDE;
|
||||
if (keys[base + 1] == EMPTY_MARKER) continue;
|
||||
final int storedLen = (int) (keys[base + 1] >>> 48);
|
||||
if (storedLen != fkLen) continue;
|
||||
if (keysMatch(base + 2, fkPacked, fkLen)) {
|
||||
return fragSources[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean keysMatch(int keysOffset, long[] fkPacked, int fkLen) {
|
||||
for (int i = 0; i < fkLen; i++) {
|
||||
if (keys[keysOffset + i] != fkPacked[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setLastUsed(long vkPacked, long[] fkPacked, int fkLen, Program program) {
|
||||
lastVK = vkPacked;
|
||||
lastFKLen = fkLen;
|
||||
lastFK0 = fkPacked[0];
|
||||
lastFK1 = fkLen >= 2 ? fkPacked[1] : 0;
|
||||
lastFK2 = fkLen >= 3 ? fkPacked[2] : 0;
|
||||
lastFK3 = fkLen >= 4 ? fkPacked[3] : 0;
|
||||
lastProgram = program;
|
||||
}
|
||||
|
||||
private int findEmptySlot(long hash) {
|
||||
final int mask = capacity - 1;
|
||||
int idx = (int) (hash & mask);
|
||||
while (keys[idx * SLOT_STRIDE + 1] != EMPTY_MARKER) {
|
||||
idx = (idx + 1) & mask;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash over (vk + fk longs). Adapted from Mesa's hash_key — mix-rotate-multiply.
|
||||
*/
|
||||
private static long hashKey(long vkPacked, long[] fkPacked, int fkLen) {
|
||||
long h = vkPacked * 0x9E3779B97F4A7C15L;
|
||||
for (int i = 0; i < fkLen; i++) {
|
||||
h ^= fkPacked[i] * 0x517CC1B727220A95L;
|
||||
h = Long.rotateLeft(h, 31) * 0x9E3779B97F4A7C15L;
|
||||
}
|
||||
return h | 1L; // ensure nonzero lower bits
|
||||
}
|
||||
|
||||
private void grow() {
|
||||
final int oldCap = capacity;
|
||||
final long[] oldKeys = keys;
|
||||
final Program[] oldPrograms = programs;
|
||||
final String[] oldFragSources = fragSources;
|
||||
|
||||
capacity = oldCap * 2;
|
||||
keys = new long[capacity * SLOT_STRIDE];
|
||||
programs = new Program[capacity];
|
||||
fragSources = new String[capacity];
|
||||
count = 0;
|
||||
|
||||
final int mask = capacity - 1;
|
||||
for (int i = 0; i < oldCap; i++) {
|
||||
final int oldBase = i * SLOT_STRIDE;
|
||||
if (oldKeys[oldBase + 1] == EMPTY_MARKER) continue;
|
||||
|
||||
final long storedHash = oldKeys[oldBase + 1] & 0x0000_FFFF_FFFF_FFFFL;
|
||||
int idx = (int) (storedHash & mask);
|
||||
while (keys[idx * SLOT_STRIDE + 1] != EMPTY_MARKER) {
|
||||
idx = (idx + 1) & mask;
|
||||
}
|
||||
final int newBase = idx * SLOT_STRIDE;
|
||||
System.arraycopy(oldKeys, oldBase, keys, newBase, SLOT_STRIDE);
|
||||
programs[idx] = oldPrograms[i];
|
||||
fragSources[idx] = oldFragSources[i];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
private String getOrGenerateVertex(VertexKey vk) {
|
||||
return vertexSourceCache.computeIfAbsent(vk.pack(), k -> VertexShaderGenerator.generate(vk));
|
||||
}
|
||||
|
||||
private String getOrGenerateGeometry(VertexKey vk) {
|
||||
return geometrySourceCache.computeIfAbsent(vk.pack(), k -> GeometryShaderGenerator.generate(vk));
|
||||
}
|
||||
|
||||
private void dumpShader(String name, String suffix, String source) {
|
||||
if (dumpDir == null) return;
|
||||
try {
|
||||
Files.createDirectories(dumpDir);
|
||||
final Path file = dumpDir.resolve("ffp_" + name + suffix);
|
||||
if (!Files.exists(file)) {
|
||||
Files.writeString(file, source, StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
GLStateManager.LOGGER.warn("Failed to dump FFP shader to {}: {}", dumpDir, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Dump all cached shaders to disk. */
|
||||
public void dumpAll(Path outputDir) {
|
||||
try {
|
||||
Files.createDirectories(outputDir);
|
||||
for (var entry : vertexSourceCache.entrySet()) {
|
||||
dumpShader(Long.toHexString(entry.getKey()), ".vert.glsl", entry.getValue());
|
||||
}
|
||||
final IdentityHashMap<String, Integer> seen = new IdentityHashMap<>();
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
if (fragSources[i] != null && !seen.containsKey(fragSources[i])) {
|
||||
final int idx = seen.size();
|
||||
seen.put(fragSources[i], idx);
|
||||
dumpShader("frag_" + idx, ".frag.glsl", fragSources[i]);
|
||||
}
|
||||
}
|
||||
for (var entry : geometrySourceCache.entrySet()) {
|
||||
dumpShader(Long.toHexString(entry.getKey()), ".geom.glsl", entry.getValue());
|
||||
}
|
||||
GLStateManager.LOGGER.info("Dumped {} vertex, {} geometry, and {} fragment FFP shader sources to {}",
|
||||
vertexSourceCache.size(), geometrySourceCache.size(), seen.size(), outputDir);
|
||||
} catch (IOException e) {
|
||||
GLStateManager.LOGGER.warn("Failed to dump FFP shaders: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Destroy all cached programs and clear caches. */
|
||||
public void destroy() {
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
if (programs[i] != null) {
|
||||
programs[i].destroy();
|
||||
}
|
||||
}
|
||||
Arrays.fill(keys, 0);
|
||||
Arrays.fill(programs, null);
|
||||
Arrays.fill(fragSources, null);
|
||||
count = 0;
|
||||
uniqueFragCount = 0;
|
||||
vertexSourceCache.clear();
|
||||
geometrySourceCache.clear();
|
||||
lastProgram = null;
|
||||
lastVK = Long.MIN_VALUE;
|
||||
GLStateManager.LOGGER.debug("FFP shader cache cleared");
|
||||
}
|
||||
|
||||
public int getProgramCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getVertexVariantCount() {
|
||||
return vertexSourceCache.size();
|
||||
}
|
||||
|
||||
public int getFragmentVariantCount() {
|
||||
return uniqueFragCount;
|
||||
}
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
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;
|
||||
|
||||
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(); }
|
||||
}
|
||||
@@ -1,447 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredAlphaHandler {
|
||||
boolean isAlphaTestLocked();
|
||||
void deferAlphaTestToggle(boolean enabled);
|
||||
void deferAlphaFunc(int function, float reference);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
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
@@ -1,8 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface GlintColorHandler {
|
||||
void onColorChanged(float red, float green, float blue, float alpha);
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
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
@@ -1,13 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface VanillaBooleanLayer {
|
||||
|
||||
boolean isOverrideHeld();
|
||||
boolean getVanilla();
|
||||
void setVanilla(boolean enabled);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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
@@ -1,9 +0,0 @@
|
||||
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
@@ -1,10 +0,0 @@
|
||||
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
@@ -1,6 +0,0 @@
|
||||
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
@@ -1,6 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ForeignDrawEndEvent extends MutableEvent {
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
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
@@ -1,8 +0,0 @@
|
||||
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
@@ -1,9 +0,0 @@
|
||||
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
@@ -1,7 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ProgramDeleteEvent extends MutableEvent {
|
||||
public int program;
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class TextureBindEvent extends MutableEvent {
|
||||
public int textureId;
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class TextureDeleteEvent extends MutableEvent {
|
||||
public int textureId;
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
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
@@ -1,6 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class VanillaBlendChangeEvent extends MutableEvent {
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
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() {}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class SectionRegistry {
|
||||
|
||||
public interface Emitter {
|
||||
long enter(int category, String text);
|
||||
void leave(long nativeId);
|
||||
void setup(int category, String name);
|
||||
boolean isConnected();
|
||||
}
|
||||
|
||||
private static final class Category {
|
||||
final int id;
|
||||
final String name;
|
||||
|
||||
Category(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Section {
|
||||
final long token;
|
||||
final int category;
|
||||
final String text;
|
||||
long nativeId;
|
||||
|
||||
Section(long token, int category, String text, long nativeId) {
|
||||
this.token = token;
|
||||
this.category = category;
|
||||
this.text = text;
|
||||
this.nativeId = nativeId;
|
||||
}
|
||||
}
|
||||
|
||||
private final Emitter emitter;
|
||||
private final List<Category> categories = new ArrayList<>();
|
||||
private final List<Section> open = new ArrayList<>();
|
||||
private long nextToken;
|
||||
private boolean connected;
|
||||
|
||||
public SectionRegistry(Emitter emitter) {
|
||||
this.emitter = emitter;
|
||||
}
|
||||
|
||||
public synchronized void registerCategory(int category, String name) {
|
||||
categories.add(new Category(category, name));
|
||||
}
|
||||
|
||||
public synchronized long enter(int category, String text) {
|
||||
syncConnection();
|
||||
final long token = ++nextToken;
|
||||
open.add(new Section(token, category, text, emitter.enter(category, text)));
|
||||
return token;
|
||||
}
|
||||
|
||||
public synchronized void leave(long token) {
|
||||
for (int i = 0; i < open.size(); i++) {
|
||||
final Section section = open.get(i);
|
||||
if (section.token == token) {
|
||||
open.remove(i);
|
||||
if (section.nativeId != 0L) emitter.leave(section.nativeId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void poll() {
|
||||
syncConnection();
|
||||
}
|
||||
|
||||
private void syncConnection() {
|
||||
final boolean now = emitter.isConnected();
|
||||
if (now == connected) return;
|
||||
connected = now;
|
||||
if (now) replay();
|
||||
}
|
||||
|
||||
private void replay() {
|
||||
for (int i = 0; i < categories.size(); i++) {
|
||||
final Category category = categories.get(i);
|
||||
emitter.setup(category.id, category.name);
|
||||
}
|
||||
for (int i = 0; i < open.size(); i++) {
|
||||
final Section section = open.get(i);
|
||||
section.nativeId = emitter.enter(section.category, section.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,472 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.sun.management.ThreadMXBean;
|
||||
import lombok.Setter;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
||||
public final class Tracy {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Tracy");
|
||||
|
||||
public static final boolean ENABLED;
|
||||
public static final boolean FINE_ZONES;
|
||||
private static final TracyBackend BACKEND;
|
||||
private static volatile boolean SHUTTING_DOWN;
|
||||
|
||||
private static final ConcurrentHashMap<String, Long> CLIENT_SRC_LOCS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, Long> SERVER_SRC_LOCS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, Long> PLAIN_SRC_LOCS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, Long> PLOT_NAMES = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, Long> FRAME_NAMES = new ConcurrentHashMap<>();
|
||||
private static final Set<String> WARNED_LEAKS = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public static final int COLOR_CLIENT = 0x3A6EA5; // blue
|
||||
public static final int COLOR_SERVER = 0xB5651D; // brown
|
||||
public static final int COLOR_WORKER = 0x3F7F3F; // green
|
||||
public static final int COLOR_SWAP = 0xAA3333; // red
|
||||
public static final int COLOR_TERRAIN = 0x2E8B8B; // teal
|
||||
public static final int COLOR_IRIS = 0x9E862E; // gold
|
||||
public static final int COLOR_FFP = 0x7A4E9E; // purple
|
||||
|
||||
public static final int SECTION_WORLD = 1;
|
||||
public static final int SECTION_SHADERS = 2;
|
||||
public static final int SECTION_BENCHMARK = 3;
|
||||
public static final int SECTION_UI = 4;
|
||||
|
||||
private static final SectionRegistry SECTIONS = new SectionRegistry(new SectionRegistry.Emitter() {
|
||||
@Override public long enter(int category, String text) { return BACKEND.sectionEnter(category, text); }
|
||||
@Override public void leave(long nativeId) { BACKEND.sectionLeave(nativeId); }
|
||||
@Override public void setup(int category, String name) { BACKEND.sectionSetup(category, name); }
|
||||
@Override public boolean isConnected() { return BACKEND.isConnected(); }
|
||||
});
|
||||
|
||||
private static final ThreadLocal<ZoneStack> STACK = new ThreadLocal<>() {
|
||||
@Override
|
||||
protected ZoneStack initialValue() {
|
||||
BACKEND.setCurrentThreadName(Thread.currentThread().getName());
|
||||
return new ZoneStack();
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
TracyBackend backend = null;
|
||||
if (SystemProperties.TRACY) {
|
||||
backend = loadAndInit();
|
||||
}
|
||||
BACKEND = backend;
|
||||
ENABLED = backend != null;
|
||||
FINE_ZONES = ENABLED && SystemProperties.TRACY_FINE_ZONES;
|
||||
if (ENABLED) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(Tracy::shutdown, "Tracy-Shutdown"));
|
||||
SECTIONS.registerCategory(SECTION_WORLD, "World");
|
||||
SECTIONS.registerCategory(SECTION_SHADERS, "Shaders");
|
||||
SECTIONS.registerCategory(SECTION_BENCHMARK, "Benchmark");
|
||||
SECTIONS.registerCategory(SECTION_UI, "UI");
|
||||
LOGGER.info("Tracy profiling enabled");
|
||||
}
|
||||
}
|
||||
|
||||
private Tracy() {}
|
||||
|
||||
private static void shutdown() {
|
||||
SHUTTING_DOWN = true;
|
||||
final Thread worker = new Thread(BACKEND::shutdown, "Tracy-Shutdown-Worker");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
try {
|
||||
worker.join(2000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private static TracyBackend loadAndInit() {
|
||||
TracyBackend found = null;
|
||||
try {
|
||||
final Iterator<TracyBackend> it = ServiceLoader.load(TracyBackend.class, TracyBackend.class.getClassLoader()).iterator();
|
||||
while (found == null && it.hasNext()) {
|
||||
found = it.next();
|
||||
}
|
||||
} catch (ServiceConfigurationError | LinkageError e) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but backend unavailable: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
if (found == null) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but no TracyBackend service present");
|
||||
return null;
|
||||
}
|
||||
if (!implementsCurrentAbi(found)) return null;
|
||||
final TracyBackend backend = found;
|
||||
final CountDownLatch done = new CountDownLatch(1);
|
||||
final AtomicReference<Boolean> result = new AtomicReference<>();
|
||||
final Thread init = new Thread(new InitRunner(backend, done, result), "Tracy-Init");
|
||||
init.setDaemon(true);
|
||||
init.start();
|
||||
try {
|
||||
if (!done.await(15, TimeUnit.SECONDS) && result.compareAndSet(null, Boolean.FALSE)) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but init timed out; profiling disabled");
|
||||
return null;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
if (!result.compareAndSet(null, Boolean.FALSE) && Boolean.TRUE.equals(result.get())) {
|
||||
backend.shutdown();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!Boolean.TRUE.equals(result.get())) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but native init failed; profiling disabled");
|
||||
return null;
|
||||
}
|
||||
return backend;
|
||||
}
|
||||
|
||||
private static final String ABI_SENTINEL = "sectionEnter";
|
||||
|
||||
private static boolean implementsCurrentAbi(TracyBackend backend) {
|
||||
try {
|
||||
backend.getClass().getMethod(ABI_SENTINEL, int.class, String.class);
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOGGER.warn("Tracy backend {} was built against an older TracyBackend interface; profiling disabled", backend.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private record InitRunner(TracyBackend backend, CountDownLatch done, AtomicReference<Boolean> result) implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean ok;
|
||||
try {
|
||||
ok = backend.init();
|
||||
} catch (LinkageError e) {
|
||||
LOGGER.warn("Tracy backend {} failed to link; profiling disabled: {}", backend.getClass().getName(), e);
|
||||
ok = false;
|
||||
}
|
||||
if (!result.compareAndSet(null, ok) && ok) {
|
||||
backend.shutdown();
|
||||
}
|
||||
done.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
public static void beginSection(String name) {
|
||||
if (!ENABLED) return;
|
||||
if ("root".equals(name)) {
|
||||
unwindToRoot(STACK.get());
|
||||
}
|
||||
if (!CaptureGate.markersThisFrame) {
|
||||
skip(name);
|
||||
return;
|
||||
}
|
||||
final boolean client = Thread.currentThread() == GLStateManager.getMainThread();
|
||||
final ConcurrentHashMap<String, Long> map = client ? CLIENT_SRC_LOCS : SERVER_SRC_LOCS;
|
||||
Long srcLoc = map.get(name);
|
||||
if (srcLoc == null) {
|
||||
srcLoc = map.computeIfAbsent(name, n -> BACKEND.internSrcLoc((client ? "cl:" : "sv:") + n, client ? classifySection(n) : COLOR_SERVER));
|
||||
}
|
||||
begin(srcLoc, name);
|
||||
}
|
||||
|
||||
private static int classifySection(String name) {
|
||||
if (name.startsWith("iris_")) return COLOR_IRIS;
|
||||
if (name.startsWith("draw_chunk_layer")) return COLOR_TERRAIN;
|
||||
return COLOR_CLIENT;
|
||||
}
|
||||
|
||||
private static void unwindToRoot(ZoneStack stack) {
|
||||
while (stack.depth() > 0 || stack.hasSkipped()) {
|
||||
final long ctx = stack.pop();
|
||||
if (ctx == ZoneStack.EMPTY) continue;
|
||||
if (stack.poppedGpu()) {
|
||||
BACKEND.gpuEndZone();
|
||||
}
|
||||
if (ctx != 0L) {
|
||||
BACKEND.endZone(ctx);
|
||||
}
|
||||
final String name = stack.poppedName();
|
||||
if (name != null && WARNED_LEAKS.add(name)) {
|
||||
LOGGER.warn("Tracy: unbalanced profiler section '{}' left open (leaked by its caller); closed at next root", name);
|
||||
BACKEND.message("unbalanced profiler section '" + name + "' closed at root", TracyBackend.SEVERITY_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void beginZone(String name) {
|
||||
beginZone(name, COLOR_WORKER);
|
||||
}
|
||||
|
||||
public static void beginZone(String name, int color) {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
if (!CaptureGate.markersThisFrame) {
|
||||
skip(name);
|
||||
return;
|
||||
}
|
||||
Long srcLoc = PLAIN_SRC_LOCS.get(name);
|
||||
if (srcLoc == null) {
|
||||
srcLoc = PLAIN_SRC_LOCS.computeIfAbsent(name, n -> BACKEND.internSrcLoc(n, color));
|
||||
}
|
||||
begin(srcLoc, name);
|
||||
}
|
||||
|
||||
public static final class ZoneId {
|
||||
private final long srcLoc;
|
||||
private final String name;
|
||||
|
||||
private ZoneId(long srcLoc, String name) {
|
||||
this.srcLoc = srcLoc;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static ZoneId zoneId(String name) {
|
||||
return zoneId(name, COLOR_WORKER);
|
||||
}
|
||||
|
||||
public static ZoneId zoneId(String name, int color) {
|
||||
if (!ENABLED) return new ZoneId(0L, name);
|
||||
Long srcLoc = PLAIN_SRC_LOCS.get(name);
|
||||
if (srcLoc == null) {
|
||||
srcLoc = PLAIN_SRC_LOCS.computeIfAbsent(name, n -> BACKEND.internSrcLoc(n, color));
|
||||
}
|
||||
return new ZoneId(srcLoc, name);
|
||||
}
|
||||
|
||||
public static void beginZone(ZoneId zone) {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
begin(zone.srcLoc, zone.name);
|
||||
}
|
||||
|
||||
private static void skip(String name) {
|
||||
STACK.get().push(0L, false, name);
|
||||
}
|
||||
|
||||
private static void begin(long srcLoc, String name) {
|
||||
if (SHUTTING_DOWN) return;
|
||||
final ZoneStack stack = STACK.get();
|
||||
if (srcLoc == 0 || !CaptureGate.markersThisFrame || stack.atCap()) {
|
||||
stack.push(0L, false, name);
|
||||
return;
|
||||
}
|
||||
final long ctx = BACKEND.beginZone(srcLoc);
|
||||
final boolean gpu = gpuReady && gpuZonesEnabled && Thread.currentThread() == GLStateManager.getMainThread() && BACKEND.gpuBeginZone(srcLoc);
|
||||
stack.push(ctx, gpu, name);
|
||||
if (srcLoc == BACKEND.dynamicSrcLoc()) {
|
||||
BACKEND.zoneText(ctx, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void endZone() {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
final ZoneStack stack = STACK.get();
|
||||
final long ctx = stack.pop();
|
||||
if (ctx == ZoneStack.EMPTY) return;
|
||||
if (stack.poppedGpu()) {
|
||||
BACKEND.gpuEndZone();
|
||||
}
|
||||
if (ctx != 0L) {
|
||||
BACKEND.endZone(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void zoneText(String text) {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
final long ctx = STACK.get().peek();
|
||||
if (ctx != ZoneStack.EMPTY && ctx != 0L) {
|
||||
BACKEND.zoneText(ctx, text);
|
||||
}
|
||||
}
|
||||
|
||||
public static void zoneValue(long value) {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
final long ctx = STACK.get().peek();
|
||||
if (ctx != ZoneStack.EMPTY && ctx != 0L) {
|
||||
BACKEND.zoneValue(ctx, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean threadNameRefreshed;
|
||||
|
||||
public static void frameMark() {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
if (!threadNameRefreshed) {
|
||||
threadNameRefreshed = true;
|
||||
BACKEND.setCurrentThreadName(Thread.currentThread().getName());
|
||||
}
|
||||
SECTIONS.poll();
|
||||
BACKEND.frameMark();
|
||||
}
|
||||
|
||||
public static void frameMark(String name) {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
Long ptr = FRAME_NAMES.get(name);
|
||||
if (ptr == null) {
|
||||
ptr = FRAME_NAMES.computeIfAbsent(name, BACKEND::internFrameName);
|
||||
}
|
||||
if (ptr != 0) BACKEND.frameMark(ptr);
|
||||
}
|
||||
|
||||
public static void plotInt(String name, long value) {
|
||||
plotInt(name, value, TracyBackend.PLOT_FORMAT_NUMBER);
|
||||
}
|
||||
|
||||
public static void plotInt(String name, long value, int format) {
|
||||
if (!ENABLED || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
|
||||
final long ptr = plotName(name, format);
|
||||
if (ptr != 0) BACKEND.plotInt(ptr, value);
|
||||
}
|
||||
|
||||
public static void plot(String name, double value) {
|
||||
if (!ENABLED || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
|
||||
final long ptr = plotName(name, TracyBackend.PLOT_FORMAT_NUMBER);
|
||||
if (ptr != 0) BACKEND.plot(ptr, value);
|
||||
}
|
||||
|
||||
private static long plotName(String name, int format) {
|
||||
Long ptr = PLOT_NAMES.get(name);
|
||||
if (ptr == null) {
|
||||
ptr = PLOT_NAMES.computeIfAbsent(name, n -> BACKEND.internPlotName(n, format));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public static long plotHandle(String name, int format) {
|
||||
if (!ENABLED || SHUTTING_DOWN) return 0;
|
||||
return plotName(name, format);
|
||||
}
|
||||
|
||||
public static long plotHandle(String name) {
|
||||
return plotHandle(name, TracyBackend.PLOT_FORMAT_NUMBER);
|
||||
}
|
||||
|
||||
public static void plotInt(long handle, long value) {
|
||||
if (handle == 0 || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
|
||||
BACKEND.plotInt(handle, value);
|
||||
}
|
||||
|
||||
public static void plot(long handle, double value) {
|
||||
if (handle == 0 || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
|
||||
BACKEND.plot(handle, value);
|
||||
}
|
||||
|
||||
public static void message(String text) {
|
||||
message(text, TracyBackend.SEVERITY_INFO);
|
||||
}
|
||||
|
||||
public static void message(String text, int severity) {
|
||||
if (!ENABLED || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
|
||||
BACKEND.message(text, severity);
|
||||
}
|
||||
|
||||
public static long sectionEnter(int category, String text) {
|
||||
if (!ENABLED || SHUTTING_DOWN) return 0L;
|
||||
return SECTIONS.enter(category, text);
|
||||
}
|
||||
|
||||
public static void sectionLeave(long token) {
|
||||
if (token == 0L || !ENABLED || SHUTTING_DOWN) return;
|
||||
SECTIONS.leave(token);
|
||||
}
|
||||
|
||||
public static boolean isConnected() {
|
||||
return ENABLED && BACKEND.isConnected();
|
||||
}
|
||||
|
||||
private static boolean gpuInitAttempted;
|
||||
private static boolean gpuReady;
|
||||
@Setter private static boolean gpuZonesEnabled = true;
|
||||
|
||||
public static void gpuCollect() {
|
||||
if (!ENABLED) return;
|
||||
if (!gpuInitAttempted) {
|
||||
gpuInitAttempted = true;
|
||||
gpuReady = BACKEND.gpuInit();
|
||||
}
|
||||
if (gpuReady) BACKEND.gpuCollect();
|
||||
}
|
||||
|
||||
private static final class AllocBean {
|
||||
static final ThreadMXBean BEAN;
|
||||
static {
|
||||
ThreadMXBean bean = null;
|
||||
try {
|
||||
if (ManagementFactory.getThreadMXBean() instanceof ThreadMXBean sun && sun.isThreadAllocatedMemorySupported()) {
|
||||
bean = sun;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
BEAN = bean;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ThreadLocal<long[]> PREV_ALLOC = ThreadLocal.withInitial(() -> new long[1]);
|
||||
|
||||
public static void plotAllocRate(long plotHandle) {
|
||||
if (!ENABLED || SHUTTING_DOWN || AllocBean.BEAN == null) return;
|
||||
if (!CaptureGate.markersThisFrame) {
|
||||
PREV_ALLOC.get()[0] = 0;
|
||||
return;
|
||||
}
|
||||
final long now = AllocBean.BEAN.getThreadAllocatedBytes(Thread.currentThread().threadId());
|
||||
if (now < 0) return;
|
||||
final long[] prev = PREV_ALLOC.get();
|
||||
if (prev[0] != 0) {
|
||||
plotInt(plotHandle, now - prev[0]);
|
||||
}
|
||||
prev[0] = now;
|
||||
}
|
||||
|
||||
private static List<GarbageCollectorMXBean> gcBeans;
|
||||
private static boolean gcBaselineValid;
|
||||
private static long prevGcCount;
|
||||
private static long prevGcTimeMs;
|
||||
private static long gcCountHandle;
|
||||
private static long gcTimeHandle;
|
||||
|
||||
public static void plotGcStats() {
|
||||
if (!ENABLED || SHUTTING_DOWN) return;
|
||||
if (!CaptureGate.markersThisFrame) {
|
||||
gcBaselineValid = false;
|
||||
return;
|
||||
}
|
||||
if (gcBeans == null) {
|
||||
gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
|
||||
gcCountHandle = plotHandle("gcCount");
|
||||
gcTimeHandle = plotHandle("gcTimeMs");
|
||||
}
|
||||
long count = 0;
|
||||
long timeMs = 0;
|
||||
for (int i = 0; i < gcBeans.size(); i++) {
|
||||
final GarbageCollectorMXBean bean = gcBeans.get(i);
|
||||
final long c = bean.getCollectionCount();
|
||||
final long t = bean.getCollectionTime();
|
||||
if (c > 0) count += c;
|
||||
if (t > 0) timeMs += t;
|
||||
}
|
||||
if (gcBaselineValid) {
|
||||
plotInt(gcCountHandle, count - prevGcCount);
|
||||
plotInt(gcTimeHandle, timeMs - prevGcTimeMs);
|
||||
if (count > prevGcCount) {
|
||||
message("GC: +" + (count - prevGcCount) + " collections, +" + (timeMs - prevGcTimeMs) + " ms");
|
||||
}
|
||||
}
|
||||
gcBaselineValid = true;
|
||||
prevGcCount = count;
|
||||
prevGcTimeMs = timeMs;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
public interface TracyBackend {
|
||||
int PLOT_FORMAT_NUMBER = 0;
|
||||
int PLOT_FORMAT_MEMORY = 1;
|
||||
int PLOT_FORMAT_PERCENTAGE = 2;
|
||||
|
||||
int SEVERITY_TRACE = 0;
|
||||
int SEVERITY_DEBUG = 1;
|
||||
int SEVERITY_INFO = 2;
|
||||
int SEVERITY_WARNING = 3;
|
||||
int SEVERITY_ERROR = 4;
|
||||
int SEVERITY_FATAL = 5;
|
||||
|
||||
boolean init();
|
||||
|
||||
long internSrcLoc(String name, int color);
|
||||
long dynamicSrcLoc();
|
||||
|
||||
long beginZone(long srcLoc);
|
||||
void endZone(long ctx);
|
||||
|
||||
void zoneText(long ctx, String text);
|
||||
void zoneValue(long ctx, long value);
|
||||
|
||||
void frameMark();
|
||||
void frameMark(long namePtr);
|
||||
|
||||
long internFrameName(String name);
|
||||
long internPlotName(String name, int format);
|
||||
|
||||
void plotInt(long namePtr, long value);
|
||||
void plot(long namePtr, double value);
|
||||
|
||||
void message(String text, int severity);
|
||||
default void message(String text) { message(text, SEVERITY_INFO); }
|
||||
|
||||
long sectionEnter(int category, String text);
|
||||
void sectionLeave(long id);
|
||||
void sectionSetup(int category, String name);
|
||||
|
||||
void setCurrentThreadName(String name);
|
||||
boolean isConnected();
|
||||
void shutdown();
|
||||
|
||||
boolean gpuInit();
|
||||
boolean gpuBeginZone(long srcLoc);
|
||||
void gpuEndZone();
|
||||
void gpuCollect();
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
public final class ZoneStack {
|
||||
public static final long EMPTY = -1L;
|
||||
public static final int MAX_DEPTH = 512;
|
||||
|
||||
private long[] stack = new long[64];
|
||||
private boolean[] gpu = new boolean[64];
|
||||
private String[] names = new String[64];
|
||||
private int depth;
|
||||
private int skipped;
|
||||
private boolean lastPoppedGpu;
|
||||
private String lastPoppedName;
|
||||
|
||||
public boolean atCap() {
|
||||
return depth == MAX_DEPTH;
|
||||
}
|
||||
|
||||
public boolean push(long ctx) {
|
||||
return push(ctx, false, null);
|
||||
}
|
||||
|
||||
public boolean push(long ctx, boolean gpuBracketed) {
|
||||
return push(ctx, gpuBracketed, null);
|
||||
}
|
||||
|
||||
public boolean push(long ctx, boolean gpuBracketed, String name) {
|
||||
if (depth == MAX_DEPTH) {
|
||||
skipped++;
|
||||
return false;
|
||||
}
|
||||
if (depth == stack.length) {
|
||||
final long[] grownStack = new long[stack.length * 2];
|
||||
System.arraycopy(stack, 0, grownStack, 0, stack.length);
|
||||
stack = grownStack;
|
||||
final boolean[] grownGpu = new boolean[gpu.length * 2];
|
||||
System.arraycopy(gpu, 0, grownGpu, 0, gpu.length);
|
||||
gpu = grownGpu;
|
||||
final String[] grownNames = new String[names.length * 2];
|
||||
System.arraycopy(names, 0, grownNames, 0, names.length);
|
||||
names = grownNames;
|
||||
}
|
||||
gpu[depth] = gpuBracketed;
|
||||
names[depth] = name;
|
||||
stack[depth++] = ctx;
|
||||
return true;
|
||||
}
|
||||
|
||||
public long pop() {
|
||||
if (skipped > 0) {
|
||||
skipped--;
|
||||
lastPoppedGpu = false;
|
||||
lastPoppedName = null;
|
||||
return EMPTY;
|
||||
}
|
||||
if (depth == 0) {
|
||||
lastPoppedGpu = false;
|
||||
lastPoppedName = null;
|
||||
return EMPTY;
|
||||
}
|
||||
lastPoppedGpu = gpu[--depth];
|
||||
lastPoppedName = names[depth];
|
||||
names[depth] = null;
|
||||
return stack[depth];
|
||||
}
|
||||
|
||||
public boolean poppedGpu() {
|
||||
return lastPoppedGpu;
|
||||
}
|
||||
|
||||
public String poppedName() {
|
||||
return lastPoppedName;
|
||||
}
|
||||
|
||||
public boolean hasSkipped() {
|
||||
return skipped > 0;
|
||||
}
|
||||
|
||||
public long peek() {
|
||||
if (skipped > 0 || depth == 0) return EMPTY;
|
||||
return stack[depth - 1];
|
||||
}
|
||||
|
||||
public int depth() {
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class AttribSnapshot {
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private static void warnOnce(String key, String fmt, Object... args) {
|
||||
if (WARN_ONCE.add(key)) GLStateManager.LOGGER.warn(fmt, args);
|
||||
}
|
||||
|
||||
public record AttribDesc(int location, int size, int type, boolean normalized, int stride, long offset, int sourceVboId, ByteBuffer readBuffer, long readBufferBaseOffset) {
|
||||
public int effectiveStride() {
|
||||
return stride != 0 ? stride : size * GLTypes.sizeBytes(type);
|
||||
}
|
||||
|
||||
public int typeSizeBytes() {
|
||||
return GLTypes.sizeBytes(type);
|
||||
}
|
||||
}
|
||||
|
||||
private final AttribDesc[] perLocation;
|
||||
private final List<ByteBuffer> allocatedBuffers;
|
||||
private boolean freed;
|
||||
|
||||
private AttribSnapshot(AttribDesc[] perLocation, List<ByteBuffer> allocatedBuffers) {
|
||||
this.perLocation = perLocation;
|
||||
this.allocatedBuffers = allocatedBuffers;
|
||||
}
|
||||
|
||||
public AttribDesc get(int location) {
|
||||
return (location >= 0 && location < perLocation.length) ? perLocation[location] : null;
|
||||
}
|
||||
|
||||
public int maxLocations() {
|
||||
return perLocation.length;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
if (freed) return;
|
||||
freed = true;
|
||||
for (ByteBuffer buf : allocatedBuffers) {
|
||||
MemoryUtilities.memFree(buf);
|
||||
}
|
||||
allocatedBuffers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Snapshot attribs over a tight vertex window {@code [firstVertex, firstVertex+vertexCount)}.
|
||||
*
|
||||
* <p>Hot path (single VBO backing every enabled attrib — the typical draw) runs without
|
||||
* allocating any hashmaps. A second distinct VBO promotes to the map-backed fallback.
|
||||
*/
|
||||
public static AttribSnapshot snapshot(int firstVertex, int vertexCount) {
|
||||
final AttribDesc[] out = new AttribDesc[VAOManager.MAX_ATTRIBS];
|
||||
final List<ByteBuffer> allocated = new ArrayList<>();
|
||||
final int prevVBO = GLStateManager.getBoundVBO();
|
||||
boolean success = false;
|
||||
|
||||
// Pass 1 — unioned [start,end] per vboId. Hot path: one source VBO, zero allocations.
|
||||
int rangeSoleVboId = 0;
|
||||
long rangeSoleStart = Long.MAX_VALUE;
|
||||
long rangeSoleEnd = Long.MIN_VALUE;
|
||||
Int2ObjectOpenHashMap<long[]> vboRanges = null;
|
||||
|
||||
final long lastVtx = (long) firstVertex + vertexCount - 1;
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
final VAOManager.Attrib a = VAOManager.get(i);
|
||||
if (a == null || !a.enabled || a.vboId == 0) continue;
|
||||
final int stride = a.effectiveStride();
|
||||
final long typeBytes = (long) a.size * GLTypes.sizeBytes(a.type);
|
||||
final long start = a.offset + (long) firstVertex * stride;
|
||||
final long end = a.offset + lastVtx * stride + typeBytes;
|
||||
|
||||
if (vboRanges != null) {
|
||||
long[] r = vboRanges.get(a.vboId);
|
||||
if (r == null) {
|
||||
vboRanges.put(a.vboId, new long[] { start, end });
|
||||
} else {
|
||||
if (start < r[0]) r[0] = start;
|
||||
if (end > r[1]) r[1] = end;
|
||||
}
|
||||
} else if (rangeSoleVboId == 0 || rangeSoleVboId == a.vboId) {
|
||||
rangeSoleVboId = a.vboId;
|
||||
if (start < rangeSoleStart) rangeSoleStart = start;
|
||||
if (end > rangeSoleEnd) rangeSoleEnd = end;
|
||||
} else {
|
||||
// Spill: second distinct vboId. Promote the accumulated range to the map.
|
||||
vboRanges = new Int2ObjectOpenHashMap<>();
|
||||
vboRanges.put(rangeSoleVboId, new long[] { rangeSoleStart, rangeSoleEnd });
|
||||
vboRanges.put(a.vboId, new long[] { start, end });
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2 — readback cache. Same spill pattern.
|
||||
int readSoleVboId = 0;
|
||||
ByteBuffer readSoleBuf = null;
|
||||
long readSoleOffset = 0;
|
||||
Int2ObjectOpenHashMap<ByteBuffer> vboBuffers = null;
|
||||
Int2LongOpenHashMap vboReadOffsets = null;
|
||||
|
||||
try {
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
final VAOManager.Attrib a = VAOManager.get(i);
|
||||
if (a == null || !a.enabled) continue;
|
||||
|
||||
if (a.vboId != 0) {
|
||||
ByteBuffer buf;
|
||||
long readOffset;
|
||||
if (vboBuffers != null) {
|
||||
buf = vboBuffers.get(a.vboId);
|
||||
readOffset = buf != null ? vboReadOffsets.get(a.vboId) : 0;
|
||||
} else if (readSoleVboId == a.vboId) {
|
||||
buf = readSoleBuf;
|
||||
readOffset = readSoleOffset;
|
||||
} else {
|
||||
buf = null;
|
||||
readOffset = 0;
|
||||
}
|
||||
|
||||
if (buf == null) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
|
||||
final int bufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (bufferSize <= 0) {
|
||||
warnOnce("vbo-orphaned", "[AttribSnapshot] GL_BUFFER_SIZE={} for VBO {} -- skipping capture", bufferSize, a.vboId);
|
||||
return null;
|
||||
}
|
||||
|
||||
final long rStart;
|
||||
final long rEnd;
|
||||
if (vboRanges != null) {
|
||||
final long[] range = vboRanges.get(a.vboId);
|
||||
rStart = range[0];
|
||||
rEnd = range[1];
|
||||
} else {
|
||||
rStart = rangeSoleStart;
|
||||
rEnd = rangeSoleEnd;
|
||||
}
|
||||
if (rEnd > bufferSize) {
|
||||
warnOnce("vbo-overrun", "[AttribSnapshot] attrib range {}..{} exceeds VBO {} size {} -- skipping capture", rStart, rEnd, a.vboId, bufferSize);
|
||||
return null;
|
||||
}
|
||||
final long ro = Math.max(0, rStart);
|
||||
final int rs = (int) (rEnd - ro);
|
||||
if (rs <= 0) {
|
||||
warnOnce("vbo-empty-range", "[AttribSnapshot] empty attrib range {}..{} for VBO {} -- skipping capture", rStart, rEnd, a.vboId);
|
||||
return null;
|
||||
}
|
||||
|
||||
buf = MemoryUtilities.memAlloc(rs);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ARRAY_BUFFER, ro, buf);
|
||||
allocated.add(buf);
|
||||
readOffset = ro;
|
||||
|
||||
if (vboBuffers != null) {
|
||||
vboBuffers.put(a.vboId, buf);
|
||||
vboReadOffsets.put(a.vboId, ro);
|
||||
} else if (readSoleVboId == 0) {
|
||||
readSoleVboId = a.vboId;
|
||||
readSoleBuf = buf;
|
||||
readSoleOffset = ro;
|
||||
} else {
|
||||
vboBuffers = new Int2ObjectOpenHashMap<>();
|
||||
vboReadOffsets = new Int2LongOpenHashMap();
|
||||
vboBuffers.put(readSoleVboId, readSoleBuf);
|
||||
vboReadOffsets.put(readSoleVboId, readSoleOffset);
|
||||
vboBuffers.put(a.vboId, buf);
|
||||
vboReadOffsets.put(a.vboId, ro);
|
||||
}
|
||||
}
|
||||
out[i] = new AttribDesc(i, a.size, a.type, a.normalized, a.stride, a.offset, a.vboId, buf, readOffset);
|
||||
} else if (a.clientPointer != null) {
|
||||
out[i] = new AttribDesc(i, a.size, a.type, a.normalized, a.stride, a.clientPointer.position(), 0, a.clientPointer, 0L);
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
return new AttribSnapshot(out, allocated);
|
||||
} finally {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
if (!success) {
|
||||
for (ByteBuffer b : allocated) MemoryUtilities.memFree(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
|
||||
/**
|
||||
* A class that stores multiple vertex formats & their corresponding buffers.
|
||||
*/
|
||||
public final class DisplayListVBO {
|
||||
|
||||
private final SubVBO[] vbos;
|
||||
private final int[] extVbos;
|
||||
|
||||
DisplayListVBO(SubVBO[] vbos) {
|
||||
this(vbos, null);
|
||||
}
|
||||
|
||||
DisplayListVBO(SubVBO[] vbos, int[] extVbos) {
|
||||
this.vbos = vbos;
|
||||
this.extVbos = extVbos;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
for (SubVBO vbo : vbos) {
|
||||
vbo.delete();
|
||||
}
|
||||
if (extVbos != null) {
|
||||
for (int extVbo : extVbos) {
|
||||
if (extVbo != 0) GLStateManager.glDeleteBuffers(extVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SubVBO[] getVBOs() {
|
||||
return vbos;
|
||||
}
|
||||
|
||||
public SubVBO getVBO(int index) {
|
||||
return vbos[index];
|
||||
}
|
||||
|
||||
public void render(int index) {
|
||||
vbos[index].render();
|
||||
}
|
||||
|
||||
public static final class SubVBO {
|
||||
|
||||
private final IVertexArrayObject vao;
|
||||
private final int drawMode;
|
||||
private final int start;
|
||||
private final int count;
|
||||
private final int vertexFlags;
|
||||
private int pendingExtVbo = 0;
|
||||
|
||||
void setPendingExtVbo(int extVbo) { this.pendingExtVbo = extVbo; }
|
||||
|
||||
public SubVBO(IVertexArrayObject vao, int drawMode, int start, int count, int vertexFlags) {
|
||||
this.vao = vao;
|
||||
this.drawMode = drawMode;
|
||||
this.start = start;
|
||||
this.count = count;
|
||||
this.vertexFlags = vertexFlags;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getDrawMode() {
|
||||
return drawMode;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
vao.delete();
|
||||
}
|
||||
|
||||
public void render() {
|
||||
if (vao == null) return;
|
||||
vao.bind();
|
||||
if (pendingExtVbo != 0) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, pendingExtVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
pendingExtVbo = 0;
|
||||
}
|
||||
if (count <= 0) { vao.unbind(); return; }
|
||||
VAOManager.setCurrentVertexFlags(vertexFlags);
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
vao.draw(drawMode, start, count);
|
||||
} finally {
|
||||
FfpExtendedAttribs.endInternalDraw();
|
||||
}
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
|
||||
public IVertexArrayObject getVAO() {
|
||||
return vao;
|
||||
}
|
||||
}
|
||||
}
|
||||
-221
@@ -1,221 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.BaseVAO;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexedVAO;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VAOManager;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vbo.IVertexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.memCopy;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.nmemFree;
|
||||
|
||||
/**
|
||||
* Compiles multiple ByteBuffers of the same Vertex format into a VBO. Any buffer added to the builder will be freed afterward.
|
||||
*/
|
||||
public final class DisplayListVBOBuilder {
|
||||
|
||||
// Removes the need for a HashMap. Since it's only an array of 16 values, this should be faster
|
||||
private final List<FormatData>[] formats = new List[VertexFlags.BITSET_SIZE];
|
||||
private int count;
|
||||
|
||||
public DisplayListVBOBuilder addDraw(VertexFormat format, int drawMode, List<ByteBuffer> buffers) {
|
||||
List<FormatData> data = formats[format.getVertexFlags()];
|
||||
if (data == null) {
|
||||
data = new ArrayList<>();
|
||||
formats[format.getVertexFlags()] = data;
|
||||
}
|
||||
data.add(new FormatData(buffers, drawMode, count));
|
||||
count++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayListVBOBuilder addDraws(List<AccumulatedDraw> draws) {
|
||||
for (AccumulatedDraw draw : draws) {
|
||||
addDraw(draw.format, draw.drawMode, draw.drawBuffers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayListVBO build() {
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
final DisplayListVBO.SubVBO[] vbos = new DisplayListVBO.SubVBO[count];
|
||||
final ArrayList<ByteBuffer> allBuffers = new ArrayList<>();
|
||||
final IntArrayList extVbos = new IntArrayList();
|
||||
for (int i = 0; i < VertexFlags.BITSET_SIZE; i++) {
|
||||
List<FormatData> formatData = formats[i];
|
||||
if (formatData == null) continue;
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[i];
|
||||
final boolean wantExt = extHandler != null && format.hasTexture();
|
||||
if (extHandler == null && GLSMConfig.extendedAttribsExpected && format.hasTexture()) {
|
||||
GLStateManager.warnOnce("dl-ext-no-handler",
|
||||
"Textured display list compiled before the immediate ext-attribute handler was installed! POM data will be missing for this geometry.");
|
||||
}
|
||||
int start = 0;
|
||||
final IVertexArrayObject vao = VAOManager.createStorageVAO(format, -1, 0); // drawMode will be ignored
|
||||
final IVertexBuffer vbo = vao.getVBO();
|
||||
|
||||
// SubVBO slots that need the ext attributes attached on their first draw.
|
||||
final IntArrayList extDrawIndexes = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extStarts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extCounts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extPrims = wantExt ? new IntArrayList() : null;
|
||||
|
||||
for (FormatData data : formatData) {
|
||||
int vertexCount;
|
||||
final List<ByteBuffer> drawBuffers = data.buffers;
|
||||
if (drawBuffers.size() == 1) {
|
||||
final ByteBuffer buffer = drawBuffers.get(0);
|
||||
allBuffers.add(buffer);
|
||||
vertexCount = format.getVertexCount(buffer);
|
||||
} else {
|
||||
allBuffers.addAll(drawBuffers);
|
||||
int size = 0;
|
||||
for (ByteBuffer buffer : drawBuffers) {
|
||||
size += buffer.limit();
|
||||
}
|
||||
vertexCount = format.getVertexCount(size);
|
||||
}
|
||||
final int flags = format.getVertexFlags();
|
||||
if (data.drawMode == GL11.GL_QUADS) {
|
||||
final IVertexArrayObject indexedVAO = new IndexedVAO(vbo, IndexBuffer.convertQuadsToTrigs(start, start + vertexCount));
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6, flags);
|
||||
if (wantExt) {
|
||||
extDrawIndexes.add(data.drawIndex);
|
||||
extStarts.add(start);
|
||||
extCounts.add(vertexCount);
|
||||
extPrims.add(4);
|
||||
}
|
||||
} else if (data.drawMode == GL11.GL_TRIANGLES) {
|
||||
if (wantExt) {
|
||||
final IVertexArrayObject triVao = new BaseVAO(vbo);
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(triVao, GL11.GL_TRIANGLES, start, vertexCount, flags);
|
||||
extDrawIndexes.add(data.drawIndex);
|
||||
extStarts.add(start);
|
||||
extCounts.add(vertexCount);
|
||||
extPrims.add(3);
|
||||
} else {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLES, start, vertexCount, flags);
|
||||
}
|
||||
} else if (data.drawMode == GL11.GL_QUAD_STRIP) {
|
||||
// GL_QUAD_STRIP is removed in core profile; an even-length GL_TRIANGLE_STRIP produces the same quads
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_STRIP, start, vertexCount & ~1, flags);
|
||||
} else if (data.drawMode == GL11.GL_POLYGON) {
|
||||
// GL_POLYGON is removed in core profile; convert to GL_TRIANGLE_FAN which is equivalent for convex polygons
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_FAN, start, vertexCount, flags);
|
||||
} else {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, data.drawMode, start, vertexCount, flags);
|
||||
}
|
||||
|
||||
|
||||
start += vertexCount;
|
||||
}
|
||||
ByteBuffer bigBuffer = mergeAndDelete(allBuffers);
|
||||
vbo.allocate(bigBuffer, start);
|
||||
|
||||
if (wantExt && !extDrawIndexes.isEmpty()) {
|
||||
final int extVbo = buildExt(extHandler, format, bigBuffer, extStarts, extCounts, extPrims);
|
||||
if (extVbo != 0) {
|
||||
extVbos.add(extVbo);
|
||||
for (int e = 0; e < extDrawIndexes.size(); e++) {
|
||||
vbos[extDrawIndexes.getInt(e)].setPendingExtVbo(extVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memFree(bigBuffer);
|
||||
|
||||
allBuffers.clear();
|
||||
}
|
||||
return new DisplayListVBO(vbos, extVbos.toIntArray());
|
||||
}
|
||||
|
||||
private static int buildExt(ImmediateExtendedAttribHandler handler, VertexFormat format, ByteBuffer bigBuffer,
|
||||
IntArrayList extStarts, IntArrayList extCounts, IntArrayList extPrims) {
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int stride = format.getVertexSize();
|
||||
final int posOffset = 0;
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
|
||||
int extVerts = 0;
|
||||
for (int q = 0; q < extStarts.size(); q++) {
|
||||
extVerts = Math.max(extVerts, extStarts.getInt(q) + extCounts.getInt(q));
|
||||
}
|
||||
|
||||
final ByteBuffer ext = memCalloc(extVerts, extStride);
|
||||
final long extAddr = memAddress0(ext);
|
||||
final long srcBase = memAddress0(bigBuffer);
|
||||
for (int q = 0; q < extStarts.size(); q++) {
|
||||
final int rStart = extStarts.getInt(q);
|
||||
final int rCount = extCounts.getInt(q);
|
||||
final int rPrim = extPrims.getInt(q);
|
||||
handler.buildPacked(srcBase + (long) rStart * stride, stride, posOffset, texOffset, normalOffset, rCount, rPrim,
|
||||
extAddr + (long) rStart * extStride, extStride);
|
||||
}
|
||||
|
||||
final int extVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(extVerts * extStride);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, ext, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
memFree(ext);
|
||||
return extVbo;
|
||||
}
|
||||
|
||||
private static ByteBuffer mergeAndDelete(List<ByteBuffer> buffers) {
|
||||
if (buffers.size() == 1) {
|
||||
return buffers.get(0);
|
||||
}
|
||||
int needed = 0;
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
needed += buffer.remaining();
|
||||
}
|
||||
|
||||
ByteBuffer out = memAlloc(needed);
|
||||
long dst = memAddress0(out);
|
||||
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
final int len = buffer.limit();
|
||||
final long address = memAddress0(buffer);
|
||||
memCopy(address, dst, len);
|
||||
dst += len;
|
||||
|
||||
nmemFree(address);
|
||||
}
|
||||
|
||||
out.limit(needed);
|
||||
return out;
|
||||
}
|
||||
|
||||
private static final class FormatData {
|
||||
|
||||
private final List<ByteBuffer> buffers;
|
||||
private final int drawMode;
|
||||
private final int drawIndex;
|
||||
|
||||
public FormatData(List<ByteBuffer> buffers, int drawMode, int drawIndex) {
|
||||
this.buffers = buffers;
|
||||
this.drawMode = drawMode;
|
||||
this.drawIndex = drawIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
-311
@@ -1,311 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.CallbackTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* Records immediate mode GL calls (glBegin/glEnd/glVertex) via {@link DirectTessellator}.
|
||||
* Used both during display list compilation and for live immediate mode emulation in core profile.
|
||||
*/
|
||||
public final class ImmediateModeRecorder {
|
||||
private static final CallbackTessellator tessellator = new CallbackTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
|
||||
private record ThreadTessellator(Thread thread, DirectTessellator tessellator) {}
|
||||
|
||||
private static final Queue<ThreadTessellator> threadTessellators = new ConcurrentLinkedQueue<>();
|
||||
private static final ThreadLocal<DirectTessellator> threadTessellator = ThreadLocal.withInitial(() -> {
|
||||
final DirectTessellator t = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
threadTessellators.add(new ThreadTessellator(Thread.currentThread(), t));
|
||||
cleanupOrphanTessellators();
|
||||
return t;
|
||||
});
|
||||
|
||||
private ImmediateModeRecorder() {
|
||||
|
||||
}
|
||||
|
||||
public static int getThreadTessellatorCount() {
|
||||
return threadTessellators.size();
|
||||
}
|
||||
|
||||
public static synchronized void cleanupOrphanTessellators() {
|
||||
threadTessellators.removeIf(tt -> {
|
||||
if (tt.thread().isAlive()) return false;
|
||||
tt.tessellator().delete();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static DirectTessellator tessellator() {
|
||||
if (Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
return tessellator;
|
||||
}
|
||||
return threadTessellator.get();
|
||||
}
|
||||
|
||||
public static DirectTessellator getInternalTessellator() {
|
||||
return tessellator();
|
||||
}
|
||||
|
||||
public static boolean isDrawing() {
|
||||
return tessellator().isDrawing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current texture coordinate.
|
||||
* Called by GLStateManager.glTexCoord*.
|
||||
*/
|
||||
public static void setTexCoord(float s, float t) {
|
||||
tessellator().setTextureUV(s, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current lightmap coordinate as packed brightness.
|
||||
* Called by GLStateManager.glMultiTexCoord2f for unit 1 during recording/drawing.
|
||||
*/
|
||||
public static void setLightmapCoord(float s, float t) {
|
||||
tessellator().setBrightness(((int) t << 16) | ((int) s & 0xFFFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current normal vector.
|
||||
* Called by GLStateManager.glNormal*.
|
||||
*/
|
||||
public static void setNormal(float x, float y, float z) {
|
||||
tessellator().setNormal(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current color on the internal tessellator.
|
||||
* Called by GLStateManager.glColor* when inside a glBegin/glEnd block.
|
||||
*/
|
||||
public static void setColor(float r, float g, float b, float a) {
|
||||
tessellator().setColorRGBA((int) (r * 255.0f), (int) (g * 255.0f), (int) (b * 255.0f), (int) (a * 255.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording a new primitive.
|
||||
* Resets the tessellator to clear any data from the previous draw.
|
||||
*
|
||||
* @param primitiveType GL primitive type (GL_QUADS, GL_TRIANGLES, etc.)
|
||||
*/
|
||||
public static void begin(int primitiveType) {
|
||||
final DirectTessellator t = tessellator();
|
||||
t.reset();
|
||||
t.startDrawing(primitiveType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording a new primitive with initial color sync from GLSM cache.
|
||||
* Used for live immediate mode emulation only (not display list recording).
|
||||
*/
|
||||
public static void beginLive(int primitiveType) {
|
||||
final DirectTessellator t = tessellator();
|
||||
t.reset();
|
||||
t.startDrawing(primitiveType);
|
||||
final var c = GLStateManager.getColor();
|
||||
t.setColorRGBA((int) (c.getRed() * 255.0f), (int) (c.getGreen() * 255.0f), (int) (c.getBlue() * 255.0f), (int) (c.getAlpha() * 255.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* End the current primitive, convert to quads/lines, and return them immediately.
|
||||
* This allows the caller to create AccumulatedDraw at the correct command position.
|
||||
*
|
||||
* @return Result containing quads, lines, and flags, or null if no geometry was produced
|
||||
*/
|
||||
public static DirectTessellator end() {
|
||||
final DirectTessellator t = tessellator();
|
||||
if (!t.isDrawing) {
|
||||
throw new IllegalStateException("glEnd called without glBegin");
|
||||
}
|
||||
|
||||
t.isDrawing = false;
|
||||
|
||||
if (t.isEmpty()) return null;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a vertex with current attributes.
|
||||
* Color is read from GLStateManager at call time.
|
||||
*/
|
||||
public static void vertex(float x, float y, float z) {
|
||||
final DirectTessellator t = tessellator();
|
||||
if (!t.isDrawing) {
|
||||
throw new IllegalStateException("glVertex called outside glBegin/glEnd");
|
||||
}
|
||||
t.addVertex(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any immediate mode geometry has been captured.
|
||||
*
|
||||
* <p>Note: With immediate flush mode, this will typically return false as geometry
|
||||
* is returned from end() immediately. This method is kept for edge cases
|
||||
* where primitives might fail to convert (e.g., incomplete quads).
|
||||
*/
|
||||
public static boolean hasGeometry() {
|
||||
return tessellator().vertexCount != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all state including current texcoord/normal.
|
||||
* Called when starting a new display list.
|
||||
*/
|
||||
public static void reset() {
|
||||
tessellator().reset();
|
||||
}
|
||||
|
||||
private static final int LOC_POSITION = Usage.POSITION.getAttributeLocation();
|
||||
private static final int LOC_COLOR = Usage.COLOR.getAttributeLocation();
|
||||
private static final int LOC_UV0 = Usage.PRIMARY_UV.getAttributeLocation();
|
||||
private static final int LOC_UV1 = Usage.SECONDARY_UV.getAttributeLocation();
|
||||
private static final int LOC_NORMAL = Usage.NORMAL.getAttributeLocation();
|
||||
private static final int NUM_ATTRIBS = 1 + Arrays.stream(Usage.values())
|
||||
.mapToInt(Usage::getAttributeLocation)
|
||||
.max().orElse(0);
|
||||
|
||||
private static final class AttribReader {
|
||||
ByteBuffer data;
|
||||
int stride;
|
||||
int baseOffset;
|
||||
int size;
|
||||
int loc;
|
||||
int type;
|
||||
boolean normalized;
|
||||
}
|
||||
|
||||
private static final AttribReader[] readers = new AttribReader[NUM_ATTRIBS];
|
||||
private static int nonPositionCount;
|
||||
private static final int[] nonPositionIndices = new int[NUM_ATTRIBS - 1];
|
||||
private static int positionReaderIndex = -1;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < NUM_ATTRIBS; i++) {
|
||||
readers[i] = new AttribReader();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a glDrawArrays call during display list recording.
|
||||
* Reads vertex data from VBOs or client memory referenced by VertexAttribState and converts to immediate mode geometry.
|
||||
*/
|
||||
public static DirectTessellator processDrawArraysFromAttribs(int mode, int first, int count) {
|
||||
final AttribSnapshot snap = AttribSnapshot.snapshot(first, count);
|
||||
if (snap == null) return null;
|
||||
try {
|
||||
prepareReaders(snap);
|
||||
if (positionReaderIndex < 0) return null;
|
||||
return readVerticesFromBuffer(mode, first, count);
|
||||
} finally {
|
||||
snap.free();
|
||||
}
|
||||
}
|
||||
|
||||
private static void prepareReaders(AttribSnapshot snap) {
|
||||
nonPositionCount = 0;
|
||||
positionReaderIndex = -1;
|
||||
|
||||
for (int i = 0; i < NUM_ATTRIBS; i++) {
|
||||
final AttribSnapshot.AttribDesc d = snap.get(i);
|
||||
if (d == null) continue;
|
||||
|
||||
final AttribReader r = readers[i];
|
||||
r.data = d.readBuffer();
|
||||
r.stride = d.effectiveStride();
|
||||
r.baseOffset = (int) (d.offset() - d.readBufferBaseOffset());
|
||||
r.size = d.size();
|
||||
r.loc = i;
|
||||
r.type = d.type();
|
||||
r.normalized = d.normalized();
|
||||
|
||||
if (i == LOC_POSITION) {
|
||||
positionReaderIndex = i;
|
||||
} else {
|
||||
nonPositionIndices[nonPositionCount++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DirectTessellator readVerticesFromBuffer(int mode, int first, int count) {
|
||||
begin(mode);
|
||||
final DirectTessellator t = tessellator();
|
||||
|
||||
for (int i = first; i < first + count; i++) {
|
||||
readVertexFromBuffer(t, i);
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
|
||||
public static int readIndex(ByteBuffer ebo, int indexType, int eboOffset, int i) {
|
||||
return switch (indexType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> ebo.get(eboOffset + i) & 0xFF;
|
||||
case GL11.GL_UNSIGNED_SHORT -> ebo.getShort(eboOffset + i * 2) & 0xFFFF;
|
||||
case GL11.GL_UNSIGNED_INT -> ebo.getInt(eboOffset + i * 4);
|
||||
default -> i;
|
||||
};
|
||||
}
|
||||
|
||||
private static void readVertexFromBuffer(DirectTessellator t, int vertexIndex) {
|
||||
for (int i = 0; i < nonPositionCount; i++) {
|
||||
final AttribReader r = readers[nonPositionIndices[i]];
|
||||
final int base = r.baseOffset + vertexIndex * r.stride;
|
||||
emitAttrib(t, r, base);
|
||||
}
|
||||
|
||||
if (positionReaderIndex >= 0) {
|
||||
final AttribReader r = readers[positionReaderIndex];
|
||||
final int base = r.baseOffset + vertexIndex * r.stride;
|
||||
t.addVertex(
|
||||
readComponent(r, base, 0),
|
||||
readComponent(r, base, 1),
|
||||
(r.size >= 3) ? readComponent(r, base, 2) : 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitAttrib(DirectTessellator t, AttribReader r, int base) {
|
||||
if (r.loc == LOC_NORMAL) {
|
||||
t.setNormal(
|
||||
readComponent(r, base, 0),
|
||||
readComponent(r, base, 1),
|
||||
readComponent(r, base, 2));
|
||||
} else if (r.loc == LOC_UV0) {
|
||||
t.setTextureUV(
|
||||
readComponent(r, base, 0),
|
||||
(r.size >= 2) ? readComponent(r, base, 1) : 0.0f);
|
||||
} else if (r.loc == LOC_UV1) {
|
||||
if (r.type == GL11.GL_SHORT) {
|
||||
final int s = r.data.getShort(base) & 0xFFFF;
|
||||
final int u = (r.size >= 2) ? (r.data.getShort(base + 2) & 0xFFFF) : 0;
|
||||
t.setBrightness((u << 16) | s);
|
||||
} else if (r.type == GL11.GL_FLOAT) {
|
||||
final int s = (int) r.data.getFloat(base);
|
||||
final int u = (r.size >= 2) ? (int) r.data.getFloat(base + 4) : 0;
|
||||
t.setBrightness((u << 16) | (s & 0xFFFF));
|
||||
}
|
||||
} else if (r.loc == LOC_COLOR) {
|
||||
final float cr = readComponent(r, base, 0);
|
||||
final float cg = readComponent(r, base, 1);
|
||||
final float cb = readComponent(r, base, 2);
|
||||
final float ca = (r.size >= 4) ? readComponent(r, base, 3) : 1.0f;
|
||||
t.setColorRGBA((int) (cr * 255.0f), (int) (cg * 255.0f), (int) (cb * 255.0f), (int) (ca * 255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
private static float readComponent(AttribReader r, int base, int component) {
|
||||
return VAOManager.Attrib.readComponent(r.type, r.normalized, r.data, base, component);
|
||||
}
|
||||
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class AttribLayoutKey {
|
||||
final int[] locations;
|
||||
final int[] sizes;
|
||||
final int[] types;
|
||||
final boolean[] normalized;
|
||||
private final int[] offsets;
|
||||
private final int stride;
|
||||
private final int hash;
|
||||
|
||||
public AttribLayoutKey(int[] locations, int[] sizes, int[] types, boolean[] normalized) {
|
||||
this.locations = locations;
|
||||
this.sizes = sizes;
|
||||
this.types = types;
|
||||
this.normalized = normalized;
|
||||
|
||||
final int n = locations.length;
|
||||
final int[] off = new int[n];
|
||||
int base = 0;
|
||||
int maxAlign = 1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
final int typeSize = GLTypes.sizeBytes(types[i]);
|
||||
if (typeSize > maxAlign) maxAlign = typeSize;
|
||||
base = (base + typeSize - 1) & -typeSize;
|
||||
off[i] = base;
|
||||
base += sizes[i] * typeSize;
|
||||
}
|
||||
this.offsets = off;
|
||||
this.stride = (base + maxAlign - 1) & -maxAlign;
|
||||
|
||||
int h = Arrays.hashCode(locations);
|
||||
h = 31 * h + Arrays.hashCode(sizes);
|
||||
h = 31 * h + Arrays.hashCode(types);
|
||||
h = 31 * h + Arrays.hashCode(normalized);
|
||||
this.hash = h;
|
||||
}
|
||||
|
||||
public int offset(int i) {
|
||||
return offsets[i];
|
||||
}
|
||||
|
||||
public int stride() {
|
||||
return stride;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof AttribLayoutKey k)) return false;
|
||||
return Arrays.equals(locations, k.locations)
|
||||
&& Arrays.equals(sizes, k.sizes)
|
||||
&& Arrays.equals(types, k.types)
|
||||
&& Arrays.equals(normalized, k.normalized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
public final class BatchedIndexedDrawCmd implements DisplayListCommand {
|
||||
private int sharedVAO;
|
||||
private int drawMode;
|
||||
private int indexCount;
|
||||
private int indexType;
|
||||
private long indexByteOffset;
|
||||
|
||||
public void fill(int sharedVAO, int drawMode, int indexCount, int indexType, long indexByteOffset) {
|
||||
this.sharedVAO = sharedVAO;
|
||||
this.drawMode = drawMode;
|
||||
this.indexCount = indexCount;
|
||||
this.indexType = indexType;
|
||||
this.indexByteOffset = indexByteOffset;
|
||||
}
|
||||
|
||||
public int getSharedVAO() { return sharedVAO; }
|
||||
public int getDrawMode() { return drawMode; }
|
||||
public int getIndexCount() { return indexCount; }
|
||||
public int getIndexType() { return indexType; }
|
||||
public long getIndexByteOffset() { return indexByteOffset; }
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (sharedVAO == 0) return;
|
||||
GLStateManager.glBindVertexArray(sharedVAO);
|
||||
GLStateManager.glDrawElements(drawMode, indexCount, indexType, indexByteOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BatchedIndexedDrawCmd(vao=" + sharedVAO + ", mode=" + drawMode + ", count=" + indexCount + ", type=" + indexType + ", offset=" + indexByteOffset + ")";
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
public final class IndexedDrawBatch {
|
||||
private int sharedVAO;
|
||||
private int sharedVBO;
|
||||
private int sharedEBO;
|
||||
private boolean deleted;
|
||||
|
||||
public IndexedDrawBatch(int sharedVAO, int sharedVBO, int sharedEBO) {
|
||||
this.sharedVAO = sharedVAO;
|
||||
this.sharedVBO = sharedVBO;
|
||||
this.sharedEBO = sharedEBO;
|
||||
}
|
||||
|
||||
public int getSharedVAO() { return sharedVAO; }
|
||||
public int getSharedVBO() { return sharedVBO; }
|
||||
public int getSharedEBO() { return sharedEBO; }
|
||||
|
||||
public void delete() {
|
||||
if (deleted) return;
|
||||
deleted = true;
|
||||
if (sharedVAO != 0) GLStateManager.glDeleteVertexArrays(sharedVAO);
|
||||
if (sharedVBO != 0) GLStateManager.glDeleteBuffers(sharedVBO);
|
||||
if (sharedEBO != 0) GLStateManager.glDeleteBuffers(sharedEBO);
|
||||
sharedVAO = sharedVBO = sharedEBO = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IndexedDrawBatch(vao=" + sharedVAO + ", vbo=" + sharedVBO + ", ebo=" + sharedEBO + ")";
|
||||
}
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
public final class IndexedDrawBatchBuilder {
|
||||
|
||||
private final List<IndexedDrawCapture> captures = new ArrayList<>();
|
||||
|
||||
public boolean isEmpty() {
|
||||
return captures.isEmpty();
|
||||
}
|
||||
|
||||
public void add(IndexedDrawCapture capture) {
|
||||
captures.add(capture);
|
||||
}
|
||||
|
||||
public List<IndexedDrawCapture> getCaptures() {
|
||||
return captures;
|
||||
}
|
||||
|
||||
public List<IndexedDrawBatch> build() {
|
||||
final List<IndexedDrawBatch> out = new ArrayList<>();
|
||||
if (captures.isEmpty()) return out;
|
||||
|
||||
final int prevVAO = GLStateManager.getBoundVAO();
|
||||
final int prevVBO = GLStateManager.getBoundVBO();
|
||||
final int prevEBO = GLStateManager.getBoundEBO();
|
||||
|
||||
final List<GroupResult> results = new ArrayList<>();
|
||||
boolean success = false;
|
||||
try {
|
||||
final Object2ObjectLinkedOpenHashMap<AttribLayoutKey, List<IndexedDrawCapture>> groups = new Object2ObjectLinkedOpenHashMap<>();
|
||||
for (IndexedDrawCapture c : captures) {
|
||||
groups.computeIfAbsent(c.layoutKey, k -> new ArrayList<>()).add(c);
|
||||
}
|
||||
|
||||
for (List<IndexedDrawCapture> group : groups.values()) {
|
||||
results.add(buildGroup(group));
|
||||
}
|
||||
// Placeholder fill is deferred until every group's GL work has succeeded.
|
||||
for (GroupResult r : results) r.commit();
|
||||
for (GroupResult r : results) out.add(r.batch);
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
for (GroupResult r : results) r.batch.delete();
|
||||
}
|
||||
GLStateManager.glBindVertexArray(prevVAO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private record GroupResult(IndexedDrawBatch batch, List<IndexedDrawCapture> captures,
|
||||
int[] drawModes, int[] indexCounts, long[] indexOffsets) {
|
||||
void commit() {
|
||||
final int sharedVAO = batch.getSharedVAO();
|
||||
for (int i = 0; i < captures.size(); i++) {
|
||||
captures.get(i).placeholder.fill(sharedVAO, drawModes[i], indexCounts[i], GL11.GL_UNSIGNED_INT, indexOffsets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static GroupResult buildGroup(List<IndexedDrawCapture> group) {
|
||||
long totalVertexBytes = 0;
|
||||
long totalIndexBytes = 0;
|
||||
for (IndexedDrawCapture c : group) {
|
||||
totalVertexBytes += (long) c.vertexCount * c.layoutKey.stride();
|
||||
totalIndexBytes += (long) c.indexCount * 4; // always GL_UNSIGNED_INT
|
||||
}
|
||||
if (totalVertexBytes > Integer.MAX_VALUE || totalIndexBytes > Integer.MAX_VALUE) {
|
||||
throw new IllegalStateException("batch exceeds 2GiB — split needed");
|
||||
}
|
||||
|
||||
int sharedVAO = 0;
|
||||
int sharedVBO = 0;
|
||||
int sharedEBO = 0;
|
||||
ByteBuffer eboData = null;
|
||||
boolean success = false;
|
||||
|
||||
final int n = group.size();
|
||||
final int[] drawModes = new int[n];
|
||||
final int[] indexCounts = new int[n];
|
||||
final long[] indexOffsets = new long[n];
|
||||
|
||||
try {
|
||||
sharedVAO = GLStateManager.glGenVertexArrays();
|
||||
sharedVBO = GLStateManager.glGenBuffers();
|
||||
sharedEBO = GLStateManager.glGenBuffers();
|
||||
eboData = MemoryUtilities.memAlloc((int) totalIndexBytes);
|
||||
|
||||
GLStateManager.glBindVertexArray(sharedVAO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, sharedVBO);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, totalVertexBytes, GL15.GL_STATIC_DRAW);
|
||||
|
||||
int vboBytePos = 0;
|
||||
int eboBytePos = 0;
|
||||
int vertexBase = 0;
|
||||
final long eboAddr = memAddress0(eboData);
|
||||
|
||||
for (int g = 0; g < n; g++) {
|
||||
final IndexedDrawCapture c = group.get(g);
|
||||
final int vBytes = c.vertexCount * c.layoutKey.stride();
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, vboBytePos, c.vertexData);
|
||||
|
||||
final long srcIdxAddr = memAddress0(c.indexData);
|
||||
final int idxBytes = c.indexCount * 4;
|
||||
if (vertexBase == 0) {
|
||||
// First capture (or a group that starts at 0) — indices are already
|
||||
// rebased to [0..vertexCount) in IndexedDrawCapture, so a raw bulk copy
|
||||
// is correct and strictly faster than the per-index loop.
|
||||
memCopy(srcIdxAddr, eboAddr + eboBytePos, idxBytes);
|
||||
} else {
|
||||
final long dstBase = eboAddr + eboBytePos;
|
||||
for (int i = 0; i < c.indexCount; i++) {
|
||||
memPutInt(dstBase + i * 4L, memGetInt(srcIdxAddr + i * 4L) + vertexBase);
|
||||
}
|
||||
}
|
||||
|
||||
drawModes[g] = c.drawMode;
|
||||
indexCounts[g] = c.indexCount;
|
||||
indexOffsets[g] = eboBytePos;
|
||||
|
||||
vboBytePos += vBytes;
|
||||
eboBytePos += idxBytes;
|
||||
vertexBase += c.vertexCount;
|
||||
}
|
||||
|
||||
eboData.position(0).limit((int) totalIndexBytes);
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEBO);
|
||||
GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, eboData, GL15.GL_STATIC_DRAW);
|
||||
|
||||
final AttribLayoutKey layout = group.get(0).layoutKey;
|
||||
final int stride = layout.stride();
|
||||
for (int i = 0; i < layout.locations.length; i++) {
|
||||
final int loc = layout.locations[i];
|
||||
final int size = layout.sizes[i];
|
||||
final int type = layout.types[i];
|
||||
GLStateManager.glVertexAttribPointer(loc, size, type, layout.normalized[i], stride, layout.offset(i));
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
}
|
||||
|
||||
success = true;
|
||||
return new GroupResult(new IndexedDrawBatch(sharedVAO, sharedVBO, sharedEBO),
|
||||
group, drawModes, indexCounts, indexOffsets);
|
||||
} finally {
|
||||
if (eboData != null) MemoryUtilities.memFree(eboData);
|
||||
if (!success) {
|
||||
if (sharedVAO != 0) GLStateManager.glDeleteVertexArrays(sharedVAO);
|
||||
if (sharedVBO != 0) GLStateManager.glDeleteBuffers(sharedVBO);
|
||||
if (sharedEBO != 0) GLStateManager.glDeleteBuffers(sharedEBO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-264
@@ -1,264 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.AttribSnapshot;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetLong;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutLong;
|
||||
|
||||
|
||||
/** {@link #create} returns null on any recoverable validation failure; callers must not record the placeholder. */
|
||||
public final class IndexedDrawCapture {
|
||||
public final AttribLayoutKey layoutKey;
|
||||
public final int drawMode;
|
||||
public final int vertexCount;
|
||||
public final int indexCount;
|
||||
public final ByteBuffer vertexData;
|
||||
public final ByteBuffer indexData;
|
||||
public final BatchedIndexedDrawCmd placeholder;
|
||||
private boolean freed;
|
||||
|
||||
public IndexedDrawCapture(AttribLayoutKey layoutKey, int drawMode, int vertexCount, int indexCount,
|
||||
ByteBuffer vertexData, ByteBuffer indexData,
|
||||
BatchedIndexedDrawCmd placeholder) {
|
||||
this.layoutKey = layoutKey;
|
||||
this.drawMode = drawMode;
|
||||
this.vertexCount = vertexCount;
|
||||
this.indexCount = indexCount;
|
||||
this.vertexData = vertexData;
|
||||
this.indexData = indexData;
|
||||
this.placeholder = placeholder;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
if (freed) return;
|
||||
freed = true;
|
||||
if (vertexData != null) MemoryUtilities.memFree(vertexData);
|
||||
if (indexData != null) MemoryUtilities.memFree(indexData);
|
||||
}
|
||||
|
||||
public boolean isFreed() {
|
||||
return freed;
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture create(int mode, int indicesCount, int srcIndexType, long indicesOffset, int eboId) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (eboId == 0) {
|
||||
GLStateManager.warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
|
||||
return null;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int srcIndexSize = GLTypes.sizeBytes(srcIndexType);
|
||||
if (srcIndexSize <= 0) return null;
|
||||
|
||||
final int prevEBO = GLStateManager.getBoundEBO();
|
||||
final int prevVBO = GLStateManager.getBoundVBO();
|
||||
ByteBuffer eboSrc = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (eboBufferSize <= 0) {
|
||||
GLStateManager.warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
|
||||
return null;
|
||||
}
|
||||
final long eboReadSize = (long) indicesCount * srcIndexSize;
|
||||
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
|
||||
GLStateManager.warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
|
||||
return null;
|
||||
}
|
||||
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
|
||||
return bake(mode, indicesCount, srcIndexType, MemoryUtilities.memAddress0(eboSrc));
|
||||
} finally {
|
||||
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture createFromClientIndices(int mode, int indicesCount, int srcIndexType, long indexSrcAddr, long srcByteLength) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (indexSrcAddr == 0L) return null;
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 - skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int idxSize = GLTypes.sizeBytes(srcIndexType);
|
||||
if (idxSize <= 0) return null;
|
||||
if ((long) indicesCount * idxSize > srcByteLength) {
|
||||
GLStateManager.warnOnce("client-idx-range", "[IndexedDrawCapture] index count {} ({} bytes) exceeds client buffer {} bytes - skipping", indicesCount, (long) indicesCount * idxSize, srcByteLength);
|
||||
return null;
|
||||
}
|
||||
return bake(mode, indicesCount, srcIndexType, indexSrcAddr);
|
||||
}
|
||||
|
||||
private static IndexedDrawCapture bake(int mode, int indicesCount, int srcIndexType, long indexSrcAddr) {
|
||||
AttribSnapshot snap = null;
|
||||
ByteBuffer vertexData = null;
|
||||
ByteBuffer indexData = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(indexSrcAddr, srcIndexType, indicesCount);
|
||||
if (minMax == -1L) {
|
||||
GLStateManager.warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
|
||||
return null;
|
||||
}
|
||||
final int minVtx = (int) (minMax & 0xFFFFFFFFL);
|
||||
final int maxVtx = (int) (minMax >>> 32);
|
||||
final int vertexCount = maxVtx - minVtx + 1;
|
||||
|
||||
snap = AttribSnapshot.snapshot(minVtx, vertexCount);
|
||||
if (snap == null) return null;
|
||||
|
||||
int enabledCount = 0;
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
if (snap.get(i) != null) enabledCount++;
|
||||
}
|
||||
if (enabledCount == 0) {
|
||||
GLStateManager.warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
|
||||
return null;
|
||||
}
|
||||
final int[] locations = new int[enabledCount];
|
||||
final int[] sizes = new int[enabledCount];
|
||||
final int[] types = new int[enabledCount];
|
||||
final boolean[] normalized = new boolean[enabledCount];
|
||||
int k = 0;
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
final AttribSnapshot.AttribDesc d = snap.get(i);
|
||||
if (d == null) continue;
|
||||
locations[k] = i;
|
||||
sizes[k] = d.size();
|
||||
types[k] = d.type();
|
||||
normalized[k] = d.normalized();
|
||||
k++;
|
||||
}
|
||||
final AttribLayoutKey layoutKey = new AttribLayoutKey(locations, sizes, types, normalized);
|
||||
final int tightStride = layoutKey.stride();
|
||||
|
||||
ByteBuffer sharedSrcBuffer = null;
|
||||
long sharedSrcBase = 0;
|
||||
int sharedSrcStride = -1;
|
||||
boolean tightSrc = true;
|
||||
for (int a = 0; a < enabledCount; a++) {
|
||||
final AttribSnapshot.AttribDesc d = snap.get(locations[a]);
|
||||
final int relOffset = (int) (d.offset() - d.readBufferBaseOffset());
|
||||
|
||||
final long readStart = relOffset + (long) minVtx * d.effectiveStride();
|
||||
final long readEnd = relOffset + (long) maxVtx * d.effectiveStride() + (long) d.size() * d.typeSizeBytes();
|
||||
if (readStart < 0 || readEnd > d.readBuffer().capacity()) {
|
||||
GLStateManager.warnOnce("vtx-oob", "[IndexedDrawCapture] attrib {} read extent {} exceeds snapshot {} bytes - skipping", d.location(), readEnd, d.readBuffer().capacity());
|
||||
return null;
|
||||
}
|
||||
if (a == 0) {
|
||||
sharedSrcBuffer = d.readBuffer();
|
||||
sharedSrcStride = d.effectiveStride();
|
||||
sharedSrcBase = MemoryUtilities.memAddress0(sharedSrcBuffer) + relOffset + (long) minVtx * sharedSrcStride;
|
||||
if (relOffset != layoutKey.offset(0)) tightSrc = false;
|
||||
} else if (d.readBuffer() != sharedSrcBuffer || d.effectiveStride() != sharedSrcStride || relOffset != layoutKey.offset(a)) {
|
||||
tightSrc = false;
|
||||
}
|
||||
}
|
||||
if (tightSrc && sharedSrcStride != tightStride) tightSrc = false;
|
||||
|
||||
vertexData = MemoryUtilities.memAlloc(vertexCount * tightStride);
|
||||
final long dstAddr = MemoryUtilities.memAddress0(vertexData);
|
||||
if (tightSrc) {
|
||||
MemoryUtilities.memCopy(sharedSrcBase, dstAddr, (long) vertexCount * tightStride);
|
||||
} else {
|
||||
for (int a = 0; a < enabledCount; a++) {
|
||||
final AttribSnapshot.AttribDesc d = snap.get(locations[a]);
|
||||
final int attribBytes = d.size() * d.typeSizeBytes();
|
||||
final int srcStride = d.effectiveStride();
|
||||
final long srcStart = MemoryUtilities.memAddress0(d.readBuffer()) + (d.offset() - d.readBufferBaseOffset()) + (long) minVtx * srcStride;
|
||||
final long dstStart = dstAddr + layoutKey.offset(a);
|
||||
copyStrided(srcStart, srcStride, dstStart, tightStride, attribBytes, vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
final int bakedDrawMode;
|
||||
final int bakedIndexCount;
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
final int quadCount = indicesCount / 4;
|
||||
bakedIndexCount = quadCount * 6;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.triangulateQuads(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
|
||||
bakedDrawMode = GL11.GL_TRIANGLES;
|
||||
} else {
|
||||
bakedIndexCount = indicesCount;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.widenIndices(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
|
||||
bakedDrawMode = mode;
|
||||
}
|
||||
|
||||
final BatchedIndexedDrawCmd placeholder = new BatchedIndexedDrawCmd();
|
||||
|
||||
final IndexedDrawCapture out = new IndexedDrawCapture(layoutKey, bakedDrawMode, vertexCount, bakedIndexCount, vertexData, indexData, placeholder);
|
||||
success = true;
|
||||
return out;
|
||||
} finally {
|
||||
if (snap != null) snap.free();
|
||||
if (!success) {
|
||||
if (vertexData != null) MemoryUtilities.memFree(vertexData);
|
||||
if (indexData != null) MemoryUtilities.memFree(indexData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy {@code attribBytes} per vertex from {@code src} (stride {@code srcStride}) to
|
||||
* {@code dst} (stride {@code dstStride}) for {@code vertexCount} vertices.
|
||||
*
|
||||
* <p>Specializes on the common attribute sizes (4 / 8 / 12 / 16) so the hot path is
|
||||
* one or two {@code memPut} calls per vertex instead of a JNI-ish {@code memCopy}
|
||||
* dispatch. Falls through to {@code memCopy} for exotic sizes.
|
||||
*/
|
||||
private static void copyStrided(long src, int srcStride, long dst, int dstStride, int attribBytes, int vertexCount) {
|
||||
switch (attribBytes) {
|
||||
case 4 -> {
|
||||
for (int v = 0; v < vertexCount; v++) {
|
||||
memPutInt(dst, memGetInt(src));
|
||||
src += srcStride; dst += dstStride;
|
||||
}
|
||||
}
|
||||
case 8 -> {
|
||||
for (int v = 0; v < vertexCount; v++) {
|
||||
memPutLong(dst, memGetLong(src));
|
||||
src += srcStride; dst += dstStride;
|
||||
}
|
||||
}
|
||||
case 12 -> {
|
||||
for (int v = 0; v < vertexCount; v++) {
|
||||
memPutLong(dst, memGetLong(src));
|
||||
memPutInt(dst + 8, memGetInt(src + 8));
|
||||
src += srcStride; dst += dstStride;
|
||||
}
|
||||
}
|
||||
case 16 -> {
|
||||
for (int v = 0; v < vertexCount; v++) {
|
||||
memPutLong(dst, memGetLong(src));
|
||||
memPutLong(dst + 8, memGetLong(src + 8));
|
||||
src += srcStride; dst += dstStride;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
for (int v = 0; v < vertexCount; v++) {
|
||||
MemoryUtilities.memCopy(src, dst, attribBytes);
|
||||
src += srcStride; dst += dstStride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
|
||||
final class PixelDataSnapshot {
|
||||
private PixelDataSnapshot() {}
|
||||
|
||||
static @Nullable ByteBuffer copy(@Nullable ByteBuffer pixels) {
|
||||
if (pixels == null) return null;
|
||||
final int pos = pixels.position();
|
||||
final ByteBuffer out = memAlloc(pixels.remaining()).order(ByteOrder.nativeOrder());
|
||||
out.put(pixels);
|
||||
out.flip();
|
||||
pixels.position(pos);
|
||||
return out;
|
||||
}
|
||||
|
||||
static @Nullable ByteBuffer copy(@Nullable IntBuffer pixels) {
|
||||
if (pixels == null) return null;
|
||||
final int pos = pixels.position();
|
||||
final ByteBuffer out = memAlloc(pixels.remaining() * 4).order(ByteOrder.nativeOrder());
|
||||
out.asIntBuffer().put(pixels);
|
||||
pixels.position(pos);
|
||||
return out;
|
||||
}
|
||||
|
||||
static @Nullable ByteBuffer copy(@Nullable FloatBuffer pixels) {
|
||||
if (pixels == null) return null;
|
||||
final int pos = pixels.position();
|
||||
final ByteBuffer out = memAlloc(pixels.remaining() * 4).order(ByteOrder.nativeOrder());
|
||||
out.asFloatBuffer().put(pixels);
|
||||
pixels.position(pos);
|
||||
return out;
|
||||
}
|
||||
|
||||
static @Nullable ByteBuffer copy(@Nullable DoubleBuffer pixels) {
|
||||
if (pixels == null) return null;
|
||||
final int pos = pixels.position();
|
||||
final ByteBuffer out = memAlloc(pixels.remaining() * 8).order(ByteOrder.nativeOrder());
|
||||
out.asDoubleBuffer().put(pixels);
|
||||
pixels.position(pos);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
/**
|
||||
* Command: glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels)
|
||||
* Specifies a two-dimensional texture image during display list playback.
|
||||
*/
|
||||
public record TexImage2DCmd(
|
||||
int target,
|
||||
int level,
|
||||
int internalformat,
|
||||
int width,
|
||||
int height,
|
||||
int border,
|
||||
int format,
|
||||
int type,
|
||||
@Nullable ByteBuffer pixels,
|
||||
@Nullable PixelUnpackState unpack
|
||||
) implements DisplayListCommand {
|
||||
|
||||
public TexImage2DCmd {
|
||||
if (pixels == null) {
|
||||
unpack = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (pixels == null) {
|
||||
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, (ByteBuffer) null);
|
||||
return;
|
||||
}
|
||||
GLStateManager.forcePixelUnpackState(unpack);
|
||||
try {
|
||||
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
} finally {
|
||||
GLStateManager.restorePixelUnpackState(unpack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
if (pixels != null) {
|
||||
memFree(pixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
|
||||
return String.format("TexImage2D(target=%s, level=%d, internalformat=%s, %dx%d, border=%d, format=%s, type=%s, data=%s, unpack=%s)",
|
||||
GLDebug.getTextureTargetName(target), level, GLDebug.getTextureFormatName(internalformat),
|
||||
width, height, border, GLDebug.getTextureFormatName(format), GLTypes.name(type), dataStr, unpack);
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
/**
|
||||
* Command: glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels)
|
||||
* Updates a portion of an existing two-dimensional texture image during display list playback.
|
||||
*/
|
||||
public record TexSubImage2DCmd(
|
||||
int target,
|
||||
int level,
|
||||
int xoffset,
|
||||
int yoffset,
|
||||
int width,
|
||||
int height,
|
||||
int format,
|
||||
int type,
|
||||
@Nullable ByteBuffer pixels,
|
||||
@Nullable PixelUnpackState unpack
|
||||
) implements DisplayListCommand {
|
||||
|
||||
public TexSubImage2DCmd {
|
||||
if (pixels == null) {
|
||||
unpack = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (pixels == null) {
|
||||
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (ByteBuffer) null);
|
||||
return;
|
||||
}
|
||||
GLStateManager.forcePixelUnpackState(unpack);
|
||||
try {
|
||||
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
} finally {
|
||||
GLStateManager.restorePixelUnpackState(unpack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
if (pixels != null) {
|
||||
memFree(pixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
|
||||
return String.format("TexSubImage2D(target=%s, level=%d, offset=(%d,%d), %dx%d, format=%s, type=%s, data=%s, unpack=%s)",
|
||||
GLDebug.getTextureTargetName(target), level, xoffset, yoffset, width, height,
|
||||
GLDebug.getTextureFormatName(format), GLTypes.name(type), dataStr, unpack);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1504 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user