mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-14 17:24:43 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
701f55b5a5 | ||
|
|
6563c7c0ce | ||
|
|
52dae51121 | ||
|
|
b42eb9a6b2 | ||
|
|
5925c28d29 | ||
|
|
fedabd0fb6 |
No files matched your search
@@ -39,7 +39,6 @@ body:
|
||||
- Java 19
|
||||
- Java 20
|
||||
- Java 21
|
||||
- Java 25
|
||||
- Other (Please Specify)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
+2
-1
@@ -36,4 +36,5 @@ addon.local.gradle
|
||||
addon.local.gradle.kts
|
||||
addon.late.local.gradle
|
||||
addon.late.local.gradle.kts
|
||||
local/
|
||||
/*.csv
|
||||
!gl_functions.csv
|
||||
@@ -7,41 +7,40 @@
|
||||
* It's a cute angle -mitchej123
|
||||
* It's the angel that pulls you out of OptiFine's hell -Roadhog360
|
||||
|
||||
## **Beta Quality - Here there be Dragons**
|
||||
* Shaders - Beta
|
||||
* Complementary + Euphoria Patches is the primary compatibility target
|
||||
* Sildurs, Seus, and any other shaders might work
|
||||
## **Beta Quality - Here there be Dragons**
|
||||
* Shaders sneak preview - Please don't report issues yet, but please do submit fixes
|
||||
* Complementary & BSL mostly function, but have some bugs
|
||||
* Sildurs, Seus, and any other shader probably doesn't work
|
||||
|
||||
|
||||
# Required Dependencies
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.9.0
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.14
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.4.2
|
||||
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
|
||||
|
||||
# Known (temporary) Incompatibilities
|
||||
* Forge Relocation - Currently non Thread Safe; safe if not in use
|
||||
* ProjectRed Frames - Currently non Thread Safe; safe if not in use
|
||||
* ProjectRed Frames - Currently non Thread Safe; safe if not in use'
|
||||
|
||||
# Potential Incompatibilities
|
||||
* EndlessIDs - Compat commits have been made, might work, might not - not fully tested
|
||||
|
||||
# Known Incompatibilities
|
||||
* FalseTweaks - Not currently planned (Looking for volunteers) [Might work if you effectively disable most of the mod]
|
||||
* SmoothFont - Not currently planned (Looking for volunteers)
|
||||
* RPLE - Not currently planned (Looking for volunteers)
|
||||
* LiteLoader - Install [LiteloaderLoader](https://github.com/Midnight145/LiteloaderLoader)
|
||||
|
||||
# Permanent Incompatibilities
|
||||
* OptiFine [Disabled, won't fix]
|
||||
* FastCraft [Disabled, won't fix]
|
||||
* BetterFPS [Disabled, won't fix]
|
||||
* OptimizationsAndTweaks [Disabled, won't fix]
|
||||
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - Use 1.2.2+
|
||||
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - use 1.2.2+
|
||||
* [LWJGL3ify 1.5.4 and below](https://github.com/GTNewHorizons/lwjgl3ify/releases/) - Use the latest version available, 1.5.7+
|
||||
* [ArchaicFix 0.6.2 and below](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) - Use 0.7.0 or above
|
||||
* [Hodgepodge 2.4.3 and below](https://github.com/GTNewHorizons/Hodgepodge/releases) - Use 2.4.5 or above
|
||||
* [Hodgepodge 2.4.3 and below](https://github.com/GTNewHorizons/Hodgepodge/releases) - Use 0.4.5 or above
|
||||
* 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
|
||||
|
||||
# Credits
|
||||
* ShadersMod
|
||||
@@ -65,7 +64,7 @@
|
||||
* Makamys for [Neodymium](https://github.com/makamys/Neodymium) - Various Tessellator -> Quad functionality taken under the LGPL 3
|
||||
* MCPatcherForge
|
||||
* Original [MCPatcher](https://bitbucket.org/prupe/mcpatcher/src/master/): Paul Rupe
|
||||
* [Forge version](https://github.com/mist475/MCPatcherForge): Mist475 under LGPL 3.0 with default ctm textures for glass and bookshelves by DarkBum under CC-BY-SA
|
||||
* [Forge version](https://bitbucket.org/prupe/mcpatcher/src/master/): Mist475 under LGPL 3.0 with default ctm textures for glass and bookshelves by DarkBum under CC-BY-SA
|
||||
* HUDCaching
|
||||
* Created by [Moulberry](https://github.com/Moulberry/MCHUDCaching), backported from [Patcher](https://github.com/Sk1erLLC/Patcher) mod by Alexdoru under the CC-BY-SA-NC 4.0 license
|
||||
* Angelica
|
||||
@@ -83,5 +82,3 @@
|
||||
* Code from both glsl-transformation-lib and Monocle taken under their respective LGPL 3.0 licenses
|
||||
* FalseTweaks
|
||||
* FalsePattern for [FalseTweaks](https://github.com/falsepattern/falsetweaks) - Item Rendering Optimizations taken from FalseTweaks under the LGPL 3.0 license
|
||||
* Beddium
|
||||
* Venn and Falsepattern for [Beddium](https://github.com/vfx-dev/Beddium) - MinimapRenderer, SafeChunkTracker, and some debug commands adapted under the LGPL 3.0 License
|
||||
+2
-22
@@ -4,10 +4,6 @@ plugins {
|
||||
|
||||
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.redirectorLogspam=true")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
@@ -15,24 +11,17 @@ tasks.test {
|
||||
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 {
|
||||
val projectVersion = project.version.toString()
|
||||
inputs.property("version", projectVersion)
|
||||
inputs.property("version", project.version.toString())
|
||||
filesMatching("META-INF/rfb-plugin/*") {
|
||||
expand("version" to projectVersion)
|
||||
expand("version" to project.version.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +31,3 @@ tasks.register<Copy>("copyDependencies") {
|
||||
from(configurations.default)
|
||||
into("testDependencies")
|
||||
}
|
||||
|
||||
val embedOnly: Configuration by configurations
|
||||
|
||||
tasks.shadowJar {
|
||||
from(embedOnly.map(::zipTree))
|
||||
relocate("com.mitchej123", "com.mitchej123")
|
||||
relocate("org.embeddedt", "org.embeddedt")
|
||||
}
|
||||
|
||||
+32
-54
@@ -46,59 +46,45 @@ configurations {
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
// 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.github.GTNewHorizons:Hodgepodge:2.5.78")
|
||||
|
||||
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.7") { transitive = false }
|
||||
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:0.4.0") { transitive = false }
|
||||
|
||||
compileOnly("org.projectlombok:lombok:1.18.42") { transitive = false }
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.42")
|
||||
compileOnly("org.projectlombok:lombok:1.18.22") { transitive = false }
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.22")
|
||||
|
||||
// Use modern models+textures to inject QuadProviders
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:TX-Loader:1.7.2:dev')
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.9.10:dev")
|
||||
|
||||
compileOnly('org.jetbrains:annotations:24.0.1')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.5.20:dev")
|
||||
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
|
||||
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-26.g5ee7d96-GTNH") {
|
||||
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-4.g6b42bca") {
|
||||
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')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.6.45-GTNH:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.3.10: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 }
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles:1.0.4:dev")
|
||||
compileOnly("com.github.GTNewHorizons:twilightforest:2.6.35: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:TinkersConstruct:1.12.14-GTNH:dev") { transitive = false }
|
||||
compileOnly("com.github.GTNewHorizons:Natura:2.7.5:dev")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.8.4:dev")
|
||||
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.6.4:dev")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.3:dev") { transitive = false }
|
||||
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.1:dev") { transitive = false }
|
||||
compileOnly(rfg.deobf('maven.modrinth:backhand:1.4.1')) { 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 }
|
||||
@@ -108,37 +94,29 @@ dependencies {
|
||||
|
||||
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("com.github.GTNewHorizons:NotEnoughIds:2.1.6:dev") // Mixin Version
|
||||
compileOnly("com.github.GTNewHorizons:NotEnoughIds-Legacy:1.4.7:dev") // ASM Version
|
||||
|
||||
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")))
|
||||
compileOnly("com.github.GTNewHorizons:HoloInventory:2.4.13-GTNH:dev")
|
||||
|
||||
runtimeOnlyNonPublishable(rfg.deobf("CoreTweaks:CoreTweaks:0.3.3.2"))
|
||||
|
||||
// Hodgepodge
|
||||
transformedMod("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev")
|
||||
|
||||
// Better Crashes
|
||||
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.1-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.0-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")
|
||||
// Tests
|
||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||
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')
|
||||
}
|
||||
devOnlyNonPublishable("com.github.javaparser:javaparser-symbol-solver-core:3.26.2") {
|
||||
exclude module: "guava"
|
||||
}
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
}
|
||||
+2084
File diff suppressed because it is too large.
Load diff
+16
-43
@@ -14,7 +14,7 @@ modId = angelica
|
||||
modGroup = com.gtnewhorizons.angelica
|
||||
|
||||
# Whether to use modGroup as the maven publishing group.
|
||||
# When false, com.github.GTNewHorizons is used.
|
||||
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
|
||||
useModGroupForPublishing = false
|
||||
|
||||
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
|
||||
@@ -39,33 +39,9 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
|
||||
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
|
||||
developmentEnvironmentUserName = Developer
|
||||
|
||||
# Enables modern Java syntax support. Valid values:
|
||||
# - false: No modern syntax, Java 8 only
|
||||
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
|
||||
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
|
||||
# - modern: Native modern Java bytecode, no downgrading
|
||||
enableModernJavaSyntax = jvmDowngrader
|
||||
|
||||
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
|
||||
# and should *not* be used in most situations. -1 disables this.
|
||||
# forceToolchainVersion = -1
|
||||
|
||||
# Target JVM version for JVM Downgrader bytecode downgrading.
|
||||
# Only used when enableModernJavaSyntax = jvmDowngrader
|
||||
# downgradeTargetVersion = 8
|
||||
|
||||
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
|
||||
# Classes will be available in META-INF/versions/N/ for each version N in this list.
|
||||
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
|
||||
jvmDowngraderMultiReleaseVersions = 17, 21, 25
|
||||
|
||||
# Specifies how JVM Downgrader API stubs are provided. Options:
|
||||
# - shade: Shade minimized stubs into the jar
|
||||
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
|
||||
# - external: Another dependency provides stubs (no constraint, no warning)
|
||||
# - (empty): Warning reminding you to configure stubs
|
||||
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
|
||||
jvmDowngraderStubsProvider = gtnhlib
|
||||
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
|
||||
# See https://github.com/bsideup/jabel for details on how this works.
|
||||
enableModernJavaSyntax = true
|
||||
|
||||
# Enables injecting missing generics into the decompiled source code for a better coding experience.
|
||||
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
|
||||
@@ -79,13 +55,22 @@ generateGradleTokenClass = com.gtnewhorizons.angelica.Tags
|
||||
# Name of the token containing the project's current version to generate/replace.
|
||||
gradleTokenVersion = VERSION
|
||||
|
||||
# [DEPRECATED] Mod ID replacement token.
|
||||
gradleTokenModId =
|
||||
|
||||
# [DEPRECATED] Mod name replacement token.
|
||||
gradleTokenModName =
|
||||
|
||||
# [DEPRECATED] Mod Group replacement token.
|
||||
gradleTokenGroupName =
|
||||
|
||||
# [DEPRECATED]
|
||||
# Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java
|
||||
# public static final String VERSION = "GRADLETOKEN_VERSION";
|
||||
# The string's content will be replaced with your mod's version when compiled. You should use this to specify your mod's
|
||||
# version in @Mod([...], version = VERSION, [...]).
|
||||
# Leave these properties empty to skip individual token replacements.
|
||||
# replaceGradleTokenInFile =
|
||||
replaceGradleTokenInFile =
|
||||
|
||||
# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can
|
||||
# leave this property empty.
|
||||
@@ -111,9 +96,7 @@ usesMixinDebug = false
|
||||
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
|
||||
mixinPlugin =
|
||||
|
||||
# Specify the package that contains all of your Mixins. The package must exist or
|
||||
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
|
||||
# it must match with this or the build will fail.
|
||||
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
|
||||
mixinsPackage = mixins
|
||||
|
||||
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
|
||||
@@ -168,7 +151,7 @@ modrinthProjectId = angelica
|
||||
# type can be one of [project, version],
|
||||
# and the name is the Modrinth project or version slug/id of the other mod.
|
||||
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
|
||||
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
|
||||
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
|
||||
modrinthRelations = required-project\:gtnhlib
|
||||
|
||||
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
|
||||
@@ -189,12 +172,6 @@ curseForgeRelations = requiredDependency\:gtnhlib
|
||||
# projects. New projects should not use this parameter.
|
||||
# customArchiveBaseName =
|
||||
|
||||
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
|
||||
# runClientWorkingDirectory = run/client
|
||||
|
||||
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
|
||||
# runServerWorkingDirectory = run/server
|
||||
|
||||
# Optional parameter to have the build automatically fail if an illegal version is used.
|
||||
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
|
||||
# The check is ONLY performed if the version is a git tag.
|
||||
@@ -224,7 +201,3 @@ disableSpotless = true
|
||||
# This is meant to be set in $HOME/.gradle/gradle.properties.
|
||||
# ideaCheckSpotlessOnBuild = true
|
||||
|
||||
# Non-GTNH properties
|
||||
org.gradle.configuration-cache = true
|
||||
org.gradle.jvmargs = -Xmx2g
|
||||
org.gradle.parallel = true
|
||||
@@ -1,12 +0,0 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/5ee463876563a46fff651cce1bc53a4c/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/5ee463876563a46fff651cce1bc53a4c/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e1118b4c7c2858d28154d086dbff010e/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/59a9771cad43219260d9aac9a8ec4d6a/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/5ee463876563a46fff651cce1bc53a4c/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/832229f0f6f0be60ed817c5a5e5848eb/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/b96cb7670c095245220e7d01d5927b58/redirect
|
||||
toolchainVersion=25
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -86,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -114,6 +115,7 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -171,6 +173,7 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -203,14 +206,15 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
Vendored
+2
-1
@@ -70,10 +70,11 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
+15
-5
@@ -8,11 +8,21 @@ repositories {
|
||||
includeGroup 'com.falsepattern'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
name = 'mega'
|
||||
url = 'https://mvn.falsepattern.com/gtmega_releases/'
|
||||
content {
|
||||
includeGroup 'mega'
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
ivy {
|
||||
name = 'CoreTweaks releases'
|
||||
url = 'https://github.com/makamys/CoreTweaks/releases/download/'
|
||||
patternLayout {
|
||||
artifact '[revision]/[module]-1.7.10-[revision]+nomixin(-[classifier])(.[ext])'
|
||||
}
|
||||
metadataSources {
|
||||
artifact()
|
||||
}
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup('CoreTweaks')
|
||||
}
|
||||
}
|
||||
maven {
|
||||
|
||||
@@ -2,8 +2,8 @@ pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
// RetroFuturaGradle
|
||||
name = "GTNH Maven"
|
||||
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
|
||||
name "GTNH Maven"
|
||||
url "https://nexus.gtnewhorizons.com/repository/public/"
|
||||
mavenContent {
|
||||
includeGroup("com.gtnewhorizons")
|
||||
includeGroupByRegex("com\\.gtnewhorizons\\..+")
|
||||
@@ -16,5 +16,5 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhsettingsconvention") version("2.0.19")
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.29'
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import com.github.javaparser.StaticJavaParser;
|
||||
import com.github.javaparser.ast.CompilationUnit;
|
||||
import com.github.javaparser.ast.Node;
|
||||
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
|
||||
import com.github.javaparser.ast.body.MethodDeclaration;
|
||||
import com.github.javaparser.ast.body.Parameter;
|
||||
import com.github.javaparser.ast.visitor.VoidVisitorWithDefaults;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Used to generate gl_functions.csv. Which contains a mapping of OpenGL functions provided by LWJGL
|
||||
* corresponding to the earliest GL version they appear in.
|
||||
*/
|
||||
public class GLFunctionParser {
|
||||
final static String glPath = "util/gl"; // Path to LWJGL OpenGL bindings source code
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException {
|
||||
HashMap<String, Set<String>> byFunction = new HashMap<>();
|
||||
File dir = new File(glPath);
|
||||
for (File file : dir.listFiles()) {
|
||||
final String filename = file.getName();
|
||||
if (filename.endsWith(".java") && filename.startsWith("GL") && Character.isDigit(filename.charAt(2))) {
|
||||
Path p = Paths.get(filename);
|
||||
|
||||
String parsedFile = removeFileExtension(p.getFileName().toString());
|
||||
|
||||
CompilationUnit cu = StaticJavaParser.parse(file);
|
||||
cu.accept(new VoidVisitorWithDefaults<>() {
|
||||
|
||||
@Override
|
||||
public void defaultAction(Node n, Object arg) {
|
||||
for( Node child : n.getChildNodes()) {
|
||||
if(child instanceof ClassOrInterfaceDeclaration cd) {
|
||||
for (MethodDeclaration md : cd.getMethods()) {
|
||||
String methodName = md.getNameAsString() + "(";
|
||||
for (Parameter p : md.getParameters()) {
|
||||
methodName = methodName + p.getTypeAsString();
|
||||
}
|
||||
methodName = methodName + ")";
|
||||
if(methodName.startsWith("n")) continue;
|
||||
|
||||
Set<String> functionSet = byFunction.computeIfAbsent(methodName, k -> new HashSet<>());
|
||||
functionSet.add(parsedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> functionVersionMap = new HashMap<>();
|
||||
|
||||
for(String function : byFunction.keySet()) {
|
||||
|
||||
Set<String> versionsForFunction = byFunction.get(function);
|
||||
int lowestInt = 46;
|
||||
String lowestString = "GL46";
|
||||
for(String version : versionsForFunction) {
|
||||
int i = Integer.parseInt(version.substring(2, 4));
|
||||
if (i < lowestInt) {
|
||||
lowestInt = i;
|
||||
lowestString = version;
|
||||
}
|
||||
}
|
||||
functionVersionMap.put(function, lowestString);
|
||||
}
|
||||
|
||||
List<String[]> outputLines = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, String> entry : functionVersionMap.entrySet()) {
|
||||
outputLines.add(new String[] {entry.getKey(), entry.getValue()});
|
||||
}
|
||||
|
||||
File output = new File("output.csv");
|
||||
try (PrintWriter writer = new PrintWriter(output)) {
|
||||
outputLines.stream().map(GLSMFunctionComparison::convertToCSV).forEach(writer::println);
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertToCSV(String[] data) {
|
||||
return Stream.of(data).map(GLSMFunctionComparison::escapeSpecialCharacters).collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
public static String escapeSpecialCharacters(String data) {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Input data cannot be null");
|
||||
}
|
||||
String escapedData = data.replaceAll("\\R", " ");
|
||||
if (data.contains(",") || data.contains("\"") || data.contains("'")) {
|
||||
data = data.replace("\"", "\"\"");
|
||||
escapedData = "\"" + data + "\"";
|
||||
}
|
||||
return escapedData;
|
||||
}
|
||||
|
||||
public static String removeFileExtension(String filename) {
|
||||
if (filename == null || filename.isEmpty()) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
String extPattern = "(?<!^)[.]" + "[^.]*$";
|
||||
return filename.replaceAll(extPattern, "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import com.github.javaparser.ParserConfiguration;
|
||||
import com.github.javaparser.StaticJavaParser;
|
||||
import com.github.javaparser.ast.CompilationUnit;
|
||||
import com.github.javaparser.ast.Node;
|
||||
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
|
||||
import com.github.javaparser.ast.body.MethodDeclaration;
|
||||
import com.github.javaparser.ast.body.Parameter;
|
||||
import com.github.javaparser.ast.visitor.VoidVisitorWithDefaults;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Used to compare OpenGL functions from gl_functions.csv to current GLStateManager
|
||||
* source code to determine what functions have an implementation in GLSM, and what ones don't.
|
||||
* Only considers functions which appear in GL 2.1 and lower
|
||||
*/
|
||||
public class GLSMFunctionComparison {
|
||||
|
||||
/**
|
||||
* If set to true, then the outputted CSV will show functions which do have an implementation in GLSM
|
||||
*/
|
||||
final static boolean INCLUDE_TRUE = false;
|
||||
|
||||
final static String glFunctionsCsvPath = "gl_functions.csv";
|
||||
final static String glsmPath = "src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java";
|
||||
|
||||
public static Map<String, String> parseGLFunctions() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(glFunctionsCsvPath))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
String[] values = line.split(",");
|
||||
if (values.length == 2) {
|
||||
String key = values[0].trim();
|
||||
String value = values[1].trim();
|
||||
int versionInt = Integer.parseInt(value.substring(2, 4));
|
||||
if (versionInt < 30) map.put(key, value);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException {
|
||||
//GLSM will fail to parse without increasing language level
|
||||
StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17);
|
||||
|
||||
Map<String, String> functionMap = parseGLFunctions();
|
||||
Set<String> glsmFunctions = new HashSet<>();
|
||||
|
||||
File glsmFile = new File(glsmPath);
|
||||
CompilationUnit cu = StaticJavaParser.parse(glsmFile);
|
||||
cu.accept(new VoidVisitorWithDefaults<>() {
|
||||
|
||||
@Override
|
||||
public void defaultAction(Node n, Object arg) {
|
||||
for (Node child : n.getChildNodes()) {
|
||||
if (child instanceof ClassOrInterfaceDeclaration cd) {
|
||||
for (MethodDeclaration md : cd.getMethods()) {
|
||||
String methodName = md.getNameAsString() + "(";
|
||||
for (Parameter p : md.getParameters()) {
|
||||
methodName = methodName + p.getTypeAsString();
|
||||
}
|
||||
methodName = methodName + ")";
|
||||
glsmFunctions.add(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
Map<String, String> functionGLSMMap = new HashMap<>();
|
||||
|
||||
for(String function : functionMap.keySet()) {
|
||||
if (!glsmFunctions.contains(function)) {
|
||||
functionGLSMMap.put(function, "false");
|
||||
} else if (INCLUDE_TRUE) {
|
||||
functionGLSMMap.put(function, "true");
|
||||
}
|
||||
}
|
||||
|
||||
List<String[]> outputLines = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, String> entry : functionGLSMMap.entrySet()) {
|
||||
outputLines.add(new String[] {entry.getKey(), functionMap.get(entry.getKey()), entry.getValue()});
|
||||
}
|
||||
|
||||
File output = new File("glsm_function_comparison.csv");
|
||||
try (PrintWriter writer = new PrintWriter(output)) {
|
||||
outputLines.stream().map(GLSMFunctionComparison::convertToCSV).forEach(writer::println);
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertToCSV(String[] data) {
|
||||
return Stream.of(data).map(GLSMFunctionComparison::escapeSpecialCharacters).collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
public static String escapeSpecialCharacters(String data) {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Input data cannot be null");
|
||||
}
|
||||
String escapedData = data.replaceAll("\\R", " ");
|
||||
if (data.contains(",") || data.contains("\"") || data.contains("'")) {
|
||||
data = data.replace("\"", "\"\"");
|
||||
escapedData = "\"" + data + "\"";
|
||||
}
|
||||
return escapedData;
|
||||
}
|
||||
|
||||
public static String removeFileExtension(String filename) {
|
||||
if (filename == null || filename.isEmpty()) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
String extPattern = "(?<!^)[.]" + "[^.]*$";
|
||||
return filename.replaceAll(extPattern, "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,26 +2,21 @@ package com.gtnewhorizons.angelica;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.ILateMixinLoader;
|
||||
import com.gtnewhorizon.gtnhmixins.LateMixin;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizons.angelica.mixins.Mixins;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@LateMixin
|
||||
@SuppressWarnings("unused")
|
||||
public class AngelicaLateMixins implements ILateMixinLoader {
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
int v = Runtime.version().feature();
|
||||
if (v >= 21) return "mixins.angelica.late.j21.json";
|
||||
if (v >= 17) return "mixins.angelica.late.j17.json";
|
||||
return "mixins.angelica.late.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedMods) {
|
||||
return IMixins.getLateMixins(Mixins.class, loadedMods);
|
||||
return Mixins.getLateMixins(loadedMods);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.config.ConfigMigrator;
|
||||
import com.gtnewhorizons.angelica.proxy.CommonProxy;
|
||||
import com.gtnewhorizons.angelica.utils.AnimationMode;
|
||||
import com.gtnewhorizons.angelica.utils.ManagedEnum;
|
||||
@@ -11,39 +10,17 @@ import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
|
||||
@Mod(
|
||||
modid = MOD_ID,
|
||||
modid = "angelica",
|
||||
name = "Angelica",
|
||||
version = Tags.VERSION,
|
||||
dependencies = """
|
||||
required-after:gtnhlib@[0.8.21,);\
|
||||
before:lwjgl3ify@[1.5.3,);\
|
||||
after:hodgepodge@[2.4.4,);\
|
||||
after:CodeChickenCore@[1.2.0,);\
|
||||
after:archaicfix@[0.7.0,);\
|
||||
""",
|
||||
dependencies = " before:lwjgl3ify@[1.5.3,);" + " after:hodgepodge@[2.4.4,);" + " after:CodeChickenCore@[1.2.0,);"
|
||||
+ " after:archaicfix@[0.7.0,);" + " required-after:gtnhlib@[0.4.2,);",
|
||||
acceptedMinecraftVersions = "[1.7.10]",
|
||||
acceptableRemoteVersions = "*",
|
||||
guiFactory = "com.gtnewhorizons.angelica.config.AngelicaGuiConfigFactory")
|
||||
public class AngelicaMod {
|
||||
public static final String MOD_ID = "angelica";
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
private static SodiumGameOptions CONFIG;
|
||||
|
||||
public static SodiumGameOptions options() {
|
||||
if (CONFIG == null) {
|
||||
CONFIG = SodiumGameOptions.load(ConfigMigrator.handleConfigMigration("angelica-options.json"));
|
||||
}
|
||||
return CONFIG;
|
||||
}
|
||||
|
||||
@SidedProxy(clientSide = "com.gtnewhorizons.angelica.proxy.ClientProxy", serverSide = "com.gtnewhorizons.angelica.proxy.CommonProxy")
|
||||
public static CommonProxy proxy;
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface IBlockAccessExtended extends IBlockAccess {
|
||||
World getWorld();
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ITexturesCache;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.SpriteExtension;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
/**
|
||||
* Exposes a method to the API that allow mods to add texture update compat
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class TextureServices {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void updateBlockTextureAnimation(IIcon icon, RenderBlocks renderBlocks) {
|
||||
if (renderBlocks instanceof ITexturesCache texturesCache) {
|
||||
texturesCache.getRenderedTextures().add(icon);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void updateTextureAnimation(IIcon icon) {
|
||||
if (icon instanceof SpriteExtension ext) {
|
||||
ext.celeritas$markActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,25 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.gtnhlib.util.font.GlyphReplacements;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
|
||||
import it.unimi.dsi.fastutil.chars.Char2ShortOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.coderbot.iris.gl.program.Program;
|
||||
import net.coderbot.iris.gl.program.ProgramBuilder;
|
||||
import jss.util.RandomXoshiro256StarStar;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
/**
|
||||
* A batching replacement for {@code FontRenderer}
|
||||
*
|
||||
@@ -35,6 +29,8 @@ public class BatchingFontRenderer {
|
||||
|
||||
/** The underlying FontRenderer object that's being accelerated */
|
||||
protected FontRenderer underlying;
|
||||
/** Cached locations for each unicode page atlas */
|
||||
private final ResourceLocation[] unicodePageLocations;
|
||||
/** Array of width of all the characters in default.png */
|
||||
protected int[] charWidth = new int[256];
|
||||
/** Array of the start/end column (in upper/lower nibble) for every glyph in the /font directory. */
|
||||
@@ -46,56 +42,50 @@ public class BatchingFontRenderer {
|
||||
private int[] colorCode;
|
||||
/** Location of the primary font atlas to bind. */
|
||||
protected final ResourceLocation locationFontTexture;
|
||||
/** The RenderEngine used to load and setup glyph textures. */
|
||||
private final TextureManager renderEngine;
|
||||
private final RandomXoshiro256StarStar fontRandom = new RandomXoshiro256StarStar();
|
||||
|
||||
private final int AAMode;
|
||||
private final int AAStrength;
|
||||
private final int texBoundAttrLocation;
|
||||
private final int fontShaderId;
|
||||
/** The full list of characters present in the default Minecraft font, excluding the Unicode font */
|
||||
@SuppressWarnings("UnnecessaryUnicodeEscape")
|
||||
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
|
||||
|
||||
final boolean isSGA;
|
||||
final boolean isSplash;
|
||||
private static final short[] MCFONT_ASCII_LUT = new short[512];
|
||||
private static final Char2ShortOpenHashMap MCFONT_UNI_LUT = new Char2ShortOpenHashMap();
|
||||
|
||||
private static class FontAAShader {
|
||||
|
||||
private static Program fontShader = null;
|
||||
public static Program getProgram() {
|
||||
if (fontShader == null) {
|
||||
String vsh, fsh;
|
||||
try {
|
||||
fsh = new String(IOUtils.toByteArray(Objects.requireNonNull(FontAAShader.class.getResourceAsStream("/assets/angelica/shaders/fontFilter.fsh"))), StandardCharsets.UTF_8);
|
||||
vsh = new String(IOUtils.toByteArray(Objects.requireNonNull(FontAAShader.class.getResourceAsStream("/assets/angelica/shaders/fontFilter.vsh"))), StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
ProgramBuilder builder = ProgramBuilder.begin("fontFilter", vsh, null, fsh, ImmutableSet.of(0));
|
||||
fontShader = builder.build();
|
||||
static {
|
||||
Arrays.fill(MCFONT_ASCII_LUT, (short) -1);
|
||||
for (short i = 0; i < MCFONT_CHARS.length(); i++) {
|
||||
char ch = MCFONT_CHARS.charAt(i);
|
||||
if (ch < MCFONT_ASCII_LUT.length) {
|
||||
MCFONT_ASCII_LUT[ch] = i;
|
||||
} else {
|
||||
MCFONT_UNI_LUT.put(ch, i);
|
||||
}
|
||||
return fontShader;
|
||||
}
|
||||
}
|
||||
|
||||
public BatchingFontRenderer(FontRenderer underlying, int[] charWidth, int[] colorCode, ResourceLocation locationFontTexture) {
|
||||
public static int lookupMcFontPosition(char ch) {
|
||||
if (ch < MCFONT_ASCII_LUT.length) {
|
||||
return MCFONT_ASCII_LUT[ch];
|
||||
} else {
|
||||
return MCFONT_UNI_LUT.getOrDefault(ch, (short) -1);
|
||||
}
|
||||
}
|
||||
|
||||
public BatchingFontRenderer(FontRenderer underlying, ResourceLocation[] unicodePageLocations, int[] charWidth,
|
||||
byte[] glyphWidth, int[] colorCode, ResourceLocation locationFontTexture, TextureManager renderEngine) {
|
||||
this.underlying = underlying;
|
||||
this.unicodePageLocations = unicodePageLocations;
|
||||
this.charWidth = charWidth;
|
||||
this.glyphWidth = glyphWidth;
|
||||
this.colorCode = colorCode;
|
||||
this.locationFontTexture = locationFontTexture;
|
||||
this.renderEngine = renderEngine;
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
batchCommandPool.add(new FontDrawCmd());
|
||||
}
|
||||
|
||||
this.isSGA = Objects.equals(this.locationFontTexture.getResourcePath(), "textures/font/ascii_sga.png");
|
||||
this.isSplash = FontStrategist.isSplashFontRendererActive(underlying);
|
||||
|
||||
FontProviderMC.get(this.isSGA).charWidth = this.charWidth;
|
||||
FontProviderMC.get(this.isSGA).locationFontTexture = this.locationFontTexture;
|
||||
|
||||
//noinspection deprecation
|
||||
fontShaderId = FontAAShader.getProgram().getProgramId();
|
||||
AAMode = GL20.glGetUniformLocation(fontShaderId, "aaMode");
|
||||
AAStrength = GL20.glGetUniformLocation(fontShaderId, "strength");
|
||||
texBoundAttrLocation = GL20.glGetAttribLocation(fontShaderId, "texBounds");
|
||||
}
|
||||
|
||||
// === Batched rendering
|
||||
@@ -107,29 +97,24 @@ public class BatchingFontRenderer {
|
||||
private static final int INITIAL_BATCH_SIZE = 256;
|
||||
private static final ResourceLocation DUMMY_RESOURCE_LOCATION = new ResourceLocation("angelica$dummy",
|
||||
"this is invalid!");
|
||||
private FloatBuffer batchVtxPositions = memAllocFloat(INITIAL_BATCH_SIZE * 2);
|
||||
private ByteBuffer batchVtxColors = memAlloc(INITIAL_BATCH_SIZE * 4);
|
||||
private FloatBuffer batchVtxTexCoords = memAllocFloat(INITIAL_BATCH_SIZE * 2);
|
||||
private IntBuffer batchIndices = memAllocInt(INITIAL_BATCH_SIZE / 2 * 3);
|
||||
private FloatBuffer batchVtxTexBounds = memAllocFloat(INITIAL_BATCH_SIZE * 4);
|
||||
private FloatBuffer batchVtxPositions = BufferUtils.createFloatBuffer(INITIAL_BATCH_SIZE * 2);
|
||||
private ByteBuffer batchVtxColors = BufferUtils.createByteBuffer(INITIAL_BATCH_SIZE * 4);
|
||||
private FloatBuffer batchVtxTexCoords = BufferUtils.createFloatBuffer(INITIAL_BATCH_SIZE * 2);
|
||||
private IntBuffer batchIndices = BufferUtils.createIntBuffer(INITIAL_BATCH_SIZE / 2 * 3);
|
||||
private final ObjectArrayList<FontDrawCmd> batchCommands = ObjectArrayList.wrap(new FontDrawCmd[64], 0);
|
||||
private final ObjectArrayList<FontDrawCmd> batchCommandPool = ObjectArrayList.wrap(new FontDrawCmd[64], 0);
|
||||
|
||||
private int blendSrcRGB = GL11.GL_SRC_ALPHA;
|
||||
private int blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
|
||||
/** */
|
||||
private void pushVtx(float x, float y, int rgba, float u, float v, float uMin, float uMax, float vMin, float vMax) {
|
||||
private void pushVtx(float x, float y, int rgba, float u, float v) {
|
||||
final int oldCap = batchVtxPositions.capacity() / 2;
|
||||
if (vtxWriterIndex >= oldCap) {
|
||||
final int newCap = oldCap * 2;
|
||||
batchVtxPositions = memRealloc(batchVtxPositions, newCap * 2);
|
||||
batchVtxColors = memRealloc(batchVtxColors, newCap * 4);
|
||||
batchVtxTexCoords = memRealloc(batchVtxTexCoords, newCap * 2);
|
||||
batchVtxTexBounds = memRealloc(batchVtxTexBounds, newCap * 4);
|
||||
batchVtxPositions = CompatMemoryUtil.memReallocDirect(batchVtxPositions, newCap * 2);
|
||||
batchVtxColors = CompatMemoryUtil.memReallocDirect(batchVtxColors, newCap * 4);
|
||||
batchVtxTexCoords = CompatMemoryUtil.memReallocDirect(batchVtxTexCoords, newCap * 2);
|
||||
final int oldIdxCap = batchIndices.capacity();
|
||||
final int newIdxCap = oldIdxCap * 2;
|
||||
batchIndices = memRealloc(batchIndices, newIdxCap);
|
||||
batchIndices = CompatMemoryUtil.memReallocDirect(batchIndices, newIdxCap);
|
||||
}
|
||||
final int idx = vtxWriterIndex;
|
||||
final int idx2 = idx * 2;
|
||||
@@ -143,28 +128,15 @@ public class BatchingFontRenderer {
|
||||
batchVtxColors.put(idx4 + 3, (byte) ((rgba >> 24) & 0xFF));
|
||||
batchVtxTexCoords.put(idx2, u);
|
||||
batchVtxTexCoords.put(idx2 + 1, v);
|
||||
batchVtxTexBounds.put(idx4, uMin);
|
||||
batchVtxTexBounds.put(idx4 + 1, uMax);
|
||||
batchVtxTexBounds.put(idx4 + 2, vMin);
|
||||
batchVtxTexBounds.put(idx4 + 3, vMax);
|
||||
vtxWriterIndex++;
|
||||
}
|
||||
|
||||
private void pushUntexRect(float x, float y, float w, float h, int rgba) {
|
||||
final int vtxId = vtxWriterIndex;
|
||||
pushVtx(x, y, rgba, 0, 0, 0, 0, 0, 0);
|
||||
pushVtx(x, y + h, rgba, 0, 0, 0, 0, 0, 0);
|
||||
pushVtx(x + w, y, rgba, 0, 0, 0, 0, 0, 0);
|
||||
pushVtx(x + w, y + h, rgba, 0, 0, 0, 0, 0, 0);
|
||||
pushQuadIdx(vtxId);
|
||||
}
|
||||
|
||||
private void pushTexRect(float x, float y, float w, float h, float itOff, int rgba, float uStart, float vStart, float uSz, float vSz) {
|
||||
final int vtxId = vtxWriterIndex;
|
||||
pushVtx(x + itOff, y, rgba, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x - itOff, y + h, rgba, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x + itOff + w, y, rgba, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x - itOff + w, y + h, rgba, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x, y, rgba, 0, 0);
|
||||
pushVtx(x, y + h, rgba, 0, 0);
|
||||
pushVtx(x + w, y, rgba, 0, 0);
|
||||
pushVtx(x + w, y + h, rgba, 0, 0);
|
||||
pushQuadIdx(vtxId);
|
||||
}
|
||||
|
||||
@@ -181,7 +153,7 @@ public class BatchingFontRenderer {
|
||||
return idx;
|
||||
}
|
||||
|
||||
private void pushDrawCmd(int startIdx, int idxCount, ResourceLocation texture, boolean isUnicode) {
|
||||
private void pushDrawCmd(int startIdx, int idxCount, ResourceLocation texture) {
|
||||
if (!batchCommands.isEmpty()) {
|
||||
final FontDrawCmd lastCmd = batchCommands.get(batchCommands.size() - 1);
|
||||
final int prevEndVtx = lastCmd.startVtx + lastCmd.idxCount;
|
||||
@@ -197,7 +169,7 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
}
|
||||
final FontDrawCmd cmd = batchCommandPool.pop();
|
||||
cmd.reset(startIdx, idxCount, texture, isUnicode);
|
||||
cmd.reset(startIdx, idxCount, texture);
|
||||
batchCommands.add(cmd);
|
||||
}
|
||||
|
||||
@@ -205,14 +177,12 @@ public class BatchingFontRenderer {
|
||||
|
||||
public int startVtx;
|
||||
public int idxCount;
|
||||
public boolean isUnicode;
|
||||
public ResourceLocation texture;
|
||||
|
||||
public void reset(int startVtx, int vtxCount, ResourceLocation texture, boolean isUnicode) {
|
||||
public void reset(int startVtx, int vtxCount, ResourceLocation texture) {
|
||||
this.startVtx = startVtx;
|
||||
this.idxCount = vtxCount;
|
||||
this.texture = texture;
|
||||
this.isUnicode = isUnicode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -271,12 +241,7 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
int lastActiveProgram;
|
||||
int fontAAModeLast = -1;
|
||||
int fontAAStrengthLast = -1;
|
||||
private void flushBatch() {
|
||||
final int prevProgram = GLStateManager.glGetInteger(GL20.GL_CURRENT_PROGRAM);
|
||||
|
||||
// Sort&Draw
|
||||
batchCommands.sort(FontDrawCmd.DRAW_ORDER_COMPARATOR);
|
||||
|
||||
@@ -288,26 +253,9 @@ public class BatchingFontRenderer {
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(blendSrcRGB, blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glShadeModel(GL11.GL_FLAT);
|
||||
|
||||
final boolean canUseAA = FontConfig.fontAAMode != 0 && prevProgram == 0;
|
||||
if (canUseAA) {
|
||||
GL20.glVertexAttribPointer(texBoundAttrLocation, 4, false, 0, batchVtxTexBounds);
|
||||
GL20.glEnableVertexAttribArray(texBoundAttrLocation);
|
||||
lastActiveProgram = prevProgram;
|
||||
GLStateManager.glUseProgram(fontShaderId);
|
||||
if (FontConfig.fontAAMode != fontAAModeLast) {
|
||||
fontAAModeLast = FontConfig.fontAAMode;
|
||||
GL20.glUniform1i(AAMode, FontConfig.fontAAMode);
|
||||
}
|
||||
if (FontConfig.fontAAStrength != fontAAStrengthLast) {
|
||||
fontAAStrengthLast = FontConfig.fontAAStrength;
|
||||
GL20.glUniform1f(AAStrength, FontConfig.fontAAStrength / 120.f);
|
||||
}
|
||||
}
|
||||
|
||||
GL13.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GL11.glTexCoordPointer(2, 0, batchVtxTexCoords);
|
||||
GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
|
||||
GL11.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 0, batchVtxColors);
|
||||
@@ -316,9 +264,6 @@ public class BatchingFontRenderer {
|
||||
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
|
||||
GL11.glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
|
||||
// Use plain for loop to avoid allocations
|
||||
final FontDrawCmd[] cmdsData = batchCommands.elements();
|
||||
final int cmdsSize = batchCommands.size();
|
||||
@@ -338,13 +283,8 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
batchIndices.limit(cmd.startVtx + cmd.idxCount);
|
||||
batchIndices.position(cmd.startVtx);
|
||||
|
||||
GL11.glDrawElements(GL11.GL_TRIANGLES, batchIndices);
|
||||
}
|
||||
if (canUseAA) {
|
||||
GLStateManager.glUseProgram(lastActiveProgram);
|
||||
GL20.glDisableVertexAttribArray(texBoundAttrLocation);
|
||||
}
|
||||
|
||||
GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
|
||||
GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
|
||||
@@ -372,28 +312,17 @@ public class BatchingFontRenderer {
|
||||
return (what >= fromInclusive) && (what <= toInclusive);
|
||||
}
|
||||
|
||||
public boolean forceDefaults() {
|
||||
return this.isSGA || this.isSplash;
|
||||
}
|
||||
|
||||
public float getGlyphScaleX() {
|
||||
return forceDefaults() ? 1 : (float) (FontConfig.glyphScale * Math.pow(2, FontConfig.glyphAspect));
|
||||
}
|
||||
|
||||
public float getGlyphScaleY() {
|
||||
return forceDefaults() ? 1 : (float) (FontConfig.glyphScale / Math.pow(2, FontConfig.glyphAspect));
|
||||
}
|
||||
|
||||
public float getGlyphSpacing() {
|
||||
return forceDefaults() ? 0 : FontConfig.glyphSpacing;
|
||||
}
|
||||
|
||||
public float getWhitespaceScale() {
|
||||
return forceDefaults() ? 1 : FontConfig.whitespaceScale;
|
||||
}
|
||||
|
||||
public float getShadowOffset() {
|
||||
return forceDefaults() ? 1 : FontConfig.fontShadowOffset;
|
||||
private ResourceLocation getUnicodePageLocation(int page) {
|
||||
final ResourceLocation lookup = unicodePageLocations[page];
|
||||
if (lookup == null) {
|
||||
final ResourceLocation rl = new ResourceLocation(String.format(
|
||||
"textures/font/unicode_page_%02x.png",
|
||||
page));
|
||||
unicodePageLocations[page] = rl;
|
||||
return rl;
|
||||
} else {
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
|
||||
private static final char FORMATTING_CHAR = 167; // §
|
||||
@@ -406,9 +335,6 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
final int shadowColor = (color & 0xfcfcfc) >> 2 | color & 0xff000000;
|
||||
|
||||
FontProviderMC.get(this.isSGA).charWidth = this.charWidth;
|
||||
FontProviderMC.get(this.isSGA).locationFontTexture = this.locationFontTexture;
|
||||
|
||||
this.beginBatch();
|
||||
float curX = anchorX;
|
||||
try {
|
||||
@@ -428,14 +354,10 @@ public class BatchingFontRenderer {
|
||||
boolean curStrikethrough = false;
|
||||
boolean curUnderline = false;
|
||||
|
||||
float glyphScaleY = getGlyphScaleY();
|
||||
float heightNorth = anchorY + (underlying.FONT_HEIGHT - 1.0f) * (0.5f - glyphScaleY / 2);
|
||||
|
||||
final float underlineY = heightNorth + (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY;
|
||||
final float underlineY = anchorY + underlying.FONT_HEIGHT - 1.0f;
|
||||
float underlineStartX = 0.0f;
|
||||
float underlineEndX = 0.0f;
|
||||
|
||||
final float strikethroughY = heightNorth + ((float) (underlying.FONT_HEIGHT / 2) - 1.0f) * glyphScaleY;
|
||||
final float strikethroughY = anchorY + (float) (underlying.FONT_HEIGHT / 2) - 1.0F;
|
||||
float strikethroughStartX = 0.0f;
|
||||
float strikethroughEndX = 0.0f;
|
||||
|
||||
@@ -447,8 +369,8 @@ public class BatchingFontRenderer {
|
||||
|
||||
if (curUnderline && underlineStartX != underlineEndX) {
|
||||
final int ulIdx = idxWriterIndex;
|
||||
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
|
||||
pushDrawCmd(ulIdx, 6, null, false);
|
||||
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, 1.0f, curColor);
|
||||
pushDrawCmd(ulIdx, 6, null);
|
||||
underlineStartX = underlineEndX;
|
||||
}
|
||||
if (curStrikethrough && strikethroughStartX != strikethroughEndX) {
|
||||
@@ -457,9 +379,9 @@ public class BatchingFontRenderer {
|
||||
strikethroughStartX,
|
||||
strikethroughY,
|
||||
strikethroughEndX - strikethroughStartX,
|
||||
glyphScaleY,
|
||||
1.0f,
|
||||
curColor);
|
||||
pushDrawCmd(ulIdx, 6, null, false);
|
||||
pushDrawCmd(ulIdx, 6, null);
|
||||
strikethroughStartX = strikethroughEndX;
|
||||
}
|
||||
|
||||
@@ -504,89 +426,148 @@ public class BatchingFontRenderer {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FontConfig.enableCustomFont && FontConfig.enableGlyphReplacements) {
|
||||
String chrReplacement = GlyphReplacements.customGlyphs.get(String.valueOf(chr));
|
||||
if (chrReplacement != null) {
|
||||
char replacement = chrReplacement.charAt(0);
|
||||
boolean isReplacementCharAvailable =
|
||||
FontProviderCustom.getPrimary().isGlyphAvailable(replacement)
|
||||
|| FontProviderCustom.getFallback().isGlyphAvailable(replacement);
|
||||
if (isReplacementCharAvailable) {
|
||||
chr = replacement;
|
||||
}
|
||||
}
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
if (curRandom && lutIndex != -1) {
|
||||
int randomReplacementIndex;
|
||||
do {
|
||||
randomReplacementIndex = fontRandom.nextInt(this.charWidth.length);
|
||||
} while (this.charWidth[lutIndex] != this.charWidth[randomReplacementIndex]);
|
||||
|
||||
lutIndex = randomReplacementIndex;
|
||||
chr = MCFONT_CHARS.charAt(lutIndex);
|
||||
}
|
||||
|
||||
if (curRandom) {
|
||||
chr = FontProviderMC.get(this.isSGA).getRandomReplacement(chr);
|
||||
}
|
||||
|
||||
FontProvider fontProvider = FontStrategist.getFontProvider(this, chr, FontConfig.enableCustomFont, unicodeFlag);
|
||||
|
||||
heightNorth = anchorY + (underlying.FONT_HEIGHT - 1.0f) * (0.5f - glyphScaleY * fontProvider.getYScaleMultiplier() / 2);
|
||||
float heightSouth = (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY * fontProvider.getYScaleMultiplier();
|
||||
final float shadowOffset = unicodeFlag ? 0.5F : 1.0F;
|
||||
|
||||
// Check ASCII space, NBSP, NNBSP
|
||||
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
|
||||
curX += 4 * this.getWhitespaceScale();
|
||||
curX += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
final float uStart = fontProvider.getUStart(chr);
|
||||
final float vStart = fontProvider.getVStart(chr);
|
||||
final float xAdvance = fontProvider.getXAdvance(chr) * getGlyphScaleX();
|
||||
final float glyphW = fontProvider.getGlyphW(chr) * getGlyphScaleX();
|
||||
final float uSz = fontProvider.getUSize(chr);
|
||||
final float vSz = fontProvider.getVSize(chr);
|
||||
final float uStart;
|
||||
final float vStart;
|
||||
final float xAdvance;
|
||||
final float glyphW;
|
||||
final float uSz;
|
||||
final float vSz;
|
||||
final float itOff = curItalic ? 1.0F : 0.0F; // italic offset
|
||||
final float shadowOffset = fontProvider.getShadowOffset();
|
||||
final int shadowCopies = FontConfig.shadowCopies;
|
||||
final int boldCopies = FontConfig.boldCopies;
|
||||
final ResourceLocation texture = fontProvider.getTexture(chr);
|
||||
final ResourceLocation texture;
|
||||
|
||||
if (lutIndex == -1 || unicodeFlag) {
|
||||
if (glyphWidth[chr] == 0) {
|
||||
continue;
|
||||
}
|
||||
// Draw unicode char
|
||||
final int uniPage = chr / 256;
|
||||
texture = getUnicodePageLocation(uniPage);
|
||||
final int startColumn = this.glyphWidth[chr] >>> 4;
|
||||
final int endColumn = this.glyphWidth[chr] & 15;
|
||||
final float startColumnF = (float) startColumn;
|
||||
final float endColumnF = (float) (endColumn + 1);
|
||||
uStart = ((float) (chr % 16 * 16) + startColumnF) / 256.0f;
|
||||
vStart = ((float) ((chr & 255) / 16 * 16)) / 256.0f;
|
||||
final float chrWidth = endColumnF - startColumnF - 0.02F;
|
||||
glyphW = chrWidth / 2.0f + 1.0f;
|
||||
xAdvance = (endColumnF - startColumnF) / 2.0F + 1.0F;
|
||||
uSz = chrWidth / 256.0f;
|
||||
vSz = 15.98f / 256.0f;
|
||||
|
||||
} else {
|
||||
// Draw "ASCII" char
|
||||
uStart = ((lutIndex % 16) * 8) / 128.0F;
|
||||
vStart = (float) ((lutIndex / 16) * 8) / 128.0F;
|
||||
xAdvance = this.charWidth[lutIndex];
|
||||
if (xAdvance == 0) {
|
||||
continue;
|
||||
}
|
||||
glyphW = xAdvance - 0.01F;
|
||||
uSz = (glyphW - 1.0F) / 128.0F;
|
||||
vSz = 7.99F / 128.0F;
|
||||
texture = locationFontTexture;
|
||||
}
|
||||
|
||||
final int vtxId = vtxWriterIndex;
|
||||
final int idxId = idxWriterIndex;
|
||||
|
||||
if (enableShadow) {
|
||||
for (int n = 1; n <= shadowCopies; n++) {
|
||||
final float shadowOffsetPart = shadowOffset * ((float) n / shadowCopies);
|
||||
pushTexRect(curX + shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
|
||||
int vtxCount = 0;
|
||||
|
||||
if (curBold) {
|
||||
pushTexRect(curX + 2.0f * shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
|
||||
}
|
||||
if (enableShadow) {
|
||||
pushVtx(curX + itOff + shadowOffset, anchorY + shadowOffset, curShadowColor, uStart, vStart);
|
||||
pushVtx(
|
||||
curX - itOff + shadowOffset,
|
||||
anchorY + 7.99F + shadowOffset,
|
||||
curShadowColor,
|
||||
uStart,
|
||||
vStart + vSz);
|
||||
pushVtx(curX + glyphW - 1.0F + itOff + shadowOffset,
|
||||
anchorY + shadowOffset,
|
||||
curShadowColor,
|
||||
uStart + uSz,
|
||||
vStart);
|
||||
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset,
|
||||
anchorY + 7.99F + shadowOffset,
|
||||
curShadowColor,
|
||||
uStart + uSz,
|
||||
vStart + vSz);
|
||||
pushQuadIdx(vtxId + vtxCount);
|
||||
vtxCount += 4;
|
||||
|
||||
if (curBold) {
|
||||
final float shadowOffset2 = 2.0f * shadowOffset;
|
||||
pushVtx(curX + itOff + shadowOffset2, anchorY + shadowOffset, curShadowColor, uStart, vStart);
|
||||
pushVtx(
|
||||
curX - itOff + shadowOffset2,
|
||||
anchorY + 7.99F + shadowOffset,
|
||||
curShadowColor,
|
||||
uStart,
|
||||
vStart + vSz);
|
||||
pushVtx(
|
||||
curX + glyphW - 1.0F + itOff + shadowOffset2,
|
||||
anchorY + shadowOffset,
|
||||
curShadowColor,
|
||||
uStart + uSz,
|
||||
vStart);
|
||||
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset2,
|
||||
anchorY + 7.99F + shadowOffset,
|
||||
curShadowColor,
|
||||
uStart + uSz,
|
||||
vStart + vSz);
|
||||
pushQuadIdx(vtxId + vtxCount);
|
||||
vtxCount += 4;
|
||||
}
|
||||
}
|
||||
|
||||
pushTexRect(curX, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
|
||||
pushVtx(curX + itOff, anchorY, curColor, uStart, vStart);
|
||||
pushVtx(curX - itOff, anchorY + 7.99F, curColor, uStart, vStart + vSz);
|
||||
pushVtx(curX + glyphW - 1.0F + itOff, anchorY, curColor, uStart + uSz, vStart);
|
||||
pushVtx(curX + glyphW - 1.0F - itOff, anchorY + 7.99F, curColor, uStart + uSz, vStart + vSz);
|
||||
pushQuadIdx(vtxId + vtxCount);
|
||||
vtxCount += 4;
|
||||
|
||||
if (curBold) {
|
||||
for (int n = 1; n <= boldCopies; n++) {
|
||||
final float shadowOffsetPart = shadowOffset * ((float) n / boldCopies);
|
||||
pushTexRect(curX + shadowOffsetPart, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
|
||||
}
|
||||
pushVtx(shadowOffset + curX + itOff, anchorY, curColor, uStart, vStart);
|
||||
pushVtx(shadowOffset + curX - itOff, anchorY + 7.99F, curColor, uStart, vStart + vSz);
|
||||
pushVtx(shadowOffset + curX + glyphW - 1.0F + itOff, anchorY, curColor, uStart + uSz, vStart);
|
||||
pushVtx(shadowOffset + curX + glyphW - 1.0F - itOff,
|
||||
anchorY + 7.99F,
|
||||
curColor,
|
||||
uStart + uSz,
|
||||
vStart + vSz);
|
||||
pushQuadIdx(vtxId + vtxCount);
|
||||
vtxCount += 4;
|
||||
}
|
||||
|
||||
/*
|
||||
Vertex-per-char counts for different configurations
|
||||
default: 4
|
||||
shadow only: 4(1 + shadowCopies)
|
||||
bold only: 4(1 + boldCopies)
|
||||
both: 4(1 + 2 * shadowCopies + boldCopies)
|
||||
*/
|
||||
int charCount = 1;
|
||||
if (enableShadow) { charCount += shadowCopies * (curBold ? 2 : 1); }
|
||||
if (curBold) { charCount += boldCopies; }
|
||||
final int vtxCount = 4 * charCount;
|
||||
pushDrawCmd(idxId, vtxCount / 2 * 3, texture, chr > 255);
|
||||
|
||||
curX += (xAdvance + (curBold ? shadowOffset : 0.0f)) + getGlyphSpacing();
|
||||
pushDrawCmd(idxId, vtxCount / 2 * 3, texture);
|
||||
curX += xAdvance + (curBold ? shadowOffset : 0.0f);
|
||||
underlineEndX = curX;
|
||||
strikethroughEndX = curX;
|
||||
}
|
||||
|
||||
if (curUnderline && underlineStartX != underlineEndX) {
|
||||
final int ulIdx = idxWriterIndex;
|
||||
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
|
||||
pushDrawCmd(ulIdx, 6, null, false);
|
||||
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, 1.0f, curColor);
|
||||
pushDrawCmd(ulIdx, 6, null);
|
||||
}
|
||||
if (curStrikethrough && strikethroughStartX != strikethroughEndX) {
|
||||
final int ulIdx = idxWriterIndex;
|
||||
@@ -594,9 +575,9 @@ public class BatchingFontRenderer {
|
||||
strikethroughStartX,
|
||||
strikethroughY,
|
||||
strikethroughEndX - strikethroughStartX,
|
||||
glyphScaleY,
|
||||
1.0f,
|
||||
curColor);
|
||||
pushDrawCmd(ulIdx, 6, null, false);
|
||||
pushDrawCmd(ulIdx, 6, null);
|
||||
}
|
||||
|
||||
} finally {
|
||||
@@ -605,25 +586,4 @@ public class BatchingFontRenderer {
|
||||
return curX + (enableShadow ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
public float getCharWidthFine(char chr) {
|
||||
if (chr == FORMATTING_CHAR) { return -1; }
|
||||
|
||||
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
|
||||
return 4 * this.getWhitespaceScale();
|
||||
}
|
||||
|
||||
FontProvider fp = FontStrategist.getFontProvider(this, chr, FontConfig.enableCustomFont, underlying.getUnicodeFlag());
|
||||
|
||||
return fp.getXAdvance(chr) * this.getGlyphScaleX();
|
||||
}
|
||||
|
||||
public void overrideBlendFunc(int srcRgb, int dstRgb) {
|
||||
blendSrcRGB = srcRgb;
|
||||
blendDstRGB = dstRgb;
|
||||
}
|
||||
|
||||
public void resetBlendFunc() {
|
||||
blendSrcRGB = GL11.GL_SRC_ALPHA;
|
||||
blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public interface FontProvider {
|
||||
|
||||
/**
|
||||
* For use with §k. Should fetch a character of the same width as provided.
|
||||
*/
|
||||
char getRandomReplacement(char chr);
|
||||
boolean isGlyphAvailable(char chr);
|
||||
float getUStart(char chr);
|
||||
float getVStart(char chr);
|
||||
float getXAdvance(char chr);
|
||||
float getGlyphW(char chr);
|
||||
float getUSize(char chr);
|
||||
float getVSize(char chr);
|
||||
float getShadowOffset();
|
||||
ResourceLocation getTexture(char chr);
|
||||
float getYScaleMultiplier();
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class FontProviderCustom implements FontProvider {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
public static final String FONT_DIR = "fonts/custom/";
|
||||
static final int ATLAS_SIZE = 128;
|
||||
static final int ATLAS_COUNT = 512;
|
||||
private final byte id; // 0 - primary font, 1 - fallback font
|
||||
private FontAtlas[] fontAtlases = new FontAtlas[ATLAS_COUNT];
|
||||
private float currentFontQuality = FontConfig.customFontQuality;
|
||||
@Setter
|
||||
private Font font;
|
||||
|
||||
private FontProviderCustom(byte id) {
|
||||
this.id = id;
|
||||
Font[] availableFonts = FontStrategist.getAvailableFonts();
|
||||
String myFontName = switch (this.id) {
|
||||
case 0 -> FontConfig.customFontNamePrimary;
|
||||
case 1 -> FontConfig.customFontNameFallback;
|
||||
default -> null;
|
||||
};
|
||||
if (Objects.equals(myFontName, "(none)")) {
|
||||
this.font = null;
|
||||
return;
|
||||
}
|
||||
int fontPos = -1;
|
||||
for (int i = 0; i < availableFonts.length; i++) {
|
||||
if (Objects.equals(myFontName, availableFonts[i].getFontName())) {
|
||||
fontPos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fontPos == -1) {
|
||||
LOGGER.info("Could not find previously set font \"{}\". ", myFontName);
|
||||
this.font = null;
|
||||
return;
|
||||
}
|
||||
this.font = availableFonts[fontPos].deriveFont(this.currentFontQuality);
|
||||
}
|
||||
private static class InstLoader {
|
||||
static final FontProviderCustom instance0 = new FontProviderCustom((byte)0);
|
||||
static final FontProviderCustom instance1 = new FontProviderCustom((byte)1);
|
||||
}
|
||||
public static FontProviderCustom getPrimary() { return InstLoader.instance0; }
|
||||
public static FontProviderCustom getFallback() { return InstLoader.instance1; }
|
||||
|
||||
public void reloadFont(int fontID) {
|
||||
this.currentFontQuality = FontConfig.customFontQuality;
|
||||
this.font = FontStrategist.getAvailableFonts()[fontID].deriveFont(this.currentFontQuality);
|
||||
|
||||
File[] files = new File(getFontDir()).listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
if (!Files.isSymbolicLink(f.toPath())) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextureManager tm = Minecraft.getMinecraft().getTextureManager();
|
||||
Map mapTextureObjects = tm.mapTextureObjects;
|
||||
for (int i = 0; i < ATLAS_COUNT; i++) {
|
||||
ResourceLocation key = new ResourceLocation(getAtlasResourceName(i));
|
||||
if (mapTextureObjects.containsKey(key)) {
|
||||
ITextureObject obj = (ITextureObject) mapTextureObjects.get(key);
|
||||
TextureUtil.deleteTexture(obj.getGlTextureId());
|
||||
mapTextureObjects.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
this.fontAtlases = new FontAtlas[ATLAS_COUNT];
|
||||
}
|
||||
|
||||
private String getFontDir() {
|
||||
return FONT_DIR + "f" + this.id + "/";
|
||||
}
|
||||
|
||||
private String getAtlasFilename(int atlasId) {
|
||||
return "f" + this.id + "p" + atlasId;
|
||||
}
|
||||
|
||||
String getAtlasResourceName(int atlasId) {
|
||||
return "minecraft:angelica_c" + getAtlasFilename(atlasId);
|
||||
}
|
||||
|
||||
String getAtlasFullPath(int atlasId) {
|
||||
return getFontDir() + getAtlasFilename(atlasId) + ".png";
|
||||
}
|
||||
|
||||
@Value
|
||||
private class GlyphData {
|
||||
float uStart;
|
||||
float vStart;
|
||||
float xAdvance;
|
||||
float glyphW;
|
||||
float uSz;
|
||||
float vSz;
|
||||
}
|
||||
|
||||
private class FontAtlas {
|
||||
|
||||
GlyphData[] glyphData = new GlyphData[ATLAS_SIZE];
|
||||
private ResourceLocation texture;
|
||||
private final int id;
|
||||
|
||||
FontAtlas(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
void construct(Font font) {
|
||||
int atlasChars = 0;
|
||||
for (int i = 0; i < ATLAS_SIZE; i++) {
|
||||
final char ch = (char) (i + ATLAS_SIZE * this.id);
|
||||
if (font.canDisplay(ch)) { atlasChars++; }
|
||||
}
|
||||
if (atlasChars == 0) { return; }
|
||||
|
||||
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = image.createGraphics();
|
||||
g2d.setFont(font);
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
g2d.dispose();
|
||||
|
||||
final int atlasTilesX = (int) Math.ceil(Math.sqrt(atlasChars) * 1.5f);
|
||||
final int atlasTilesY = (int) Math.ceil((double) atlasChars / atlasTilesX);
|
||||
final float charSeparator = currentFontQuality / 3f;
|
||||
int rowWidth = 0;
|
||||
int maxRowWidth = 0;
|
||||
atlasChars = 0;
|
||||
|
||||
for (int i = 0; i < ATLAS_SIZE; i++) {
|
||||
if (atlasChars % atlasTilesX == 0) {
|
||||
maxRowWidth = Math.max(maxRowWidth, rowWidth);
|
||||
rowWidth = 0;
|
||||
}
|
||||
final char ch = (char)(i + ATLAS_SIZE * this.id);
|
||||
if (font.canDisplay(ch)) {
|
||||
rowWidth += (int) (charSeparator + fm.charWidth(ch));
|
||||
atlasChars++;
|
||||
}
|
||||
}
|
||||
maxRowWidth = Math.max(maxRowWidth, rowWidth);
|
||||
|
||||
final int lineHeight = fm.getHeight();
|
||||
final float desc = fm.getDescent();
|
||||
|
||||
final int imageWidth = (int) (maxRowWidth + charSeparator);
|
||||
final int imageHeight = (int) ((charSeparator + lineHeight) * atlasTilesY + charSeparator);
|
||||
|
||||
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
|
||||
g2d = image.createGraphics();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g2d.setFont(font);
|
||||
fm = g2d.getFontMetrics();
|
||||
|
||||
int tileX = 0, tileY = 0; // position in atlas tiles
|
||||
int imgX = (int) charSeparator; // position in pixels
|
||||
|
||||
for (int i = 0; i < ATLAS_SIZE; i++) {
|
||||
final char ch = (char) (i + ATLAS_SIZE * this.id);
|
||||
if (!font.canDisplay(ch)) { continue; }
|
||||
|
||||
if (tileX >= atlasTilesX) {
|
||||
tileX = 0;
|
||||
imgX = (int) charSeparator;
|
||||
tileY++;
|
||||
}
|
||||
|
||||
final int charWidth = fm.charWidth(ch);
|
||||
final float charAspectRatio = (float) charWidth / lineHeight;
|
||||
final float inset = currentFontQuality / 16;
|
||||
g2d.drawString(Character.toString(ch), imgX, (lineHeight + charSeparator) * (tileY + 1) - desc);
|
||||
final float uStart = (float) (imgX - inset * charAspectRatio) / imageWidth;
|
||||
final float vStart = ((lineHeight + charSeparator) * (tileY + 1) - lineHeight - inset) / imageHeight;
|
||||
final float xAdvance = charAspectRatio * 8 * charWidth / (charWidth + 2 * inset * charAspectRatio);
|
||||
final float glyphW = charAspectRatio * 8 + 1;
|
||||
final float uSz = (float) (charWidth + 2 * inset * charAspectRatio) / imageWidth;
|
||||
final float vSz = (float) (lineHeight + 2 * inset) / imageHeight;
|
||||
this.glyphData[i] = new GlyphData(uStart, vStart, xAdvance, glyphW, uSz, vSz);
|
||||
imgX += (int) (charWidth + charSeparator);
|
||||
tileX++;
|
||||
}
|
||||
g2d.dispose();
|
||||
try {
|
||||
Files.createDirectories(Paths.get(getFontDir()));
|
||||
ImageIO.write(image, "png", new File(getAtlasFullPath(this.id)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.texture = new ResourceLocation(getAtlasResourceName(this.id));
|
||||
}
|
||||
}
|
||||
|
||||
private FontAtlas getAtlas(char chr) {
|
||||
int id = chr / ATLAS_SIZE;
|
||||
FontAtlas fa = this.fontAtlases[id];
|
||||
if (fa == null) {
|
||||
fa = new FontAtlas(id);
|
||||
fa.construct(this.font);
|
||||
this.fontAtlases[id] = fa;
|
||||
}
|
||||
return fa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlyphAvailable(char chr) {
|
||||
if (this.font == null) { return false; }
|
||||
return (getAtlas(chr).glyphData[chr % ATLAS_SIZE] != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getRandomReplacement(char chr) {
|
||||
return chr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUStart(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVStart(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXAdvance(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].xAdvance * FontConfig.customFontScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphW(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].glyphW * FontConfig.customFontScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUSize(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uSz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVSize(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vSz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShadowOffset() {
|
||||
return FontConfig.fontShadowOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(char chr) {
|
||||
return getAtlas(chr).texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYScaleMultiplier() {
|
||||
return FontConfig.customFontScale;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import it.unimi.dsi.fastutil.chars.Char2ShortOpenHashMap;
|
||||
import jss.util.RandomXoshiro256StarStar;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public final class FontProviderMC implements FontProvider {
|
||||
|
||||
/**
|
||||
* The full list of characters present in the default Minecraft font, excluding the Unicode font
|
||||
*/
|
||||
@SuppressWarnings("UnnecessaryUnicodeEscape")
|
||||
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
|
||||
|
||||
private FontProviderMC() {}
|
||||
private static class InstLoader {
|
||||
static final FontProviderMC instance = new FontProviderMC();
|
||||
static final FontProviderMC instanceSGA = new FontProviderMC();
|
||||
}
|
||||
public static FontProviderMC get(boolean isSGA) {
|
||||
return (isSGA ? InstLoader.instanceSGA : InstLoader.instance);
|
||||
}
|
||||
|
||||
private static final Char2ShortOpenHashMap MCFONT_ASCII_MAP = new Char2ShortOpenHashMap();
|
||||
ResourceLocation locationFontTexture = null;
|
||||
private final RandomXoshiro256StarStar fontRandom = new RandomXoshiro256StarStar();
|
||||
public int[] charWidth;
|
||||
|
||||
static {
|
||||
for (short i = 0; i < MCFONT_CHARS.length(); i++) {
|
||||
MCFONT_ASCII_MAP.put(MCFONT_CHARS.charAt(i), i);
|
||||
}
|
||||
}
|
||||
|
||||
public int lookupMcFontPosition(char ch) {
|
||||
if (isGlyphAvailable(ch)) {
|
||||
return MCFONT_ASCII_MAP.get(ch);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlyphAvailable(char chr) {
|
||||
return MCFONT_ASCII_MAP.containsKey(chr);
|
||||
}
|
||||
|
||||
public char getRandomReplacement(char chr){
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
if (lutIndex != -1) {
|
||||
int randomReplacementIndex;
|
||||
do {
|
||||
randomReplacementIndex = fontRandom.nextInt(this.charWidth.length);
|
||||
} while (this.charWidth[lutIndex] != this.charWidth[randomReplacementIndex]);
|
||||
|
||||
lutIndex = randomReplacementIndex;
|
||||
return MCFONT_CHARS.charAt(lutIndex);
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUStart(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return ((lutIndex % 16) * 8) / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVStart(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return (float) ((lutIndex / 16) * 8) / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXAdvance(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return charWidth[lutIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphW(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return charWidth[lutIndex] - 0.01F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUSize(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return (charWidth[lutIndex] - 1.01F) / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVSize(char chr) {
|
||||
return 7.99F / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShadowOffset() {
|
||||
return FontConfig.fontShadowOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(char chr) {
|
||||
return locationFontTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYScaleMultiplier() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public final class FontProviderUnicode implements FontProvider {
|
||||
|
||||
private FontProviderUnicode() {
|
||||
try {
|
||||
InputStream inputstream = Minecraft.getMinecraft().getResourceManager()
|
||||
.getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
inputstream.read(this.glyphWidth);
|
||||
}
|
||||
catch (IOException ioexception) {
|
||||
throw new RuntimeException(ioexception);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InstLoader { static final FontProviderUnicode instance = new FontProviderUnicode(); }
|
||||
public static FontProviderUnicode get() { return FontProviderUnicode.InstLoader.instance; }
|
||||
|
||||
private static final ResourceLocation[] unicodePageLocations = new ResourceLocation[256];
|
||||
private final byte[] glyphWidth = new byte[65536];
|
||||
|
||||
private ResourceLocation getUnicodePageLocation(int page) {
|
||||
final ResourceLocation lookup = unicodePageLocations[page];
|
||||
if (lookup == null) {
|
||||
final ResourceLocation rl = new ResourceLocation(String.format(
|
||||
"textures/font/unicode_page_%02x.png",
|
||||
page));
|
||||
unicodePageLocations[page] = rl;
|
||||
return rl;
|
||||
} else {
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlyphAvailable(char chr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getRandomReplacement(char chr) {
|
||||
return chr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUStart(char chr) {
|
||||
final float startColumnF = (float)((this.glyphWidth[chr] >>> 4) & 15);
|
||||
return ((float) (chr % 16 * 16) + startColumnF + 0.21f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVStart(char chr) {
|
||||
return ((float) ((chr & 255) / 16 * 16) + 0.21f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXAdvance(char chr) {
|
||||
final int startColumn = (this.glyphWidth[chr] >>> 4) & 15;
|
||||
final int endColumn = this.glyphWidth[chr] & 15;
|
||||
final float startColumnF = (float) startColumn;
|
||||
final float endColumnF = (float) (endColumn + 1);
|
||||
return (endColumnF - startColumnF) / 2.0F + 1.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphW(char chr) {
|
||||
final int startColumn = (this.glyphWidth[chr] >>> 4) & 15;
|
||||
final int endColumn = this.glyphWidth[chr] & 15;
|
||||
final float startColumnF = (float) startColumn;
|
||||
final float endColumnF = (float) (endColumn + 1);
|
||||
final float chrWidth = endColumnF - startColumnF - 0.02F;
|
||||
return chrWidth / 2.0f + 1.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUSize(char chr) {
|
||||
final int startColumn = (this.glyphWidth[chr] >>> 4) & 15;
|
||||
final int endColumn = this.glyphWidth[chr] & 15;
|
||||
final float startColumnF = (float) startColumn;
|
||||
final float endColumnF = (float) (endColumn + 1);
|
||||
final float chrWidth = endColumnF - startColumnF - 0.02F;
|
||||
return (chrWidth - 0.42f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVSize(char chr) {
|
||||
return (16.0f - 0.42f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShadowOffset() {
|
||||
return FontConfig.fontShadowOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(char chr) {
|
||||
final int uniPage = chr / 256;
|
||||
return getUnicodePageLocation(uniPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYScaleMultiplier() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ResourceAccessor;
|
||||
import cpw.mods.fml.client.SplashProgress;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.resources.DefaultResourcePack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.File;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FontStrategist {
|
||||
|
||||
@Getter
|
||||
private static final Font[] availableFonts;
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
static {
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
LOGGER.warn("GraphicsEnvironment.isHeadless() returned true! Custom fonts will be unavailable. This is likely a MacOS issue.");
|
||||
availableFonts = new Font[0];
|
||||
} else {
|
||||
// get available fonts without duplicates (250 copies of dialog.plain need not apply)
|
||||
Font[] availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||
HashMap<String, Font> fontSet = new HashMap<>();
|
||||
HashMultiset<String> duplicates = HashMultiset.create(); // for debugging
|
||||
|
||||
for (Font font : availableFontsDirty) {
|
||||
String fontName = font.getFontName();
|
||||
if (fontSet.containsKey(fontName)) {
|
||||
duplicates.add(fontName);
|
||||
} else {
|
||||
fontSet.put(fontName, font);
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicates.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder(duplicates.size() + " duplicate font(s) found in the list reported by Java: ");
|
||||
for (Iterator<String> iter = duplicates.stream().distinct().iterator(); iter.hasNext(); ) {
|
||||
String dupe = iter.next();
|
||||
sb.append(duplicates.count(dupe)).append("x ").append(dupe);
|
||||
if (iter.hasNext()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append(". Some fonts may be missing from the font selection menu.");
|
||||
LOGGER.warn(sb.toString());
|
||||
}
|
||||
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
|
||||
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, availableFonts.length);
|
||||
}
|
||||
|
||||
// create and add the resource pack that provides fonts
|
||||
HashMap<String, File> packMap = new HashMap<>();
|
||||
for (int i = 0; i < FontProviderCustom.ATLAS_COUNT; i++) {
|
||||
packMap.put(FontProviderCustom.getPrimary().getAtlasResourceName(i), new File(FontProviderCustom.getPrimary().getAtlasFullPath(i)));
|
||||
packMap.put(FontProviderCustom.getFallback().getAtlasResourceName(i), new File(FontProviderCustom.getFallback().getAtlasFullPath(i)));
|
||||
}
|
||||
|
||||
DefaultResourcePack fontResourcePack = new DefaultResourcePack(packMap);
|
||||
List defaultResourcePacks = ((ResourceAccessor) Minecraft.getMinecraft()).angelica$getDefaultResourcePacks();
|
||||
defaultResourcePacks.add(fontResourcePack);
|
||||
}
|
||||
|
||||
/**
|
||||
Lets you get a FontProvider per char while respecting font priority and fallbacks, the unicode flag, whether or not
|
||||
SGA is on, if we're in a splash screen, if a font can even display a character in the first place, etc.
|
||||
*/
|
||||
public static FontProvider getFontProvider(BatchingFontRenderer me, char chr, boolean customFontEnabled, boolean forceUnicode) {
|
||||
if (me.isSGA && FontProviderMC.get(true).isGlyphAvailable(chr)) {
|
||||
return FontProviderMC.get(true);
|
||||
}
|
||||
if (customFontEnabled && !me.isSplash) {
|
||||
FontProvider fp;
|
||||
fp = FontProviderCustom.getPrimary();
|
||||
if (fp.isGlyphAvailable(chr)) { return fp; }
|
||||
fp = FontProviderCustom.getFallback();
|
||||
if (fp.isGlyphAvailable(chr)) { return fp; }
|
||||
return FontProviderUnicode.get();
|
||||
} else {
|
||||
if (!forceUnicode && FontProviderMC.get(false).isGlyphAvailable(chr)) {
|
||||
return FontProviderMC.get(false);
|
||||
} else {
|
||||
return FontProviderUnicode.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void reloadCustomFontProviders() {
|
||||
FontProviderCustom.getPrimary().setFont(null);
|
||||
FontProviderCustom.getFallback().setFont(null);
|
||||
for (int i = 0; i < availableFonts.length; i++) {
|
||||
if (Objects.equals(FontConfig.customFontNamePrimary, availableFonts[i].getFontName())) {
|
||||
FontProviderCustom.getPrimary().reloadFont(i);
|
||||
}
|
||||
if (Objects.equals(FontConfig.customFontNameFallback, availableFonts[i].getFontName())) {
|
||||
FontProviderCustom.getFallback().reloadFont(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSplashFontRendererActive(FontRenderer fontRenderer) {
|
||||
// noinspection deprecation
|
||||
boolean active = fontRenderer instanceof SplashProgress.SplashFontRenderer;
|
||||
|
||||
try {
|
||||
Class<?> customSplashClass = Class.forName("gkappa.modernsplash.CustomSplash$SplashFontRenderer");
|
||||
active = active || customSplashClass.isInstance(fontRenderer);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfigStorage;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
|
||||
import jss.notfine.core.Settings;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.Option;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionGroup;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionImpl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionImpact;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DynamicLightsOptionPages {
|
||||
|
||||
public static OptionPage dynamicLights() {
|
||||
if (!DynamicLights.configEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<OptionGroup> groups = new ArrayList<>();
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
|
||||
.build());
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
|
||||
.build());
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
|
||||
.build());
|
||||
|
||||
// Entity type toggles - grouped by mod ID
|
||||
List<EntityTypeEntry> allEntities = EntityLightConfig.getAllEntityTypes();
|
||||
Map<String, List<EntityTypeEntry>> byMod = new LinkedHashMap<>();
|
||||
|
||||
for (EntityTypeEntry entry : allEntities) {
|
||||
byMod.computeIfAbsent(entry.getModId(), _ -> new ArrayList<>()).add(entry);
|
||||
}
|
||||
|
||||
// Create a group for each mod's entities
|
||||
for (Map.Entry<String, List<EntityTypeEntry>> modEntry : byMod.entrySet()) {
|
||||
String modId = modEntry.getKey();
|
||||
List<EntityTypeEntry> entities = modEntry.getValue();
|
||||
|
||||
OptionGroup.Builder groupBuilder = OptionGroup.createBuilder();
|
||||
|
||||
for (EntityTypeEntry entity : entities) {
|
||||
Option<Boolean> option = createEntityOption(entity);
|
||||
if (option != null) {
|
||||
groupBuilder.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
// Only add group if it has options
|
||||
OptionGroup group = groupBuilder.build();
|
||||
if (!group.getOptions().isEmpty()) {
|
||||
groups.add(group);
|
||||
}
|
||||
}
|
||||
|
||||
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
private static Option<Boolean> createEntityOption(EntityTypeEntry entry) {
|
||||
final String className = entry.getClassName();
|
||||
|
||||
// Resolve class once during option creation, not on every get/set
|
||||
Class<?> resolvedClass;
|
||||
try {
|
||||
resolvedClass = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Class not loaded, skip this option
|
||||
return null;
|
||||
}
|
||||
|
||||
final Class<?> entityClass = resolvedClass;
|
||||
|
||||
return OptionImpl.createBuilder(Boolean.class, EntityLightConfigStorage.INSTANCE)
|
||||
.setName(entry.getDisplayName() + " (" + entry.getModId() + ")")
|
||||
.setTooltip(I18n.format("options.dynamiclights.entity.tooltip", entry.getDisplayName()))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding(
|
||||
(_, value) -> EntityLightConfig.setEntityTypeEnabled(entityClass, value),
|
||||
storage -> EntityLightConfig.isEntityTypeEnabled(entityClass)
|
||||
)
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,561 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigException;
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
|
||||
import com.gtnewhorizons.angelica.client.font.FontStrategist;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import net.coderbot.iris.gui.element.widget.IrisButton;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiSlot;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FontConfigScreen extends GuiScreen {
|
||||
|
||||
private static final Font[] availableFonts = FontStrategist.getAvailableFonts();
|
||||
private final GuiScreen parent;
|
||||
private final String title;
|
||||
private final String searchPrompt;
|
||||
private final String testAreaPrompt;
|
||||
private List<String> testAreaInfo;
|
||||
private List<String> noFontsWarning;
|
||||
private String currentPrimaryFontName;
|
||||
private String currentFallbackFontName;
|
||||
private FontList fontList;
|
||||
private int selectedPrimaryFontListPos = -1;
|
||||
private int selectedFallbackFontListPos = -1;
|
||||
private ArrayList<Font> displayedFonts;
|
||||
private GuiTextField searchBox;
|
||||
private GuiTextField testArea;
|
||||
private int sliderPages = 0;
|
||||
private int displayedSliderPage = 0;
|
||||
private final int sliderWidth = 160;
|
||||
private final int sliderHeight = 20;
|
||||
|
||||
// need to save these separately
|
||||
private IrisButton fwdButton;
|
||||
private IrisButton backButton;
|
||||
|
||||
SliderClone.Option optFontQuality = new SliderClone.Option(6, 72, 6);
|
||||
SliderClone.Option optShadowOffset = new SliderClone.Option(0, 2, 0.05f);
|
||||
SliderClone.Option optShadowCopies = new SliderClone.Option(1, 8, 1);
|
||||
SliderClone.Option optBoldCopies = new SliderClone.Option(1, 8, 1);
|
||||
SliderClone.Option optGlyphAspect = new SliderClone.Option(-1, 1, 0.05f);
|
||||
SliderClone.Option optGlyphScale = new SliderClone.Option(0.1f, 3, 0.05f);
|
||||
SliderClone.Option optWhitespaceScale = new SliderClone.Option(0.1f, 3, 0.05f);
|
||||
SliderClone.Option optGlyphSpacing = new SliderClone.Option(-2f, 2f, 0.05f);
|
||||
SliderClone.Option optFontAAMode = new SliderClone.Option(0, 2, 1);
|
||||
SliderClone.Option optFontAAStrength = new SliderClone.Option(1, 24, 1);
|
||||
SliderClone.Option optCustomFontScale = new SliderClone.Option(0.1f, 3, 0.05f);
|
||||
|
||||
public FontConfigScreen(GuiScreen parent) {
|
||||
this.title = I18n.format("options.angelica.fontconfig.title");
|
||||
this.searchPrompt = I18n.format("options.angelica.fontconfig.searchprompt");
|
||||
this.testAreaPrompt = I18n.format("options.angelica.fontconfig.testareaprompt");
|
||||
this.parent = parent;
|
||||
this.currentPrimaryFontName = FontConfig.customFontNamePrimary;
|
||||
this.currentFallbackFontName = FontConfig.customFontNameFallback;
|
||||
this.displayedFonts = new ArrayList<>(Arrays.asList(availableFonts));
|
||||
for (int i = 0; i < availableFonts.length; i++) {
|
||||
if (Objects.equals(this.currentPrimaryFontName, availableFonts[i].getFontName())) {
|
||||
selectedPrimaryFontListPos = i;
|
||||
}
|
||||
if (Objects.equals(this.currentFallbackFontName, availableFonts[i].getFontName())) {
|
||||
selectedFallbackFontListPos = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void initGui() {
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 120, 24, 240, 20);
|
||||
searchBox.setMaxStringLength(64);
|
||||
fontList = new FontList();
|
||||
testArea = new GuiTextField(this.fontRendererObj, this.width * 5 / 6 - this.width / 7, 63 - 10, this.width * 2 / 7, 20);
|
||||
testArea.setMaxStringLength(512);
|
||||
testArea.setText(I18n.format("options.angelica.fontconfig.testareaexample"));
|
||||
initButtons();
|
||||
}
|
||||
|
||||
private void initButtons() {
|
||||
final int halfWidth = this.width / 2;
|
||||
final int sliderSpacing = 4;
|
||||
|
||||
final int x1 = halfWidth - sliderWidth - sliderSpacing;
|
||||
final int x2 = halfWidth;
|
||||
final int x3 = halfWidth + sliderWidth + sliderSpacing;
|
||||
final int y3 = this.height - 65 + 2 * (sliderHeight + sliderSpacing);
|
||||
|
||||
this.buttonList.add(new IrisButton(x1 - sliderWidth / 2, y3 - sliderHeight / 2, sliderWidth, sliderHeight,
|
||||
FontConfig.enableCustomFont ? I18n.format("options.angelica.fontconfig.disable_custom_font") :
|
||||
I18n.format("options.angelica.fontconfig.enable_custom_font"), this::toggleCustomFont));
|
||||
this.buttonList.add(new IrisButton(x2 - sliderWidth / 2, y3 - sliderHeight / 2, sliderWidth, sliderHeight,
|
||||
I18n.format("options.angelica.fontconfig.reset_values"), this::resetValues));
|
||||
this.buttonList.add(new IrisButton(x3 - sliderWidth / 2, y3 - sliderHeight / 2, sliderWidth, sliderHeight,
|
||||
I18n.format("gui.done"), button -> this.onClose()));
|
||||
|
||||
backButton = new IrisButton(halfWidth - 20 - 224, this.height - 89 - 10, 40, 20,
|
||||
"<", button -> this.switchPage(-1));
|
||||
fwdButton = new IrisButton(halfWidth - 20 + 224, this.height - 89 - 10, 40, 20,
|
||||
">", button -> this.switchPage(1));
|
||||
this.buttonList.add(fwdButton);
|
||||
this.buttonList.add(backButton);
|
||||
|
||||
initSliders();
|
||||
}
|
||||
|
||||
private void initSliders() {
|
||||
ArrayList<SliderClone> sliders = new ArrayList<>();
|
||||
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optFontQuality)
|
||||
.initialValue(FontConfig.customFontQuality)
|
||||
.setter(this::setFontQuality)
|
||||
.langKey("options.angelica.fontconfig.font_quality")
|
||||
.formatString("%2.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optShadowOffset)
|
||||
.initialValue(FontConfig.fontShadowOffset)
|
||||
.setter(value -> FontConfig.fontShadowOffset = value)
|
||||
.langKey("options.angelica.fontconfig.shadow_offset")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphAspect)
|
||||
.initialValue(FontConfig.glyphAspect)
|
||||
.setter(value -> FontConfig.glyphAspect = value)
|
||||
.langKey("options.angelica.fontconfig.glyph_aspect")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphScale)
|
||||
.initialValue(FontConfig.glyphScale)
|
||||
.setter(value -> FontConfig.glyphScale = value)
|
||||
.langKey("options.angelica.fontconfig.glyph_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optFontAAStrength)
|
||||
.initialValue(FontConfig.fontAAStrength)
|
||||
.setter(value -> FontConfig.fontAAStrength = value.intValue())
|
||||
.langKey("options.angelica.fontconfig.font_aa_strength")
|
||||
.formatString("%.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optFontAAMode)
|
||||
.initialValue(FontConfig.fontAAMode)
|
||||
.setter(value -> FontConfig.fontAAMode = value.intValue())
|
||||
.formatter(this::fontAAModeFormat)
|
||||
.langKey("options.angelica.fontconfig.aamode")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optWhitespaceScale)
|
||||
.initialValue(FontConfig.whitespaceScale)
|
||||
.setter(value -> FontConfig.whitespaceScale = value)
|
||||
.langKey("options.angelica.fontconfig.whitespace_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphSpacing)
|
||||
.initialValue(FontConfig.glyphSpacing)
|
||||
.setter(value -> FontConfig.glyphSpacing = value)
|
||||
.langKey("options.angelica.fontconfig.glyph_spacing")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optShadowCopies)
|
||||
.initialValue(FontConfig.shadowCopies)
|
||||
.setter(value -> FontConfig.shadowCopies = value.intValue())
|
||||
.langKey("options.angelica.fontconfig.shadow_copies")
|
||||
.formatString("%.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optBoldCopies)
|
||||
.initialValue(FontConfig.boldCopies)
|
||||
.setter(value -> FontConfig.boldCopies = value.intValue())
|
||||
.langKey("options.angelica.fontconfig.bold_copies")
|
||||
.formatString("%.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optCustomFontScale)
|
||||
.initialValue(FontConfig.customFontScale)
|
||||
.setter(value -> FontConfig.customFontScale = value)
|
||||
.langKey("options.angelica.fontconfig.custom_font_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
|
||||
final int halfWidth = this.width / 2;
|
||||
final int sliderSpacing = 4;
|
||||
|
||||
final int x1 = halfWidth - sliderWidth - sliderSpacing;
|
||||
final int x2 = halfWidth;
|
||||
final int x3 = halfWidth + sliderWidth + sliderSpacing;
|
||||
final int y1 = this.height - 65;
|
||||
final int y2 = this.height - 65 + sliderHeight + sliderSpacing;
|
||||
|
||||
this.sliderPages = sliders.size() / 6;
|
||||
this.displayedSliderPage = Math.min(this.displayedSliderPage, this.sliderPages);
|
||||
|
||||
this.backButton.enabled = (this.displayedSliderPage != 0);
|
||||
this.fwdButton.enabled = (this.displayedSliderPage != this.sliderPages);
|
||||
|
||||
final int start = 6 * this.displayedSliderPage;
|
||||
final int end = Math.min(6 * (this.displayedSliderPage + 1), sliders.size());
|
||||
List<SliderClone> s = sliders.subList(start, end);
|
||||
try {
|
||||
s.get(0).setCenterPosition(x1, y1);
|
||||
s.get(1).setCenterPosition(x1, y2);
|
||||
s.get(2).setCenterPosition(x2, y1);
|
||||
s.get(3).setCenterPosition(x2, y2);
|
||||
s.get(4).setCenterPosition(x3, y1);
|
||||
s.get(5).setCenterPosition(x3, y2);
|
||||
} catch (IndexOutOfBoundsException ignored) {
|
||||
// we're on the last page and it has less than 6 sliders to display
|
||||
}
|
||||
this.buttonList.addAll(s);
|
||||
}
|
||||
|
||||
private void onClose() {
|
||||
applyChanges(true);
|
||||
this.mc.displayGuiScreen(parent);
|
||||
}
|
||||
|
||||
private void applyChanges(boolean finalApply) {
|
||||
int pos;
|
||||
pos = selectedPrimaryFontListPos;
|
||||
if (pos >= 0 && pos < displayedFonts.size()) {
|
||||
FontConfig.customFontNamePrimary = displayedFonts.get(pos).getFontName();
|
||||
}
|
||||
pos = selectedFallbackFontListPos;
|
||||
if (pos >= 0 && pos < displayedFonts.size()) {
|
||||
FontConfig.customFontNameFallback = displayedFonts.get(pos).getFontName();
|
||||
}
|
||||
|
||||
FontStrategist.reloadCustomFontProviders();
|
||||
|
||||
if (finalApply) {
|
||||
ConfigurationManager.save(FontConfig.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetValues(IrisButton button) {
|
||||
try {
|
||||
ConfigurationManager.getConfigElements(FontConfig.class).forEach(elem -> elem.set(elem.getDefault()));
|
||||
} catch (ConfigException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
currentPrimaryFontName = null;
|
||||
currentFallbackFontName = null;
|
||||
selectedPrimaryFontListPos = -1;
|
||||
selectedFallbackFontListPos = -1;
|
||||
super.buttonList.clear();
|
||||
this.initButtons();
|
||||
}
|
||||
|
||||
private void toggleCustomFont(IrisButton button) {
|
||||
FontConfig.enableCustomFont = !FontConfig.enableCustomFont;
|
||||
applyChanges(false);
|
||||
button.displayString = FontConfig.enableCustomFont ?
|
||||
I18n.format("options.angelica.fontconfig.disable_custom_font") :
|
||||
I18n.format("options.angelica.fontconfig.enable_custom_font");
|
||||
}
|
||||
|
||||
private void switchPage(int offset) {
|
||||
this.displayedSliderPage = MathHelper.clamp_int(this.displayedSliderPage + offset, 0, this.sliderPages);
|
||||
this.buttonList.removeIf(guiButton -> guiButton instanceof SliderClone);
|
||||
this.initSliders();
|
||||
}
|
||||
|
||||
private int qualityLast = FontConfig.customFontQuality;
|
||||
|
||||
private void setFontQuality(float quality) {
|
||||
FontConfig.customFontQuality = (int) quality;
|
||||
if (qualityLast != (int) quality) {
|
||||
applyChanges(false);
|
||||
}
|
||||
qualityLast = (int) quality;
|
||||
}
|
||||
|
||||
private String fontAAModeFormat(float AAmode) {
|
||||
return switch ((int) AAmode) {
|
||||
case 2 -> I18n.format("options.angelica.fontconfig.aamode.aa_16x");
|
||||
case 1 -> I18n.format("options.angelica.fontconfig.aamode.aa_4x");
|
||||
default -> I18n.format("options.angelica.fontconfig.aamode.aa_none");
|
||||
};
|
||||
}
|
||||
|
||||
private float lastMouseX = 0;
|
||||
private float lastMouseY = 0;
|
||||
private long lastStillTime = 0;
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float delta) {
|
||||
drawBackground(0);
|
||||
|
||||
this.fontList.drawScreen(mouseX, mouseY, delta);
|
||||
if (availableFonts.length == 0) {
|
||||
if (this.noFontsWarning == null) {
|
||||
this.noFontsWarning = this.fontRendererObj.listFormattedStringToWidth(
|
||||
I18n.format("options.angelica.fontconfig.no_fonts_warning"), this.width / 2
|
||||
);
|
||||
}
|
||||
int y = 80;
|
||||
for (String s : this.noFontsWarning) {
|
||||
drawCenteredString(this.fontRendererObj, s, this.width / 3, y, 0xFFFFFF);
|
||||
y += this.fontRendererObj.FONT_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
this.searchBox.drawTextBox();
|
||||
if (!this.searchBox.isFocused() && this.searchBox.getText().isEmpty()) {
|
||||
this.drawCenteredString(this.fontRendererObj, this.searchPrompt,
|
||||
this.searchBox.xPosition + this.fontRendererObj.getStringWidth(this.searchPrompt) / 2 + 4,
|
||||
this.searchBox.yPosition + this.searchBox.height / 2 - 4, 0xFFFFFF);
|
||||
}
|
||||
drawCenteredString(this.fontRendererObj, this.title, (int) (this.width * 0.5), 8, 0xFFFFFF);
|
||||
drawCenteredString(this.fontRendererObj, I18n.format("options.angelica.fontconfig.currentfonts",
|
||||
FontConfig.customFontNamePrimary, FontConfig.customFontNameFallback), (int) (this.width * 0.5), this.height - 92, 0xFFFFFF);
|
||||
|
||||
if (this.testAreaInfo == null) {
|
||||
this.testAreaInfo = this.fontRendererObj.listFormattedStringToWidth(
|
||||
I18n.format("options.angelica.fontconfig.testareainfo"), this.width / 4
|
||||
);
|
||||
}
|
||||
int cumulativeY = 73;
|
||||
for (String s : this.testAreaInfo) {
|
||||
drawCenteredString(this.fontRendererObj, s, this.width * 5 / 6, cumulativeY, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT;
|
||||
}
|
||||
cumulativeY += 4;
|
||||
this.testArea.yPosition = cumulativeY;
|
||||
this.testArea.drawTextBox();
|
||||
if (!this.testArea.isFocused() && this.testArea.getText().isEmpty()) {
|
||||
this.drawCenteredString(this.fontRendererObj, this.testAreaPrompt,
|
||||
this.testArea.xPosition + this.fontRendererObj.getStringWidth(this.testAreaPrompt) / 2 + 4,
|
||||
this.testArea.yPosition + this.testArea.height / 2 - 4, 0xFFFFFF);
|
||||
}
|
||||
List<String> testText = this.fontRendererObj.listFormattedStringToWidth(
|
||||
this.testArea.getText().replace('&', '§'), this.width / 4
|
||||
);
|
||||
cumulativeY += this.testArea.height + 4;
|
||||
for (String s : testText) {
|
||||
drawCenteredString(this.fontRendererObj, s, this.width * 5 / 6, cumulativeY, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
super.drawScreen(mouseX, mouseY, delta);
|
||||
|
||||
for (GuiButton guiButton : buttonList) {
|
||||
if (!(guiButton instanceof SliderClone slider)) { continue; }
|
||||
final int top = slider.yPosition;
|
||||
final int bot = slider.yPosition + slider.height;
|
||||
final int left = slider.xPosition;
|
||||
final int right = slider.xPosition + slider.width;
|
||||
if (mouseY < top || mouseY >= bot || mouseX < left || mouseX >= right) { continue; }
|
||||
if (mouseX == lastMouseX && mouseY == lastMouseY) {
|
||||
if (lastStillTime == 0) {
|
||||
lastStillTime = System.currentTimeMillis();
|
||||
}
|
||||
if (lastStillTime + 500L < System.currentTimeMillis()) {
|
||||
displayTooltip(mouseX, mouseY, slider.tooltipKey);
|
||||
}
|
||||
} else {
|
||||
lastStillTime = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
lastMouseX = mouseX;
|
||||
lastMouseY = mouseY;
|
||||
}
|
||||
|
||||
private void displayTooltip(int x, int y, String langKey) {
|
||||
List<String> lines = this.fontRendererObj.listFormattedStringToWidth(I18n.format(langKey), this.width / 2);
|
||||
this.drawHoveringText(lines, x, y, this.fontRendererObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton guiButton) {
|
||||
if (guiButton.enabled) {
|
||||
if (guiButton instanceof IrisButton irisButton) {
|
||||
irisButton.onPress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char typedChar, int keyCode) {
|
||||
if (this.searchBox.isFocused()) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.searchBox.setFocused(false);
|
||||
return;
|
||||
}
|
||||
this.searchBox.textboxKeyTyped(typedChar, keyCode);
|
||||
this.displayedFonts = filterFonts(this.searchBox.getText().toLowerCase());
|
||||
}
|
||||
if (this.testArea.isFocused()) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.testArea.setFocused(false);
|
||||
return;
|
||||
}
|
||||
this.testArea.textboxKeyTyped(typedChar, keyCode);
|
||||
}
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
this.searchBox.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
this.testArea.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
this.fontList.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScreen() {
|
||||
this.searchBox.updateCursorCounter();
|
||||
this.testArea.updateCursorCounter();
|
||||
}
|
||||
|
||||
// this could be more efficient, but it's good enough as is
|
||||
private ArrayList<Font> filterFonts(String search) {
|
||||
ArrayList<Font> results;
|
||||
if (search == null || search.isEmpty()) {
|
||||
results = new ArrayList<>(Arrays.asList(availableFonts));
|
||||
} else {
|
||||
results = Arrays.stream(availableFonts).filter((font -> font.getFontName().toLowerCase().contains(search))).collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
selectedPrimaryFontListPos = -1;
|
||||
selectedFallbackFontListPos = -1;
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
if (Objects.equals(currentPrimaryFontName, results.get(i).getFontName())) {
|
||||
selectedPrimaryFontListPos = i;
|
||||
}
|
||||
if (Objects.equals(currentFallbackFontName, results.get(i).getFontName())) {
|
||||
selectedFallbackFontListPos = i;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
class FontList extends GuiSlot {
|
||||
|
||||
public FontList() {
|
||||
super(FontConfigScreen.this.mc, FontConfigScreen.this.width * 2 / 3, FontConfigScreen.this.height, 52, FontConfigScreen.this.height - 102, 18);
|
||||
}
|
||||
|
||||
private void mouseClicked(int mouseX, int mouseY, int mouseButton) {
|
||||
if (mouseY < this.top || mouseY > this.bottom) { return; }
|
||||
|
||||
int listLeftBound = this.width / 2 - this.getListWidth() / 2;
|
||||
int listRightBound = this.width / 2 + this.getListWidth() / 2;
|
||||
if (mouseX < listLeftBound || mouseX > listRightBound) { return; }
|
||||
|
||||
int mousePosInList = mouseY - this.top - this.headerPadding + (int)this.amountScrolled - 4;
|
||||
int slotIndex = mousePosInList / this.slotHeight;
|
||||
if (slotIndex < 0 || slotIndex >= this.getSize()) { return; }
|
||||
|
||||
if (mouseButton == 0) {
|
||||
onElemClicked(slotIndex, false);
|
||||
} else if (mouseButton == 1) {
|
||||
onElemClicked(slotIndex, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onElemClicked(int index, boolean rightClick) {
|
||||
if (!rightClick) {
|
||||
selectedPrimaryFontListPos = index;
|
||||
currentPrimaryFontName = displayedFonts.get(index).getFontName();
|
||||
} else {
|
||||
selectedFallbackFontListPos = index;
|
||||
currentFallbackFontName = displayedFonts.get(index).getFontName();
|
||||
}
|
||||
applyChanges(false);
|
||||
}
|
||||
|
||||
protected boolean isSelected(int index) {
|
||||
return (index == selectedPrimaryFontListPos || index == selectedFallbackFontListPos);
|
||||
}
|
||||
|
||||
protected int getSize() {
|
||||
return displayedFonts.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getContentHeight() {
|
||||
return this.getSize() * 18;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getScrollBarX() {
|
||||
return this.width * 15 / 16 - 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getListWidth() {
|
||||
return this.width * 2 / 3;
|
||||
}
|
||||
|
||||
// see onElemClicked for the proper version that supports right-clicking
|
||||
protected void elementClicked(int index, boolean doubleClicked, int mouseX, int mouseY) {}
|
||||
|
||||
protected void drawBackground() {
|
||||
// do nothing, the font list doesn't occupy the entire screen
|
||||
}
|
||||
|
||||
protected void drawSlot(int index, int x, int y, int p_148126_4_, Tessellator tessellator, int p_148126_6_, int p_148126_7_) {
|
||||
int color = 0xffffff;
|
||||
if (index == selectedPrimaryFontListPos) {
|
||||
color &= 0xffff55;
|
||||
}
|
||||
if (index == selectedFallbackFontListPos) {
|
||||
color &= 0x55ffff;
|
||||
}
|
||||
drawCenteredString(fontRendererObj, displayedFonts.get(index).getFontName(), this.width / 2, y + 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SliderClone extends GuiButton {
|
||||
|
||||
private final Function<Float, String> displayStringFormatter;
|
||||
private final Consumer<Float> valueSetter;
|
||||
private final Option option;
|
||||
private float sliderPercentage;
|
||||
public final String tooltipKey;
|
||||
public boolean mouseDragging;
|
||||
|
||||
@AllArgsConstructor
|
||||
public static class Option {
|
||||
float min;
|
||||
float max;
|
||||
float step;
|
||||
|
||||
float denormalize(float alpha) {
|
||||
float lerp = min * (1 - alpha) + max * alpha;
|
||||
return Math.round(lerp / step) * step;
|
||||
}
|
||||
float normalize(float value) {
|
||||
return (value - min) / (max - min);
|
||||
}
|
||||
}
|
||||
|
||||
public SliderClone(int x, int y, int width, int height, Option option, float initialValue,
|
||||
Consumer<Float> setter, Function<Float, String> formatter, String tooltipKey) {
|
||||
super(0, x, y, width, height, "");
|
||||
this.option = option;
|
||||
this.sliderPercentage = option.normalize(initialValue);
|
||||
this.displayString = formatter.apply(initialValue);
|
||||
this.valueSetter = setter;
|
||||
this.displayStringFormatter = formatter;
|
||||
this.tooltipKey = tooltipKey;
|
||||
}
|
||||
|
||||
public static SliderCloneBuilder builder() {
|
||||
return new SliderCloneBuilder();
|
||||
}
|
||||
|
||||
public void setCenterPosition(int x, int y) {
|
||||
this.xPosition = x - this.width / 2;
|
||||
this.yPosition = y - this.height / 2;
|
||||
}
|
||||
|
||||
@Accessors(fluent = true) @Setter
|
||||
public static class SliderCloneBuilder {
|
||||
private int x = 0, y = 0; // center position, not a corner
|
||||
private int width = 144, height = 20;
|
||||
private Option option;
|
||||
private float initialValue;
|
||||
private Consumer<Float> setter;
|
||||
private String formatString = "%3.2f";
|
||||
private String langKey;
|
||||
|
||||
// overrides: if null, these get set to default values
|
||||
private String tooltipKey;
|
||||
private Function<Float, String> formatter;
|
||||
|
||||
public SliderClone build() {
|
||||
if (this.formatter == null) {
|
||||
this.formatter = value -> I18n.format(this.langKey, String.format(this.formatString, value));
|
||||
}
|
||||
|
||||
if (this.tooltipKey == null) {
|
||||
this.tooltipKey = this.langKey + ".tooltip";
|
||||
}
|
||||
|
||||
return new SliderClone(
|
||||
this.x - this.width / 2,
|
||||
this.y - this.height / 2,
|
||||
this.width,
|
||||
this.height,
|
||||
this.option,
|
||||
this.initialValue,
|
||||
this.setter,
|
||||
this.formatter,
|
||||
this.tooltipKey
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns 0 if the button is disabled, 1 if the mouse is NOT hovering over this button and 2 if it IS hovering over
|
||||
* this button.
|
||||
*/
|
||||
public int getHoverState(boolean mouseOver) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the mouse button is dragged. Equivalent of MouseListener.mouseDragged(MouseEvent e).
|
||||
*/
|
||||
protected void mouseDragged(Minecraft mc, int mouseX, int mouseY) {
|
||||
if (this.visible) {
|
||||
if (this.mouseDragging) {
|
||||
this.sliderPercentage = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
|
||||
|
||||
if (this.sliderPercentage < 0.0F) {
|
||||
this.sliderPercentage = 0.0F;
|
||||
}
|
||||
|
||||
if (this.sliderPercentage > 1.0F) {
|
||||
this.sliderPercentage = 1.0F;
|
||||
}
|
||||
|
||||
float f = this.option.denormalize(this.sliderPercentage);
|
||||
valueSetter.accept(f);
|
||||
this.sliderPercentage = this.option.normalize(f);
|
||||
this.displayString = displayStringFormatter.apply(f);
|
||||
}
|
||||
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(this.sliderPercentage * (float)(this.width - 8)), this.yPosition, 0, 66, 4, 20);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(this.sliderPercentage * (float)(this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the mouse has been pressed on this control. Equivalent of MouseListener.mousePressed(MouseEvent e).
|
||||
*/
|
||||
public boolean mousePressed(Minecraft mc, int mouseX, int mouseY) {
|
||||
if (super.mousePressed(mc, mouseX, mouseY)) {
|
||||
this.sliderPercentage = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
|
||||
|
||||
if (this.sliderPercentage < 0.0F) {
|
||||
this.sliderPercentage = 0.0F;
|
||||
}
|
||||
|
||||
if (this.sliderPercentage > 1.0F) {
|
||||
this.sliderPercentage = 1.0F;
|
||||
}
|
||||
|
||||
float f = this.option.denormalize(this.sliderPercentage);
|
||||
valueSetter.accept(f);
|
||||
this.sliderPercentage = this.option.normalize(f);
|
||||
this.displayString = displayStringFormatter.apply(f);
|
||||
this.mouseDragging = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the mouse button is released. Equivalent of MouseListener.mouseReleased(MouseEvent e).
|
||||
*/
|
||||
public void mouseReleased(int mouseX, int mouseY) {
|
||||
this.mouseDragging = false;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.commands;
|
||||
|
||||
// Debug commands adapted from Beddium by Ven and FalsePattern
|
||||
|
||||
import com.gtnewhorizons.angelica.debug.ChunkDebugMinimap;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.ICommand;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AngelicaCommand extends CommandBase {
|
||||
|
||||
private static final List<String> SUBCOMMANDS = Arrays.asList("wireframe", "fog", "minimap", "help");
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "angelica";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandUsage(ICommandSender sender) {
|
||||
return "/angelica <wireframe|fog|minimap|help>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRequiredPermissionLevel() {
|
||||
return 0; // Available to all players (client-side only)
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> addTabCompletionOptions(ICommandSender sender, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return getListOfStringsMatchingLastWord(args, SUBCOMMANDS.toArray(new String[0]));
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processCommand(ICommandSender sender, String[] args) {
|
||||
if (args.length == 0) {
|
||||
sendHelp(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "wireframe" -> handleWireframe(sender);
|
||||
case "fog" -> handleFog(sender);
|
||||
case "minimap" -> handleMinimap(sender);
|
||||
default -> sendHelp(sender);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWireframe(ICommandSender sender) {
|
||||
CeleritasWorldRenderer.DEBUG_WIREFRAME_MODE = !CeleritasWorldRenderer.DEBUG_WIREFRAME_MODE;
|
||||
final String state = CeleritasWorldRenderer.DEBUG_WIREFRAME_MODE ? "ON" : "OFF";
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Wireframe mode: " + state));
|
||||
}
|
||||
|
||||
private void handleFog(ICommandSender sender) {
|
||||
CeleritasDebugScreenHandler.showFogDebug = !CeleritasDebugScreenHandler.showFogDebug;
|
||||
final String f3State = CeleritasDebugScreenHandler.showFogDebug ? "ON" : "OFF";
|
||||
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Fog debug (F3): " + f3State));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " " + CeleritasDebugScreenHandler.getFogDebugString()));
|
||||
}
|
||||
|
||||
private void handleMinimap(ICommandSender sender) {
|
||||
ChunkDebugMinimap.toggle();
|
||||
final String state = ChunkDebugMinimap.isEnabled() ? "ON" : "OFF";
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Chunk debug minimap: " + state));
|
||||
}
|
||||
|
||||
private void sendHelp(ICommandSender sender) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] Debug Commands:"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica wireframe" + EnumChatFormatting.WHITE + " - Toggle wireframe rendering"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica fog" + EnumChatFormatting.WHITE + " - Toggle fog debug on F3"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica minimap" + EnumChatFormatting.WHITE + " - Toggle chunk debug overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
if (o instanceof ICommand cmd) {
|
||||
return this.getCommandName().compareTo(cmd.getCommandName());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,17 @@ public class BlockError extends Block {
|
||||
public BlockError() {
|
||||
super(Material.rock);
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(new EventHandler());
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void onTextureStitch(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() == 0) {
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
icons[i] = event.map.registerIcon("angelica:error_block_" + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,15 +45,4 @@ public class BlockError extends Block {
|
||||
return meta >= 0 && meta < icons.length ? icons[meta] : null;
|
||||
}
|
||||
|
||||
public static class EventHandler{
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void onTextureStitch(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() == 0) {
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
icons[i] = event.map.registerIcon("angelica:error_block_" + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,34 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadProvider;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.util.DirectionUtil;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ModeledBlock;
|
||||
import com.gtnewhorizons.angelica.utils.AssetLoader;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/// Unline {@link BlockError}, this *is* registered on both sides. It's intended to test functionality in a SP
|
||||
/// development environment.
|
||||
public class BlockTest extends Block {
|
||||
import static com.gtnewhorizons.angelica.models.VanillaModels.LECTERN;
|
||||
|
||||
public class BlockTest extends Block implements ModeledBlock {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private QuadProvider model;
|
||||
|
||||
public BlockTest() {
|
||||
super(Material.wood);
|
||||
setHardness(0.7f);
|
||||
|
||||
super(Material.rock);
|
||||
this.setBlockTextureName("missingno");
|
||||
this.setModel((world, pos, block, meta, dir, random, color, quadPool) -> {
|
||||
if (meta < 2 || meta > 5) meta = 2;
|
||||
return LECTERN.models[meta - 2].getQuads(world, pos, block, meta, dir, random, color, quadPool);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -22,6 +36,13 @@ public class BlockTest extends Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBlockIcons(IIconRegister reg) {
|
||||
for (String s : AssetLoader.testTexs) {
|
||||
reg.registerIcon(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a block is placed using its ItemBlock. Args: World, X, Y, Z, side, hitX, hitY, hitZ, block metadata
|
||||
*/
|
||||
@@ -29,16 +50,11 @@ public class BlockTest extends Block {
|
||||
public int onBlockPlaced(@NotNull World worldIn, int x, int y, int z, int side, float subX, float subY, float subZ, int meta) {
|
||||
|
||||
// Face NORTH if placed up or down
|
||||
final var s = ForgeDirection.getOrientation(side);
|
||||
final ForgeDirection s = DirectionUtil.ALL_DIRECTIONS[side];
|
||||
if (s == ForgeDirection.UP || s == ForgeDirection.DOWN)
|
||||
return 2;
|
||||
|
||||
// Face the placed side
|
||||
return side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderType() {
|
||||
return JSON_ISBRH_ID;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.ARBDebugOutput;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
/**
|
||||
* Checks for known problematic driver configurations and applies workarounds. Adapted from Embeddium/Sodium's LateDriverScanner.
|
||||
*
|
||||
* <p>The NVIDIA workaround can be disabled via: {@code -Dangelica.disableNvidiaWorkaround=true}
|
||||
*/
|
||||
public class DriverCompatabilityCheck {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
private static final boolean DISABLE_NVIDIA_WORKAROUND = Boolean.getBoolean("angelica.disableNvidiaWorkaround");
|
||||
|
||||
/**
|
||||
* Should be called after GL context is created and GLStateManager is initialized.
|
||||
*/
|
||||
public static void checkDriverCompatibility() {
|
||||
final String vendor = GL11.glGetString(GL11.GL_VENDOR);
|
||||
final String renderer = GL11.glGetString(GL11.GL_RENDERER);
|
||||
final String version = GL11.glGetString(GL11.GL_VERSION);
|
||||
|
||||
LOGGER.info("OpenGL Vendor: {}", vendor);
|
||||
LOGGER.info("OpenGL Renderer: {}", renderer);
|
||||
LOGGER.info("OpenGL Version: {}", version);
|
||||
|
||||
if (vendor != null && vendor.contains("NVIDIA")) {
|
||||
if (DISABLE_NVIDIA_WORKAROUND) {
|
||||
LOGGER.info("NVIDIA workaround disabled via -Dangelica.disableNvidiaWorkaround=true");
|
||||
} else {
|
||||
applyNvidiaWorkaround();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/godotengine/godot/issues/33969#issuecomment-917846774
|
||||
* Attempt to force the driver to disable its submission thread by enabling synchronous debug output.
|
||||
* This usually suppresses bad behavior from NVIDIA's "Threaded Optimizations" feature.
|
||||
*/
|
||||
private static void applyNvidiaWorkaround() {
|
||||
if (!GLStateManager.capabilities.GL_ARB_debug_output) {
|
||||
LOGGER.warn("Cannot apply NVIDIA workaround: ARB_debug_output not available");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Enabling workaround for NVIDIA threaded optimizations");
|
||||
GL11.glEnable(ARBDebugOutput.GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.compat;
|
||||
|
||||
import com.falsepattern.chunk.api.DataRegistry;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ExtendedBlockStorageAccessor;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ExtendedNibbleArray;
|
||||
import com.gtnewhorizons.neid.mixins.interfaces.IExtendedBlockStorageMixin;
|
||||
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.NibbleArray;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
import ru.fewizz.idextender.Hooks;
|
||||
|
||||
public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
|
||||
public boolean hasSky;
|
||||
@@ -16,7 +19,7 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
|
||||
}
|
||||
|
||||
public ExtendedBlockStorageExt(Chunk chunk, ExtendedBlockStorage storage) {
|
||||
super(storage.yBase, storage.getSkylightArray() != null);
|
||||
super(((ExtendedBlockStorageAccessor) storage).getYBase(), storage.getSkylightArray() != null);
|
||||
|
||||
if (ModStatus.isChunkAPILoaded) {
|
||||
if (storage.getSkylightArray() != null) {
|
||||
@@ -30,7 +33,7 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
|
||||
System.arraycopy(((IExtendedBlockStorageMixin)(Object)storage).getBlock16BArray(), 0, block16BArray, 0, block16BArray.length);
|
||||
if(storage.getBlockMSBArray() != null) {
|
||||
this.setBlockMSBArray(new NibbleArray(block16BArray.length, 4));
|
||||
copyNibbleArray(storage.getBlockMSBArray(), this.getBlockMSBArray());
|
||||
copyNibbleArray((ExtendedNibbleArray) storage.getBlockMSBArray(), (ExtendedNibbleArray) this.getBlockMSBArray());
|
||||
}
|
||||
arrayLen = block16BArray.length;
|
||||
if (ModStatus.isNEIDMetadataExtended) {
|
||||
@@ -38,36 +41,42 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
|
||||
System.arraycopy(((IExtendedBlockStorageMixin)(Object)storage).getBlock16BMetaArray(), 0, block16BMetaArray, 0, block16BMetaArray.length);
|
||||
}
|
||||
}
|
||||
else if (ModStatus.isOldNEIDLoaded){
|
||||
final short[] blockLSBArray = Hooks.get(this);
|
||||
System.arraycopy(Hooks.get(storage), 0, blockLSBArray, 0, blockLSBArray.length);
|
||||
// getBlockMSBArray is nuked in asm version
|
||||
arrayLen = blockLSBArray.length;
|
||||
}
|
||||
else {
|
||||
final byte[] blockLSBArray = this.getBlockLSBArray();
|
||||
System.arraycopy(storage.getBlockLSBArray(), 0, blockLSBArray, 0, blockLSBArray.length);
|
||||
if(storage.getBlockMSBArray() != null) {
|
||||
this.setBlockMSBArray(new NibbleArray(blockLSBArray.length, 4));
|
||||
copyNibbleArray(storage.getBlockMSBArray(), this.getBlockMSBArray());
|
||||
copyNibbleArray((ExtendedNibbleArray) storage.getBlockMSBArray(), (ExtendedNibbleArray) this.getBlockMSBArray());
|
||||
}
|
||||
arrayLen = blockLSBArray.length;
|
||||
}
|
||||
|
||||
|
||||
if (!ModStatus.isNEIDMetadataExtended) copyNibbleArray(storage.getMetadataArray(), this.getMetadataArray());
|
||||
copyNibbleArray(storage.getBlocklightArray(), this.getBlocklightArray());
|
||||
if (!ModStatus.isNEIDMetadataExtended) copyNibbleArray((ExtendedNibbleArray) storage.getMetadataArray(), (ExtendedNibbleArray)this.getMetadataArray());
|
||||
copyNibbleArray((ExtendedNibbleArray) storage.getBlocklightArray(), (ExtendedNibbleArray)this.getBlocklightArray());
|
||||
if(storage.getSkylightArray() != null) {
|
||||
hasSky = true;
|
||||
if(this.getSkylightArray() == null) {
|
||||
this.setSkylightArray(new NibbleArray(arrayLen, 4));
|
||||
}
|
||||
copyNibbleArray(storage.getSkylightArray(), this.getSkylightArray());
|
||||
copyNibbleArray((ExtendedNibbleArray) storage.getSkylightArray(), (ExtendedNibbleArray) this.getSkylightArray());
|
||||
}
|
||||
}
|
||||
this.blockRefCount = storage.blockRefCount;
|
||||
((ExtendedBlockStorageAccessor) this).setBlockRefCount(((ExtendedBlockStorageAccessor) storage).getBlockRefCount());
|
||||
}
|
||||
|
||||
|
||||
private static void copyNibbleArray(NibbleArray srcArray, NibbleArray dstArray) {
|
||||
private static void copyNibbleArray(ExtendedNibbleArray srcArray, ExtendedNibbleArray dstArray) {
|
||||
if (srcArray == null || dstArray == null) {
|
||||
throw new RuntimeException("NibbleArray is null src: " + (srcArray == null) + " dst: " + (dstArray == null));
|
||||
}
|
||||
final byte[] data = srcArray.data;
|
||||
System.arraycopy(data, 0, dstArray.data, 0, data.length);
|
||||
final byte[] data = srcArray.getData();
|
||||
System.arraycopy(data, 0, dstArray.getData(), 0, data.length);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,24 @@
|
||||
package com.gtnewhorizons.angelica.compat;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
|
||||
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.versioning.ArtifactVersion;
|
||||
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
|
||||
import mods.battlegear2.Battlegear;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import net.dries007.holoInventory.HoloInventory;
|
||||
import xonin.backhand.Backhand;
|
||||
|
||||
public class ModStatus {
|
||||
public static final Logger LOGGER = LogManager.getLogger("ModCompat");
|
||||
|
||||
public static BackhandReflectionCompat backhandCompat;
|
||||
public static boolean isBetterCrashesLoaded;
|
||||
/**
|
||||
* Mixin Version
|
||||
*/
|
||||
public static boolean isNEIDLoaded;
|
||||
/**
|
||||
* ASM Version
|
||||
*/
|
||||
public static boolean isOldNEIDLoaded;
|
||||
|
||||
public static boolean isBetterCrashesLoaded;
|
||||
public static boolean isNEIDMetadataExtended;
|
||||
public static boolean isLotrLoaded;
|
||||
public static boolean isChunkAPILoaded;
|
||||
@@ -25,28 +30,28 @@ public class ModStatus {
|
||||
public static boolean isThaumcraftLoaded;
|
||||
public static boolean isThaumicHorizonsLoaded;
|
||||
public static boolean isBaublesLoaded;
|
||||
public static boolean isFluidLoggedLoaded;
|
||||
|
||||
public static void preInit(){
|
||||
isBackhandLoaded = Loader.isModLoaded("backhand");
|
||||
if(isBackhandLoaded) {
|
||||
isBackhandLoaded = BackhandReflectionCompat.isBackhandLoaded();
|
||||
}
|
||||
|
||||
isBetterCrashesLoaded = Loader.isModLoaded("bettercrashes");
|
||||
isNEIDLoaded = Loader.isModLoaded("neid");
|
||||
isOldNEIDLoaded = Loader.isModLoaded("notenoughIDs");
|
||||
isLotrLoaded = Loader.isModLoaded("lotr");
|
||||
isChunkAPILoaded = Loader.isModLoaded("chunkapi");
|
||||
isEIDBiomeLoaded = Loader.isModLoaded("endlessids_biome");
|
||||
isXaerosMinimapLoaded = Loader.isModLoaded("XaeroMinimap");
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
isBattlegearLoaded = Loader.isModLoaded("battlegear2");
|
||||
isBackhandLoaded = Loader.isModLoaded("backhand");
|
||||
isThaumcraftLoaded = Loader.isModLoaded("Thaumcraft");
|
||||
isThaumicHorizonsLoaded = Loader.isModLoaded("ThaumicHorizons");
|
||||
isBaublesLoaded = Loader.isModLoaded("Baubles");
|
||||
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
|
||||
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
if (isHoloInventoryLoaded){
|
||||
isHoloInventoryLoaded = new DefaultArtifactVersion("2.4.4-GTNH")
|
||||
.compareTo(
|
||||
LoadControllerHelper.getOwningMod(HoloInventory.class).getProcessedVersion()
|
||||
) <= 0;
|
||||
}
|
||||
|
||||
// remove compat with original release of BG2
|
||||
if (isBattlegearLoaded){
|
||||
@@ -56,9 +61,17 @@ public class ModStatus {
|
||||
) <= 0;
|
||||
}
|
||||
|
||||
if (isBackhandLoaded){
|
||||
isBackhandLoaded = new DefaultArtifactVersion("1.4.1")
|
||||
.compareTo(
|
||||
LoadControllerHelper.getOwningMod(Backhand.class).getProcessedVersion()
|
||||
) <= 0;
|
||||
}
|
||||
|
||||
|
||||
isNEIDMetadataExtended = false;
|
||||
if (isNEIDLoaded) {
|
||||
final int majorVersion = Integer.parseInt(Loader.instance().getIndexedModList().get("neid").getVersion().split("\\.")[0]);
|
||||
int majorVersion = Integer.parseInt(Loader.instance().getIndexedModList().get("neid").getVersion().split("\\.")[0]);
|
||||
if (majorVersion >= 2) {
|
||||
isNEIDMetadataExtended = true;
|
||||
}
|
||||
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat.backhand;
|
||||
|
||||
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
|
||||
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
|
||||
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
|
||||
|
||||
public class BackhandReflectionCompat {
|
||||
private static final String BACKHAND_CLASS = "xonin.backhand.Backhand";
|
||||
private static final String ITEM_RENDERER_HOOKS_CLASS = "xonin.backhand.client.hooks.ItemRendererHooks";
|
||||
private static final String BACKHAND_UTILS_CLASS = "xonin.backhand.api.core.BackhandUtils";
|
||||
|
||||
private static boolean isLoaded;
|
||||
private static final MethodHandle renderOffhandReturn;
|
||||
private static final MethodHandle getOffhandItem;
|
||||
|
||||
static {
|
||||
MethodHandle renderOffhandReturnTemp = null;
|
||||
MethodHandle getOffhandItemTemp = null;
|
||||
|
||||
try {
|
||||
final Class<?> backhandClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), BACKHAND_CLASS);
|
||||
final boolean versionCheck = new DefaultArtifactVersion("1.6.9").compareTo(LoadControllerHelper.getOwningMod(backhandClass).getProcessedVersion()) <= 0;
|
||||
|
||||
if (versionCheck) {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
final Class<?> itemRendererHooksClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), ITEM_RENDERER_HOOKS_CLASS);
|
||||
final Class<?> backhandUtilsClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), BACKHAND_UTILS_CLASS);
|
||||
|
||||
renderOffhandReturnTemp = lookup.findStatic(itemRendererHooksClass, "renderOffhandReturn", MethodType.methodType(void.class, float.class));
|
||||
getOffhandItemTemp = lookup.findStatic(backhandUtilsClass, "getOffhandItem", MethodType.methodType(ItemStack.class, EntityPlayer.class));
|
||||
isLoaded = true;
|
||||
LOGGER.info("Backhand compat loaded");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to load Backhand compat", e);
|
||||
isLoaded = false;
|
||||
}
|
||||
|
||||
renderOffhandReturn = renderOffhandReturnTemp;
|
||||
getOffhandItem = getOffhandItemTemp;
|
||||
}
|
||||
|
||||
public static boolean isBackhandLoaded() {
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
public static void renderOffhand(float partialTicks) {
|
||||
if (isLoaded && renderOffhandReturn != null) {
|
||||
try {
|
||||
renderOffhandReturn.invokeExact(partialTicks);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Failed to invoke HoloInventory renderEvent", t);
|
||||
isLoaded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack getOffhandItem(EntityPlayer player) {
|
||||
if (isLoaded && getOffhandItem != null) {
|
||||
try {
|
||||
return (ItemStack) getOffhandItem.invokeExact(player);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Failed to invoke HoloInventory renderEvent", t);
|
||||
isLoaded = false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat.battlegear2;
|
||||
|
||||
import mods.battlegear2.api.core.IInventoryPlayerBattle;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* This class exists to isolate classes that exist in Battlegear 2 but not
|
||||
* in Battlegear 2 for Backhand in order to avoid class loading exceptions.
|
||||
*/
|
||||
public class Battlegear2Compat {
|
||||
public static ItemStack getBattlegear2Offhand(EntityPlayer player) {
|
||||
return ((IInventoryPlayerBattle) player.inventory).battlegear2$getCurrentOffhandWeapon();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ public class BetterCrashesCompat implements StateManager.IResettable {
|
||||
@Override
|
||||
public void resetState() {
|
||||
LOGGER.info("Reloading SodiumRenderer");
|
||||
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).angelica$reload();
|
||||
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).reload();
|
||||
LOGGER.info("Resetting GLStateManager");
|
||||
GLStateManager.reset();
|
||||
}
|
||||
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat.holoinventory;
|
||||
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
|
||||
|
||||
public class HoloInventoryReflectionCompat {
|
||||
private static final String RENDERER_CLASS = "net.dries007.holoInventory.client.Renderer";
|
||||
|
||||
private static boolean isLoaded;
|
||||
private static final MethodHandle renderEventPostMethod;
|
||||
private static final MethodHandle renderEventMethod;
|
||||
private static final MethodHandle angelicaOverrideSetter;
|
||||
|
||||
static {
|
||||
MethodHandle renderEventPostMethodTemp = null;
|
||||
MethodHandle renderEventMethodTemp = null;
|
||||
MethodHandle angelicaOverrideSetterTemp = null;
|
||||
final Object rendererInstance;
|
||||
final Class<?> rendererClass;
|
||||
|
||||
try {
|
||||
rendererClass = Class.forName(RENDERER_CLASS);
|
||||
|
||||
// Get and cache the Renderer.INSTANCE
|
||||
final Field instanceField = rendererClass.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
rendererInstance = instanceField.get(null);
|
||||
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
try {
|
||||
renderEventPostMethodTemp = lookup.findVirtual(rendererClass, "renderEvent", MethodType.methodType(void.class, RenderGameOverlayEvent.Post.class));
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Fallback for 2.5.0-GTNH and earlier
|
||||
renderEventMethodTemp = lookup.findVirtual(rendererClass, "renderEvent", MethodType.methodType(void.class, RenderGameOverlayEvent.class));
|
||||
}
|
||||
|
||||
final Field angelicaOverrideField = rendererClass.getDeclaredField("angelicaOverride");
|
||||
angelicaOverrideField.setAccessible(true);
|
||||
angelicaOverrideSetterTemp = lookup.unreflectSetter(angelicaOverrideField);
|
||||
|
||||
// Bind the method handles to the instance
|
||||
if (rendererInstance != null) {
|
||||
if (renderEventPostMethodTemp != null) {
|
||||
renderEventPostMethodTemp = renderEventPostMethodTemp.bindTo(rendererInstance);
|
||||
}
|
||||
if (renderEventMethodTemp != null) {
|
||||
renderEventMethodTemp = renderEventMethodTemp.bindTo(rendererInstance);
|
||||
}
|
||||
angelicaOverrideSetterTemp = angelicaOverrideSetterTemp.bindTo(rendererInstance);
|
||||
}
|
||||
|
||||
isLoaded = true;
|
||||
LOGGER.info("Successfully initialized HoloInventory compatibility layer");
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to initialize HoloInventory compatibility layer", e);
|
||||
isLoaded = false;
|
||||
}
|
||||
|
||||
renderEventPostMethod = renderEventPostMethodTemp;
|
||||
renderEventMethod = renderEventMethodTemp;
|
||||
angelicaOverrideSetter = angelicaOverrideSetterTemp;
|
||||
}
|
||||
|
||||
public static void renderEvent(RenderGameOverlayEvent event) {
|
||||
if (!isLoaded) return;
|
||||
try {
|
||||
if (event instanceof RenderGameOverlayEvent.Post && renderEventPostMethod != null) {
|
||||
renderEventPostMethod.invokeExact((RenderGameOverlayEvent.Post) event);
|
||||
} else {
|
||||
renderEventMethod.invokeExact(event);
|
||||
}
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Failed to invoke HoloInventory renderEvent", t);
|
||||
isLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAngelicaOverride(boolean value) {
|
||||
if (!isLoaded) return;
|
||||
try {
|
||||
angelicaOverrideSetter.invokeExact(value);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Failed to invoke HoloInventory renderEvent", t);
|
||||
isLoaded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat.iris;
|
||||
|
||||
/**
|
||||
* Interface implemented by BiomeGenBase via mixin to cache biome category.
|
||||
* This avoids recomputing the category every frame for shader uniforms.
|
||||
*/
|
||||
public interface BiomeCategoryCache {
|
||||
/**
|
||||
* Gets the cached biome category ordinal, or -1 if not yet computed.
|
||||
*/
|
||||
int iris$getCachedCategory();
|
||||
|
||||
/**
|
||||
* Sets the cached biome category ordinal.
|
||||
*/
|
||||
void iris$setCachedCategory(int category);
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat.iris;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import net.coderbot.iris.parsing.BiomeCategories;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Detects and categorizes biomes from popular modded biome mods using cached class checks.
|
||||
* Currently supports: BiomesOPlenty (BOP), Realistic World Gen (RWG), lotr (b36.15)
|
||||
*/
|
||||
public class ModdedBiomeDetector {
|
||||
private static boolean initialized = false;
|
||||
|
||||
// BiomesOPlenty (BOP) cache - dimension-specific base classes
|
||||
// BOP 1.7.10 hierarchy: BOPBiome -> BOPEndBiome/BOPNetherBiome/BOPOverworldBiome -> specific biomes
|
||||
private static Class<?> bopEndBiomeClass = null;
|
||||
private static Class<?> bopNetherBiomeClass = null;
|
||||
private static Class<?> bopOceanBiomeClass = null;
|
||||
|
||||
// Realistic World Gen (RWG) cache - base biome classes that extend BiomeGenBase
|
||||
// LinkedHashMap maintains insertion order for deterministic behavior
|
||||
private static final Map<Class<?>, BiomeCategories> rwgClassMap = new LinkedHashMap<>();
|
||||
|
||||
private static Class<?> lotrBiomeClass = null;
|
||||
|
||||
private static synchronized void initialize() {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
|
||||
initializeBOP();
|
||||
initializeRWG();
|
||||
initializeLOTR();
|
||||
}
|
||||
|
||||
private static void initializeBOP() {
|
||||
try {
|
||||
bopEndBiomeClass = Class.forName("biomesoplenty.common.biome.BOPEndBiome");
|
||||
bopNetherBiomeClass = Class.forName("biomesoplenty.common.biome.BOPNetherBiome");
|
||||
bopOceanBiomeClass = Class.forName("biomesoplenty.common.biome.BOPOceanBiome");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// BOP not installed
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeRWG() {
|
||||
// RWG adds actual BiomeGenBase subclasses in rwg.biomes.base package
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeOcean", BiomeCategories.OCEAN);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeRiver", BiomeCategories.RIVER);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomePlains", BiomeCategories.PLAINS);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeColdPlains", BiomeCategories.PLAINS);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeHotPlains", BiomeCategories.PLAINS);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeHotDesert", BiomeCategories.DESERT);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeSnowDesert", BiomeCategories.DESERT); // Cold desert, but still desert
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeJungle", BiomeCategories.JUNGLE);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeTropicalIsland", BiomeCategories.JUNGLE); // Tropical islands are jungle-like
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeRedwood", BiomeCategories.FOREST); // Redwood/Sequoia forests
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeTemperateForest", BiomeCategories.FOREST);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeColdForest", BiomeCategories.TAIGA); // Cold forests are taiga-like
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeHotForest", BiomeCategories.FOREST);
|
||||
tryAddRWGClass("rwg.biomes.base.BaseBiomeSnowForest", BiomeCategories.TAIGA); // Snow forests are taiga
|
||||
}
|
||||
|
||||
private static void initializeLOTR() {
|
||||
if (ModStatus.isLotrLoaded) {
|
||||
try {
|
||||
lotrBiomeClass = Class.forName("lotr.common.world.biome.LOTRBiome");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// lotr not installed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void tryAddRWGClass(String className, BiomeCategories category) {
|
||||
try {
|
||||
final Class<?> clazz = Class.forName(className);
|
||||
rwgClassMap.put(clazz, category);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// Class not available, RWG not installed or different version
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to detect the biome category for modded biomes.
|
||||
* Returns null if no modded biome detection applies.
|
||||
*/
|
||||
public static BiomeCategories detectModdedBiome(BiomeGenBase biome) {
|
||||
if (!initialized) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
// BiomesOPlenty detection - dimension-specific classes give us clear categories
|
||||
if (bopEndBiomeClass != null && bopEndBiomeClass.isInstance(biome)) {
|
||||
return BiomeCategories.THE_END;
|
||||
}
|
||||
if (bopNetherBiomeClass != null && bopNetherBiomeClass.isInstance(biome)) {
|
||||
return BiomeCategories.NETHER;
|
||||
}
|
||||
if (bopOceanBiomeClass != null && bopOceanBiomeClass.isInstance(biome)) {
|
||||
return BiomeCategories.OCEAN;
|
||||
}
|
||||
// For other BOP overworld biomes (e.g., Alps, Arctic, BambooForest),
|
||||
// fall through to name/property-based detection
|
||||
|
||||
// Realistic World Gen detection
|
||||
if (biome.getBiomeClass().getName().contains("rwg.biomes")) {
|
||||
for (Map.Entry<Class<?>, BiomeCategories> entry : rwgClassMap.entrySet()) {
|
||||
if (entry.getKey().isInstance(biome)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lotr
|
||||
if (lotrBiomeClass!= null && lotrBiomeClass.isInstance(biome)) {
|
||||
return detectLOTRBiome(biome);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BiomeCategories detectLOTRBiome(BiomeGenBase biome) {
|
||||
return switch (biome.biomeName) {
|
||||
case "river", "farHaradJungleLake", "lake" -> BiomeCategories.RIVER;
|
||||
case "rohan", "rivendell", "rhunIsland", "rhunLandHills", "rhunLandSteppe", "rhunLand", "andrast",
|
||||
"blackrootVale", "lamedon", "imlothMelui", "towerHills", "dorwinion", "dale", "shireMoors", "wold",
|
||||
"anduinVale", "rhun", "lebennin", "pertorogwaith", "breeland", "island", "pinnathGelin",
|
||||
"minhiriath", "pelennor", "adornland", "nanCurunir", "ithilienWasteland", "fangornClearing",
|
||||
"barrowDowns", "dorEnErnilHills", "dorEnErnil", "nurn", "wilderland", "eastBight", "lindon",
|
||||
"eregion", "enedwaith", "meneltarma", "anduinHills", "eriador", "angle", "loneLandsHills",
|
||||
"loneLands", "pelargir", "rohanUrukHighlands", "celebrant", "gondor", "shire", "nurnen", "farHarad" ->
|
||||
BiomeCategories.PLAINS;
|
||||
case "mistyMountains", "windMountains", "farHaradVolcano", "redMountains", "haradMountains",
|
||||
"angmarMountains", "blueMountains", "greyMountains", "whiteMountains", "mordorMountains" ->
|
||||
BiomeCategories.MOUNTAIN;
|
||||
case "shireWoodlands", "rhunIslandForest", "rhunRedForest", "pukel", "lossarnach", "gulfHaradForest",
|
||||
"nearHaradFertileForest", "dolGuldur", "rhunForest", "tauredainClearing", "umbarForest",
|
||||
"farHaradForest", "chetwood", "woodlandRealmHills", "mirkwoodNorth", "whiteDowns", "ithilienHills",
|
||||
"gondorWoodlands", "rohanWoodlands", "fangornWasteland", "mirkwoodMountains", "lindonWoodlands",
|
||||
"lothlorienEdge", "erynVorn", "oldForest", "fangorn", "dunland", "ithilien", "mirkwoodCorrupted",
|
||||
"woodlandRealm", "trollshaws", "lothlorien", "farHaradKanuka" -> BiomeCategories.FOREST;
|
||||
case "mordor", "lastDesert", "harnedor", "easternDesolation", "morgulVale", "gorgoroth", "udun",
|
||||
"nearHaradRedDesert", "nearHaradSemiDesert", "lostladen",
|
||||
"nearHaradHills", "nearHarad", "nanUngol", "dagorlad", "brownLands" -> BiomeCategories.DESERT;
|
||||
case "ironHills", "rivendellHills", "windMountainsFoothills", "lamedonHills", "redMountainsFoothills",
|
||||
"tolfalas", "dorwinionHills", "whiteMountainsFoothills", "blueMountainsFoothills",
|
||||
"greyMountainsFoothills", "mistyMountainsFoothills", "erebor", "angmar", "eriadorDowns",
|
||||
"ettenmoors", "emynMuil" -> BiomeCategories.EXTREME_HILLS;
|
||||
case "deadMarshes", "shireMarshes", "farHaradMangrove", "farHaradSwamp", "swanfleet", "nindalf",
|
||||
"longMarshes", "entwashMouth", "nurnMarshes", "gladdenFields", "midgewater", "anduinMouth" ->
|
||||
BiomeCategories.SWAMP;
|
||||
case "harondor", "nearHaradOasis", "nearHaradRiverbank", "farHaradBushlandHills", "farHaradBushland",
|
||||
"gulfHarad", "nearHaradFertile", "umbarHills", "umbar", "farHaradAridHills", "farHaradArid" ->
|
||||
BiomeCategories.SAVANNA;
|
||||
case "ocean" -> BiomeCategories.OCEAN;
|
||||
case "forodwaith", "forodwaithGlacier", "tundra", "forodwaithMountains" -> BiomeCategories.ICY;
|
||||
case "lindonCoast", "beachWhite", "farHaradCoast", "forodwaithCoast", "beachGravel", "beach" ->
|
||||
BiomeCategories.BEACH;
|
||||
case "coldfells", "wilderlandNorth", "taiga" -> BiomeCategories.TAIGA;
|
||||
case "farHaradJungle", "halfTrollForest", "farHaradJungleMountains",
|
||||
"farHaradCloudForest", "farHaradJungleEdge" -> BiomeCategories.JUNGLE;
|
||||
case "utumno" -> BiomeCategories.NONE;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
package com.gtnewhorizons.angelica.compat.lwjgl;
|
||||
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.Pointer.BITS64;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongPredicate;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
/**
|
||||
* Backported from LWJGL3 under the BSD 3-Clause "New" or "Revised" License license
|
||||
*
|
||||
* <p>This class provides functionality for managing native memory.
|
||||
*
|
||||
* <p>All methods in this class will make use of {@link sun.misc.Unsafe} if it's available, for performance. If Unsafe is not available, the fallback
|
||||
* implementations make use of reflection and, in the worst-case, JNI.</p>
|
||||
*
|
||||
* <p>Method names in this class are prefixed with {@code mem} to avoid ambiguities when used with static imports.</p>
|
||||
*/
|
||||
public class CompatMemoryUtil {
|
||||
public static final long NULL = 0;
|
||||
|
||||
static final sun.misc.Unsafe UNSAFE;
|
||||
|
||||
private static final Class<? extends ByteBuffer> BUFFER_BYTE;
|
||||
private static final Class<? extends IntBuffer> BUFFER_INT;
|
||||
private static final Class<? extends FloatBuffer> BUFFER_FLOAT;
|
||||
|
||||
private static final long MARK;
|
||||
private static final long POSITION;
|
||||
private static final long LIMIT;
|
||||
private static final long CAPACITY;
|
||||
|
||||
private static final long ADDRESS;
|
||||
|
||||
static {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(0).order(ByteOrder.nativeOrder());
|
||||
BUFFER_BYTE = bb.getClass();
|
||||
BUFFER_INT = bb.asIntBuffer().getClass();
|
||||
BUFFER_FLOAT = bb.asFloatBuffer().getClass();
|
||||
|
||||
UNSAFE = getUnsafeInstance();
|
||||
|
||||
MARK = getMarkOffset();
|
||||
POSITION = getPositionOffset();
|
||||
LIMIT = getLimitOffset();
|
||||
CAPACITY = getCapacityOffset();
|
||||
|
||||
ADDRESS = getAddressOffset();
|
||||
}
|
||||
|
||||
public static ByteBuffer memReallocDirect(ByteBuffer old, int capacity) {
|
||||
ByteBuffer newBuf = BufferUtils.createByteBuffer(capacity);
|
||||
int oldPos = old.position();
|
||||
old.rewind();
|
||||
newBuf.put(old);
|
||||
newBuf.position(Math.min(capacity, oldPos));
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
public static IntBuffer memReallocDirect(IntBuffer old, int capacity) {
|
||||
IntBuffer newBuf = BufferUtils.createIntBuffer(capacity);
|
||||
int oldPos = old.position();
|
||||
old.rewind();
|
||||
newBuf.put(old);
|
||||
newBuf.position(Math.min(capacity, oldPos));
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
public static FloatBuffer memReallocDirect(FloatBuffer old, int capacity) {
|
||||
FloatBuffer newBuf = BufferUtils.createFloatBuffer(capacity);
|
||||
int oldPos = old.position();
|
||||
old.rewind();
|
||||
newBuf.put(old);
|
||||
newBuf.position(Math.min(capacity, oldPos));
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
-------------------------------------
|
||||
UNSAFE MEMORY ACCESS API
|
||||
-------------------------------------
|
||||
------------------------------------- */
|
||||
|
||||
private static final int FILL_PATTERN_32 = Integer.divideUnsigned(-1, 255);
|
||||
private static final long FILL_PATTERN_64 = Long.divideUnsigned(-1L, 255L);
|
||||
|
||||
/**
|
||||
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
|
||||
*
|
||||
* @param ptr the starting memory address
|
||||
* @param value the value to set (memSet will convert it to unsigned byte)
|
||||
* @param bytes the number of bytes to set
|
||||
*/
|
||||
public static void memSet(long ptr, int value, long bytes) {
|
||||
if (/*DEBUG*/ false && (ptr == NULL || bytes < 0)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
/*
|
||||
- Unsafe.setMemory is very slow.
|
||||
- A custom Java loop is fastest at small sizes, approximately up to 256 bytes.
|
||||
- The native memset becomes fastest at bigger sizes, when the JNI overhead becomes negligible.
|
||||
TODO: verify this
|
||||
*/
|
||||
|
||||
//UNSAFE.setMemory(ptr, bytes, (byte)(value & 0xFF));
|
||||
//if (bytes < 256L) {
|
||||
int p = (int)ptr;
|
||||
if (BITS64) {
|
||||
if ((p & 7) == 0) {
|
||||
memSet64(ptr, value, (int)bytes & 0xFF);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ((p & 3) == 0) {
|
||||
memSet32(p, value, (int)bytes & 0xFF);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//}
|
||||
//nmemset(ptr, value, bytes);
|
||||
}
|
||||
private static void memSet64(long ptr, int value, int bytes) {
|
||||
int aligned = bytes & ~7;
|
||||
|
||||
// Aligned body
|
||||
long valuel = (value & 0xFF) * FILL_PATTERN_64;
|
||||
for (int i = 0; i < aligned; i += 8) {
|
||||
UNSAFE.putLong(null, ptr + i, valuel);
|
||||
}
|
||||
|
||||
// Unaligned tail
|
||||
byte valueb = (byte)(value & 0xFF);
|
||||
for (int i = aligned; i < bytes; i++) {
|
||||
UNSAFE.putByte(null, ptr + i, valueb);
|
||||
}
|
||||
}
|
||||
private static void memSet32(int ptr, int value, int bytes) {
|
||||
int aligned = bytes & ~3;
|
||||
|
||||
// Aligned body
|
||||
int vi = (value & 0xFF) * FILL_PATTERN_32;
|
||||
for (int i = 0; i < aligned; i += 4) {
|
||||
UNSAFE.putInt(null, (ptr + i) & 0xFFFF_FFFFL, vi);
|
||||
}
|
||||
|
||||
// Unaligned tail
|
||||
byte vb = (byte)(value & 0xFF);
|
||||
for (int i = aligned; i < bytes; i++) {
|
||||
UNSAFE.putByte(null, (ptr + i) & 0xFFFF_FFFFL, vb);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean memGetBoolean(long ptr) { return UNSAFE.getByte(null, ptr) != 0; }
|
||||
public static byte memGetByte(long ptr) { return UNSAFE.getByte(null, ptr); }
|
||||
public static short memGetShort(long ptr) { return UNSAFE.getShort(null, ptr); }
|
||||
public static int memGetInt(long ptr) { return UNSAFE.getInt(null, ptr); }
|
||||
public static long memGetLong(long ptr) { return UNSAFE.getLong(null, ptr); }
|
||||
public static float memGetFloat(long ptr) { return UNSAFE.getFloat(null, ptr); }
|
||||
public static double memGetDouble(long ptr) { return UNSAFE.getDouble(null, ptr); }
|
||||
|
||||
public static void memPutByte(long ptr, byte value) { UNSAFE.putByte(null, ptr, value); }
|
||||
public static void memPutShort(long ptr, short value) { UNSAFE.putShort(null, ptr, value); }
|
||||
public static void memPutInt(long ptr, int value) { UNSAFE.putInt(null, ptr, value); }
|
||||
public static void memPutLong(long ptr, long value) { UNSAFE.putLong(null, ptr, value); }
|
||||
public static void memPutFloat(long ptr, float value) { UNSAFE.putFloat(null, ptr, value); }
|
||||
public static void memPutDouble(long ptr, double value) { UNSAFE.putDouble(null, ptr, value); }
|
||||
|
||||
// -------------------------------------------------
|
||||
// -------------------------------------------------
|
||||
// -------------------------------------------------
|
||||
|
||||
private static sun.misc.Unsafe getUnsafeInstance() {
|
||||
java.lang.reflect.Field[] fields = sun.misc.Unsafe.class.getDeclaredFields();
|
||||
|
||||
/*
|
||||
Different runtimes use different names for the Unsafe singleton,
|
||||
so we cannot use .getDeclaredField and we scan instead. For example:
|
||||
|
||||
Oracle: theUnsafe
|
||||
PERC : m_unsafe_instance
|
||||
Android: THE_ONE
|
||||
*/
|
||||
for (java.lang.reflect.Field field : fields) {
|
||||
if (!field.getType().equals(sun.misc.Unsafe.class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int modifiers = field.getModifiers();
|
||||
if (!(java.lang.reflect.Modifier.isStatic(modifiers) && java.lang.reflect.Modifier.isFinal(modifiers))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
return (sun.misc.Unsafe)field.get(null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("LWJGL requires sun.misc.Unsafe to be available.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches an object and its superclasses for a field with the same name and type. If the field isn't
|
||||
* public, it may not be stable across library/JVM versions, and these calls are likely not portable. You have been
|
||||
* warned!
|
||||
*/
|
||||
private static long getFieldOffset(Class<?> containerType, Class<?> fieldType, String name) {
|
||||
Class<?> c = containerType;
|
||||
while (c != Object.class) {
|
||||
Field[] fields = c.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (!field.getType().isAssignableFrom(fieldType) || Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long offset = UNSAFE.objectFieldOffset(field);
|
||||
if (field.getName().equals(name)) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
throw new UnsupportedOperationException("Failed to find field offset in class.");
|
||||
}
|
||||
|
||||
private static long getFieldOffset(Class<?> containerType, Class<?> fieldType, LongPredicate predicate) {
|
||||
Class<?> c = containerType;
|
||||
while (c != Object.class) {
|
||||
Field[] fields = c.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (!field.getType().isAssignableFrom(fieldType) || Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long offset = UNSAFE.objectFieldOffset(field);
|
||||
if (predicate.test(offset)) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
throw new UnsupportedOperationException("Failed to find field offset in class.");
|
||||
}
|
||||
|
||||
private static long getFieldOffsetInt(Object container, int value) {
|
||||
return getFieldOffset(container.getClass(), int.class, offset -> UNSAFE.getInt(container, offset) == value);
|
||||
}
|
||||
|
||||
private static long getAddressOffset() {
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(0);
|
||||
return getFieldOffset(bb.getClass(), long.class, "address");
|
||||
|
||||
// TODO: do this portably
|
||||
// long MAGIC_ADDRESS = 0xDEADBEEF8BADF00DL & (BITS32 ? 0xFFFF_FFFFL : 0xFFFF_FFFF_FFFF_FFFFL);
|
||||
// ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(MAGIC_ADDRESS, 0));
|
||||
//return getFieldOffset(bb.getClass(), long.class, offset -> UNSAFE.getLong(bb, offset) == MAGIC_ADDRESS);
|
||||
}
|
||||
|
||||
private static final int MAGIC_CAPACITY = 0x0D15EA5E;
|
||||
private static final int MAGIC_POSITION = 0x00FACADE;
|
||||
|
||||
private static long getMarkOffset() {
|
||||
// TODO: is this actually reliable without NewDirectByteBuffer?
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(0);
|
||||
return getFieldOffsetInt(bb, -1);
|
||||
// ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(1L, 0));
|
||||
// return getFieldOffsetInt(bb, -1);
|
||||
}
|
||||
|
||||
private static long getPositionOffset() {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
|
||||
bb.position(MAGIC_POSITION);
|
||||
return getFieldOffsetInt(bb, MAGIC_POSITION);
|
||||
}
|
||||
|
||||
private static long getLimitOffset() {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
|
||||
bb.limit(MAGIC_POSITION);
|
||||
return getFieldOffsetInt(bb, MAGIC_POSITION);
|
||||
}
|
||||
|
||||
private static long getCapacityOffset() {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
|
||||
bb.limit(0);
|
||||
return getFieldOffsetInt(bb, MAGIC_CAPACITY);
|
||||
}
|
||||
|
||||
static IntBuffer wrapBufferInt(long address, int capacity) {
|
||||
IntBuffer buffer;
|
||||
try {
|
||||
buffer = (IntBuffer)UNSAFE.allocateInstance(BUFFER_INT);
|
||||
} catch (InstantiationException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
|
||||
UNSAFE.putLong(buffer, ADDRESS, address);
|
||||
UNSAFE.putInt(buffer, MARK, -1);
|
||||
UNSAFE.putInt(buffer, LIMIT, capacity);
|
||||
UNSAFE.putInt(buffer, CAPACITY, capacity);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static FloatBuffer wrapBufferFloat(long address, int capacity) {
|
||||
FloatBuffer buffer;
|
||||
try {
|
||||
buffer = (FloatBuffer)UNSAFE.allocateInstance(BUFFER_FLOAT);
|
||||
} catch (InstantiationException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
|
||||
UNSAFE.putLong(buffer, ADDRESS, address);
|
||||
UNSAFE.putInt(buffer, MARK, -1);
|
||||
UNSAFE.putInt(buffer, LIMIT, capacity);
|
||||
UNSAFE.putInt(buffer, CAPACITY, capacity);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* This class is backported from LWJGL3 under the BSD 3-clause "New" or "Revised" License
|
||||
*/
|
||||
|
||||
package com.gtnewhorizons.angelica.compat.lwjgl;
|
||||
|
||||
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutDouble;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutFloat;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutInt;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutLong;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutShort;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memSet;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.wrapBufferFloat;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.wrapBufferInt;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Arrays;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.MemoryUtil;
|
||||
|
||||
/**
|
||||
* An off-heap memory stack.
|
||||
*
|
||||
* <p>This class should be used in a thread-local manner for stack allocations.</p>
|
||||
*
|
||||
* @ see Configuration#STACK_SIZE
|
||||
* @ see Configuration#DEBUG_STACK
|
||||
*/
|
||||
@SuppressWarnings("LombokGetterMayBeUsed")
|
||||
public class MemoryStack extends Pointer.Default implements AutoCloseable {
|
||||
|
||||
private static final int DEFAULT_STACK_SIZE = /*Configuration.STACK_SIZE.get(64)*/ 64 * 1024;
|
||||
private static final int DEFAULT_STACK_FRAMES = 8;
|
||||
|
||||
private static final ThreadLocal<MemoryStack> TLS = ThreadLocal.withInitial(MemoryStack::create);
|
||||
|
||||
static {
|
||||
if (DEFAULT_STACK_SIZE < 0) {
|
||||
throw new IllegalStateException("Invalid stack size.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private final @Nullable ByteBuffer container;
|
||||
|
||||
private final int size;
|
||||
|
||||
private int pointer;
|
||||
|
||||
private int[] frames;
|
||||
protected int frameIndex;
|
||||
|
||||
/**
|
||||
* Creates a new {@code MemoryStack} backed by the specified memory region.
|
||||
*
|
||||
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
|
||||
*
|
||||
* @param container the backing memory buffer, may be null
|
||||
* @param address the backing memory address
|
||||
* @param size the backing memory size
|
||||
*/
|
||||
protected MemoryStack(@Nullable ByteBuffer container, long address, int size) {
|
||||
super(address);
|
||||
this.container = container;
|
||||
|
||||
this.size = size;
|
||||
this.pointer = size;
|
||||
|
||||
this.frames = new int[DEFAULT_STACK_FRAMES];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code MemoryStack} with the default size.
|
||||
*
|
||||
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
|
||||
*/
|
||||
public static MemoryStack create() {
|
||||
return create(DEFAULT_STACK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code MemoryStack} with the specified size.
|
||||
*
|
||||
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
|
||||
*
|
||||
* @param capacity the maximum number of bytes that may be allocated on the stack
|
||||
*/
|
||||
public static MemoryStack create(int capacity) {
|
||||
return create(BufferUtils.createByteBuffer(capacity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code MemoryStack} backed by the specified memory buffer.
|
||||
*
|
||||
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
|
||||
*
|
||||
* @param buffer the backing memory buffer
|
||||
*/
|
||||
public static MemoryStack create(ByteBuffer buffer) {
|
||||
long address = MemoryUtil.getAddress(buffer);
|
||||
int size = buffer.remaining();
|
||||
return new MemoryStack(buffer, address, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code MemoryStack} backed by the specified memory region.
|
||||
*
|
||||
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
|
||||
*
|
||||
* @param address the backing memory address
|
||||
* @param size the backing memory size
|
||||
*/
|
||||
public static MemoryStack ncreate(long address, int size) {
|
||||
return new MemoryStack(null, address, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current stack pointer and pushes a new frame to the stack.
|
||||
*
|
||||
* <p>This method should be called when entering a method, before doing any stack allocations. When exiting a method, call the {@link #pop} method to
|
||||
* restore the previous stack frame.</p>
|
||||
*
|
||||
* <p>Pairs of push/pop calls may be nested. Care must be taken to:</p>
|
||||
* <ul>
|
||||
* <li>match every push with a pop</li>
|
||||
* <li>not call pop before push has been called at least once</li>
|
||||
* <li>not nest push calls to more than the maximum supported depth</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return this stack
|
||||
*/
|
||||
public MemoryStack push() {
|
||||
if (frameIndex == frames.length) {
|
||||
frameOverflow();
|
||||
}
|
||||
|
||||
frames[frameIndex++] = pointer;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void frameOverflow() {
|
||||
if (/*DEBUG*/ false) {
|
||||
AngelicaTweaker.LOGGER.warn("[WARNING] Out of frame stack space (" + frames.length + ") in thread: " + Thread.currentThread());
|
||||
}
|
||||
frames = Arrays.copyOf(frames, frames.length * 3 / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the current stack frame and moves the stack pointer to the end of the previous stack frame.
|
||||
*
|
||||
* @return this stack
|
||||
*/
|
||||
public MemoryStack pop() {
|
||||
pointer = frames[--frameIndex];
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #pop} on this {@code MemoryStack}.
|
||||
*
|
||||
* <p>This method should not be used directly. It is called automatically when the {@code MemoryStack} is used as a resource in a try-with-resources
|
||||
* statement.</p>
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
//noinspection resource
|
||||
pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the backing off-heap memory.
|
||||
*
|
||||
* <p>The stack grows "downwards", so the bottom of the stack is at {@code address + size}, while the top is at {@code address}.</p>
|
||||
*/
|
||||
public long getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the backing off-heap memory.
|
||||
*
|
||||
* <p>This is the maximum number of bytes that may be allocated on the stack.</p>
|
||||
*/
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current frame index.
|
||||
*
|
||||
* <p>This is the current number of nested {@link #push} calls.</p>
|
||||
*/
|
||||
public int getFrameIndex() {
|
||||
return frameIndex;
|
||||
}
|
||||
|
||||
/** Returns the memory address at the current stack pointer. */
|
||||
public long getPointerAddress() {
|
||||
return address + (pointer & 0xFFFF_FFFFL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current stack pointer.
|
||||
*
|
||||
* <p>The stack grows "downwards", so when the stack is empty {@code pointer} is equal to {@code size}. On every allocation {@code pointer} is reduced by
|
||||
* the allocated size (after alignment) and {@code address + pointer} points to the first byte of the last allocation.</p>
|
||||
*
|
||||
* <p>Effectively, this methods returns how many more bytes may be allocated on the stack.</p>
|
||||
*/
|
||||
public int getPointer() {
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current stack pointer.
|
||||
*
|
||||
* <p>This method directly manipulates the stack pointer. Using it irresponsibly may break the internal state of the stack. It should only be used in rare
|
||||
* cases or in auto-generated code.</p>
|
||||
*/
|
||||
public void setPointer(int pointer) {
|
||||
if (/*CHECKS*/ false) {
|
||||
checkPointer(pointer);
|
||||
}
|
||||
|
||||
this.pointer = pointer;
|
||||
}
|
||||
|
||||
private void checkPointer(int pointer) {
|
||||
if (pointer < 0 || size < pointer) {
|
||||
throw new IndexOutOfBoundsException("Invalid stack pointer");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkAlignment(int alignment) {
|
||||
if (Integer.bitCount(alignment) != 1) {
|
||||
throw new IllegalArgumentException("Alignment must be a power-of-two value.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #nmalloc(int, int)} with {@code alignment} equal to {@link Pointer#POINTER_SIZE POINTER_SIZE}.
|
||||
*
|
||||
* @param size the allocation size
|
||||
*
|
||||
* @return the memory address on the stack for the requested allocation
|
||||
*/
|
||||
public long nmalloc(int size) {
|
||||
return nmalloc(POINTER_SIZE, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a block of {@code size} bytes of memory on the stack. The content of the newly allocated block of memory is not initialized, remaining with
|
||||
* indeterminate values.
|
||||
*
|
||||
* @param alignment the required alignment
|
||||
* @param size the allocation size
|
||||
*
|
||||
* @return the memory address on the stack for the requested allocation
|
||||
*/
|
||||
public long nmalloc(int alignment, int size) {
|
||||
// Align address to the specified alignment
|
||||
long address = (this.address + pointer - size) & ~Integer.toUnsignedLong(alignment - 1);
|
||||
|
||||
pointer = (int)(address - this.address);
|
||||
if (/*CHECKS*/ false && pointer < 0) {
|
||||
throw new OutOfMemoryError("Out of stack space.");
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a block of memory on the stack for an array of {@code num} elements, each of them {@code size} bytes long, and initializes all its bits to
|
||||
* zero.
|
||||
*
|
||||
* @param alignment the required element alignment
|
||||
* @param num num the number of elements to allocate
|
||||
* @param size the size of each element
|
||||
*
|
||||
* @return the memory address on the stack for the requested allocation
|
||||
*/
|
||||
public long ncalloc(int alignment, int num, int size) {
|
||||
int bytes = num * size;
|
||||
long address = nmalloc(alignment, bytes);
|
||||
memSet(address, 0, bytes);
|
||||
return address;
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** Unsafe version of {@link #shorts(short)}. */
|
||||
public long nshort(short value) {
|
||||
long a = nmalloc(2, 2);
|
||||
memPutShort(a, value);
|
||||
return a;
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** Int version of {@link #malloc(int)}. */
|
||||
public IntBuffer mallocInt(int size) { return wrapBufferInt(nmalloc(4, size << 2), size); }
|
||||
|
||||
/** Unsafe version of {@link #ints(int)}. */
|
||||
public long nint(int value) {
|
||||
long a = nmalloc(4, 4);
|
||||
memPutInt(a, value);
|
||||
return a;
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** Unsafe version of {@link #longs(long)}. */
|
||||
public long nlong(long value) {
|
||||
long a = nmalloc(8, 8);
|
||||
memPutLong(a, value);
|
||||
return a;
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** Float version of {@link #malloc(int)}. */
|
||||
public FloatBuffer mallocFloat(int size) { return wrapBufferFloat(nmalloc(4, size << 2), size); }
|
||||
|
||||
/** Unsafe version of {@link #floats(float)}. */
|
||||
public long nfloat(float value) {
|
||||
long a = nmalloc(4, 4);
|
||||
memPutFloat(a, value);
|
||||
return a;
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** Unsafe version of {@link #doubles(double)}. */
|
||||
public long ndouble(double value) {
|
||||
long a = nmalloc(8, 8);
|
||||
memPutDouble(a, value);
|
||||
return a;
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// -------------------------------------------------
|
||||
// -------------------------------------------------
|
||||
|
||||
|
||||
/** Returns the stack of the current thread. */
|
||||
public static MemoryStack stackGet() {
|
||||
return TLS.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #push} on the stack of the current thread.
|
||||
*
|
||||
* @return the stack of the current thread.
|
||||
*/
|
||||
public static MemoryStack stackPush() {
|
||||
return stackGet().push();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #pop} on the stack of the current thread.
|
||||
*
|
||||
* @return the stack of the current thread.
|
||||
*/
|
||||
public static MemoryStack stackPop() {
|
||||
return stackGet().pop();
|
||||
}
|
||||
|
||||
/** Thread-local version of {@link #nmalloc(int)}. */
|
||||
public static long nstackMalloc(int size) { return stackGet().nmalloc(size); }
|
||||
/** Thread-local version of {@link #nmalloc(int, int)}. */
|
||||
public static long nstackMalloc(int alignment, int size) { return stackGet().nmalloc(alignment, size); }
|
||||
/** Thread-local version of {@link #ncalloc}. */
|
||||
public static long nstackCalloc(int alignment, int num, int size) { return stackGet().ncalloc(alignment, num, size); }
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This class is backported from LWJGL3 under the BSD 3-clause "New" or "Revised" License
|
||||
*/
|
||||
package com.gtnewhorizons.angelica.compat.lwjgl;
|
||||
|
||||
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.NULL;
|
||||
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.UNSAFE;
|
||||
import static org.lwjgl.LWJGLUtil.CHECKS;
|
||||
|
||||
import net.minecraft.util.Util;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Pointer interface.
|
||||
*
|
||||
* <p>LWJGL can run on both 32bit and 64bit architectures. Since LWJGL applications deal with native memory directly, this interface provides necessary
|
||||
* information about the underlying architecture of the running JVM process.</p>
|
||||
*
|
||||
* <p>When interacting with native functions, pointer values are mapped to Java {@code long}. LWJGL automatically converts long values to the correct pointer
|
||||
* addresses when used in native code. Native functions sometimes require arrays of pointer values; the {@link PointerBuffer} class may be used for that
|
||||
* purpose. It has an API similar to a {@link java.nio.LongBuffer} but handles pointer casts automatically.</p>
|
||||
*/
|
||||
public interface Pointer {
|
||||
|
||||
/** The pointer size in bytes. Will be 4 on a 32bit JVM and 8 on a 64bit one. */
|
||||
int POINTER_SIZE = UNSAFE.addressSize();
|
||||
|
||||
/** The pointer size power-of-two. Will be 2 on a 32bit JVM and 3 on a 64bit one. */
|
||||
int POINTER_SHIFT = POINTER_SIZE == 8 ? 3 : 2;
|
||||
|
||||
/** The value of {@code sizeof(long)} for the current platform. */
|
||||
int CLONG_SIZE = POINTER_SIZE == 8 && Util.getOSType() == Util.EnumOS.WINDOWS ? 4 : POINTER_SIZE;
|
||||
|
||||
/** The value of {@code sizeof(long)} as a power-of-two. */
|
||||
int CLONG_SHIFT = CLONG_SIZE == 8 ? 3 : 2;
|
||||
|
||||
/** Will be true on a 32bit JVM. */
|
||||
boolean BITS32 = POINTER_SIZE * 8 == 32;
|
||||
|
||||
/** Will be true on a 64bit JVM. */
|
||||
boolean BITS64 = POINTER_SIZE * 8 == 64;
|
||||
|
||||
/**
|
||||
* Returns the raw pointer address as a {@code long} value.
|
||||
*
|
||||
* @return the pointer address
|
||||
*/
|
||||
long address();
|
||||
|
||||
/** Default {@link Pointer} implementation. */
|
||||
abstract class Default implements Pointer {
|
||||
|
||||
// Removed final due to JDK-8139758. TODO: Restore if the fix is backported to JDK 8.
|
||||
protected long address;
|
||||
|
||||
protected Default(long address) {
|
||||
if (CHECKS && address == NULL) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Pointer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Pointer that = (Pointer)o;
|
||||
|
||||
return address == that.address();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (int)(address ^ (address >>> 32));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s pointer [0x%X]", getClass().getSimpleName(), address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-1
@@ -15,7 +15,6 @@ public class ByteBufferBackedInputStream extends InputStream {
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (!buf.hasRemaining()) {
|
||||
return -1;
|
||||
|
||||
@@ -9,65 +9,35 @@ import net.minecraft.util.MathHelper;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
/**
|
||||
* Singleton camera instance that tracks the current camera position and orientation.
|
||||
*/
|
||||
@Getter
|
||||
public class Camera {
|
||||
public static final Camera INSTANCE = new Camera();
|
||||
final Vector3d pos = new Vector3d();
|
||||
final BlockPos blockPos = new BlockPos();
|
||||
float pitch;
|
||||
float yaw;
|
||||
EntityLivingBase entity;
|
||||
boolean thirdPerson;
|
||||
final float partialTicks;
|
||||
|
||||
private final Vector3d pos = new Vector3d();
|
||||
private final Vector3d entityPos = new Vector3d();
|
||||
private final BlockPos blockPos = new BlockPos();
|
||||
private final Vector3f offset = new Vector3f();
|
||||
private final Matrix4f inverseModelView = new Matrix4f();
|
||||
|
||||
private float pitch;
|
||||
private float yaw;
|
||||
private EntityLivingBase entity;
|
||||
private boolean thirdPerson;
|
||||
private float partialTicks;
|
||||
|
||||
private Camera() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the camera position and orientation based on the current entity and partial ticks.
|
||||
*
|
||||
* @param entity The entity to track (usually the render view entity)
|
||||
* @param partialTicks The partial tick time for interpolation
|
||||
*/
|
||||
public void update(EntityLivingBase entity, float partialTicks) {
|
||||
public Camera(EntityLivingBase entity, float partialTicks) {
|
||||
this.partialTicks = partialTicks;
|
||||
final Vector4f offset = new Vector4f(); // third person offset
|
||||
final Matrix4f inverseModelView = new Matrix4f(RenderingState.INSTANCE.getModelViewMatrix()).invert();
|
||||
inverseModelView.transform(offset);
|
||||
|
||||
final double camX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks + offset.x;
|
||||
final double camY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks + offset.y;
|
||||
final double camZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks + offset.z;
|
||||
this.entity = entity;
|
||||
|
||||
// Entity position (interpolated, at eye level)
|
||||
final double entityX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks;
|
||||
final double entityY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks;
|
||||
final double entityZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks;
|
||||
final double eyeHeight = entity.getEyeHeight();
|
||||
|
||||
entityPos.set(entityX, entityY + eyeHeight, entityZ);
|
||||
|
||||
thirdPerson = Minecraft.getMinecraft().gameSettings.thirdPersonView > 0;
|
||||
|
||||
final double camX, camY, camZ;
|
||||
offset.set(0, 0, 0);
|
||||
|
||||
if (thirdPerson) {
|
||||
// Third person: use inverse modelview to find camera offset
|
||||
inverseModelView.set(RenderingState.INSTANCE.getModelViewMatrix()).invert();
|
||||
inverseModelView.transformPosition(offset);
|
||||
}
|
||||
camX = entityX + offset.x;
|
||||
camY = entityY + eyeHeight + offset.y;
|
||||
camZ = entityZ + offset.z;
|
||||
|
||||
pos.set(camX, camY, camZ);
|
||||
blockPos.set(MathHelper.floor_double(camX), MathHelper.floor_double(camY), MathHelper.floor_double(camZ));
|
||||
pitch = entity.cameraPitch;
|
||||
yaw = entity.rotationYaw;
|
||||
thirdPerson = Minecraft.getMinecraft().gameSettings.thirdPersonView == 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,6 @@ public class ChunkPos {
|
||||
return (long)x & 4294967295L | ((long)z & 4294967295L) << 32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int i = 1664525 * this.x + 1013904223;
|
||||
@@ -48,7 +47,6 @@ public class ChunkPos {
|
||||
return i ^ j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat.mojang;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.PlayerControllerMP;
|
||||
|
||||
public final class GameModeUtil {
|
||||
private GameModeUtil() {
|
||||
}
|
||||
|
||||
public static boolean isSpectator() {
|
||||
final PlayerControllerMP controller = Minecraft.getMinecraft().playerController;
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
return controller.currentGameType.getID() == 3;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.compat.mojang;
|
||||
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
public enum InteractionHand {
|
||||
MAIN_HAND,
|
||||
OFF_HAND;
|
||||
|
||||
InteractionHand() {
|
||||
}
|
||||
|
||||
public ItemStack getItemInHand(EntityPlayer player){
|
||||
if (ModStatus.isBackhandLoaded && this == InteractionHand.OFF_HAND){ // off hand (requires backhand)
|
||||
return ModStatus.backhandCompat.getOffhandItem(player);
|
||||
}
|
||||
else { // main hand
|
||||
return player.getHeldItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.compat.mojang;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.lwjgl.MemoryStack;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -7,8 +8,6 @@ import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.Iris;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -59,20 +58,15 @@ public class NativeImage extends BufferedImage {
|
||||
|
||||
// final int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_WIDTH);
|
||||
// final int height = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_HEIGHT);
|
||||
|
||||
final IntBuffer buffer = MemoryUtilities.memAllocInt(size);
|
||||
|
||||
try {
|
||||
try (final MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final IntBuffer buffer = stack.mallocInt(size);
|
||||
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, level, format.glFormat, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
|
||||
int[] data = new int[size];
|
||||
buffer.get(data);
|
||||
setRGB(0, 0, width, height, data, 0, width);
|
||||
} finally {
|
||||
MemoryUtilities.memFree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToFile(File file) throws IOException{
|
||||
try {
|
||||
ImageIO.write(this, "png", file);
|
||||
@@ -110,15 +104,18 @@ public class NativeImage extends BufferedImage {
|
||||
|
||||
|
||||
public enum Format {
|
||||
RGBA(4, GL11.GL_RGBA),
|
||||
RGB(3, GL11.GL_RGB);
|
||||
RGBA(4, GL11.GL_RGBA, BufferedImage.TYPE_INT_ARGB),
|
||||
RGB(3, GL11.GL_RGB, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
private final int components;
|
||||
private final int glFormat;
|
||||
private final int imageType;
|
||||
|
||||
Format(int components, int glFormat) {
|
||||
|
||||
Format(int components, int glFormat, int imageType) {
|
||||
this.components = components;
|
||||
this.glFormat = glFormat;
|
||||
this.imageType = imageType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.gtnewhorizons.angelica.compat.nd;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
public class BufferWriter {
|
||||
// Adapted from Neodymium
|
||||
|
||||
private final ByteBuffer buf;
|
||||
|
||||
private final FloatBuffer floatBuffer;
|
||||
private final ShortBuffer shortBuffer;
|
||||
private final IntBuffer intBuffer;
|
||||
|
||||
public BufferWriter(ByteBuffer buf) {
|
||||
this.buf = buf;
|
||||
this.floatBuffer = buf.asFloatBuffer();
|
||||
this.shortBuffer = buf.asShortBuffer();
|
||||
this.intBuffer = buf.asIntBuffer();
|
||||
}
|
||||
|
||||
private void incrementPosition(int add) {
|
||||
buf.position(buf.position() + add);
|
||||
floatBuffer.position(buf.position() / 4);
|
||||
shortBuffer.position(buf.position() / 2);
|
||||
intBuffer.position(buf.position() / 4);
|
||||
}
|
||||
|
||||
public void writeFloat(float x) {
|
||||
try {
|
||||
floatBuffer.put(x);
|
||||
|
||||
incrementPosition(4);
|
||||
} catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeInt(int x) {
|
||||
intBuffer.put(x);
|
||||
|
||||
incrementPosition(4);
|
||||
}
|
||||
|
||||
public void writeByte(byte x) {
|
||||
buf.put(x); // this increments the buffer position by 1
|
||||
|
||||
incrementPosition(0);
|
||||
}
|
||||
|
||||
public int position() {
|
||||
return buf.position();
|
||||
}
|
||||
|
||||
public void writeShort(short s) {
|
||||
shortBuffer.put(s);
|
||||
|
||||
incrementPosition(2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.compat.nd.writers;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ItemVBOQuadWriter implements IWriteQuads {
|
||||
private boolean direct;
|
||||
|
||||
public ItemVBOQuadWriter() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
public void init(boolean direct) {
|
||||
// This would need to be re-initialized if the direct vs indirect changes
|
||||
this.direct = direct;
|
||||
}
|
||||
@Override
|
||||
public void writeQuad(QuadView quad, ByteBuffer buf) {
|
||||
if(direct) {
|
||||
writeQuadDirect(quad, buf);
|
||||
} else {
|
||||
writeQuadIndirect(quad, buf);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
|
||||
throw new UnsupportedOperationException("Direct mode not supported yet");
|
||||
}
|
||||
|
||||
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
|
||||
for(int idx = 0; idx < 4; ++idx) {
|
||||
// Position
|
||||
buf.putFloat(quad.getX(idx));
|
||||
buf.putFloat(quad.getY(idx));
|
||||
buf.putFloat(quad.getZ(idx));
|
||||
|
||||
// Texture
|
||||
buf.putFloat(quad.getTexU(idx));
|
||||
buf.putFloat(quad.getTexV(idx));
|
||||
|
||||
// Normals + Padding
|
||||
buf.putInt(quad.getNormal(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.compat.nd.writers;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PositionColorTextureQuadWriter implements IWriteQuads {
|
||||
private boolean direct;
|
||||
|
||||
public PositionColorTextureQuadWriter() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
public void init(boolean direct) {
|
||||
// This would need to be re-initialized if the direct vs indirect changes
|
||||
this.direct = direct;
|
||||
}
|
||||
@Override
|
||||
public void writeQuad(QuadView quad, ByteBuffer buf) {
|
||||
if(direct) {
|
||||
writeQuadDirect(quad, buf);
|
||||
} else {
|
||||
writeQuadIndirect(quad, buf);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
|
||||
throw new UnsupportedOperationException("Direct mode not supported yet");
|
||||
}
|
||||
|
||||
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
|
||||
for(int idx = 0; idx < 4; ++idx) {
|
||||
// Position
|
||||
buf.putFloat(quad.getX(idx));
|
||||
buf.putFloat(quad.getY(idx));
|
||||
buf.putFloat(quad.getZ(idx));
|
||||
|
||||
// Color
|
||||
buf.putInt(quad.getColor(idx));
|
||||
|
||||
// Texture
|
||||
buf.putFloat(quad.getTexU(idx));
|
||||
buf.putFloat(quad.getTexV(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.gtnewhorizons.angelica.compat.nd.writers;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PositionQuadWriter implements IWriteQuads {
|
||||
private boolean direct;
|
||||
|
||||
public PositionQuadWriter() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
public void init(boolean direct) {
|
||||
// This would need to be re-initialized if the direct vs indirect changes
|
||||
this.direct = direct;
|
||||
}
|
||||
@Override
|
||||
public void writeQuad(QuadView quad, ByteBuffer buf) {
|
||||
if(direct) {
|
||||
writeQuadDirect(quad, buf);
|
||||
} else {
|
||||
writeQuadIndirect(quad, buf);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
|
||||
throw new UnsupportedOperationException("Direct mode not supported yet");
|
||||
}
|
||||
|
||||
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
|
||||
for(int idx = 0; idx < 4; ++idx) {
|
||||
// Position
|
||||
buf.putFloat(quad.getX(idx));
|
||||
buf.putFloat(quad.getY(idx));
|
||||
buf.putFloat(quad.getZ(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.gtnewhorizons.angelica.compat.nd.writers;
|
||||
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PositionTextureColorQuadWriter implements IWriteQuads {
|
||||
private boolean direct;
|
||||
|
||||
public PositionTextureColorQuadWriter() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
public void init(boolean direct) {
|
||||
// This would need to be re-initialized if the direct vs indirect changes
|
||||
this.direct = direct;
|
||||
}
|
||||
@Override
|
||||
public void writeQuad(QuadView quad, ByteBuffer buf) {
|
||||
if(direct) {
|
||||
writeQuadDirect(quad, buf);
|
||||
} else {
|
||||
writeQuadIndirect(quad, buf);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
|
||||
throw new UnsupportedOperationException("Direct mode not supported yet");
|
||||
}
|
||||
|
||||
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
|
||||
for(int idx = 0; idx < 4; ++idx) {
|
||||
// Position
|
||||
buf.putFloat(quad.getX(idx));
|
||||
buf.putFloat(quad.getY(idx));
|
||||
buf.putFloat(quad.getZ(idx));
|
||||
|
||||
// Texture
|
||||
buf.putFloat(quad.getTexU(idx));
|
||||
buf.putFloat(quad.getTexV(idx));
|
||||
|
||||
// Color
|
||||
buf.putInt(quad.getColor(idx));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.compat.nd.writers;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.quad.writers.IWriteQuads;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PositionTextureQuadWriter implements IWriteQuads {
|
||||
private boolean direct;
|
||||
|
||||
public PositionTextureQuadWriter() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
public void init(boolean direct) {
|
||||
// This would need to be re-initialized if the direct vs indirect changes
|
||||
this.direct = direct;
|
||||
}
|
||||
@Override
|
||||
public void writeQuad(QuadView quad, ByteBuffer buf) {
|
||||
if(direct) {
|
||||
writeQuadDirect(quad, buf);
|
||||
} else {
|
||||
writeQuadIndirect(quad, buf);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeQuadDirect(QuadView quad, ByteBuffer buf) {
|
||||
throw new UnsupportedOperationException("Direct mode not supported yet");
|
||||
}
|
||||
|
||||
protected void writeQuadIndirect(QuadView quad, ByteBuffer buf) {
|
||||
for(int idx = 0; idx < 4; ++idx) {
|
||||
// Position
|
||||
buf.putFloat(quad.getX(idx));
|
||||
buf.putFloat(quad.getY(idx));
|
||||
buf.putFloat(quad.getZ(idx));
|
||||
|
||||
// Texture
|
||||
buf.putFloat(quad.getTexU(idx));
|
||||
buf.putFloat(quad.getTexV(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,17 +34,10 @@ public class MatrixStack {
|
||||
public void pop() {
|
||||
this.matrixStack.removeLast();
|
||||
}
|
||||
public boolean isEmpty() {
|
||||
public boolean clear() {
|
||||
return this.matrixStack.size() == 1;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
while (this.matrixStack.size() > 1) {
|
||||
this.matrixStack.removeLast();
|
||||
}
|
||||
loadIdentity();
|
||||
}
|
||||
|
||||
public void translate(double d, double e, double f) {
|
||||
final Entry lv = (Entry)this.matrixStack.getLast();
|
||||
lv.model.translate((float)d, (float)e, (float)f);
|
||||
|
||||
@@ -72,6 +72,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
private final MultiPhaseParameters phases;
|
||||
private final int hash;
|
||||
private final Optional<RenderLayer> affectedOutline;
|
||||
private final boolean outline;
|
||||
|
||||
private MultiPhase(String name, VertexFormat vertexFormat, int drawMode, int expectedBufferSize, boolean hasCrumbling, boolean translucent, MultiPhaseParameters phases) {
|
||||
super(name, vertexFormat, drawMode, expectedBufferSize, () -> {
|
||||
@@ -83,6 +84,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
this.affectedOutline = phases.outlineMode == RenderLayer.OutlineMode.AFFECTS_OUTLINE ? phases.texture.getId().map((arg2) -> {
|
||||
return getOutline(arg2, phases.cull);
|
||||
}) : Optional.empty();
|
||||
this.outline = phases.outlineMode == RenderLayer.OutlineMode.IS_OUTLINE;
|
||||
this.hash = Objects.hash(new Object[]{super.hashCode(), phases});
|
||||
}
|
||||
|
||||
@@ -95,23 +97,20 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
return this.affectedOutline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
return this == object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RenderType[" + this.phases + ']';
|
||||
}
|
||||
|
||||
static {
|
||||
CACHE = new ObjectOpenCustomHashSet<>(RenderLayer.MultiPhase.HashStrategy.INSTANCE);
|
||||
CACHE = new ObjectOpenCustomHashSet(RenderLayer.MultiPhase.HashStrategy.INSTANCE);
|
||||
}
|
||||
|
||||
static enum HashStrategy implements Hash.Strategy<MultiPhase> {
|
||||
@@ -169,7 +168,6 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
this.phases = ImmutableList.of(this.texture, this.transparency, this.diffuseLighting, this.shadeModel, this.alpha, this.depthTest, this.cull, this.lightmap, this.fog, this.layering, this.target, this.texturing, this.writeMaskState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
@@ -181,12 +179,10 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(new Object[]{this.phases, this.outlineMode});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CompositeState[" + this.phases + ", outlineProperty=" + this.outlineMode + ']';
|
||||
}
|
||||
@@ -297,7 +293,6 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.compat.toremove;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import me.jellysquid.mods.sodium.client.SodiumClientMod;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
@@ -11,6 +11,7 @@ import org.lwjgl.opengl.GL11;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalDouble;
|
||||
|
||||
|
||||
public abstract class RenderPhase {
|
||||
@@ -97,7 +98,6 @@ public abstract class RenderPhase {
|
||||
this.endAction.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
@@ -109,12 +109,10 @@ public abstract class RenderPhase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
@@ -147,7 +145,7 @@ public abstract class RenderPhase {
|
||||
FOG = new Fog("fog", () -> {
|
||||
// Unclear what this should do
|
||||
// BackgroundRenderer.setFogBlack(), also levelFogColor()
|
||||
AngelicaMod.LOGGER.debug("Fog - Not setting level fog color");
|
||||
SodiumClientMod.LOGGER.debug("Fog - Not setting level fog color");
|
||||
GLStateManager.enableFog();
|
||||
}, GLStateManager::disableFog);
|
||||
BLACK_FOG = new Fog("black_fog", () -> {
|
||||
@@ -156,7 +154,7 @@ public abstract class RenderPhase {
|
||||
}, () -> {
|
||||
// Unclear what this should do
|
||||
// BackgroundRenderer.setFogBlack(), also levelFogColor()
|
||||
AngelicaMod.LOGGER.debug("Fog - Not setting level fog color");
|
||||
SodiumClientMod.LOGGER.debug("Fog - Not setting level fog color");
|
||||
GLStateManager.disableFog();
|
||||
});
|
||||
MAIN_TARGET = new Target("main_target", () -> {
|
||||
@@ -164,7 +162,7 @@ public abstract class RenderPhase {
|
||||
});
|
||||
OUTLINE_TARGET = new Target("outline_target", () -> {
|
||||
// TODO: Sodium
|
||||
AngelicaMod.LOGGER.debug("NOT enabling the entity outline framebuffer");
|
||||
SodiumClientMod.LOGGER.debug("NOT enabling the entity outline framebuffer");
|
||||
//MinecraftClient.getInstance().worldRenderer.getEntityOutlinesFramebuffer().beginWrite(false);
|
||||
}, () -> {
|
||||
Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
|
||||
@@ -172,7 +170,7 @@ public abstract class RenderPhase {
|
||||
TRANSLUCENT_TARGET = new Target("translucent_target", () -> {
|
||||
if (Minecraft.isFancyGraphicsEnabled()) {
|
||||
// TODO: Sodium
|
||||
AngelicaMod.LOGGER.debug("NOT enabling the translucent framebuffer");
|
||||
SodiumClientMod.LOGGER.debug("NOT enabling the translucent framebuffer");
|
||||
// MinecraftClient.getInstance().worldRenderer.getTranslucentFramebuffer().beginWrite(false);
|
||||
}
|
||||
|
||||
@@ -345,7 +343,7 @@ public abstract class RenderPhase {
|
||||
super("lightmap", () -> {
|
||||
if (lightmap) {
|
||||
// TODO: Sodium - LightmapTextureManager
|
||||
AngelicaMod.LOGGER.debug("Lightmap - enable (not implemented)");
|
||||
SodiumClientMod.LOGGER.debug("Lightmap - enable (not implemented)");
|
||||
// throw new RuntimeException("Not Implemented Yet");
|
||||
// MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager().enable();
|
||||
|
||||
@@ -354,7 +352,7 @@ public abstract class RenderPhase {
|
||||
}, () -> {
|
||||
if (lightmap) {
|
||||
// TODO: Sodium - LightmapTextureManager
|
||||
AngelicaMod.LOGGER.debug("Lightmap - disable (not implemented)");
|
||||
SodiumClientMod.LOGGER.debug("Lightmap - disable (not implemented)");
|
||||
// throw new RuntimeException("Not Implemented Yet");
|
||||
// MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager().disable();
|
||||
}
|
||||
|
||||
@@ -4,21 +4,10 @@ import com.gtnewhorizon.gtnhlib.config.Config;
|
||||
|
||||
@Config(modid = "angelica", filename = "angelica-modules")
|
||||
public class AngelicaConfig {
|
||||
@Config.Comment("Enable Celeritas terrain rendering")
|
||||
@Config.Comment("Enable Sodium rendering")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableCeleritas;
|
||||
|
||||
@Config.Comment("Enable multi-threaded chunk building for improved performance [Requires Celeritas]")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresWorldRestart
|
||||
public static boolean enableThreadedChunkBuilding;
|
||||
|
||||
@Config.Comment("Number of chunk builder threads. 0 = auto-detect, -1 = use single-threaded fallback")
|
||||
@Config.DefaultInt(0)
|
||||
@Config.RangeInt(min = -1, max = 16)
|
||||
@Config.RequiresWorldRestart
|
||||
public static int chunkBuilderThreadCount;
|
||||
public static boolean enableSodium;
|
||||
|
||||
@Config.Comment("Enable NotFine Options")
|
||||
@Config.DefaultBoolean(false)
|
||||
@@ -28,7 +17,12 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableReesesSodiumOptions;
|
||||
|
||||
@Config.Comment("Inject BakedModel rendering into some vanilla blocks")
|
||||
@Config.Comment("Enable Sodium fluid rendering")
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableSodiumFluidRendering;
|
||||
|
||||
@Config.Comment("Inject QuadProvider rendering into some vanilla blocks")
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean injectQPRendering;
|
||||
@@ -38,25 +32,20 @@ public class AngelicaConfig {
|
||||
@Config.Ignore()
|
||||
public static boolean enableTestBlocks;
|
||||
|
||||
@Config.Comment("Enable Iris Shaders [Requires Celeritas]")
|
||||
@Config.Comment("Enable Iris Shaders [Requires Sodium]")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableIris;
|
||||
|
||||
@Config.Comment("Enable MCPatcherForge features, still in Alpha. Individual features are toggled in mcpatcher.json")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableMCPatcherForgeFeatures;
|
||||
|
||||
@Config.Comment("Replace cloud renderer with a VBO version.")
|
||||
@Config.Comment("Replace some vanilla render paths with more optimized versions. Disable if you encounter mixin conflicts.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableVBOClouds;
|
||||
|
||||
@Config.Comment("Uses cached attributes for VBO rendering, resulting in less CPU overhead. Disable if you notice any graphical issues.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableVAO;
|
||||
public static boolean enableVBO;
|
||||
|
||||
@Config.Comment("Enable NotFine features")
|
||||
@Config.DefaultBoolean(true)
|
||||
@@ -88,16 +77,12 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableHudCachingEventTransformer;
|
||||
|
||||
@Config.Comment("Enable HUD Caching at runtime. Requires enableHudCaching to be on at startup. [Experimental]")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean hudCachingActive;
|
||||
|
||||
@Config.Comment("The amount of frames to wait before updating the HUD elements. [Experimental]")
|
||||
@Config.DefaultInt(20)
|
||||
@Config.RangeInt(min = 1, max = 60)
|
||||
public static int hudCachingFPS = 20;
|
||||
|
||||
@Config.Comment("Batch drawScreen fonts")
|
||||
@Config.Comment("Batch drawScreen fonts [Experimental]")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableFontRenderer;
|
||||
@@ -148,11 +133,6 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableDebugLogging;
|
||||
|
||||
@Config.Comment("Enables PBR atlas dumping")
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.Name("Enable PBR Debug")
|
||||
public static boolean enablePBRDebug;
|
||||
|
||||
@Config.Comment("Enable Zoom")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
@@ -163,76 +143,9 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean optimizeInWorldItemRendering;
|
||||
|
||||
@Config.Comment("Upper limit for the amount of VBO's to cache for optimized item rendering. Higher number can potentially use more VRAM.")
|
||||
@Config.DefaultInt(512)
|
||||
@Config.RangeInt(min = 256, max = 1024)
|
||||
public static int itemRendererCacheSize;
|
||||
|
||||
@Config.Comment("Render distance for the spinning mob inside mod spawners")
|
||||
@Config.DefaultDouble(16D)
|
||||
@Config.RangeDouble(min = 16D, max = 64D)
|
||||
public static double mobSpawnerRenderDistance;
|
||||
|
||||
@Config.Comment("Switches to an alternate FPS limiter that gives more stable frametimes, in exchange for slightly " +
|
||||
"more latency. Will never introduce more than one frame of latency, and has a lower impact at higher framerates.")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean sleepBeforeSwap;
|
||||
|
||||
@Config.Comment("Allows unicode languages to use an odd gui scale")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean removeUnicodeEvenScaling;
|
||||
|
||||
@Config.Comment({"Block corners and edges between chunks might have \"cracks\" (various lines/dots) in them.",
|
||||
"While using \"Compact Vertex Format\" makes the situation even worse.",
|
||||
"This option fixes it.",
|
||||
"Requires texture reloading (F3 + T) after changing this option to take effect"})
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean fixBlockCrack;
|
||||
|
||||
@Config.Comment({
|
||||
"The \"epsilon\" value for the fixBlockCrack option. ",
|
||||
"Set this a bit higher if you can still see lines/dots between solid blocks in dark areas.",
|
||||
"May cause intense flickering (z-fighting) between blocks if the value is too high"
|
||||
})
|
||||
@Config.RangeDouble(min = 0, max = 0.005)
|
||||
@Config.DefaultDouble(0.001)
|
||||
public static double blockCrackFixEpsilon;
|
||||
|
||||
@Config.Comment("Block classes that have bugs when rendering with the fixBlockCrack can be put here to avoid manipulating them")
|
||||
@Config.DefaultStringList({
|
||||
"net.minecraft.block.BlockCauldron",
|
||||
"net.minecraft.block.BlockStairs"
|
||||
})
|
||||
public static String[] blockCrackFixBlacklist;
|
||||
|
||||
@Config.Comment({"Block classes that have render pass other than 0 but still need to be manipulated.",
|
||||
"Add a block class here if you see flickering (z-fighting) with fixBlockCrack enabled"
|
||||
})
|
||||
@Config.DefaultStringList({
|
||||
"gregtech.common.blocks.BlockOres",
|
||||
"gregtech.common.blocks.GTBlockOre",
|
||||
"shukaro.artifice.block.world.BlockOre",
|
||||
"bartworks.system.material.BWMetaGeneratedOres",
|
||||
"gtPlusPlus.core.block.base.BlockBaseOre",
|
||||
"org.pfaa.geologica.block.BrokenGeoBlock",
|
||||
"org.pfaa.geologica.block.BrickGeoBlock",
|
||||
})
|
||||
public static String[] blockCrackFixRenderPassWhitelist__;
|
||||
|
||||
@Config.Comment("Register HardcodedCustomUniforms in Iris Shaders. May help with compatibility in certain shader packs")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean enableHardcodedCustomUniforms;
|
||||
|
||||
@Config.Comment("Define IS_IRIS in shader macros.")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean defineIsIris;
|
||||
|
||||
@Config.Comment("Renders chunks before neighbors are ready. Improves loading at render distance edges, useful for low render distance servers.")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean useVanillaChunkTracking;
|
||||
|
||||
@Config.Comment("Disables additional F3 information added by Angelica.")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean disableF3Additions;
|
||||
@Config.Comment("Max amount of display lists to cache for optimized item rendering. Higher number will use more VRAM")
|
||||
@Config.DefaultInt(256)
|
||||
@Config.RangeInt(min = 64, max = 1024)
|
||||
public static int itemRendererDisplayListCacheSize;
|
||||
|
||||
}
|
||||
@@ -6,13 +6,6 @@ import net.minecraft.client.gui.GuiScreen;
|
||||
|
||||
public class AngelicaGuiConfig extends SimpleGuiConfig {
|
||||
public AngelicaGuiConfig(GuiScreen parent) throws ConfigException {
|
||||
super(parent,
|
||||
"angelica",
|
||||
"Angelica",
|
||||
true,
|
||||
AngelicaConfig.class,
|
||||
CompatConfig.class,
|
||||
FontConfig.class
|
||||
);
|
||||
super(parent, AngelicaConfig.class, "angelica", "Angelica");
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.gtnewhorizons.angelica.config;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.config.Config;
|
||||
|
||||
@Config(modid = "angelica", filename = "angelica-compat", category = "compat")
|
||||
@Config(modid = "angelica", filename = "angelica-compat")
|
||||
public class CompatConfig {
|
||||
|
||||
@Config.Comment("Apply fixes to the LOTR mod")
|
||||
@@ -39,29 +39,4 @@ public class CompatConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean fixImmersiveEngineering;
|
||||
|
||||
@Config.Comment("Apply fixes to Security Craft")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean fixSecurityCraft;
|
||||
|
||||
@Config.Comment("Apply tweaks to NTM:Space")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean tweakNTMSpace;
|
||||
|
||||
@Config.Comment("Apply fixes to NTM:Space")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean fixNTMSpace;
|
||||
|
||||
@Config.Comment("Disables rendering of the planet below the player at high altitudes [Requires tweakNTMSpace]")
|
||||
@Config.DefaultEnum("ShadersOnly")
|
||||
public static altitudePlanetRendererState NTMSpace_disableAltitudePlanetRenderer;
|
||||
public enum altitudePlanetRendererState {
|
||||
Always,
|
||||
ShadersOnly,
|
||||
Never
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.config;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.config.Config;
|
||||
|
||||
@Config(modid = "angelica", category = "customfont")
|
||||
public class FontConfig {
|
||||
@Config.Comment("Whether or not to use custom fonts when drawing text.")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean enableCustomFont;
|
||||
|
||||
@Config.Comment("Name of the primary custom font. Best not to set it from here.")
|
||||
@Config.DefaultString("(none)")
|
||||
public static String customFontNamePrimary;
|
||||
|
||||
@Config.Comment("Name of the fallback custom font. Best not to set it from here.")
|
||||
@Config.DefaultString("(none)")
|
||||
public static String customFontNameFallback;
|
||||
|
||||
@Config.Comment("The quality at which custom fonts are rendered, making them less pixelated but increasing memory usage.")
|
||||
@Config.DefaultInt(30)
|
||||
@Config.RangeInt(min = 6, max = 72)
|
||||
public static int customFontQuality;
|
||||
|
||||
@Config.Comment("Controls the distance at which the font's shadow is drawn.")
|
||||
@Config.DefaultFloat(1F)
|
||||
@Config.RangeFloat(min = 0F, max = 2F)
|
||||
public static float fontShadowOffset;
|
||||
|
||||
@Config.Comment("The number of shadows to be drawn behind text at various offsets.")
|
||||
@Config.DefaultInt(1)
|
||||
@Config.RangeInt(min = 1, max = 8)
|
||||
public static int shadowCopies;
|
||||
|
||||
@Config.Comment("The number of bold copies to be drawn behind text at various offsets.")
|
||||
@Config.DefaultInt(2)
|
||||
@Config.RangeInt(min = 1, max = 8)
|
||||
public static int boldCopies;
|
||||
|
||||
@Config.Comment("Influences the aspect ratio of each glyph.")
|
||||
@Config.DefaultFloat(0F)
|
||||
@Config.RangeFloat(min = -1F, max = 1F)
|
||||
public static float glyphAspect;
|
||||
|
||||
@Config.Comment("Scale of each glyph, whitespace excluded.")
|
||||
@Config.DefaultFloat(1F)
|
||||
@Config.RangeFloat(min = 0.1F, max = 3F)
|
||||
public static float glyphScale;
|
||||
|
||||
@Config.Comment("Whitespace scale.")
|
||||
@Config.DefaultFloat(1F)
|
||||
@Config.RangeFloat(min = 0.1F, max = 3F)
|
||||
public static float whitespaceScale;
|
||||
|
||||
@Config.Comment("Adds extra spacing between glyphs.")
|
||||
@Config.DefaultFloat(0F)
|
||||
@Config.RangeFloat(min = -2F, max = 2F)
|
||||
public static float glyphSpacing;
|
||||
|
||||
@Config.Comment("Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.")
|
||||
@Config.DefaultInt(2)
|
||||
@Config.RangeInt(min = 0, max = 2)
|
||||
public static int fontAAMode;
|
||||
|
||||
@Config.Comment("Affects font antialiasing sample spacing. Higher values increase blur.")
|
||||
@Config.DefaultInt(7)
|
||||
@Config.RangeInt(min = 1, max = 24)
|
||||
public static int fontAAStrength;
|
||||
|
||||
@Config.Comment("Custom font scale multiplier, for bugfixes.")
|
||||
@Config.DefaultFloat(1.5F)
|
||||
@Config.RangeFloat(min = 0.1F, max = 3F)
|
||||
public static float customFontScale;
|
||||
|
||||
@Config.Comment({"Mods can provide Angelica with a mapping of special characters to either get replaced with " +
|
||||
"different characters or rendered using the default Unicode font. Setting this to false disables the " +
|
||||
"replacement, which is useful in cases where custom fonts have direct support for these special characters. ",
|
||||
"See for additional details: https://github.com/GTNewHorizons/Angelica/issues/1239#issuecomment-3729877936"})
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableGlyphReplacements;
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Adapated from: Beddium for usage in Angelica
|
||||
*
|
||||
* Copyright (C) 2025 Ven, FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, only version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.gtnewhorizons.angelica.debug;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.AngelicaRenderSectionManager;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ChunkProviderClient;
|
||||
import net.minecraft.world.chunk.EmptyChunk;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Debug overlay that renders a minimap showing chunk loading and rendering state. Toggle via /angelica minimap
|
||||
*/
|
||||
public class ChunkDebugMinimap {
|
||||
private static final ChunkDebugMinimap INSTANCE = new ChunkDebugMinimap();
|
||||
@Getter
|
||||
private static volatile boolean enabled = false;
|
||||
|
||||
private static final int RANGE = 32; // Chunks in each direction from player
|
||||
|
||||
public static synchronized void toggle() {
|
||||
if (enabled) disable();
|
||||
else enable();
|
||||
}
|
||||
|
||||
public static synchronized void enable() {
|
||||
if (enabled) return;
|
||||
enabled = true;
|
||||
MinecraftForge.EVENT_BUS.register(INSTANCE);
|
||||
}
|
||||
|
||||
public static synchronized void disable() {
|
||||
if (!enabled) return;
|
||||
enabled = false;
|
||||
MinecraftForge.EVENT_BUS.unregister(INSTANCE);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderOverlay(RenderGameOverlayEvent.Text event) {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc.theWorld == null || mc.thePlayer == null) return;
|
||||
|
||||
final CeleritasWorldRenderer renderer;
|
||||
try {
|
||||
renderer = CeleritasWorldRenderer.getInstance();
|
||||
} catch (IllegalStateException e) {
|
||||
return; // Renderer not initialized
|
||||
}
|
||||
|
||||
final AngelicaRenderSectionManager manager = renderer.getRenderSectionManager();
|
||||
if (manager == null) return;
|
||||
|
||||
final float partialTicks = event.partialTicks;
|
||||
final double pX = mc.thePlayer.prevPosX + (mc.thePlayer.posX - mc.thePlayer.prevPosX) * partialTicks;
|
||||
final double pZ = mc.thePlayer.prevPosZ + (mc.thePlayer.posZ - mc.thePlayer.prevPosZ) * partialTicks;
|
||||
|
||||
final int playerChunkX = ((int) Math.floor(pX)) >> 4;
|
||||
final int playerChunkZ = ((int) Math.floor(pZ)) >> 4;
|
||||
|
||||
// Save GL state
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GLStateManager.glPushMatrix();
|
||||
|
||||
// Setup rendering
|
||||
GLStateManager.glScaled(2, 2, 1);
|
||||
GLStateManager.glTranslated(32, 32, 0);
|
||||
|
||||
GLStateManager.disableLighting();
|
||||
GLStateManager.disableTexture();
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
|
||||
final ChunkProviderClient chunkProvider = (ChunkProviderClient) mc.theWorld.getChunkProvider();
|
||||
|
||||
for (int x = playerChunkX - RANGE; x < playerChunkX + RANGE; x++) {
|
||||
for (int z = playerChunkZ - RANGE; z < playerChunkZ + RANGE; z++) {
|
||||
drawChunk(x, z, playerChunkX, playerChunkZ, manager, chunkProvider);
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glEnd();
|
||||
|
||||
// Restore GL state
|
||||
GLStateManager.enableLighting();
|
||||
GLStateManager.enableDepthTest();
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
|
||||
private void drawChunk(int chunkX, int chunkZ, int playerChunkX, int playerChunkZ, AngelicaRenderSectionManager manager, ChunkProviderClient chunkProvider) {
|
||||
boolean initialized = false;
|
||||
float r = 0, g = 0, b = 0;
|
||||
|
||||
// Check all 16 sections in the chunk column
|
||||
for (int y = 0; y < 16; y++) {
|
||||
if (manager.isSectionBuilt(chunkX, y, chunkZ) && !manager.isSectionVisuallyEmpty(chunkX, y, chunkZ)) {
|
||||
initialized = true;
|
||||
g = 1; // Green = built
|
||||
if (manager.isSectionVisible(chunkX, y, chunkZ)) {
|
||||
b = 1; // Cyan = visible
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
// Check if chunk is loaded
|
||||
final var chunk = chunkProvider.provideChunk(chunkX, chunkZ);
|
||||
if (chunk instanceof EmptyChunk) {
|
||||
r = 1; // Red = unloaded
|
||||
} else {
|
||||
b = 1; // Blue = loaded but not built
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glColor4f(r, g, b, 0.8f);
|
||||
|
||||
final int xStart = chunkX - playerChunkX;
|
||||
final int zStart = chunkZ - playerChunkZ;
|
||||
final int xEnd = xStart + 1;
|
||||
final int zEnd = zStart + 1;
|
||||
|
||||
GLStateManager.glVertex3d(xStart, zStart, 0);
|
||||
GLStateManager.glVertex3d(xStart, zEnd, 0);
|
||||
GLStateManager.glVertex3d(xEnd, zEnd, 0);
|
||||
GLStateManager.glVertex3d(xEnd, zStart, 0);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import static org.lwjgl.opengl.GL11.glEnable;
|
||||
import static org.lwjgl.opengl.GL11.glEnableClientState;
|
||||
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
|
||||
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL15.glBufferData;
|
||||
import static org.lwjgl.opengl.GL15.glGenBuffers;
|
||||
import static org.lwjgl.opengl.GL20.glDisableVertexAttribArray;
|
||||
@@ -28,10 +28,9 @@ import static org.lwjgl.opengl.GL20.glUniform1;
|
||||
import static org.lwjgl.opengl.GL20.glUniform1f;
|
||||
import static org.lwjgl.opengl.GL20.glUniform1i;
|
||||
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack.*;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.shader.ShaderProgram;
|
||||
import com.gtnewhorizons.angelica.compat.lwjgl.MemoryStack;
|
||||
import java.nio.FloatBuffer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
@@ -132,7 +131,7 @@ public abstract class F3Graph {
|
||||
// Load vertex buffer
|
||||
vertBuf = glGenBuffers();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertBuf);
|
||||
try (final MemoryStack stack = stackPush()) {
|
||||
try (final MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final FloatBuffer vertices = stack.mallocFloat(VERT_COUNT * VERT_FLOATS);
|
||||
// Since we use a triangle strip, we only need 4 vertices. The quad extends to the top of the screen so spikes
|
||||
// don't get truncated. The max height is replaced in the vert shader, no need to be precise.
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Calculates the appropriate tick mode for a dynamic light source based on distance from camera and position relative to camera direction.
|
||||
*/
|
||||
public final class AdaptiveTickCalculator {
|
||||
|
||||
// Distance thresholds in blocks (configurable via settings)
|
||||
private static int slowDistance = 32; // ~2 chunks
|
||||
private static int slowerDistance = 64; // ~4 chunks
|
||||
private static int backgroundDistance = 128; // ~8 chunks
|
||||
|
||||
// Squared thresholds for efficient distance comparison
|
||||
private static int slowDistanceSq = slowDistance * slowDistance;
|
||||
private static int slowerDistanceSq = slowerDistance * slowerDistance;
|
||||
private static int backgroundDistanceSq = backgroundDistance * backgroundDistance;
|
||||
|
||||
private AdaptiveTickCalculator() {}
|
||||
|
||||
/**
|
||||
* Calculate the appropriate tick mode for a light source.
|
||||
*/
|
||||
public static AdaptiveTickMode calculate(@NotNull IDynamicLightSource source, double cameraX, double cameraY, double cameraZ, double lookDirX, double lookDirZ) {
|
||||
double dx = source.angelica$getDynamicLightX() - cameraX;
|
||||
double dy = source.angelica$getDynamicLightY() - cameraY;
|
||||
double dz = source.angelica$getDynamicLightZ() - cameraZ;
|
||||
|
||||
double distSq = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
// Check if behind camera (dot product of direction to source with look direction)
|
||||
// Only use X and Z for horizontal check (more stable as player looks up/down)
|
||||
double dot = dx * lookDirX + dz * lookDirZ;
|
||||
boolean behindCamera = dot < 0;
|
||||
|
||||
// Behind camera always gets BACKGROUND mode
|
||||
if (behindCamera) {
|
||||
return AdaptiveTickMode.BACKGROUND;
|
||||
}
|
||||
|
||||
// Distance-based mode selection
|
||||
if (distSq > backgroundDistanceSq) {
|
||||
return AdaptiveTickMode.BACKGROUND;
|
||||
}
|
||||
if (distSq > slowerDistanceSq) {
|
||||
return AdaptiveTickMode.SLOWER;
|
||||
}
|
||||
if (distSq > slowDistanceSq) {
|
||||
return AdaptiveTickMode.SLOW;
|
||||
}
|
||||
|
||||
return AdaptiveTickMode.REAL_TIME;
|
||||
}
|
||||
|
||||
public static void setSlowDistance(int distance) {
|
||||
slowDistance = distance;
|
||||
slowDistanceSq = distance * distance;
|
||||
}
|
||||
|
||||
public static void setSlowerDistance(int distance) {
|
||||
slowerDistance = distance;
|
||||
slowerDistanceSq = distance * distance;
|
||||
}
|
||||
|
||||
public static void setBackgroundDistance(int distance) {
|
||||
backgroundDistance = distance;
|
||||
backgroundDistanceSq = distance * distance;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights;
|
||||
|
||||
/**
|
||||
* Defines tick modes for adaptive dynamic light updates. Distant or off-screen light sources update less frequently to save CPU.
|
||||
*/
|
||||
public enum AdaptiveTickMode {
|
||||
/** Updates every tick - for nearby, visible light sources */
|
||||
REAL_TIME(1),
|
||||
/** Updates every 5 ticks - for light sources at moderate distance */
|
||||
SLOW(5),
|
||||
/** Updates every 10 ticks - for light sources at far distance */
|
||||
SLOWER(10),
|
||||
/** Updates every 20 ticks - for light sources behind the camera or very far */
|
||||
BACKGROUND(20);
|
||||
|
||||
private final int delay;
|
||||
|
||||
AdaptiveTickMode(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public boolean shouldTickThisFrame(int worldTick, int sourceHash) {
|
||||
if (delay <= 1) {
|
||||
return true;
|
||||
}
|
||||
return (worldTick % delay) == Math.abs(sourceHash % delay);
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* Manages deferred chunk rebuilds for dynamic lights with optional frustum culling. Instead of scheduling chunk rebuilds immediately
|
||||
* when light sources move, this tracks pending rebuilds and only schedules visible chunks.
|
||||
*/
|
||||
public class ChunkRebuildManager {
|
||||
|
||||
/** Maps packed chunk position to ticks waiting */
|
||||
private final Long2IntOpenHashMap pendingRebuilds = new Long2IntOpenHashMap();
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
/** Maximum ticks a pending rebuild can wait before being forced through */
|
||||
private static int maxTicksWaiting = 100;
|
||||
|
||||
/** Reusable lists for processVisible - only used from render thread */
|
||||
private final LongArrayList candidates = new LongArrayList();
|
||||
private final LongArrayList toRebuild = new LongArrayList();
|
||||
private final LongArrayList toIncrement = new LongArrayList();
|
||||
|
||||
public static void setMaxTicksWaiting(int ticks) {
|
||||
maxTicksWaiting = Math.max(20, Math.min(ticks, 600)); // Clamp 1-30 seconds (20-600 ticks)
|
||||
}
|
||||
|
||||
public void requestRebuild(int x, int y, int z) {
|
||||
final long packed = CoordinatePacker.pack(x, y, z);
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// Reset wait counter if already pending, otherwise add new entry
|
||||
pendingRebuilds.put(packed, 0);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void processVisible(Viewport viewport, @NotNull IDynamicLightWorldRenderer renderer) {
|
||||
candidates.clear();
|
||||
toRebuild.clear();
|
||||
toIncrement.clear();
|
||||
|
||||
// Phase 1: Collect all pending chunks with read lock (fast)
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
if (pendingRebuilds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Collect candidates that might need processing
|
||||
final var iterator = pendingRebuilds.long2IntEntrySet().fastIterator();
|
||||
while (iterator.hasNext()) {
|
||||
final var entry = iterator.next();
|
||||
final long packed = entry.getLongKey();
|
||||
final int ticksWaiting = entry.getIntValue();
|
||||
|
||||
// Force process if no viewport or waited too long
|
||||
if (viewport == null || ticksWaiting >= maxTicksWaiting) {
|
||||
toRebuild.add(packed);
|
||||
} else {
|
||||
candidates.add(packed);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
// Phase 2: Check visibility
|
||||
for (int i = 0; i < candidates.size(); i++) {
|
||||
long packed = candidates.getLong(i);
|
||||
int x = CoordinatePacker.unpackX(packed);
|
||||
int y = CoordinatePacker.unpackY(packed);
|
||||
int z = CoordinatePacker.unpackZ(packed);
|
||||
|
||||
if (isChunkVisible(viewport, x, y, z)) {
|
||||
toRebuild.add(packed);
|
||||
} else {
|
||||
toIncrement.add(packed);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Update map with write lock (brief)
|
||||
if (!toRebuild.isEmpty() || !toIncrement.isEmpty()) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// Remove chunks we're going to rebuild
|
||||
LongIterator rebuildIter = toRebuild.iterator();
|
||||
while (rebuildIter.hasNext()) {
|
||||
pendingRebuilds.remove(rebuildIter.nextLong());
|
||||
}
|
||||
LongIterator incIter = toIncrement.iterator();
|
||||
while (incIter.hasNext()) {
|
||||
long packed = incIter.nextLong();
|
||||
int current = pendingRebuilds.get(packed);
|
||||
if (pendingRebuilds.containsKey(packed)) {
|
||||
pendingRebuilds.put(packed, current + 1);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4: Schedule rebuilds outside of lock
|
||||
for (int i = 0; i < toRebuild.size(); i++) {
|
||||
final long packed = toRebuild.getLong(i);
|
||||
renderer.scheduleRebuildForChunk(
|
||||
CoordinatePacker.unpackX(packed),
|
||||
CoordinatePacker.unpackY(packed),
|
||||
CoordinatePacker.unpackZ(packed),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isChunkVisible(Viewport viewport, int chunkX, int chunkY, int chunkZ) {
|
||||
// Convert chunk section coords to block coords
|
||||
int blockX = chunkX << 4;
|
||||
int blockY = chunkY << 4;
|
||||
int blockZ = chunkZ << 4;
|
||||
|
||||
// Check if the 16x16x16 chunk section box is visible
|
||||
return viewport.isBoxVisible(
|
||||
blockX, blockY, blockZ,
|
||||
blockX + 16, blockY + 16, blockZ + 16
|
||||
);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
pendingRebuilds.clear();
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getPendingCount() {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return pendingRebuilds.size();
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,54 +6,43 @@ import com.gtnewhorizon.gtnhlib.blockpos.IBlockPos;
|
||||
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
|
||||
import com.gtnewhorizons.angelica.api.IDynamicLightProducer;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.battlegear2.Battlegear2Compat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.PrimedEntityAccessor;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
|
||||
import mods.battlegear2.api.core.IBattlePlayer;
|
||||
import mods.battlegear2.api.core.IInventoryPlayerBattle;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.monster.EntityCreeper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xonin.backhand.api.core.BackhandUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class DynamicLights {
|
||||
private static DynamicLights instance;
|
||||
private static IDynamicLightWorldRenderer activeRenderer;
|
||||
public static DynamicLightsMode Mode = DynamicLightsMode.OFF;
|
||||
public static boolean ShaderForce = false;
|
||||
public static boolean FrustumCullingEnabled = true;
|
||||
public static boolean AdaptiveTickingEnabled = true;
|
||||
|
||||
public static final boolean configEnabled = AngelicaConfig.enableDynamicLights;
|
||||
|
||||
private static final double MAX_RADIUS = 7.75;
|
||||
private static final double MAX_RADIUS_SQUARED = MAX_RADIUS * MAX_RADIUS;
|
||||
private final Set<IDynamicLightSource> dynamicLightSources = new ObjectOpenHashSet<>();
|
||||
private final ReentrantReadWriteLock lightSourcesLock = new ReentrantReadWriteLock();
|
||||
private volatile int lightSourceCount = 0;
|
||||
private final ChunkRebuildManager chunkRebuildManager = new ChunkRebuildManager();
|
||||
private long lastUpdate = System.currentTimeMillis();
|
||||
private int lastUpdateCount = 0;
|
||||
|
||||
@@ -64,31 +53,19 @@ public class DynamicLights {
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return configEnabled &&
|
||||
return AngelicaConfig.enableDynamicLights &&
|
||||
Mode.isEnabled() &&
|
||||
// if shader force is enabled then true
|
||||
// if not, then true when shaders are not in use
|
||||
(ShaderForce || !IrisApi.getInstance().isShaderPackInUse());
|
||||
}
|
||||
|
||||
public boolean hasLightSources() {
|
||||
return lightSourceCount > 0;
|
||||
}
|
||||
|
||||
public static IDynamicLightWorldRenderer getActiveRenderer() {
|
||||
return activeRenderer;
|
||||
}
|
||||
|
||||
public static void setActiveRenderer(IDynamicLightWorldRenderer renderer) {
|
||||
activeRenderer = renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all light sources.
|
||||
*
|
||||
* @param renderer the renderer
|
||||
*/
|
||||
public void updateAll(@NotNull IDynamicLightWorldRenderer renderer) {
|
||||
public void updateAll(@NotNull SodiumWorldRenderer renderer) {
|
||||
if (!isEnabled())
|
||||
return;
|
||||
|
||||
@@ -97,43 +74,8 @@ public class DynamicLights {
|
||||
this.lastUpdate = now;
|
||||
this.lastUpdateCount = 0;
|
||||
|
||||
// Get camera info for adaptive ticking
|
||||
double cameraX = 0, cameraY = 0, cameraZ = 0;
|
||||
double lookDirX = 0, lookDirZ = 1;
|
||||
int worldTick = 0;
|
||||
|
||||
if (AdaptiveTickingEnabled) {
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc.theWorld != null && mc.renderViewEntity != null) {
|
||||
cameraX = mc.renderViewEntity.posX;
|
||||
cameraY = mc.renderViewEntity.posY + mc.renderViewEntity.getEyeHeight();
|
||||
cameraZ = mc.renderViewEntity.posZ;
|
||||
worldTick = (int) (mc.theWorld.getTotalWorldTime() & 0x7FFFFFFF);
|
||||
|
||||
// Calculate look direction from yaw (normalized horizontal direction)
|
||||
float yaw = mc.renderViewEntity.rotationYaw;
|
||||
double yawRad = Math.toRadians(yaw);
|
||||
lookDirX = -Math.sin(yawRad);
|
||||
lookDirZ = Math.cos(yawRad);
|
||||
}
|
||||
}
|
||||
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
for (var lightSource : this.dynamicLightSources) {
|
||||
// Entity type filter
|
||||
if (!EntityLightConfig.isEntityTypeEnabled(lightSource)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adaptive ticking filter
|
||||
if (AdaptiveTickingEnabled) {
|
||||
AdaptiveTickMode mode = AdaptiveTickCalculator.calculate(
|
||||
lightSource, cameraX, cameraY, cameraZ, lookDirX, lookDirZ);
|
||||
if (!mode.shouldTickThisFrame(worldTick, lightSource.hashCode())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (lightSource.angelica$updateDynamicLight(renderer))
|
||||
this.lastUpdateCount++;
|
||||
}
|
||||
@@ -147,9 +89,7 @@ public class DynamicLights {
|
||||
|
||||
public void addLightSource(IDynamicLightSource lightSource) {
|
||||
this.lightSourcesLock.writeLock().lock();
|
||||
if (this.dynamicLightSources.add(lightSource)) {
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
}
|
||||
this.dynamicLightSources.add(lightSource);
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
}
|
||||
|
||||
@@ -167,9 +107,8 @@ public class DynamicLights {
|
||||
it = dynamicLightSources.next();
|
||||
if (it.equals(lightSource)) {
|
||||
dynamicLightSources.remove();
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
if (activeRenderer != null)
|
||||
lightSource.angelica$scheduleTrackedChunksRebuild(activeRenderer);
|
||||
if (SodiumWorldRenderer.getInstance() != null)
|
||||
lightSource.angelica$scheduleTrackedChunksRebuild(SodiumWorldRenderer.getInstance());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -191,11 +130,10 @@ public class DynamicLights {
|
||||
it = dynamicLightSources.next();
|
||||
if (filter.test(it)) {
|
||||
dynamicLightSources.remove();
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
if (activeRenderer != null) {
|
||||
if (SodiumWorldRenderer.getInstance() != null) {
|
||||
if (it.angelica$getLuminance() > 0)
|
||||
it.angelica$resetDynamicLight();
|
||||
it.angelica$scheduleTrackedChunksRebuild(activeRenderer);
|
||||
it.angelica$scheduleTrackedChunksRebuild(SodiumWorldRenderer.getInstance());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -215,13 +153,12 @@ public class DynamicLights {
|
||||
while (dynamicLightSources.hasNext()) {
|
||||
it = dynamicLightSources.next();
|
||||
dynamicLightSources.remove();
|
||||
if (activeRenderer != null) {
|
||||
if (SodiumWorldRenderer.getInstance() != null) {
|
||||
if (it.angelica$getLuminance() > 0)
|
||||
it.angelica$resetDynamicLight();
|
||||
it.angelica$scheduleTrackedChunksRebuild(activeRenderer);
|
||||
it.angelica$scheduleTrackedChunksRebuild(SodiumWorldRenderer.getInstance());
|
||||
}
|
||||
}
|
||||
this.lightSourceCount = 0;
|
||||
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
}
|
||||
@@ -266,20 +203,6 @@ public class DynamicLights {
|
||||
return MathHelper.clamp_double(result, 0, 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets dynamic light level at an exact world position (for smooth per-vertex lighting).
|
||||
*/
|
||||
public double getDynamicLightLevel(double x, double y, double z) {
|
||||
double result = 0;
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
for (var lightSource : this.dynamicLightSources) {
|
||||
result = maxDynamicLightLevelExact(x, y, z, lightSource, result);
|
||||
}
|
||||
this.lightSourcesLock.readLock().unlock();
|
||||
|
||||
return MathHelper.clamp_double(result, 0, 15);
|
||||
}
|
||||
|
||||
public double getDynamicLightLevel(@NotNull BlockPos pos) {
|
||||
return this.getDynamicLightLevel(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
@@ -306,29 +229,6 @@ public class DynamicLights {
|
||||
return currentLightLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates dynamic light level at an exact position (no block center offset).
|
||||
* Used for smooth per-vertex lighting.
|
||||
*/
|
||||
public static double maxDynamicLightLevelExact(double x, double y, double z, @NotNull IDynamicLightSource lightSource, double currentLightLevel) {
|
||||
int luminance = lightSource.angelica$getLuminance();
|
||||
if (luminance > 0) {
|
||||
double dx = x - lightSource.angelica$getDynamicLightX();
|
||||
double dy = y - lightSource.angelica$getDynamicLightY();
|
||||
double dz = z - lightSource.angelica$getDynamicLightZ();
|
||||
|
||||
double distanceSquared = dx * dx + dy * dy + dz * dz;
|
||||
if (distanceSquared <= MAX_RADIUS_SQUARED) {
|
||||
double multiplier = 1.0 - Math.sqrt(distanceSquared) / MAX_RADIUS;
|
||||
double lightLevel = multiplier * (double) luminance;
|
||||
if (lightLevel > currentLightLevel) {
|
||||
return lightLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentLightLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dynamic light level generated by the light source at the specified position.
|
||||
*
|
||||
@@ -345,51 +245,6 @@ public class DynamicLights {
|
||||
return this.getLightmapWithDynamicLight(this.getDynamicLightLevel(x, y, z), lightmap);
|
||||
}
|
||||
|
||||
public List<IDynamicLightSource> getSourcesForChunk(int chunkMinX, int chunkMinY, int chunkMinZ) {
|
||||
// Chunk center
|
||||
final double centerX = chunkMinX + 8.0;
|
||||
final double centerY = chunkMinY + 8.0;
|
||||
final double centerZ = chunkMinZ + 8.0;
|
||||
|
||||
// Max distance from chunk center to any corner is sqrt(3) * 8 ≈ 13.86
|
||||
// Add MAX_RADIUS to get the search radius (13.86 + 7.75)^2 ≈ 467
|
||||
final double searchRadiusSq = 470.0; // Slightly padded for safety
|
||||
|
||||
List<IDynamicLightSource> result = null;
|
||||
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
try {
|
||||
for (IDynamicLightSource source : this.dynamicLightSources) {
|
||||
double dx = source.angelica$getDynamicLightX() - centerX;
|
||||
double dy = source.angelica$getDynamicLightY() - centerY;
|
||||
double dz = source.angelica$getDynamicLightZ() - centerZ;
|
||||
double distSq = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
if (distSq <= searchRadiusSq) {
|
||||
if (result == null) {
|
||||
result = new ArrayList<>();
|
||||
}
|
||||
result.add(source);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lightSourcesLock.readLock().unlock();
|
||||
}
|
||||
|
||||
return result != null ? result : Collections.emptyList();
|
||||
}
|
||||
|
||||
public double getDynamicLightLevelFromSources(double x, double y, double z, List<IDynamicLightSource> sources) {
|
||||
if (sources == null || sources.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
double result = 0;
|
||||
for (int i = 0, size = sources.size(); i < size; i++) {
|
||||
result = maxDynamicLightLevelExact(x, y, z, sources.get(i), result);
|
||||
}
|
||||
return MathHelper.clamp_double(result, 0, 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lightmap with combined light levels.
|
||||
*
|
||||
@@ -420,7 +275,7 @@ public class DynamicLights {
|
||||
* @param renderer the renderer
|
||||
* @param chunkPos the chunk position
|
||||
*/
|
||||
public static void scheduleChunkRebuild(@NotNull IDynamicLightWorldRenderer renderer, @NotNull IBlockPos chunkPos) {
|
||||
public static void scheduleChunkRebuild(@NotNull SodiumWorldRenderer renderer, @NotNull IBlockPos chunkPos) {
|
||||
scheduleChunkRebuild(renderer, chunkPos.getX(), chunkPos.getY(), chunkPos.getZ());
|
||||
}
|
||||
|
||||
@@ -430,37 +285,14 @@ public class DynamicLights {
|
||||
* @param renderer the renderer
|
||||
* @param chunkPos the packed chunk position
|
||||
*/
|
||||
public static void scheduleChunkRebuild(@NotNull IDynamicLightWorldRenderer renderer, long chunkPos) {
|
||||
public static void scheduleChunkRebuild(@NotNull SodiumWorldRenderer renderer, long chunkPos) {
|
||||
scheduleChunkRebuild(renderer, CoordinatePacker.unpackX(chunkPos), CoordinatePacker.unpackY(chunkPos), CoordinatePacker.unpackZ(chunkPos));
|
||||
}
|
||||
|
||||
public static void scheduleChunkRebuild(@NotNull IDynamicLightWorldRenderer renderer, int x, int y, int z) {
|
||||
if (FrustumCullingEnabled && instance != null) {
|
||||
instance.chunkRebuildManager.requestRebuild(x, y, z);
|
||||
} else {
|
||||
renderer.scheduleRebuildForChunk(x, y, z, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void scheduleChunkRebuildForRemoval(@NotNull IDynamicLightWorldRenderer renderer, int x, int y, int z) {
|
||||
public static void scheduleChunkRebuild(@NotNull SodiumWorldRenderer renderer, int x, int y, int z) {
|
||||
renderer.scheduleRebuildForChunk(x, y, z, false);
|
||||
}
|
||||
|
||||
public static void scheduleChunkRebuildForRemoval(@NotNull IDynamicLightWorldRenderer renderer, long chunkPos) {
|
||||
scheduleChunkRebuildForRemoval(renderer, CoordinatePacker.unpackX(chunkPos), CoordinatePacker.unpackY(chunkPos), CoordinatePacker.unpackZ(chunkPos));
|
||||
}
|
||||
|
||||
public void processChunkRebuilds(@Nullable Viewport viewport) {
|
||||
if (activeRenderer == null) {
|
||||
return;
|
||||
}
|
||||
chunkRebuildManager.processVisible(viewport, activeRenderer);
|
||||
}
|
||||
|
||||
public ChunkRebuildManager getChunkRebuildManager() {
|
||||
return chunkRebuildManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the tracked chunk sets.
|
||||
*
|
||||
@@ -501,7 +333,10 @@ public class DynamicLights {
|
||||
* @param submergedInWater {@code true} if the stack is submerged in water, else {@code false}
|
||||
* @return the luminance of the item
|
||||
*/
|
||||
public static int getLuminanceFromItemStack(@NotNull ItemStack stack) {
|
||||
public static int getLuminanceFromItemStack(@NotNull ItemStack stack, boolean submergedInWater) {
|
||||
// TODO only have certain items not glow in water?
|
||||
if (submergedInWater) return 0;
|
||||
|
||||
Item item = stack.getItem();
|
||||
if (item instanceof ItemBlock itemBlock) {
|
||||
Block block = itemBlock.field_150939_a;
|
||||
@@ -519,53 +354,49 @@ public class DynamicLights {
|
||||
|
||||
public static int getLuminanceFromEntity(@NotNull Entity entity) {
|
||||
|
||||
if (entity.isBurning()) return 15;
|
||||
|
||||
boolean inWater = entity.isInsideOfMaterial(Material.water);
|
||||
|
||||
// TODO only have certain items not glow in water?
|
||||
if (inWater) return 0;
|
||||
if (entity.fire > 0) return 15;
|
||||
|
||||
if (entity instanceof EntityItem item) {
|
||||
return getLuminanceFromItemStack(item.getEntityItem());
|
||||
return getLuminanceFromItemStack(item.getEntityItem(), item.isInsideOfMaterial(Material.water));
|
||||
}
|
||||
|
||||
int luminance = 0;
|
||||
if (entity instanceof EntityLivingBase living) {
|
||||
int luminance = 0;
|
||||
|
||||
boolean inWater = living.isInsideOfMaterial(Material.water);
|
||||
|
||||
// check equipment + hand for light (should work for all entities)
|
||||
ItemStack itemStack;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if ((itemStack = living.getEquipmentInSlot(i)) != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(itemStack));
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(itemStack, inWater));
|
||||
}
|
||||
}
|
||||
|
||||
if (living instanceof EntityPlayer player) {
|
||||
if (ModStatus.isBattlegearLoaded &&
|
||||
player instanceof IBattlePlayer battlePlayer &&
|
||||
battlePlayer.battlegear2$isBattlemode()
|
||||
) {
|
||||
ItemStack offhand = Battlegear2Compat.getBattlegear2Offhand(player);
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand));
|
||||
}
|
||||
if (ModStatus.isBattlegearLoaded &&
|
||||
living instanceof EntityPlayer player &&
|
||||
player instanceof IBattlePlayer battlePlayer &&
|
||||
battlePlayer.battlegear2$isBattlemode()
|
||||
) {
|
||||
ItemStack offhand = ((IInventoryPlayerBattle) player.inventory).battlegear2$getCurrentOffhandWeapon();
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand, inWater));
|
||||
}
|
||||
else if (ModStatus.isBackhandLoaded){
|
||||
ItemStack offhand = ModStatus.backhandCompat.getOffhandItem(player);
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand));
|
||||
}
|
||||
}
|
||||
else if (ModStatus.isBackhandLoaded && living instanceof EntityPlayer player){
|
||||
ItemStack offhand = BackhandUtils.getOffhandItem(player);
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand, inWater));
|
||||
}
|
||||
}
|
||||
|
||||
if (ModStatus.isBaublesLoaded){
|
||||
var playerBaubles = PlayerHandler.getPlayerBaubles(player);
|
||||
if (playerBaubles != null){
|
||||
for (int i = 0; i < playerBaubles.getSizeInventory(); i++){
|
||||
var stack = playerBaubles.getStackInSlot(i);
|
||||
if (stack != null){
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(stack));
|
||||
}
|
||||
if (ModStatus.isBaublesLoaded && living instanceof EntityPlayer player){
|
||||
var playerBaubles = PlayerHandler.getPlayerBaubles(player);
|
||||
if (playerBaubles != null){
|
||||
for (int i = 0; i < playerBaubles.getSizeInventory(); i++){
|
||||
var stack = playerBaubles.getStackInSlot(i);
|
||||
if (stack != null){
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(stack, inWater));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -575,10 +406,7 @@ public class DynamicLights {
|
||||
}
|
||||
|
||||
// TODO: Creepers? TNT? Dusts?
|
||||
if (entity instanceof PrimedEntityAccessor primedEntity) {
|
||||
luminance = Math.max(luminance, primedEntity.angelica$getLuminance());
|
||||
}
|
||||
|
||||
return luminance;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -26,13 +27,11 @@ public interface IDynamicLightSource {
|
||||
double angelica$getDynamicLightZ();
|
||||
|
||||
/**
|
||||
* Returns the dynamic light source world renderer.
|
||||
* Returns the dynamic light source world.
|
||||
*
|
||||
* @return the world renderer instance
|
||||
* @return the world instance
|
||||
*/
|
||||
default IDynamicLightWorldRenderer angelica$getDynamicLightWorld() {
|
||||
return DynamicLights.getActiveRenderer();
|
||||
}
|
||||
SodiumWorldRenderer angelica$getDynamicLightWorld();
|
||||
|
||||
/**
|
||||
* Returns whether the dynamic light is enabled or not.
|
||||
@@ -73,9 +72,8 @@ public interface IDynamicLightSource {
|
||||
* Executed at each tick.
|
||||
*/
|
||||
void angelica$dynamicLightTick();
|
||||
default void angelica$updateLights() {}
|
||||
|
||||
boolean angelica$updateDynamicLight(@NotNull IDynamicLightWorldRenderer renderer);
|
||||
boolean angelica$updateDynamicLight(@NotNull SodiumWorldRenderer renderer);
|
||||
|
||||
void angelica$scheduleTrackedChunksRebuild(@NotNull IDynamicLightWorldRenderer renderer);
|
||||
void angelica$scheduleTrackedChunksRebuild(@NotNull SodiumWorldRenderer renderer);
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights;
|
||||
|
||||
/**
|
||||
* Common interface for world renderers that support dynamic lights chunk rebuilds.
|
||||
* Implemented by CeleritasWorldRenderer.
|
||||
*/
|
||||
public interface IDynamicLightWorldRenderer {
|
||||
/**
|
||||
* Schedules a chunk rebuild for the render section at the given chunk coordinates.
|
||||
*/
|
||||
void scheduleRebuildForChunk(int x, int y, int z, boolean important);
|
||||
|
||||
/**
|
||||
* Returns whether this renderer is currently active (has a world loaded).
|
||||
*/
|
||||
boolean isActive();
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights.config;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.concurrent.cas.CasMap;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightSource;
|
||||
import cpw.mods.fml.common.registry.EntityRegistry;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityList;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class EntityLightConfig {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica/DynamicLights");
|
||||
|
||||
private static final Set<String> disabledClassNames = ConcurrentHashMap.newKeySet();
|
||||
private static final CasMap<Class<?>, Boolean> enabledCache = new CasMap<>();
|
||||
|
||||
private static File configFile;
|
||||
private static boolean initialized = false;
|
||||
|
||||
private EntityLightConfig() {}
|
||||
|
||||
public static void init(@NotNull File configDir) {
|
||||
if (initialized) return;
|
||||
|
||||
configFile = new File(configDir, "angelica-dynamiclights-entities.txt");
|
||||
load();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public static boolean isEntityTypeEnabled(@NotNull IDynamicLightSource source) {
|
||||
if (!initialized) return true;
|
||||
return isEntityTypeEnabled(source.getClass());
|
||||
}
|
||||
|
||||
public static boolean isEntityTypeEnabled(@NotNull Class<?> entityClass) {
|
||||
if (!initialized) return true;
|
||||
|
||||
Boolean cached = enabledCache.get(entityClass);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
final boolean enabled = !disabledClassNames.contains(entityClass.getName());
|
||||
Boolean existing = enabledCache.putIfAbsent(entityClass, enabled);
|
||||
return existing != null ? existing : enabled;
|
||||
}
|
||||
|
||||
public static void setEntityTypeEnabled(@NotNull Class<?> entityClass, boolean enabled) {
|
||||
enabledCache.put(entityClass, enabled);
|
||||
|
||||
final String className = entityClass.getName();
|
||||
if (enabled) {
|
||||
disabledClassNames.remove(className);
|
||||
} else {
|
||||
disabledClassNames.add(className);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<EntityTypeEntry> getAllEntityTypes() {
|
||||
List<EntityTypeEntry> entries = new ArrayList<>();
|
||||
Set<String> seenClasses = new HashSet<>();
|
||||
|
||||
Map<String, Class<? extends Entity>> stringToClass = EntityList.stringToClassMapping;
|
||||
|
||||
for (Map.Entry<String, Class<? extends Entity>> entry : stringToClass.entrySet()) {
|
||||
String entityId = entry.getKey();
|
||||
Class<?> entityClass = entry.getValue();
|
||||
String className = entityClass.getName();
|
||||
|
||||
if (seenClasses.contains(className)) continue;
|
||||
seenClasses.add(className);
|
||||
|
||||
String modId = getModIdForEntity(entityClass, entityId);
|
||||
String displayName = getDisplayName(entityId, entityClass);
|
||||
boolean enabled = !disabledClassNames.contains(className);
|
||||
|
||||
entries.add(new EntityTypeEntry(displayName, className, modId, enabled));
|
||||
}
|
||||
|
||||
// Sort by mod ID, then by display name
|
||||
entries.sort((a, b) -> {
|
||||
int modCompare = a.getModId().compareToIgnoreCase(b.getModId());
|
||||
if (modCompare != 0) return modCompare;
|
||||
return a.getDisplayName().compareToIgnoreCase(b.getDisplayName());
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static String getModIdForEntity(Class<?> entityClass, String entityId) {
|
||||
// Check EntityRegistry for mod entity entries
|
||||
if (Entity.class.isAssignableFrom(entityClass)) {
|
||||
EntityRegistry.EntityRegistration registration = EntityRegistry.instance()
|
||||
.lookupModSpawn((Class<? extends Entity>) entityClass, false);
|
||||
if (registration != null && registration.getContainer() != null) {
|
||||
return registration.getContainer().getModId();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's a vanilla entity (no dot in entity ID)
|
||||
if (!entityId.contains(".")) {
|
||||
return "minecraft";
|
||||
}
|
||||
|
||||
// Extract mod ID from entity ID (format: modid.EntityName)
|
||||
int dotIndex = entityId.indexOf('.');
|
||||
if (dotIndex > 0) {
|
||||
return entityId.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static String getDisplayName(String entityId, Class<?> entityClass) {
|
||||
// Use the simple class name, removing package
|
||||
String simpleName = entityClass.getSimpleName();
|
||||
if (simpleName.isEmpty()) {
|
||||
// Anonymous class, use entity ID
|
||||
return entityId;
|
||||
}
|
||||
return simpleName;
|
||||
}
|
||||
|
||||
public static void load() {
|
||||
if (configFile == null || !configFile.exists()) return;
|
||||
|
||||
disabledClassNames.clear();
|
||||
enabledCache.clear();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty() || line.startsWith("#")) continue;
|
||||
|
||||
// Lines are entity class names that are DISABLED
|
||||
disabledClassNames.add(line);
|
||||
}
|
||||
LOGGER.info("Loaded {} disabled entity types from config", disabledClassNames.size());
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Failed to load entity light config: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void save() {
|
||||
if (configFile == null) return;
|
||||
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(configFile))) {
|
||||
writer.write("# Angelica Dynamic Lights - Disabled Entity Types");
|
||||
writer.newLine();
|
||||
writer.write("# Each line is a fully qualified class name of an entity type that is DISABLED.");
|
||||
writer.newLine();
|
||||
writer.write("# Remove a line to re-enable dynamic lighting for that entity type.");
|
||||
writer.newLine();
|
||||
writer.newLine();
|
||||
|
||||
List<String> sorted = new ArrayList<>(disabledClassNames);
|
||||
Collections.sort(sorted);
|
||||
|
||||
for (String className : sorted) {
|
||||
writer.write(className);
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
LOGGER.info("Saved {} disabled entity types to config", disabledClassNames.size());
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Failed to save entity light config: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights.config;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
|
||||
|
||||
public class EntityLightConfigStorage implements OptionStorage<EntityLightConfigStorage> {
|
||||
|
||||
public static final EntityLightConfigStorage INSTANCE = new EntityLightConfigStorage();
|
||||
|
||||
private EntityLightConfigStorage() {}
|
||||
|
||||
@Override
|
||||
public EntityLightConfigStorage getData() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
EntityLightConfig.save();
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights.config;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EntityTypeEntry {
|
||||
private final String displayName;
|
||||
private final String className;
|
||||
private final String modId;
|
||||
private boolean enabled;
|
||||
|
||||
public EntityTypeEntry(@NotNull String displayName, @NotNull String className, @NotNull String modId, boolean enabled) {
|
||||
this.displayName = displayName;
|
||||
this.className = className;
|
||||
this.modId = modId;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getModId() {
|
||||
return modId;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayName + " (" + modId + ") [" + (enabled ? "ON" : "OFF") + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.embeddedt.embeddium.impl.render.chunk.fog.FogService;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.shader.ChunkFogMode;
|
||||
|
||||
public class AngelicaFogService implements FogService {
|
||||
private final float[] fogColorArray = new float[4];
|
||||
|
||||
@Override
|
||||
public float getFogEnd() {
|
||||
return GLStateManager.getFogState().getEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFogStart() {
|
||||
return GLStateManager.getFogState().getStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFogDensity() {
|
||||
return GLStateManager.getFogState().getDensity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFogShapeIndex() {
|
||||
return 0; // 1.7.10 only has one fog shape
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFogCutoff() {
|
||||
return getFogEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getFogColor() {
|
||||
final var fogState = GLStateManager.getFogState();
|
||||
final var color = fogState.getFogColor();
|
||||
fogColorArray[0] = (float) color.x;
|
||||
fogColorArray[1] = (float) color.y;
|
||||
fogColorArray[2] = (float) color.z;
|
||||
fogColorArray[3] = fogState.getFogAlpha();
|
||||
return fogColorArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkFogMode getFogMode() {
|
||||
if (!GLStateManager.getFogMode().isEnabled()) {
|
||||
return ChunkFogMode.NONE;
|
||||
}
|
||||
return ChunkFogMode.fromGLMode(GLStateManager.getFogState().getFogMode());
|
||||
}
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
|
||||
import com.mitchej123.glsm.GLStateManagerService;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class AngelicaGLStateManagerService implements GLStateManagerService {
|
||||
|
||||
private IntBuffer intArrayBuffer = BufferUtils.createIntBuffer(16);
|
||||
|
||||
private IntBuffer getIntArrayBuffer(int size) {
|
||||
if (intArrayBuffer.capacity() < size) {
|
||||
intArrayBuffer = BufferUtils.createIntBuffer(size);
|
||||
}
|
||||
intArrayBuffer.clear();
|
||||
return intArrayBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
// ===================== QUERY OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public int glGetInteger(int pname) {
|
||||
return GLStateManager.glGetInteger(pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String glGetString(int pname) {
|
||||
return GL11.glGetString(pname);
|
||||
}
|
||||
|
||||
// ===================== FRAMEBUFFER OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public int glGenFramebuffers() {
|
||||
return GL30.glGenFramebuffers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteFramebuffers(int framebuffer) {
|
||||
GL30.glDeleteFramebuffers(framebuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glBindFramebuffer(int target, int framebuffer) {
|
||||
GL30.glBindFramebuffer(target, framebuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int glCheckFramebufferStatus(int target) {
|
||||
return GL30.glCheckFramebufferStatus(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
|
||||
GL30.glFramebufferTexture2D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
// ===================== SHADER OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public int glCreateShader(int type) {
|
||||
return GL20.glCreateShader(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glCompileShader(int shader) {
|
||||
GL20.glCompileShader(shader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int glGetShaderi(int shader, int pname) {
|
||||
return GL20.glGetShaderi(shader, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String glGetShaderInfoLog(int shader, int maxLength) {
|
||||
return GL20.glGetShaderInfoLog(shader, maxLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteShader(int shader) {
|
||||
GL20.glDeleteShader(shader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int glCreateProgram() {
|
||||
return GL20.glCreateProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glAttachShader(int program, int shader) {
|
||||
GL20.glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glLinkProgram(int program) {
|
||||
GL20.glLinkProgram(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int glGetProgrami(int program, int pname) {
|
||||
return GL20.glGetProgrami(program, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glUseProgram(int program) {
|
||||
GLStateManager.glUseProgram(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteProgram(int program) {
|
||||
GL20.glDeleteProgram(program);
|
||||
}
|
||||
|
||||
// ===================== UNIFORM/ATTRIBUTE OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public int glGetUniformLocation(int program, CharSequence name) {
|
||||
return GL20.glGetUniformLocation(program, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glUniform1i(int location, int value) {
|
||||
GL20.glUniform1i(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int glGetAttribLocation(int program, CharSequence name) {
|
||||
return GL20.glGetAttribLocation(program, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glBindAttribLocation(int program, int index, CharSequence name) {
|
||||
GL20.glBindAttribLocation(program, index, name);
|
||||
}
|
||||
|
||||
// ===================== VAO OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public int glGenVertexArrays() {
|
||||
return UniversalVAO.genVertexArrays();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glBindVertexArray(int array) {
|
||||
GLStateManager.glBindVertexArray(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteVertexArrays(int array) {
|
||||
UniversalVAO.deleteVertexArrays(array);
|
||||
}
|
||||
|
||||
// ===================== BUFFER OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public int glGenBuffers() {
|
||||
return GL15.glGenBuffers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glBindBuffer(int target, int buffer) {
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteBuffers(int buffer) {
|
||||
GL15.glDeleteBuffers(buffer);
|
||||
}
|
||||
|
||||
// ===================== TEXTURE OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public int glGenTextures() {
|
||||
return GL11.glGenTextures();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glGenTextures(int[] textures) {
|
||||
IntBuffer buffer = getIntArrayBuffer(textures.length);
|
||||
GL11.glGenTextures(buffer);
|
||||
buffer.get(textures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteTextures(int texture) {
|
||||
GLStateManager.glDeleteTextures(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteTextures(int[] textures) {
|
||||
IntBuffer buffer = getIntArrayBuffer(textures.length);
|
||||
buffer.put(textures);
|
||||
buffer.flip();
|
||||
GLStateManager.glDeleteTextures(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glActiveTexture(int texture) {
|
||||
GLStateManager.glActiveTexture(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int glGetTexLevelParameteri(int target, int level, int pname) {
|
||||
return GLStateManager.glGetTexLevelParameteri(target, level, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int glGetTexLevelParameter(int target, int level, int pname) {
|
||||
return GLStateManager.glGetTexLevelParameteri(target, level, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, int width, int height) {
|
||||
GL11.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glPixelStorei(int pname, int param) {
|
||||
GLStateManager.glPixelStorei(pname, param);
|
||||
}
|
||||
|
||||
// ===================== STATE OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public void enableCullFace() {
|
||||
GLStateManager.enableCull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCullFace() {
|
||||
GLStateManager.disableCull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableBlend() {
|
||||
GLStateManager.enableBlend();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableBlend() {
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
GLStateManager.tryBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableDepthTest() {
|
||||
GLStateManager.enableDepthTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableDepthTest() {
|
||||
GLStateManager.disableDepthTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDepthFunc(int func) {
|
||||
GLStateManager.glDepthFunc(func);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDepthMask(boolean flag) {
|
||||
GLStateManager.glDepthMask(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glViewport(int x, int y, int width, int height) {
|
||||
GLStateManager.glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
|
||||
GLStateManager.glColorMask(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glClearColor(float red, float green, float blue, float alpha) {
|
||||
GLStateManager.glClearColor(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glClear(int mask) {
|
||||
GLStateManager.glClear(mask);
|
||||
}
|
||||
|
||||
// ===================== MOJANG ADDITIONS =====================
|
||||
|
||||
@Override
|
||||
public void clear(int mask, boolean checkError) {
|
||||
GLStateManager.glClear(mask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTexture(int texture) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveTexture() {
|
||||
return GLStateManager.getActiveTextureUnit() + GL13.GL_TEXTURE0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveTextureAccessor() {
|
||||
return GLStateManager.getActiveTextureUnit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBoundTexture(int internalUnit) {
|
||||
return GLStateManager.getBoundTextureForServerState(internalUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveBoundTexture() {
|
||||
return GLStateManager.getBoundTextureForServerState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewportWidth() {
|
||||
return GLStateManager.getViewportState().width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewportHeight() {
|
||||
return GLStateManager.getViewportState().height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDepthStateMask() {
|
||||
return GLStateManager.getDepthState().isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlendEnabled() {
|
||||
return GLStateManager.getBlendMode().isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoundTexture(int unit, int texture) {
|
||||
int currentUnit = GLStateManager.getActiveTextureUnit();
|
||||
if (currentUnit != unit) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
}
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
if (currentUnit != unit) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + currentUnit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextureBinding(int unit) {
|
||||
return GLStateManager.getBoundTextureForServerState(unit);
|
||||
}
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.mitchej123.glsm.RenderSystemService;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public class AngelicaRenderSystemService implements RenderSystemService {
|
||||
|
||||
// Shader state (Mojang-specific, tracked here)
|
||||
private final int[] shaderTextures = new int[16];
|
||||
private final float[] shaderColor = {1f, 1f, 1f, 1f};
|
||||
private final float[] shaderFogColor = {0f, 0f, 0f, 1f};
|
||||
private float shaderFogStart = 0f;
|
||||
private float shaderFogEnd = 0f;
|
||||
private int fogShape = 0;
|
||||
private float shaderLineWidth = 1f;
|
||||
private final Matrix4f projectionMatrix = new Matrix4f();
|
||||
private final FloatBuffer projectionMatrixBuffer = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
// ===================== TEXTURE OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public void glActiveTexture(int texture) {
|
||||
GLStateManager.glActiveTexture(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTexture(int texture) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
// ===================== STATE OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public void enableCullFace() {
|
||||
GLStateManager.enableCull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCullFace() {
|
||||
GLStateManager.disableCull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableBlend() {
|
||||
GLStateManager.enableBlend();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableBlend() {
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultBlendFunc() {
|
||||
GLStateManager.defaultBlendFunc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
GLStateManager.tryBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnknownBlendState() {
|
||||
// No-op: Angelica tracks blend state accurately
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableDepthTest() {
|
||||
GLStateManager.enableDepthTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableDepthTest() {
|
||||
GLStateManager.disableDepthTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void depthFunc(int depthFunc) {
|
||||
GLStateManager.glDepthFunc(depthFunc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void depthMask(boolean flag) {
|
||||
GLStateManager.glDepthMask(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glViewport(int x, int y, int width, int height) {
|
||||
GLStateManager.glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glClearColor(float red, float green, float blue, float alpha) {
|
||||
GLStateManager.glClearColor(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(int mask, boolean checkError) {
|
||||
GLStateManager.glClear(mask);
|
||||
}
|
||||
|
||||
// ===================== UNIFORM OPERATIONS =====================
|
||||
|
||||
@Override
|
||||
public void glUniform1i(int location, int value) {
|
||||
GL20.glUniform1i(location, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glUniformMatrix3(int location, boolean transpose, FloatBuffer value) {
|
||||
GL20.glUniformMatrix3(location, transpose, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glUniformMatrix4(int location, boolean transpose, FloatBuffer value) {
|
||||
GL20.glUniformMatrix4(location, transpose, value);
|
||||
}
|
||||
|
||||
// ===================== THREAD ASSERTIONS =====================
|
||||
|
||||
@Override
|
||||
public void assertOnRenderThread() {
|
||||
// No-op: 1.7.10 is single-threaded for rendering
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertOnRenderThreadOrInit() {
|
||||
// No-op: 1.7.10 is single-threaded for rendering
|
||||
}
|
||||
|
||||
// ===================== SHADER STATE =====================
|
||||
|
||||
@Override
|
||||
public void setShaderTexture(int shaderTexture, int textureId) {
|
||||
if (shaderTexture >= 0 && shaderTexture < 16) {
|
||||
shaderTextures[shaderTexture] = textureId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShaderTexture(int shaderTexture) {
|
||||
return (shaderTexture >= 0 && shaderTexture < 16) ? shaderTextures[shaderTexture] : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShaderColor(float red, float green, float blue, float alpha) {
|
||||
shaderColor[0] = red;
|
||||
shaderColor[1] = green;
|
||||
shaderColor[2] = blue;
|
||||
shaderColor[3] = alpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getShaderColor() {
|
||||
return shaderColor.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShaderLineWidth(float lineWidth) {
|
||||
shaderLineWidth = lineWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShaderLineWidth() {
|
||||
return shaderLineWidth;
|
||||
}
|
||||
|
||||
// ===================== FOG STATE =====================
|
||||
|
||||
@Override
|
||||
public void setShaderFogColor(float red, float green, float blue, float alpha) {
|
||||
shaderFogColor[0] = red;
|
||||
shaderFogColor[1] = green;
|
||||
shaderFogColor[2] = blue;
|
||||
shaderFogColor[3] = alpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getShaderFogColor() {
|
||||
return shaderFogColor.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShaderFogStart(float start) {
|
||||
shaderFogStart = start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShaderFogStart() {
|
||||
return shaderFogStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShaderFogEnd(float end) {
|
||||
shaderFogEnd = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShaderFogEnd() {
|
||||
return shaderFogEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFogShape(int shape) {
|
||||
fogShape = shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFogShape() {
|
||||
return fogShape;
|
||||
}
|
||||
|
||||
// ===================== PROJECTION MATRIX =====================
|
||||
|
||||
@Override
|
||||
public Matrix4f getProjectionMatrix() {
|
||||
return new Matrix4f(projectionMatrix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectionMatrixOrth(Matrix4f m) {
|
||||
projectionMatrix.set(m);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
projectionMatrix.get(projectionMatrixBuffer);
|
||||
projectionMatrixBuffer.rewind();
|
||||
GLStateManager.glLoadMatrix(projectionMatrixBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectionMatrixOrigin(Matrix4f m) {
|
||||
setProjectionMatrixOrth(m);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.AccumulatedDraw;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandBufferProcessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builds final command buffers from raw command buffers and compiled VBO data.
|
||||
* Bridges the gap between VBO compilation (glsm types) and buffer processing (recording types).
|
||||
*/
|
||||
public final class CommandBufferBuilder {
|
||||
|
||||
private CommandBufferBuilder() {}
|
||||
|
||||
/**
|
||||
* Build commands with transform collapsing optimization.
|
||||
* Accumulates MODELVIEW transforms and emits MultMatrix at barriers (draws, CallList).
|
||||
* Uses mergedRanges where consecutive same-transform draws are combined.
|
||||
*/
|
||||
public static void buildFromRawBuffer(
|
||||
CommandBuffer rawBuffer,
|
||||
List<AccumulatedDraw> accumulatedDraws,
|
||||
CommandBuffer finalBuffer) {
|
||||
|
||||
rawBuffer.resetRead();
|
||||
int cmdIndex = 0;
|
||||
int rangeIndex = 0;
|
||||
|
||||
AccumulatedDraw draw;
|
||||
while (rawBuffer.hasRemaining()) {
|
||||
// Read and process the command
|
||||
CommandBufferProcessor.copyCommand(rawBuffer, finalBuffer);
|
||||
|
||||
// Emit draw ranges at this command position
|
||||
while (rangeIndex < accumulatedDraws.size() && (draw = accumulatedDraws.get(rangeIndex)).commandIndex == cmdIndex) {
|
||||
emitDrawRangeToBuffer(draw, finalBuffer, rangeIndex);
|
||||
rangeIndex++;
|
||||
}
|
||||
|
||||
cmdIndex++;
|
||||
}
|
||||
|
||||
while (rangeIndex < accumulatedDraws.size()) {
|
||||
emitDrawRangeToBuffer(accumulatedDraws.get(rangeIndex), finalBuffer, rangeIndex);
|
||||
rangeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a draw range to the output buffer, with transform if needed.
|
||||
*/
|
||||
private static void emitDrawRangeToBuffer(
|
||||
AccumulatedDraw draw,
|
||||
CommandBuffer out,
|
||||
int vboIdx) {
|
||||
|
||||
if (draw.restoreData != null) {
|
||||
out.writeDrawRangeRestore(vboIdx, draw.restoreData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the draw range command
|
||||
out.writeDrawRange(vboIdx);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -2,17 +2,14 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -22,66 +19,24 @@ public class Feature {
|
||||
GL13.GL_MULTISAMPLE_BIT, GL11.GL_PIXEL_MODE_BIT, GL11.GL_POINT_BIT, GL11.GL_POLYGON_BIT, GL11.GL_POLYGON_STIPPLE_BIT, GL11.GL_SCISSOR_BIT,
|
||||
GL11.GL_STENCIL_BUFFER_BIT, GL11.GL_TEXTURE_BIT, GL11.GL_TRANSFORM_BIT, GL11.GL_VIEWPORT_BIT };
|
||||
|
||||
static final Int2ObjectMap<List<IStateStack<?>>> maskToFeaturesMap = new Int2ObjectOpenHashMap<>();
|
||||
static final Int2ObjectMap<IStateStack<?>[]> maskToNonBooleanStacksMap = new Int2ObjectOpenHashMap<>();
|
||||
static final Int2ObjectMap<Set<IStateStack<?>>> maskToFeaturesMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static List<IStateStack<?>> maskToFeatures(int mask) {
|
||||
static Set<IStateStack<?>> maskToFeatures(int mask) {
|
||||
if(maskToFeaturesMap.containsKey(mask)) {
|
||||
return maskToFeaturesMap.get(mask);
|
||||
}
|
||||
|
||||
final Set<IStateStack<?>> features = new HashSet<>();
|
||||
|
||||
for(int attrib : Feature.supportedAttribs) {
|
||||
if((mask & attrib) == attrib) {
|
||||
features.addAll(getFeatures(attrib));
|
||||
}
|
||||
}
|
||||
|
||||
final List<IStateStack<?>> asList = new ArrayList<>(features);
|
||||
|
||||
maskToFeaturesMap.put(mask, asList);
|
||||
return asList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only non-BooleanStateStack instances for the given mask.
|
||||
* These use traditional push/pop without global depth tracking.
|
||||
*/
|
||||
static IStateStack<?>[] maskToNonBooleanStacks(int mask) {
|
||||
IStateStack<?>[] cached = maskToNonBooleanStacksMap.get(mask);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
final List<IStateStack<?>> all = maskToFeatures(mask);
|
||||
final List<IStateStack<?>> nonBooleans = new ArrayList<>();
|
||||
for (int i = 0; i < all.size(); i++) {
|
||||
final IStateStack<?> stack = all.get(i);
|
||||
if (!(stack instanceof BooleanStateStack)) {
|
||||
nonBooleans.add(stack);
|
||||
}
|
||||
}
|
||||
cached = nonBooleans.toArray(new IStateStack<?>[0]);
|
||||
maskToNonBooleanStacksMap.put(mask, cached);
|
||||
return cached;
|
||||
maskToFeaturesMap.put(mask, features);
|
||||
return features;
|
||||
}
|
||||
|
||||
private static final Map<Integer, Set<IStateStack<?>>> attribToFeatures = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Helper method to add all texture gen states (S, T, R, Q) for all texture units to a collection.
|
||||
* Used by both GL_ENABLE_BIT and GL_TEXTURE_BIT.
|
||||
*/
|
||||
private static void addTextureGenStates(Set<IStateStack<?>> collection) {
|
||||
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
|
||||
collection.add(GLStateManager.textures.getTexGenSStates(i));
|
||||
collection.add(GLStateManager.textures.getTexGenTStates(i));
|
||||
collection.add(GLStateManager.textures.getTexGenRStates(i));
|
||||
collection.add(GLStateManager.textures.getTexGenQStates(i));
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
attribToFeatures.put(GL11.GL_COLOR_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.alphaTest // GL_ALPHA_TEST enable bit
|
||||
@@ -90,10 +45,10 @@ public class Feature {
|
||||
, GLStateManager.blendState // Blending source and destination functions
|
||||
// Constant blend color
|
||||
// Blending equation
|
||||
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP enable bit
|
||||
, GLStateManager.ditherState // GL_DITHER enable bit
|
||||
, GLStateManager.drawBuffer // GL_DRAW_BUFFER setting
|
||||
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP enable bit
|
||||
// GL_DITHER enable bit
|
||||
// GL_DRAW_BUFFER setting
|
||||
// GL_COLOR_LOGIC_OP enable bit
|
||||
// GL_INDEX_LOGIC_OP enable bit
|
||||
// Logic op function
|
||||
, GLStateManager.colorMask // Color-mode and index-mode writemasks
|
||||
, GLStateManager.clearColor // Color-mode and index-mode clear values
|
||||
@@ -119,15 +74,14 @@ public class Feature {
|
||||
|
||||
final HashSet<IStateStack<?>> enableBits = new HashSet<>(ImmutableSet.of(
|
||||
GLStateManager.alphaTest // GL_ALPHA_TEST flag
|
||||
, GLStateManager.autoNormalState // GL_AUTO_NORMAL flag
|
||||
// GL_AUTO_NORMAL flag
|
||||
, GLStateManager.blendMode // GL_BLEND flag
|
||||
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP flag
|
||||
// Enable bits for the user-definable clipping planes
|
||||
, GLStateManager.colorMaterial // GL_COLOR_MATERIAL
|
||||
, GLStateManager.cullState // GL_CULL_FACE flag
|
||||
, GLStateManager.depthTest // GL_DEPTH_TEST flag
|
||||
, GLStateManager.ditherState // GL_DITHER flag
|
||||
// GL_DITHER flag
|
||||
, GLStateManager.fogMode // GL_FOG flag
|
||||
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP flag
|
||||
, GLStateManager.lightStates[0] // GL_LIGHT0
|
||||
, GLStateManager.lightStates[1] // GL_LIGHT1
|
||||
, GLStateManager.lightStates[2] // GL_LIGHT2
|
||||
@@ -137,57 +91,36 @@ public class Feature {
|
||||
, GLStateManager.lightStates[6] // GL_LIGHT6
|
||||
, GLStateManager.lightStates[7] // GL_LIGHT7
|
||||
, GLStateManager.lightingState // GL_LIGHTING flag
|
||||
, GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
|
||||
, GLStateManager.lineStippleState // GL_LINE_STIPPLE flag
|
||||
, GLStateManager.map1Color4State // GL_MAP1_COLOR_4
|
||||
, GLStateManager.map1IndexState // GL_MAP1_INDEX
|
||||
, GLStateManager.map1NormalState // GL_MAP1_NORMAL
|
||||
, GLStateManager.map1TextureCoord1State // GL_MAP1_TEXTURE_COORD_1
|
||||
, GLStateManager.map1TextureCoord2State // GL_MAP1_TEXTURE_COORD_2
|
||||
, GLStateManager.map1TextureCoord3State // GL_MAP1_TEXTURE_COORD_3
|
||||
, GLStateManager.map1TextureCoord4State // GL_MAP1_TEXTURE_COORD_4
|
||||
, GLStateManager.map1Vertex3State // GL_MAP1_VERTEX_3
|
||||
, GLStateManager.map1Vertex4State // GL_MAP1_VERTEX_4
|
||||
, GLStateManager.map2Color4State // GL_MAP2_COLOR_4
|
||||
, GLStateManager.map2IndexState // GL_MAP2_INDEX
|
||||
, GLStateManager.map2NormalState // GL_MAP2_NORMAL
|
||||
, GLStateManager.map2TextureCoord1State // GL_MAP2_TEXTURE_COORD_1
|
||||
, GLStateManager.map2TextureCoord2State // GL_MAP2_TEXTURE_COORD_2
|
||||
, GLStateManager.map2TextureCoord3State // GL_MAP2_TEXTURE_COORD_3
|
||||
, GLStateManager.map2TextureCoord4State // GL_MAP2_TEXTURE_COORD_4
|
||||
, GLStateManager.map2Vertex3State // GL_MAP2_VERTEX_3
|
||||
, GLStateManager.map2Vertex4State // GL_MAP2_VERTEX_4
|
||||
, GLStateManager.multisampleState // GL_MULTISAMPLE flag
|
||||
, GLStateManager.normalizeState // GL_NORMALIZE flag
|
||||
, GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
|
||||
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
|
||||
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
|
||||
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
|
||||
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
|
||||
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE flag
|
||||
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
|
||||
, GLStateManager.sampleAlphaToCoverageState // GL_SAMPLE_ALPHA_TO_COVERAGE flag
|
||||
, GLStateManager.sampleAlphaToOneState // GL_SAMPLE_ALPHA_TO_ONE flag
|
||||
, GLStateManager.sampleCoverageState // GL_SAMPLE_COVERAGE flag
|
||||
// GL_LINE_SMOOTH flag
|
||||
// GL_LINE_STIPPLE flag
|
||||
// GL_INDEX_LOGIC_OP flag
|
||||
// GL_COLOR_LOGIC_OP flag
|
||||
// GL_MAP1_x where x is a map type
|
||||
// GL_MAP2_x where x is a map type
|
||||
// GL_MULTISAMPLE flag
|
||||
// GL_NORMALIZE flag
|
||||
// GL_POINT_SMOOTH flag
|
||||
// GL_POLYGON_OFFSET_LINE flag
|
||||
// GL_POLYGON_OFFSET_FILL flag
|
||||
// GL_POLYGON_OFFSET_POINT flag
|
||||
// GL_POLYGON_SMOOTH flag
|
||||
// GL_POLYGON_STIPPLE flag
|
||||
// GL_SAMPLE_ALPHA_TO_COVERAGE flag
|
||||
// GL_SAMPLE_ALPHA_TO_ONE flag
|
||||
// GL_SAMPLE_COVERAGE flag
|
||||
, GLStateManager.scissorTest // GL_SCISSOR_TEST flag
|
||||
, GLStateManager.stencilTest // GL_STENCIL_TEST flag
|
||||
// GL_STENCIL_TEST flag
|
||||
// GL_TEXTURE_1D flag
|
||||
// GL_TEXTURE_2D flag - Below
|
||||
// GL_TEXTURE_3D flag
|
||||
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
));
|
||||
|
||||
// Enable bits for the user-definable clipping planes
|
||||
for(int i = 0; i < GLStateManager.clipPlaneStates.length; i++) {
|
||||
enableBits.add(GLStateManager.clipPlaneStates[i]);
|
||||
}
|
||||
|
||||
// GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D flags
|
||||
// GL_TEXTURE_2D flag
|
||||
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
|
||||
enableBits.add(GLStateManager.textures.getTexture1DStates(i));
|
||||
enableBits.add(GLStateManager.textures.getTextureUnitStates(i));
|
||||
enableBits.add(GLStateManager.textures.getTexture3DStates(i));
|
||||
}
|
||||
|
||||
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
addTextureGenStates(enableBits);
|
||||
|
||||
attribToFeatures.put(GL11.GL_ENABLE_BIT, enableBits);
|
||||
attribToFeatures.put(GL11.GL_EVAL_BIT, ImmutableSet.of(
|
||||
// GL_MAP1_x enable bits, where x is a map type
|
||||
@@ -248,9 +181,10 @@ public class Feature {
|
||||
, GLStateManager.shadeModelState // GL_SHADE_MODEL setting
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LINE_BIT, ImmutableSet.of(
|
||||
GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
|
||||
, GLStateManager.lineStippleState // GL_LINE_STIPPLE enable bit
|
||||
, GLStateManager.lineState // Line stipple pattern, repeat counter, and width
|
||||
// GL_LINE_SMOOTH flag
|
||||
// GL_LINE_STIPPLE enable bit
|
||||
// Line stipple pattern and repeat counter
|
||||
// Line width
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LIST_BIT, ImmutableSet.of(
|
||||
// GL_LIST_BASE setting
|
||||
@@ -275,17 +209,21 @@ public class Feature {
|
||||
// GL_READ_BUFFER setting
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POINT_BIT, ImmutableSet.of(
|
||||
GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
|
||||
, GLStateManager.pointState // Point size
|
||||
// GL_POINT_SMOOTH flag
|
||||
// Point size
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POLYGON_BIT, ImmutableSet.of(
|
||||
GLStateManager.cullState // GL_CULL_FACE enable bit
|
||||
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
|
||||
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE enable bit
|
||||
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
|
||||
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
|
||||
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
|
||||
, GLStateManager.polygonState // GL_CULL_FACE_MODE, GL_FRONT_FACE, GL_POLYGON_MODE, GL_POLYGON_OFFSET_FACTOR/UNITS
|
||||
// GL_CULL_FACE_MODE value
|
||||
// GL_FRONT_FACE indicator
|
||||
// GL_POLYGON_MODE setting
|
||||
// GL_POLYGON_SMOOTH flag
|
||||
// GL_POLYGON_STIPPLE enable bit
|
||||
// GL_POLYGON_OFFSET_FILL flag
|
||||
// GL_POLYGON_OFFSET_LINE flag
|
||||
// GL_POLYGON_OFFSET_POINT flag
|
||||
// GL_POLYGON_OFFSET_FACTOR
|
||||
// GL_POLYGON_OFFSET_UNITS
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POLYGON_STIPPLE_BIT, ImmutableSet.of(
|
||||
// Polygon stipple pattern
|
||||
@@ -295,8 +233,12 @@ public class Feature {
|
||||
// Scissor box
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_STENCIL_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.stencilTest // GL_STENCIL_TEST enable bit
|
||||
, GLStateManager.stencilState // Stencil function, ref, mask, ops, writemask, clear value
|
||||
// GL_STENCIL_TEST enable bit
|
||||
// Stencil function and reference value
|
||||
// Stencil value mask
|
||||
// Stencil fail, pass, and depth buffer pass actions
|
||||
// Stencil buffer clear value
|
||||
// Stencil buffer writemask
|
||||
));
|
||||
final Set<IStateStack<?>> textureAttribs = new HashSet<>(ImmutableSet.of(
|
||||
GLStateManager.activeTextureUnit // Active texture unit
|
||||
@@ -320,16 +262,13 @@ public class Feature {
|
||||
// textureAttribs.add(GLStateManager.textures.getInfo(i))
|
||||
}
|
||||
|
||||
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
addTextureGenStates(textureAttribs);
|
||||
|
||||
attribToFeatures.put(GL11.GL_TEXTURE_BIT, textureAttribs);
|
||||
|
||||
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, ImmutableSet.of(
|
||||
// Coefficients of the six clipping planes
|
||||
// Enable bits for the user-definable clipping planes
|
||||
GLStateManager.matrixMode
|
||||
, GLStateManager.normalizeState // GL_NORMALIZE flag
|
||||
// GL_NORMALIZE flag
|
||||
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_VIEWPORT_BIT, ImmutableSet.of(
|
||||
|
||||
@@ -9,16 +9,15 @@ import org.lwjgl.opengl.AMDDebugOutput;
|
||||
import org.lwjgl.opengl.AMDDebugOutputCallback;
|
||||
import org.lwjgl.opengl.ARBDebugOutput;
|
||||
import org.lwjgl.opengl.ARBDebugOutputCallback;
|
||||
import org.lwjgl.opengl.EXTBlendColor;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.opengl.KHRDebug;
|
||||
import org.lwjgl.opengl.KHRDebugCallback;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
import static org.lwjgl.opengl.ARBDebugOutput.glDebugMessageCallbackARB;
|
||||
|
||||
@@ -32,9 +31,20 @@ public final class GLDebug {
|
||||
LOGGER.warn("setupDebugMessageCallback called from non-main thread!");
|
||||
return 0;
|
||||
}
|
||||
return setupDebugMessageCallbackImpl();
|
||||
return setupDebugMessageCallback(System.err);
|
||||
}
|
||||
|
||||
private static void trace(Consumer<String> output) {
|
||||
/*
|
||||
* We can not just use a fixed stacktrace element offset, because some methods
|
||||
* are intercepted and some are not. So, check the package name.
|
||||
*/
|
||||
StackTraceElement[] elems = filterStackTrace(new Throwable(), 4).getStackTrace();
|
||||
for (StackTraceElement ste : elems) {
|
||||
output.accept(ste.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static Throwable filterStackTrace(Throwable throwable, int offset) {
|
||||
StackTraceElement[] elems = throwable.getStackTrace();
|
||||
StackTraceElement[] filtered = new StackTraceElement[elems.length];
|
||||
@@ -52,48 +62,36 @@ public final class GLDebug {
|
||||
return throwable;
|
||||
}
|
||||
|
||||
private static String buildStackTrace() {
|
||||
StackTraceElement[] elems = filterStackTrace(new Throwable(), 4).getStackTrace();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (StackTraceElement elem : elems) {
|
||||
sb.append("\n\t").append(elem.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
public static void printTrace(PrintStream stream) {
|
||||
trace(new Consumer<String>() {
|
||||
boolean first = true;
|
||||
|
||||
private static void logDebugMessage(int id, String source, String type, String severity, String message) {
|
||||
String fullMessage = String.format("[GL] %s %s (0x%X) from %s: %s%s", severity, type, id, source, message, buildStackTrace());
|
||||
|
||||
if ("HIGH".equals(severity)) {
|
||||
LOGGER.error(fullMessage);
|
||||
} else if ("MEDIUM".equals(severity)) {
|
||||
LOGGER.warn(fullMessage);
|
||||
} else {
|
||||
LOGGER.info(fullMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static void logDebugMessageAMD(int id, String category, String severity, String message) {
|
||||
String fullMessage = String.format("[GL] %s %s (0x%X): %s%s", severity, category, id, message, buildStackTrace());
|
||||
|
||||
if ("HIGH".equals(severity)) {
|
||||
LOGGER.error(fullMessage);
|
||||
} else if ("MEDIUM".equals(severity)) {
|
||||
LOGGER.warn(fullMessage);
|
||||
} else {
|
||||
LOGGER.info(fullMessage);
|
||||
}
|
||||
public void accept(String str) {
|
||||
if (first) {
|
||||
printDetail(stream, "Stacktrace", str);
|
||||
first = false;
|
||||
} else {
|
||||
printDetailLine(stream, "Stacktrace", str);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up debug callbacks
|
||||
* @return 0 for failure, 1 for success, 2 for restart required.
|
||||
*/
|
||||
private static int setupDebugMessageCallbackImpl() {
|
||||
public static int setupDebugMessageCallback(PrintStream stream) {
|
||||
if (GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_KHR_debug) {
|
||||
LOGGER.info("[GL] Using OpenGL 4.3 for error logging.");
|
||||
final KHRDebugCallback proc = new KHRDebugCallback((source, type, id, severity, message) -> {
|
||||
logDebugMessage(id, getDebugSource(source), getDebugType(type), getDebugSeverity(severity), message);
|
||||
KHRDebugCallback proc = new KHRDebugCallback((source, type, id, severity, message) -> {
|
||||
stream.println("[LWJGL] OpenGL debug message");
|
||||
printDetail(stream, "ID", String.format("0x%X", id));
|
||||
printDetail(stream, "Source", getDebugSource(source));
|
||||
printDetail(stream, "Type", getDebugType(type));
|
||||
printDetail(stream, "Severity", getDebugSeverity(severity));
|
||||
printDetail(stream, "Message", message);
|
||||
printTrace(stream);
|
||||
});
|
||||
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
|
||||
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
|
||||
@@ -101,9 +99,6 @@ public final class GLDebug {
|
||||
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_NOTIFICATION, null, false);
|
||||
GL43.glDebugMessageCallback(proc);
|
||||
|
||||
// Enable synchronous debug output so errors are reported immediately with accurate stack traces
|
||||
GL11.glEnable(GL43.GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
|
||||
if ((GL11.glGetInteger(GL30.GL_CONTEXT_FLAGS) & GL43.GL_CONTEXT_FLAG_DEBUG_BIT) == 0) {
|
||||
LOGGER.warn("[GL] Warning: A non-debug context may not produce any debug output.");
|
||||
GL11.glEnable(GL43.GL_DEBUG_OUTPUT);
|
||||
@@ -112,22 +107,30 @@ public final class GLDebug {
|
||||
return 1;
|
||||
} else if (GLStateManager.capabilities.GL_ARB_debug_output) {
|
||||
LOGGER.info("[GL] Using ARB_debug_output for error logging.");
|
||||
final ARBDebugOutputCallback proc = new ARBDebugOutputCallback((source, type, id, severity, message) -> {
|
||||
logDebugMessage(id, getSourceARB(source), getTypeARB(type), getSeverityARB(severity), message);
|
||||
ARBDebugOutputCallback proc = new ARBDebugOutputCallback((source, type, id, severity, message) -> {
|
||||
stream.println("[LWJGL] ARB_debug_output message");
|
||||
printDetail(stream, "ID", String.format("0x%X", id));
|
||||
printDetail(stream, "Source", getSourceARB(source));
|
||||
printDetail(stream, "Type", getTypeARB(type));
|
||||
printDetail(stream, "Severity", getSeverityARB(severity));
|
||||
printDetail(stream, "Message", message);
|
||||
printTrace(stream);
|
||||
});
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_LOW, null, false);
|
||||
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_NOTIFICATION, null, false);
|
||||
ARBDebugOutput.glDebugMessageCallbackARB(proc);
|
||||
|
||||
// Enable synchronous debug output so errors are reported immediately with accurate stack traces
|
||||
GL11.glEnable(ARBDebugOutput.GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
return 1;
|
||||
} else if (GLStateManager.capabilities.GL_AMD_debug_output) {
|
||||
LOGGER.info("[GL] Using AMD_debug_output for error logging.");
|
||||
final AMDDebugOutputCallback proc = new AMDDebugOutputCallback((id, category, severity, message) -> {
|
||||
logDebugMessageAMD(id, getCategoryAMD(category), getSeverityAMD(severity), message);
|
||||
AMDDebugOutputCallback proc = new AMDDebugOutputCallback((id, category, severity, message) -> {
|
||||
stream.println("[LWJGL] AMD_debug_output message");
|
||||
printDetail(stream, "ID", String.format("0x%X", id));
|
||||
printDetail(stream, "Category", getCategoryAMD(category));
|
||||
printDetail(stream, "Severity", getSeverityAMD(severity));
|
||||
printDetail(stream, "Message", message);
|
||||
printTrace(stream);
|
||||
});
|
||||
AMDDebugOutput.glDebugMessageEnableAMD(0, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
|
||||
AMDDebugOutput.glDebugMessageEnableAMD(0, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
|
||||
@@ -163,6 +166,17 @@ public final class GLDebug {
|
||||
}
|
||||
}
|
||||
|
||||
private static void printDetail(PrintStream stream, String type, String message) {
|
||||
stream.printf("\t%s: %s\n", type, message);
|
||||
}
|
||||
|
||||
private static void printDetailLine(PrintStream stream, String type, String message) {
|
||||
stream.append(" ");
|
||||
for (int i = 0; i < type.length(); i++) {
|
||||
stream.append(" ");
|
||||
}
|
||||
stream.append(message).append("\n");
|
||||
}
|
||||
|
||||
private static String getDebugSource(int source) {
|
||||
return switch (source) {
|
||||
@@ -255,355 +269,6 @@ public final class GLDebug {
|
||||
};
|
||||
}
|
||||
|
||||
public static String getMatrixModeName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_MODELVIEW -> "MODELVIEW";
|
||||
case GL11.GL_PROJECTION -> "PROJECTION";
|
||||
case GL11.GL_TEXTURE -> "TEXTURE";
|
||||
case GL11.GL_COLOR -> "COLOR";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getCapabilityName(int cap) {
|
||||
return switch (cap) {
|
||||
case GL11.GL_ALPHA_TEST -> "ALPHA_TEST";
|
||||
case GL11.GL_BLEND -> "BLEND";
|
||||
case GL11.GL_COLOR_MATERIAL -> "COLOR_MATERIAL";
|
||||
case GL11.GL_CULL_FACE -> "CULL_FACE";
|
||||
case GL11.GL_DEPTH_TEST -> "DEPTH_TEST";
|
||||
case GL11.GL_FOG -> "FOG";
|
||||
case GL11.GL_LIGHTING -> "LIGHTING";
|
||||
case GL11.GL_LINE_SMOOTH -> "LINE_SMOOTH";
|
||||
case GL11.GL_NORMALIZE -> "NORMALIZE";
|
||||
case GL11.GL_POINT_SMOOTH -> "POINT_SMOOTH";
|
||||
case GL11.GL_POLYGON_OFFSET_FILL -> "POLYGON_OFFSET_FILL";
|
||||
case GL11.GL_POLYGON_OFFSET_LINE -> "POLYGON_OFFSET_LINE";
|
||||
case GL11.GL_POLYGON_SMOOTH -> "POLYGON_SMOOTH";
|
||||
case GL12.GL_RESCALE_NORMAL -> "RESCALE_NORMAL";
|
||||
case GL11.GL_SCISSOR_TEST -> "SCISSOR_TEST";
|
||||
case GL11.GL_STENCIL_TEST -> "STENCIL_TEST";
|
||||
case GL11.GL_TEXTURE_1D -> "TEXTURE_1D";
|
||||
case GL11.GL_TEXTURE_2D -> "TEXTURE_2D";
|
||||
case GL11.GL_LIGHT0 -> "LIGHT0";
|
||||
case GL11.GL_LIGHT1 -> "LIGHT1";
|
||||
case GL11.GL_LIGHT2 -> "LIGHT2";
|
||||
case GL11.GL_LIGHT3 -> "LIGHT3";
|
||||
case GL11.GL_LIGHT4 -> "LIGHT4";
|
||||
case GL11.GL_LIGHT5 -> "LIGHT5";
|
||||
case GL11.GL_LIGHT6 -> "LIGHT6";
|
||||
case GL11.GL_LIGHT7 -> "LIGHT7";
|
||||
default -> String.format("0x%X", cap);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getComparisonFuncName(int func) {
|
||||
return switch (func) {
|
||||
case GL11.GL_NEVER -> "NEVER";
|
||||
case GL11.GL_LESS -> "LESS";
|
||||
case GL11.GL_EQUAL -> "EQUAL";
|
||||
case GL11.GL_LEQUAL -> "LEQUAL";
|
||||
case GL11.GL_GREATER -> "GREATER";
|
||||
case GL11.GL_NOTEQUAL -> "NOTEQUAL";
|
||||
case GL11.GL_GEQUAL -> "GEQUAL";
|
||||
case GL11.GL_ALWAYS -> "ALWAYS";
|
||||
default -> String.format("0x%X", func);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getBlendFactorName(int factor) {
|
||||
return switch (factor) {
|
||||
case GL11.GL_ZERO -> "ZERO";
|
||||
case GL11.GL_ONE -> "ONE";
|
||||
case GL11.GL_SRC_COLOR -> "SRC_COLOR";
|
||||
case GL11.GL_ONE_MINUS_SRC_COLOR -> "ONE_MINUS_SRC_COLOR";
|
||||
case GL11.GL_DST_COLOR -> "DST_COLOR";
|
||||
case GL11.GL_ONE_MINUS_DST_COLOR -> "ONE_MINUS_DST_COLOR";
|
||||
case GL11.GL_SRC_ALPHA -> "SRC_ALPHA";
|
||||
case GL11.GL_ONE_MINUS_SRC_ALPHA -> "ONE_MINUS_SRC_ALPHA";
|
||||
case GL11.GL_DST_ALPHA -> "DST_ALPHA";
|
||||
case GL11.GL_ONE_MINUS_DST_ALPHA -> "ONE_MINUS_DST_ALPHA";
|
||||
case EXTBlendColor.GL_CONSTANT_COLOR_EXT -> "CONSTANT_COLOR";
|
||||
case EXTBlendColor.GL_ONE_MINUS_CONSTANT_COLOR_EXT -> "ONE_MINUS_CONSTANT_COLOR";
|
||||
case EXTBlendColor.GL_CONSTANT_ALPHA_EXT -> "CONSTANT_ALPHA";
|
||||
case EXTBlendColor.GL_ONE_MINUS_CONSTANT_ALPHA_EXT -> "ONE_MINUS_CONSTANT_ALPHA";
|
||||
case GL11.GL_SRC_ALPHA_SATURATE -> "SRC_ALPHA_SATURATE";
|
||||
default -> String.format("0x%X", factor);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getShadeModelName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_FLAT -> "FLAT";
|
||||
case GL11.GL_SMOOTH -> "SMOOTH";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getTextureTargetName(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_TEXTURE_1D -> "TEXTURE_1D";
|
||||
case GL11.GL_TEXTURE_2D -> "TEXTURE_2D";
|
||||
case GL12.GL_TEXTURE_3D -> "TEXTURE_3D";
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> "TEXTURE_CUBE_MAP";
|
||||
default -> String.format("0x%X", target);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getTexturePnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_MIN_FILTER -> "TEXTURE_MIN_FILTER";
|
||||
case GL11.GL_TEXTURE_MAG_FILTER -> "TEXTURE_MAG_FILTER";
|
||||
case GL11.GL_TEXTURE_WRAP_S -> "TEXTURE_WRAP_S";
|
||||
case GL11.GL_TEXTURE_WRAP_T -> "TEXTURE_WRAP_T";
|
||||
case GL12.GL_TEXTURE_WRAP_R -> "TEXTURE_WRAP_R";
|
||||
case GL12.GL_TEXTURE_MIN_LOD -> "TEXTURE_MIN_LOD";
|
||||
case GL12.GL_TEXTURE_MAX_LOD -> "TEXTURE_MAX_LOD";
|
||||
case GL12.GL_TEXTURE_BASE_LEVEL -> "TEXTURE_BASE_LEVEL";
|
||||
case GL12.GL_TEXTURE_MAX_LEVEL -> "TEXTURE_MAX_LEVEL";
|
||||
case GL11.GL_TEXTURE_BORDER_COLOR -> "TEXTURE_BORDER_COLOR";
|
||||
case GL11.GL_TEXTURE_PRIORITY -> "TEXTURE_PRIORITY";
|
||||
case GL14.GL_TEXTURE_LOD_BIAS -> "TEXTURE_LOD_BIAS";
|
||||
case GL14.GL_GENERATE_MIPMAP -> "GENERATE_MIPMAP";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getTextureFormatName(int format) {
|
||||
return switch (format) {
|
||||
case GL11.GL_RED -> "RED";
|
||||
case GL11.GL_GREEN -> "GREEN";
|
||||
case GL11.GL_BLUE -> "BLUE";
|
||||
case GL11.GL_ALPHA -> "ALPHA";
|
||||
case GL11.GL_RGB -> "RGB";
|
||||
case GL11.GL_RGBA -> "RGBA";
|
||||
case GL11.GL_LUMINANCE -> "LUMINANCE";
|
||||
case GL11.GL_LUMINANCE_ALPHA -> "LUMINANCE_ALPHA";
|
||||
case GL12.GL_BGR -> "BGR";
|
||||
case GL12.GL_BGRA -> "BGRA";
|
||||
case GL11.GL_DEPTH_COMPONENT -> "DEPTH_COMPONENT";
|
||||
case GL11.GL_STENCIL_INDEX -> "STENCIL_INDEX";
|
||||
case GL30.GL_DEPTH_STENCIL -> "DEPTH_STENCIL";
|
||||
// Internal formats
|
||||
case GL11.GL_RGB8 -> "RGB8";
|
||||
case GL11.GL_RGBA8 -> "RGBA8";
|
||||
case GL11.GL_ALPHA8 -> "ALPHA8";
|
||||
case GL11.GL_LUMINANCE8 -> "LUMINANCE8";
|
||||
case GL11.GL_LUMINANCE8_ALPHA8 -> "LUMINANCE8_ALPHA8";
|
||||
default -> String.format("0x%X", format);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getDataTypeName(int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> "UNSIGNED_BYTE";
|
||||
case GL11.GL_BYTE -> "BYTE";
|
||||
case GL11.GL_UNSIGNED_SHORT -> "UNSIGNED_SHORT";
|
||||
case GL11.GL_SHORT -> "SHORT";
|
||||
case GL11.GL_UNSIGNED_INT -> "UNSIGNED_INT";
|
||||
case GL11.GL_INT -> "INT";
|
||||
case GL11.GL_FLOAT -> "FLOAT";
|
||||
case GL11.GL_DOUBLE -> "DOUBLE";
|
||||
case GL12.GL_UNSIGNED_BYTE_3_3_2 -> "UNSIGNED_BYTE_3_3_2";
|
||||
case GL12.GL_UNSIGNED_SHORT_4_4_4_4 -> "UNSIGNED_SHORT_4_4_4_4";
|
||||
case GL12.GL_UNSIGNED_SHORT_5_5_5_1 -> "UNSIGNED_SHORT_5_5_5_1";
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8 -> "UNSIGNED_INT_8_8_8_8";
|
||||
case GL12.GL_UNSIGNED_INT_10_10_10_2 -> "UNSIGNED_INT_10_10_10_2";
|
||||
default -> String.format("0x%X", type);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getLightName(int light) {
|
||||
return switch (light) {
|
||||
case GL11.GL_LIGHT0 -> "LIGHT0";
|
||||
case GL11.GL_LIGHT1 -> "LIGHT1";
|
||||
case GL11.GL_LIGHT2 -> "LIGHT2";
|
||||
case GL11.GL_LIGHT3 -> "LIGHT3";
|
||||
case GL11.GL_LIGHT4 -> "LIGHT4";
|
||||
case GL11.GL_LIGHT5 -> "LIGHT5";
|
||||
case GL11.GL_LIGHT6 -> "LIGHT6";
|
||||
case GL11.GL_LIGHT7 -> "LIGHT7";
|
||||
default -> String.format("0x%X", light);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getLightPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> "AMBIENT";
|
||||
case GL11.GL_DIFFUSE -> "DIFFUSE";
|
||||
case GL11.GL_SPECULAR -> "SPECULAR";
|
||||
case GL11.GL_POSITION -> "POSITION";
|
||||
case GL11.GL_SPOT_DIRECTION -> "SPOT_DIRECTION";
|
||||
case GL11.GL_SPOT_EXPONENT -> "SPOT_EXPONENT";
|
||||
case GL11.GL_SPOT_CUTOFF -> "SPOT_CUTOFF";
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> "CONSTANT_ATTENUATION";
|
||||
case GL11.GL_LINEAR_ATTENUATION -> "LINEAR_ATTENUATION";
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> "QUADRATIC_ATTENUATION";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getMaterialPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> "AMBIENT";
|
||||
case GL11.GL_DIFFUSE -> "DIFFUSE";
|
||||
case GL11.GL_SPECULAR -> "SPECULAR";
|
||||
case GL11.GL_EMISSION -> "EMISSION";
|
||||
case GL11.GL_SHININESS -> "SHININESS";
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> "AMBIENT_AND_DIFFUSE";
|
||||
case GL11.GL_COLOR_INDEXES -> "COLOR_INDEXES";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getFaceName(int face) {
|
||||
return switch (face) {
|
||||
case GL11.GL_FRONT -> "FRONT";
|
||||
case GL11.GL_BACK -> "BACK";
|
||||
case GL11.GL_FRONT_AND_BACK -> "FRONT_AND_BACK";
|
||||
default -> String.format("0x%X", face);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getColorMaterialModeName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_AMBIENT -> "AMBIENT";
|
||||
case GL11.GL_DIFFUSE -> "DIFFUSE";
|
||||
case GL11.GL_SPECULAR -> "SPECULAR";
|
||||
case GL11.GL_EMISSION -> "EMISSION";
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> "AMBIENT_AND_DIFFUSE";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getFogPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_FOG_MODE -> "FOG_MODE";
|
||||
case GL11.GL_FOG_DENSITY -> "FOG_DENSITY";
|
||||
case GL11.GL_FOG_START -> "FOG_START";
|
||||
case GL11.GL_FOG_END -> "FOG_END";
|
||||
case GL11.GL_FOG_INDEX -> "FOG_INDEX";
|
||||
case GL11.GL_FOG_COLOR -> "FOG_COLOR";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getLightModelPnameName(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> "LIGHT_MODEL_AMBIENT";
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> "LIGHT_MODEL_LOCAL_VIEWER";
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> "LIGHT_MODEL_TWO_SIDE";
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> "LIGHT_MODEL_COLOR_CONTROL";
|
||||
default -> String.format("0x%X", pname);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getClearMaskString(int mask) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean first = true;
|
||||
if ((mask & GL11.GL_COLOR_BUFFER_BIT) != 0) {
|
||||
sb.append("COLOR");
|
||||
first = false;
|
||||
}
|
||||
if ((mask & GL11.GL_DEPTH_BUFFER_BIT) != 0) {
|
||||
if (!first) sb.append("|");
|
||||
sb.append("DEPTH");
|
||||
first = false;
|
||||
}
|
||||
if ((mask & GL11.GL_STENCIL_BUFFER_BIT) != 0) {
|
||||
if (!first) sb.append("|");
|
||||
sb.append("STENCIL");
|
||||
first = false;
|
||||
}
|
||||
if ((mask & GL11.GL_ACCUM_BUFFER_BIT) != 0) {
|
||||
if (!first) sb.append("|");
|
||||
sb.append("ACCUM");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getCullFaceName(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_FRONT -> "FRONT";
|
||||
case GL11.GL_BACK -> "BACK";
|
||||
case GL11.GL_FRONT_AND_BACK -> "FRONT_AND_BACK";
|
||||
default -> String.format("0x%X", mode);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getLogicOpName(int opcode) {
|
||||
return switch (opcode) {
|
||||
case GL11.GL_CLEAR -> "CLEAR";
|
||||
case GL11.GL_AND -> "AND";
|
||||
case GL11.GL_AND_REVERSE -> "AND_REVERSE";
|
||||
case GL11.GL_COPY -> "COPY";
|
||||
case GL11.GL_AND_INVERTED -> "AND_INVERTED";
|
||||
case GL11.GL_NOOP -> "NOOP";
|
||||
case GL11.GL_XOR -> "XOR";
|
||||
case GL11.GL_OR -> "OR";
|
||||
case GL11.GL_NOR -> "NOR";
|
||||
case GL11.GL_EQUIV -> "EQUIV";
|
||||
case GL11.GL_INVERT -> "INVERT";
|
||||
case GL11.GL_OR_REVERSE -> "OR_REVERSE";
|
||||
case GL11.GL_COPY_INVERTED -> "COPY_INVERTED";
|
||||
case GL11.GL_OR_INVERTED -> "OR_INVERTED";
|
||||
case GL11.GL_NAND -> "NAND";
|
||||
case GL11.GL_SET -> "SET";
|
||||
default -> String.format("0x%X", opcode);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getFramebufferStatusName(int status) {
|
||||
return switch (status) {
|
||||
case GL30.GL_FRAMEBUFFER_COMPLETE -> "COMPLETE";
|
||||
case GL30.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -> "INCOMPLETE_ATTACHMENT";
|
||||
case GL30.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT -> "INCOMPLETE_MISSING_ATTACHMENT";
|
||||
case GL30.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER -> "INCOMPLETE_DRAW_BUFFER";
|
||||
case GL30.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER -> "INCOMPLETE_READ_BUFFER";
|
||||
case GL30.GL_FRAMEBUFFER_UNSUPPORTED -> "UNSUPPORTED";
|
||||
case GL30.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE -> "INCOMPLETE_MULTISAMPLE";
|
||||
case 0 -> "NO_FRAMEBUFFER_BOUND";
|
||||
default -> String.format("UNKNOWN(0x%X)", status);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getInternalFormatName(int format) {
|
||||
return switch (format) {
|
||||
// Basic
|
||||
case GL11.GL_RGBA -> "RGBA";
|
||||
case GL11.GL_RGB -> "RGB";
|
||||
// 8-bit normalized
|
||||
case GL30.GL_R8 -> "R8";
|
||||
case GL30.GL_RG8 -> "RG8";
|
||||
case GL11.GL_RGB8 -> "RGB8";
|
||||
case GL11.GL_RGBA8 -> "RGBA8";
|
||||
// 8-bit signed normalized
|
||||
case 0x8F94 -> "R8_SNORM";
|
||||
case 0x8F95 -> "RG8_SNORM";
|
||||
case 0x8F96 -> "RGB8_SNORM";
|
||||
case 0x8F97 -> "RGBA8_SNORM";
|
||||
// 16-bit normalized
|
||||
case GL30.GL_R16 -> "R16";
|
||||
case GL30.GL_RG16 -> "RG16";
|
||||
case GL11.GL_RGB16 -> "RGB16";
|
||||
case GL11.GL_RGBA16 -> "RGBA16";
|
||||
// 16-bit float
|
||||
case GL30.GL_R16F -> "R16F";
|
||||
case GL30.GL_RG16F -> "RG16F";
|
||||
case GL30.GL_RGB16F -> "RGB16F";
|
||||
case GL30.GL_RGBA16F -> "RGBA16F";
|
||||
// 32-bit float
|
||||
case GL30.GL_R32F -> "R32F";
|
||||
case GL30.GL_RG32F -> "RG32F";
|
||||
case GL30.GL_RGB32F -> "RGB32F";
|
||||
case GL30.GL_RGBA32F -> "RGBA32F";
|
||||
// Packed
|
||||
case GL30.GL_R11F_G11F_B10F -> "R11F_G11F_B10F";
|
||||
case GL11.GL_RGB10_A2 -> "RGB10_A2";
|
||||
default -> String.format("0x%X", format);
|
||||
};
|
||||
}
|
||||
|
||||
private static DebugState debugState;
|
||||
|
||||
private interface DebugState {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -4,32 +4,20 @@ 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.DSAUnsupported;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3i;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.ARBBufferStorage;
|
||||
import org.lwjgl.opengl.ARBClearBufferObject;
|
||||
import org.lwjgl.opengl.ARBClearTexture;
|
||||
import org.lwjgl.opengl.ARBShaderImageLoadStore;
|
||||
import org.lwjgl.opengl.ARBShaderStorageBufferObject;
|
||||
import org.lwjgl.opengl.EXTShaderImageLoadStore;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
import org.lwjgl.opengl.NVXGpuMemoryInfo;
|
||||
|
||||
import net.coderbot.iris.gl.shader.StandardMacros;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -42,20 +30,8 @@ import java.nio.IntBuffer;
|
||||
public class RenderSystem {
|
||||
private static final Logger LOGGER = LogManager.getLogger("RenderSystem");
|
||||
private static DSAAccess dsaState;
|
||||
private static boolean hasMultibind;
|
||||
private static boolean supportsCompute;
|
||||
private static boolean supportsImageLoadStore;
|
||||
private static boolean supportsSSBO;
|
||||
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;
|
||||
|
||||
// Sampler object state tracking (null if unsupported)
|
||||
private static int[] samplers;
|
||||
|
||||
private RenderSystem() {}
|
||||
|
||||
@@ -65,6 +41,7 @@ public class RenderSystem {
|
||||
dsaState = new DSACore();
|
||||
LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
|
||||
}
|
||||
hasMultibind = GLStateManager.capabilities.OpenGL45;
|
||||
|
||||
} catch (NoSuchFieldError ignored) {}
|
||||
try {
|
||||
@@ -78,52 +55,11 @@ public class RenderSystem {
|
||||
LOGGER.info("No DSA support detected, falling back to legacy OpenGL.");
|
||||
}
|
||||
|
||||
try {
|
||||
hasMultibind |= GLStateManager.capabilities.GL_ARB_multi_bind;
|
||||
} catch (NoSuchFieldError ignored) {}
|
||||
|
||||
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;
|
||||
supportsBufferStorage = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_buffer_storage;
|
||||
supportsClearTexture = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_clear_texture;
|
||||
|
||||
// Cache maximum image units
|
||||
if (supportsImageLoadStore) {
|
||||
if (GLStateManager.capabilities.OpenGL42) {
|
||||
maxImageUnits = GL11.glGetInteger(GL42.GL_MAX_IMAGE_UNITS);
|
||||
} else if (GLStateManager.capabilities.GL_ARB_shader_image_load_store) {
|
||||
maxImageUnits = GL11.glGetInteger(ARBShaderImageLoadStore.GL_MAX_IMAGE_UNITS);
|
||||
} else {
|
||||
maxImageUnits = GL11.glGetInteger(EXTShaderImageLoadStore.GL_MAX_IMAGE_UNITS_EXT);
|
||||
}
|
||||
} else {
|
||||
maxImageUnits = 0;
|
||||
}
|
||||
|
||||
// Cache maximum SSBO bindings
|
||||
if (supportsSSBO) {
|
||||
if (GLStateManager.capabilities.OpenGL43) {
|
||||
maxSSBOBindings = GL11.glGetInteger(GL43.GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
|
||||
} else {
|
||||
maxSSBOBindings = GL11.glGetInteger(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[GL11.glGetInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)];
|
||||
}
|
||||
|
||||
maxGlslVersion = Integer.parseInt(StandardMacros.getGlVersion(GL20.GL_SHADING_LANGUAGE_VERSION));
|
||||
supportsGpuShader4 = GLStateManager.capabilities.GL_EXT_gpu_shader4;
|
||||
|
||||
LOGGER.info("Max GLSL version: {}, GPU Shader4: {}", maxGlslVersion, supportsGpuShader4);
|
||||
LOGGER.info("Image Load/Store: {}, Max Image Units: {}", supportsImageLoadStore, maxImageUnits);
|
||||
LOGGER.info("SSBO: {}, Max SSBO Bindings: {}", supportsSSBO, maxSSBOBindings);
|
||||
LOGGER.info("Buffer Storage: {}, Clear Texture: {}, Sampler Objects: {}", supportsBufferStorage, supportsClearTexture, supportsSamplerObjects);
|
||||
}
|
||||
|
||||
public static void generateMipmaps(int texture, int mipmapTarget) {
|
||||
@@ -135,11 +71,8 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
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) {
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
if (target == GL11.GL_TEXTURE_2D && level == 0) {
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
public static void uniformMatrix4fv(int location, boolean transpose, FloatBuffer matrix) {
|
||||
@@ -147,7 +80,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
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);
|
||||
GL11.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
|
||||
}
|
||||
|
||||
public static void uniform1f(int location, float v0) {
|
||||
@@ -170,8 +103,6 @@ public class RenderSystem {
|
||||
GL20.glUniform3f(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) { GL20.glUniform3i(location, v0, v1, v2); }
|
||||
|
||||
public static void uniform4f(int location, float v0, float v1, float v2, float v3) {
|
||||
GL20.glUniform4f(location, v0, v1, v2, v3);
|
||||
}
|
||||
@@ -204,33 +135,6 @@ public class RenderSystem {
|
||||
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) {
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
|
||||
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexImage1D(target, level, internalformat, width, border, format, type, pixels);
|
||||
}
|
||||
|
||||
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) {
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL12.glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
}
|
||||
|
||||
public static String getProgramInfoLog(int program) {
|
||||
return GL20.glGetProgramInfoLog(program, GL20.glGetProgrami(program, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
@@ -279,10 +183,6 @@ public class RenderSystem {
|
||||
return dsaState.getTexParameteri(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) {
|
||||
if (GLStateManager.capabilities.OpenGL42) {
|
||||
GL42.glBindImageTexture(unit, texture, level, layered, layer, access, format);
|
||||
@@ -292,27 +192,13 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int getMaxImageUnits() {
|
||||
return maxImageUnits;
|
||||
}
|
||||
|
||||
public static boolean supportsImageLoadStore() {
|
||||
return supportsImageLoadStore;
|
||||
}
|
||||
|
||||
public static boolean supportsSSBO() {
|
||||
return supportsSSBO;
|
||||
}
|
||||
|
||||
public static boolean supportsBufferStorage() {
|
||||
return supportsBufferStorage;
|
||||
}
|
||||
|
||||
public static boolean supportsClearTexture() {
|
||||
return supportsClearTexture;
|
||||
}
|
||||
|
||||
public static int getMaxSSBOBindings() {
|
||||
return maxSSBOBindings;
|
||||
if (GLStateManager.capabilities.OpenGL42) {
|
||||
return GL11.glGetInteger(GL42.GL_MAX_IMAGE_UNITS);
|
||||
} else if (GLStateManager.capabilities.GL_EXT_shader_image_load_store) {
|
||||
return GL11.glGetInteger(EXTShaderImageLoadStore.GL_MAX_IMAGE_UNITS_EXT);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void getProgramiv(int program, int value, IntBuffer storage) {
|
||||
@@ -327,14 +213,6 @@ public class RenderSystem {
|
||||
GL43.glDispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
}
|
||||
|
||||
public static void dispatchComputeIndirect(long offset) {
|
||||
GL43.glDispatchComputeIndirect(offset);
|
||||
}
|
||||
|
||||
public static void bindBuffer(int target, int buffer) {
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
}
|
||||
|
||||
public static void memoryBarrier(int barriers) {
|
||||
if (supportsCompute) {
|
||||
GL42.glMemoryBarrier(barriers);
|
||||
@@ -347,12 +225,10 @@ public class RenderSystem {
|
||||
|
||||
public static void disableBufferBlend(int buffer) {
|
||||
GL30.glDisablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
}
|
||||
|
||||
public static void enableBufferBlend(int buffer) {
|
||||
GL30.glEnablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
}
|
||||
|
||||
public static void blendFuncSeparatei(int buffer, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
@@ -363,23 +239,19 @@ public class RenderSystem {
|
||||
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();
|
||||
GL11.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GL11.glPushMatrix();
|
||||
matrix.get(0, PROJECTION_MATRIX_BUFFER);
|
||||
GLStateManager.glLoadMatrix(PROJECTION_MATRIX_BUFFER);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GL11.glLoadMatrix(PROJECTION_MATRIX_BUFFER);
|
||||
GL11.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
public static void restoreProjectionMatrix() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GL11.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GL11.glPopMatrix();
|
||||
GL11.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) {
|
||||
@@ -402,122 +274,4 @@ public class RenderSystem {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int createBuffers() {
|
||||
return GL15.glGenBuffers();
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
GL30.glBindBufferBase(target, index, buffer);
|
||||
}
|
||||
|
||||
public static void bufferStorage(int target, long size, int flags) {
|
||||
if (GLStateManager.capabilities.OpenGL44) {
|
||||
GL44.glBufferStorage(target, size, flags);
|
||||
} else if (GLStateManager.capabilities.GL_ARB_buffer_storage) {
|
||||
ARBBufferStorage.glBufferStorage(target, size, flags);
|
||||
} else {
|
||||
// Fallback: use mutable storage
|
||||
GL15.glBufferData(target, size, GL15.GL_DYNAMIC_DRAW);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (GLStateManager.capabilities.OpenGL43) {
|
||||
GL43.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
|
||||
} else if (GLStateManager.capabilities.GL_ARB_clear_buffer_object) {
|
||||
ARBClearBufferObject.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteBuffers(int buffer) {
|
||||
GL15.glDeleteBuffers(buffer);
|
||||
}
|
||||
|
||||
public static long getVRAM() {
|
||||
if (GLStateManager.capabilities.GL_NVX_gpu_memory_info) {
|
||||
return GL11.glGetInteger(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) {
|
||||
if (GLStateManager.capabilities.OpenGL44) {
|
||||
GL44.glClearTexImage(texture, level, format, type, (ByteBuffer) null);
|
||||
} else {
|
||||
ARBClearTexture.glClearTexImage(texture, level, format, type, (ByteBuffer) null);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
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 GL33.glGenSamplers();
|
||||
}
|
||||
|
||||
public static void destroySampler(int sampler) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GL33.glDeleteSamplers(sampler);
|
||||
}
|
||||
|
||||
public static void samplerParameteri(int sampler, int pname, int param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GL33.glSamplerParameteri(sampler, pname, param);
|
||||
}
|
||||
|
||||
public static void bindSamplerToUnit(int unit, int sampler) {
|
||||
if (!supportsSamplerObjects) return;
|
||||
if (samplers[unit] == sampler) return;
|
||||
GL33.glBindSampler(unit, sampler);
|
||||
samplers[unit] = sampler;
|
||||
}
|
||||
|
||||
public static void unbindAllSamplers() {
|
||||
if (!supportsSamplerObjects) return;
|
||||
for (int i = 0; i < samplers.length; i++) {
|
||||
if (samplers[i] != 0) {
|
||||
GL33.glBindSampler(i, 0);
|
||||
samplers[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.MultMatrixCmd;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class for tracking and collapsing matrix transforms during optimization.
|
||||
* Implements the transform accumulation and emission strategy:
|
||||
* <ul>
|
||||
* <li>accumulated: total relative transform (what GL matrix should be)</li>
|
||||
* <li>lastEmitted: what we've emitted (what GL matrix is)</li>
|
||||
* <li>stack: saved accumulated values at Push points</li>
|
||||
* </ul>
|
||||
*/
|
||||
class TransformOptimizer {
|
||||
private static final Logger LOGGER = LogManager.getLogger("TransformOptimizer");
|
||||
|
||||
private final Matrix4f accumulated = new Matrix4f(); // Current accumulated transform
|
||||
private final Matrix4f lastEmitted = new Matrix4f(); // Last emitted transform
|
||||
private final Deque<Matrix4f> stack = new ArrayDeque<>(); // For Push/Pop
|
||||
private final int listId; // For logging
|
||||
private boolean absoluteMatrixFlag = false; // True after LoadMatrix, cleared by LoadIdentity emit
|
||||
|
||||
TransformOptimizer(int listId) {
|
||||
this.listId = listId;
|
||||
accumulated.identity();
|
||||
lastEmitted.identity();
|
||||
}
|
||||
|
||||
Matrix4f getAccumulated() {
|
||||
return accumulated;
|
||||
}
|
||||
|
||||
void loadIdentity() {
|
||||
accumulated.identity();
|
||||
}
|
||||
|
||||
void pushTransform() {
|
||||
stack.push(new Matrix4f(accumulated));
|
||||
}
|
||||
|
||||
void popTransform() {
|
||||
if (!stack.isEmpty()) {
|
||||
final Matrix4f popped = stack.pop();
|
||||
accumulated.set(popped);
|
||||
// After Pop, GL state is restored to the pushed value so lastEmitted should also reflect this
|
||||
lastEmitted.set(accumulated);
|
||||
} else {
|
||||
LOGGER.warn("[TransformOptimizer] list={} Pop with empty stack - resetting to identity", listId);
|
||||
accumulated.identity();
|
||||
lastEmitted.identity();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isIdentity() {
|
||||
return DisplayListManager.isIdentity(accumulated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's a pending transform that hasn't been emitted yet.
|
||||
*/
|
||||
boolean hasPendingTransform() {
|
||||
return !accumulated.equals(lastEmitted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a MultMatrix command if accumulated differs from lastEmitted.
|
||||
* Updates lastEmitted to match accumulated after emission.
|
||||
*/
|
||||
void emitPendingTransform(List<DisplayListCommand> output) {
|
||||
if (!accumulated.equals(lastEmitted)) {
|
||||
emitTransformTo(output, accumulated);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a MultMatrix command to make GL state match a target transform.
|
||||
* Used when a draw expects a specific transform that may differ from accumulated.
|
||||
* Updates lastEmitted to match target after emission.
|
||||
*
|
||||
* @param output The command list to emit to
|
||||
* @param target The target transform to reach
|
||||
*/
|
||||
void emitTransformTo(List<DisplayListCommand> output, Matrix4f target) {
|
||||
if (target.equals(lastEmitted)) {
|
||||
// Already at target - nothing to emit
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate delta: what we need to multiply to go from lastEmitted to target
|
||||
// delta = inverse(lastEmitted) * target
|
||||
// But for simplicity, if lastEmitted is identity, just emit target
|
||||
if (DisplayListManager.isIdentity(lastEmitted)) {
|
||||
// Simple case: just emit target
|
||||
output.add(MultMatrixCmd.create(target));
|
||||
} else {
|
||||
// Need to emit delta: inv(lastEmitted) * target
|
||||
final Matrix4f delta = new Matrix4f(lastEmitted).invert().mul(target);
|
||||
output.add(MultMatrixCmd.create(delta));
|
||||
}
|
||||
lastEmitted.set(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a draw's expected transform differs from what we've emitted.
|
||||
*/
|
||||
boolean needsTransformForDraw(Matrix4f drawTransform) {
|
||||
return !drawTransform.equals(lastEmitted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark that the GL matrix is at an absolute value (after LoadMatrix).
|
||||
* Subsequent LoadIdentity commands must be emitted to actually reset GL to identity.
|
||||
*/
|
||||
void markAbsoluteMatrix() {
|
||||
absoluteMatrixFlag = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the GL matrix is at an absolute value and clear the flag.
|
||||
* Used to determine if LoadIdentity needs to emit.
|
||||
* @return true if LoadMatrix was called and not yet reset by LoadIdentity
|
||||
*/
|
||||
boolean checkAndClearAbsoluteMatrix() {
|
||||
boolean was = absoluteMatrixFlag;
|
||||
absoluteMatrixFlag = false;
|
||||
return was;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
/// Matches
|
||||
public enum Vendor {
|
||||
AMD("amd"),
|
||||
INTEL("intel"),
|
||||
MESA("mesa"),
|
||||
NVIDIA("nvidia"),
|
||||
UNKNOWN("");
|
||||
|
||||
final String[] names;
|
||||
|
||||
Vendor(String... names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
public static Vendor getVendor(String vendorString) {
|
||||
for (var v : values()) {
|
||||
for (var name : v.names) {
|
||||
if (vendorString.contains(name)) return v;
|
||||
}
|
||||
}
|
||||
|
||||
// Shouldn't be needed as "" matches everything, but sanity checks good.
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.compat;
|
||||
|
||||
/**
|
||||
* Simple holder for fog color values captured from vanilla rendering.
|
||||
* Used by terrain renderers to access fog state.
|
||||
*/
|
||||
public class FogHelper {
|
||||
public static float red;
|
||||
public static float green;
|
||||
public static float blue;
|
||||
}
|
||||
@@ -1,34 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.glsm.debug;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
// Adapted from https://github.com/makamys/CoreTweaks/blob/master/src/main/java/makamys/coretweaks/util/OpenGLDebugging.java;
|
||||
// originally from https://github.com/TheGreyGhost/MinecraftByExample/blob/1-8final/src/main/java/minecraftbyexample/usefultools/OpenGLdebugging.java
|
||||
|
||||
@@ -37,13 +25,12 @@ public class OpenGLDebugging {
|
||||
public static final Logger LOGGER = LogManager.getLogger("gldumper");
|
||||
|
||||
private static final Consumer<String> LINE_LOGGER = LOGGER::debug;
|
||||
private static final Set<String> seenErrors = new HashSet<>();
|
||||
public static final String GL_IS_ENABLED = "glIsEnabled()";
|
||||
public static final String GL_GET_BOOLEANV = "glGetBooleanv()";
|
||||
public static final String GL_GET_INTEGERV = "glGetIntegerv()";
|
||||
public static final String GL_GET_FLOATV = "glGetFloatv()";
|
||||
|
||||
public static class GLproperty {
|
||||
public class GLproperty {
|
||||
|
||||
public GLproperty(int glConstant, String name, String description, String category, String fetchCommand) {
|
||||
this.gLConstant = glConstant;
|
||||
@@ -458,250 +445,30 @@ public class OpenGLDebugging {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getGLSMErrorLog() {
|
||||
return getGLSMErrorLog(null, false, false);
|
||||
}
|
||||
|
||||
// Re-entrancy guard - getGLSMErrorLog calls glActiveTexture which could trigger checkGLSMOnce
|
||||
private static boolean inCheck = false;
|
||||
|
||||
/**
|
||||
* Enhanced GLSM error logging with context, stack trace, and first-occurrence filtering.
|
||||
*
|
||||
* @param context Optional context string to include in error messages (e.g., "During rendering")
|
||||
* @param logStacktrace If true, includes the current stack trace in the error output
|
||||
* @param onlyFirstOccurrence If true, only logs each unique error once
|
||||
* @return Error log string if mismatches found, null otherwise
|
||||
*/
|
||||
public static String getGLSMErrorLog(String context, boolean logStacktrace, boolean onlyFirstOccurrence) {
|
||||
if (inCheck) return null;
|
||||
if (!GLStateManager.isCachingEnabled() || GLStateManager.isRecordingDisplayList()) {
|
||||
return null;
|
||||
}
|
||||
inCheck = true;
|
||||
try {
|
||||
String group = "checkGLSM" + (context == null ? "" : " " + context);
|
||||
GLDebug.pushGroup(group);
|
||||
final StringBuilder error = new StringBuilder();
|
||||
public static void checkGLSM() {
|
||||
boolean mismatch = false;
|
||||
|
||||
if (context != null && !context.isEmpty()) {
|
||||
error.append("[").append(context).append("] ");
|
||||
}
|
||||
|
||||
for (int i = 0; i < instance.propertyList.length; ++i) {
|
||||
final GLproperty gLProperty = instance.propertyList[i];
|
||||
if (gLProperty.fetchCommand.equals(GL_GET_FLOATV) && gLProperty.name.endsWith("_MATRIX")) {
|
||||
if(gLProperty.fetchCommand.equals(GL_GET_FLOATV) && gLProperty.name.endsWith("_MATRIX")) {
|
||||
final Matrix4f cached = gLProperty.getAsMatrix4f(true);
|
||||
final Matrix4f uncached = gLProperty.getAsMatrix4f(false);
|
||||
|
||||
// Skip comparison if either matrix is degenerate (contains NaN/Infinity) these represent invalid GL state from bad parameters, not cache bugs
|
||||
if (isMatrixDegenerate(cached) || isMatrixDegenerate(uncached)) continue;
|
||||
|
||||
if (!matricesEquivalent(cached, uncached, 0.001f)) {
|
||||
final String errorKey = "MATRIX:" + gLProperty.name;
|
||||
if (!onlyFirstOccurrence || seenErrors.add(errorKey)) {
|
||||
final String errorMsg = "GLSM mismatch: " + gLProperty.name + "\ncached:\n" + cached + "uncached:\n" + uncached;
|
||||
LOGGER.error(errorMsg);
|
||||
error.append(errorMsg).append('\n');
|
||||
mismatch = true;
|
||||
}
|
||||
// Some precision issues due to RAD vs degrees, and translated (vs f)
|
||||
if (!cached.equals(uncached, 0.001f)) {
|
||||
LOGGER.error("GLSM mismatch: " + gLProperty.name + "\ncached:\n" + cached + "uncached:\n" + uncached);
|
||||
mismatch = true;
|
||||
}
|
||||
} else {
|
||||
final String cached = gLProperty.getAsString(true);
|
||||
final String uncached = gLProperty.getAsString(false);
|
||||
if (!cached.equals(uncached)) {
|
||||
final String errorKey = "PROPERTY:" + gLProperty.name + ":" + cached + ":" + uncached;
|
||||
if (!onlyFirstOccurrence || seenErrors.add(errorKey)) {
|
||||
error.append("GLSM mismatch: ").append(gLProperty.name).append(" cached: ").append(cached).append(" uncached: ").append(uncached);
|
||||
error.append('\n');
|
||||
mismatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final TextureUnitArray textureUnits = GLStateManager.getTextures();
|
||||
final int currentActiveTexture = GLStateManager.getActiveTextureUnit();
|
||||
|
||||
// Check GL_ACTIVE_TEXTURE
|
||||
final int glActiveUnit = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE) - GL13.GL_TEXTURE0;
|
||||
if (glActiveUnit != currentActiveTexture) {
|
||||
final String errorKey = "ACTIVE_TEXTURE:" + currentActiveTexture + ":" + glActiveUnit;
|
||||
if (!onlyFirstOccurrence || seenErrors.add(errorKey)) {
|
||||
error.append("GLSM mismatch: GL_ACTIVE_TEXTURE cached: ").append(currentActiveTexture)
|
||||
.append(" uncached: ").append(glActiveUnit).append('\n');
|
||||
mismatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check per-unit texture state
|
||||
for (int i = 0; i < textureUnits.textureMatricies.length; i++) {
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + i);
|
||||
|
||||
// Check GL_TEXTURE_2D enabled
|
||||
final boolean cachedEnabled = textureUnits.getTextureUnitStates(i).isEnabled();
|
||||
final boolean uncachedEnabled = GL11.glIsEnabled(GL11.GL_TEXTURE_2D);
|
||||
if (cachedEnabled != uncachedEnabled) {
|
||||
final String errorKey = "TEXTURE_2D:" + i + ":" + cachedEnabled + ":" + uncachedEnabled;
|
||||
if (!onlyFirstOccurrence || seenErrors.add(errorKey)) {
|
||||
error.append("GLSM mismatch: GL_TEXTURE_2D cached: ").append(cachedEnabled)
|
||||
.append(" uncached: ").append(uncachedEnabled)
|
||||
.append(", texture unit: GL_TEXTURE").append(i).append('\n');
|
||||
mismatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check GL_TEXTURE_BINDING_2D
|
||||
final int cachedBinding = textureUnits.getTextureUnitBindings(i).getBinding();
|
||||
final int uncachedBinding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
if (cachedBinding != uncachedBinding) {
|
||||
final String errorKey = "TEXTURE_BINDING_2D:" + i + ":" + cachedBinding + ":" + uncachedBinding;
|
||||
if (!onlyFirstOccurrence || seenErrors.add(errorKey)) {
|
||||
error.append("GLSM mismatch: GL_TEXTURE_BINDING_2D cached: ").append(cachedBinding)
|
||||
.append(" uncached: ").append(uncachedBinding)
|
||||
.append(", texture unit: GL_TEXTURE").append(i).append('\n');
|
||||
LOGGER.error("GLSM mismatch: " + gLProperty.name + " cached: " + cached + " uncached: " + uncached);
|
||||
mismatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + currentActiveTexture);
|
||||
|
||||
// Add stack trace if requested
|
||||
if (mismatch && logStacktrace) {
|
||||
error.append("\nStack trace:\n");
|
||||
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
for (int i = 2; i < stackTrace.length; i++) {
|
||||
error.append(" at ").append(stackTrace[i].toString()).append('\n');
|
||||
}
|
||||
}
|
||||
GLDebug.popGroup();
|
||||
|
||||
return mismatch ? error.toString() : null;
|
||||
} finally {
|
||||
inCheck = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkGLSMOnce(String context) {
|
||||
final String output = getGLSMErrorLog(context, true, true);
|
||||
|
||||
if (output != null) {
|
||||
// Only log on actual mismatch
|
||||
LOGGER.error("[{}] MISMATCH thread={}\n{}", context, Thread.currentThread().getName(), output);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a matrix contains any non-finite values (NaN or Infinity).
|
||||
* Such matrices represent invalid/degenerate GL state (e.g., zero near/far in projection).
|
||||
*/
|
||||
private static boolean isMatrixDegenerate(Matrix4f matrix) {
|
||||
for (int col = 0; col < 4; col++) {
|
||||
for (int row = 0; row < 4; row++) {
|
||||
if (!Float.isFinite(matrix.get(col, row))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two matrices with tolerance for NaN/Infinity.
|
||||
* Treats NaN == NaN and Infinity == Infinity as equal (both are degenerate states).
|
||||
* For finite values, uses epsilon comparison.
|
||||
*/
|
||||
private static boolean matricesEquivalent(Matrix4f a, Matrix4f b, float epsilon) {
|
||||
// Compare all 16 elements
|
||||
for (int col = 0; col < 4; col++) {
|
||||
for (int row = 0; row < 4; row++) {
|
||||
final float aVal = a.get(col, row);
|
||||
final float bVal = b.get(col, row);
|
||||
|
||||
// If both are non-finite (NaN or Infinity), consider them equal
|
||||
if (!Float.isFinite(aVal) && !Float.isFinite(bVal)) {
|
||||
// Both non-finite: check if both NaN or both same Infinity
|
||||
if (Float.isNaN(aVal) && Float.isNaN(bVal)) continue;
|
||||
if (Float.isInfinite(aVal) && Float.isInfinite(bVal)) {
|
||||
// Check same sign of infinity
|
||||
if ((aVal > 0) == (bVal > 0)) continue;
|
||||
}
|
||||
return false; // Different non-finite values
|
||||
}
|
||||
|
||||
// If one is finite and other is not, they differ
|
||||
if (Float.isFinite(aVal) != Float.isFinite(bVal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Both finite: use epsilon comparison
|
||||
if (Math.abs(aVal - bVal) > epsilon) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void checkGLSM() {
|
||||
final String output = getGLSMErrorLog();
|
||||
|
||||
if (output == null) {
|
||||
if (!mismatch) {
|
||||
LOGGER.info("GLSM state matches!");
|
||||
} else {
|
||||
LOGGER.error(output);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyTexture(ResourceLocation resource, File output) {
|
||||
final Object object = Minecraft.getMinecraft().renderEngine.mapTextureObjects.get(resource);
|
||||
|
||||
if (object != null) {
|
||||
copyTexture(((ITextureObject) object).getGlTextureId(), output);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyTexture(int textureID, File output) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
|
||||
|
||||
int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH);
|
||||
int height = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_HEIGHT);
|
||||
|
||||
BufferedImage bufferedimage = new BufferedImage(width, height, 1);
|
||||
final int[] pixelValues = ((DataBufferInt) bufferedimage.getRaster().getDataBuffer()).getData();
|
||||
IntBuffer pixelBuffer = BufferUtils.createIntBuffer(pixelValues.length);
|
||||
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer);
|
||||
pixelBuffer.get(pixelValues);
|
||||
|
||||
// Flip texture
|
||||
final int halfHeight = height / 2;
|
||||
final int[] rowBuffer = new int[width];
|
||||
for (int y = 0; y < halfHeight; y++) {
|
||||
final int top = y * width;
|
||||
final int bottom = (height - 1 - y) * width;
|
||||
|
||||
System.arraycopy(pixelValues, top, rowBuffer, 0, width);
|
||||
System.arraycopy(pixelValues, bottom, pixelValues, top, width);
|
||||
System.arraycopy(rowBuffer, 0, pixelValues, bottom, width);
|
||||
}
|
||||
|
||||
File dir = output.getParentFile();
|
||||
dir.mkdirs();
|
||||
copyBufferedImageToFile(bufferedimage, output);
|
||||
bufferedimage.flush();
|
||||
}
|
||||
|
||||
|
||||
private static void copyBufferedImageToFile(BufferedImage image, File file) {
|
||||
try {
|
||||
ImageIO.write(image, "png", file);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.ARBDirectStateAccess;
|
||||
import org.lwjgl.opengl.GL45;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
@@ -15,29 +14,6 @@ public class DSAARB extends DSAUnsupported {
|
||||
ARBDirectStateAccess.glGenerateTextureMipmap(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
// Note: DSA glTextureImage2D doesn't exist in ARB_direct_state_access
|
||||
// We need to use glTextureStorage2D + glTextureSubImage2D, or fall back to bind-based approach
|
||||
// For simplicity, use the fallback which binds, uploads, and restores
|
||||
super.textureImage2D(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) {
|
||||
super.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
ARBDirectStateAccess.glTextureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
ARBDirectStateAccess.glTextureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texParameteri(int texture, int target, int pname, int param) {
|
||||
if(!GLStateManager.updateTexParameteriCache(target, texture, pname, param)) return;
|
||||
@@ -72,11 +48,6 @@ public class DSAARB extends DSAUnsupported {
|
||||
return GLStateManager.getTexParameterOrDefault(texture, pname, () -> ARBDirectStateAccess.glGetTextureParameteri(texture, pname));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
return ARBDirectStateAccess.glGetTextureLevelParameteri(texture, level, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
ARBDirectStateAccess.glCopyTextureSubImage2D(destTexture, i, i1, i2, i3, i4, width, height);
|
||||
@@ -84,7 +55,7 @@ public class DSAARB extends DSAUnsupported {
|
||||
|
||||
@Override
|
||||
public void bindTextureToUnit(int unit, int texture) {
|
||||
if(GLStateManager.getBoundTextureForServerState(unit) == texture) return;
|
||||
if(GLStateManager.getBoundTexture(unit) == texture) return;
|
||||
|
||||
if (texture == 0) {
|
||||
super.bindTextureToUnit(unit, texture);
|
||||
@@ -94,11 +65,6 @@ public class DSAARB extends DSAUnsupported {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTextureToUnit(int target, int unit, int texture) {
|
||||
bindTextureToUnit(unit, texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
final int buffer = GL45.glCreateBuffers();
|
||||
@@ -126,19 +92,4 @@ public class DSAARB extends DSAUnsupported {
|
||||
public int createTexture(int target) {
|
||||
return ARBDirectStateAccess.glCreateTextures(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
|
||||
ARBDirectStateAccess.glTextureStorage1D(texture, levels, internalFormat, width);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
|
||||
ARBDirectStateAccess.glTextureStorage2D(texture, levels, internalFormat, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
|
||||
ARBDirectStateAccess.glTextureStorage3D(texture, levels, internalFormat, width, height, depth);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.glsm.dsa;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public interface DSAAccess {
|
||||
void generateMipmaps(int texture, int target);
|
||||
|
||||
// Texture image upload - DSA allows specifying texture directly without binding
|
||||
void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels);
|
||||
void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels);
|
||||
void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels);
|
||||
void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels);
|
||||
|
||||
void texParameteri(int texture, int target, int pname, int param);
|
||||
void texParameterf(int texture, int target, int pname, float param);
|
||||
void texParameteriv(int texture, int target, int pname, IntBuffer params);
|
||||
@@ -23,12 +16,9 @@ public interface DSAAccess {
|
||||
|
||||
int getTexParameteri(int texture, int target, int pname);
|
||||
|
||||
int getTexLevelParameteri(int texture, int level, int pname);
|
||||
|
||||
void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height);
|
||||
|
||||
void bindTextureToUnit(int unit, int texture);
|
||||
void bindTextureToUnit(int target, int unit, int texture);
|
||||
|
||||
int bufferStorage(int target, FloatBuffer data, int usage);
|
||||
|
||||
@@ -38,9 +28,4 @@ public interface DSAAccess {
|
||||
|
||||
int createFramebuffer();
|
||||
int createTexture(int target);
|
||||
|
||||
// Texture storage methods for custom images
|
||||
void textureStorage1D(int texture, int target, int levels, int internalFormat, int width);
|
||||
void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height);
|
||||
void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth);
|
||||
}
|
||||
@@ -2,16 +2,13 @@ package com.gtnewhorizons.angelica.glsm.dsa;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
@@ -23,39 +20,6 @@ public class DSAUnsupported implements DSAAccess {
|
||||
GL30.glGenerateMipmap(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
// Get what cache thinks is bound - we'll restore to this to keep cache/GL in sync
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding); // Restore to cache value
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
final int cachedBinding = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GL11.glBindTexture(target, cachedBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texParameteri(int texture, int target, int pname, int param) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
@@ -76,13 +40,13 @@ public class DSAUnsupported implements DSAAccess {
|
||||
|
||||
@Override
|
||||
public void readBuffer(int framebuffer, int buffer) {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
OpenGlHelper.func_153171_g/*glBindFramebuffer*/(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
GL11.glReadBuffer(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBuffers(int framebuffer, IntBuffer buffers) {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
OpenGlHelper.func_153171_g/*glBindFramebuffer*/(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
GL20.glDrawBuffers(buffers);
|
||||
}
|
||||
|
||||
@@ -92,18 +56,9 @@ public class DSAUnsupported implements DSAAccess {
|
||||
return GLStateManager.glGetTexParameteri(target, pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
final int previous = GLStateManager.getBoundTextureForServerState();
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
final int result = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, pname);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
final int previous = GLStateManager.getBoundTextureForServerState();
|
||||
final int previous = GLStateManager.getBoundTexture();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, destTexture);
|
||||
GL11.glCopyTexSubImage2D(target, i, i1, i2, i3, i4, width, height);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
@@ -115,18 +70,12 @@ public class DSAUnsupported implements DSAAccess {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTextureToUnit(int target, int unit, int texture) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
final int buffer = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
GL15.glBindBuffer(target, buffer);
|
||||
RenderSystem.bufferData(target, data, usage);
|
||||
GLStateManager.glBindBuffer(target, 0);
|
||||
GL15.glBindBuffer(target, 0);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
@@ -134,21 +83,21 @@ public class DSAUnsupported implements DSAAccess {
|
||||
@Override
|
||||
public void blitFramebuffer(int source, int dest, int offsetX, int offsetY, int width, int height, int offsetX2, int offsetY2, int width2, int height2,
|
||||
int bufferChoice, int filter) {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, source);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, dest);
|
||||
OpenGlHelper.func_153171_g/*glBindFramebuffer*/(GL30.GL_READ_FRAMEBUFFER, source);
|
||||
OpenGlHelper.func_153171_g/*glBindFramebuffer*/(GL30.GL_DRAW_FRAMEBUFFER, dest);
|
||||
GL30.glBlitFramebuffer(offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
GLStateManager.glBindFramebuffer(fbtarget, fb);
|
||||
OpenGlHelper.func_153171_g/*glBindFramebuffer*/(fbtarget, fb);
|
||||
GL30.glFramebufferTexture2D(fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int createFramebuffer() {
|
||||
final int framebuffer = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
final int framebuffer = OpenGlHelper.func_153165_e/*glGenFramebuffers*/();
|
||||
OpenGlHelper.func_153171_g/*glBindFramebuffer*/(GL30.GL_FRAMEBUFFER, framebuffer);
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
@@ -158,28 +107,4 @@ public class DSAUnsupported implements DSAAccess {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
|
||||
final int previous = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_1D);
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL42.glTexStorage1D(target, levels, internalFormat, width);
|
||||
GL11.glBindTexture(target, previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
|
||||
final int previous = (target == GL11.GL_TEXTURE_2D) ? GLStateManager.getBoundTextureForServerState() : GL11.glGetInteger(GL31.GL_TEXTURE_BINDING_RECTANGLE);
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL42.glTexStorage2D(target, levels, internalFormat, width, height);
|
||||
GL11.glBindTexture(target, previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
|
||||
final int previous = GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D);
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL42.glTexStorage3D(target, levels, internalFormat, width, height, depth);
|
||||
GL11.glBindTexture(target, previous);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents an accumulated draw call during display list compilation.
|
||||
* Matrix-as-Data Architecture: Stores untransformed vertices + captured matrix.
|
||||
* The matrix will be applied at runtime, not baked into vertices.
|
||||
*/
|
||||
public final class AccumulatedDraw {
|
||||
public final VertexFormat format;
|
||||
public final List<ByteBuffer> drawBuffers;
|
||||
public final int drawMode;
|
||||
public final int commandIndex; // Position in command list for state tracking
|
||||
public final int stateGeneration; // Position in command list for state tracking
|
||||
public RestoreData restoreData;
|
||||
|
||||
public AccumulatedDraw(DirectTessellator tessellator, int commandIndex, int stateGeneration, boolean copyLast) {
|
||||
this.format = tessellator.getVertexFormat();
|
||||
this.drawMode = tessellator.drawMode;
|
||||
this.drawBuffers = new ArrayList<>();
|
||||
this.drawBuffers.add(tessellator.allocateBufferCopy());
|
||||
this.commandIndex = commandIndex;
|
||||
this.stateGeneration = stateGeneration;
|
||||
|
||||
if (copyLast) {
|
||||
this.restoreData = new RestoreData(tessellator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void mergeDraw(DirectTessellator tessellator, boolean copyLast) {
|
||||
this.drawBuffers.add(tessellator.allocateBufferCopy());
|
||||
|
||||
if (copyLast) {
|
||||
this.restoreData = new RestoreData(tessellator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds last vertex attribute values for GL state restoration after VBO draw.
|
||||
* Used to sync GLSM cache with actual GL state after VBO rendering.
|
||||
*/
|
||||
public static final class RestoreData {
|
||||
public final int lastColor;
|
||||
public final int lastNormal;
|
||||
public final float lastTexCoordU;
|
||||
public final float lastTexCoordV;
|
||||
public final int flags;
|
||||
|
||||
public RestoreData(DirectTessellator tessellator) {
|
||||
this.lastColor = tessellator.getPackedColor();
|
||||
this.lastNormal = tessellator.getPackedNormal();
|
||||
this.lastTexCoordU = (float) tessellator.getLastTextureU();
|
||||
this.lastTexCoordV = (float) tessellator.getLastTextureV();
|
||||
this.flags = tessellator.getVertexFormat().getVertexFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,765 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
public final class CommandBuffer {
|
||||
private static final int DEFAULT_CAPACITY = 4096; // Guess at typical display list size
|
||||
|
||||
private ByteBuffer buffer;
|
||||
private long basePointer;
|
||||
private long writePointer;
|
||||
private final List<Object> complexObjects = new ArrayList<>();
|
||||
|
||||
public CommandBuffer() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
public CommandBuffer(int initialCapacity) {
|
||||
this.buffer = memAlloc(initialCapacity);
|
||||
this.basePointer = memAddress(buffer);
|
||||
this.writePointer = basePointer;
|
||||
}
|
||||
|
||||
// === Low-level write operations ===
|
||||
|
||||
private void writeInt(int value) {
|
||||
memPutInt(writePointer, value);
|
||||
writePointer += 4;
|
||||
}
|
||||
|
||||
private void writeLong(long value) {
|
||||
memPutLong(writePointer, value);
|
||||
writePointer += 8;
|
||||
}
|
||||
|
||||
private void writeFloat(float value) {
|
||||
memPutFloat(writePointer, value);
|
||||
writePointer += 4;
|
||||
}
|
||||
|
||||
private void writeDouble(double value) {
|
||||
memPutDouble(writePointer, value);
|
||||
writePointer += 8;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int needed) {
|
||||
final int size = size();
|
||||
final int remaining = buffer.capacity() - size;
|
||||
if (remaining < needed) {
|
||||
final int newCapacity = Math.max(buffer.capacity() * 2, buffer.capacity() + needed);
|
||||
buffer = memRealloc(buffer, newCapacity);
|
||||
basePointer = memAddress(buffer);
|
||||
writePointer = basePointer + size;
|
||||
}
|
||||
}
|
||||
|
||||
// === Single int commands ===
|
||||
|
||||
public void writeEnable(int cap) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.ENABLE);
|
||||
writeInt(cap);
|
||||
}
|
||||
|
||||
public void writeDisable(int cap) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DISABLE);
|
||||
writeInt(cap);
|
||||
}
|
||||
|
||||
public void writeClear(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CLEAR);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeClearStencil(int s) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CLEAR_STENCIL);
|
||||
writeInt(s);
|
||||
}
|
||||
|
||||
public void writeCullFace(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CULL_FACE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeDepthFunc(int func) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DEPTH_FUNC);
|
||||
writeInt(func);
|
||||
}
|
||||
|
||||
public void writeShadeModel(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.SHADE_MODEL);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeLogicOp(int opcode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.LOGIC_OP);
|
||||
writeInt(opcode);
|
||||
}
|
||||
|
||||
public void writeMatrixMode(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.MATRIX_MODE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeActiveTexture(int texture) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.ACTIVE_TEXTURE);
|
||||
writeInt(texture);
|
||||
}
|
||||
|
||||
public void writeUseProgram(int program) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.USE_PROGRAM);
|
||||
writeInt(program);
|
||||
}
|
||||
|
||||
public void writePushAttrib(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.PUSH_ATTRIB);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writePopAttrib() {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.POP_ATTRIB);
|
||||
writeInt(0); // unused padding
|
||||
}
|
||||
|
||||
public void writeLoadIdentity() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.LOAD_IDENTITY);
|
||||
}
|
||||
|
||||
public void writePushMatrix() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.PUSH_MATRIX);
|
||||
}
|
||||
|
||||
public void writePopMatrix() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.POP_MATRIX);
|
||||
}
|
||||
|
||||
public void writeStencilMask(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.STENCIL_MASK);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeDepthMask(boolean flag) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DEPTH_MASK);
|
||||
writeInt(flag ? 1 : 0);
|
||||
}
|
||||
|
||||
public void writeFrontFace(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.FRONT_FACE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
// === Two int commands ===
|
||||
|
||||
public void writeBindTexture(int target, int texture) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.BIND_TEXTURE);
|
||||
writeInt(target);
|
||||
writeInt(texture);
|
||||
}
|
||||
|
||||
public void writePolygonMode(int face, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.POLYGON_MODE);
|
||||
writeInt(face);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeColorMaterial(int face, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.COLOR_MATERIAL);
|
||||
writeInt(face);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeLineStipple(int factor, int pattern) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LINE_STIPPLE);
|
||||
writeInt(factor);
|
||||
writeInt(pattern);
|
||||
}
|
||||
|
||||
public void writeStencilMaskSeparate(int face, int mask) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.STENCIL_MASK_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeFogi(int pname, int param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.FOGI);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void writeHint(int target, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.HINT);
|
||||
writeInt(target);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
// === Three int commands ===
|
||||
|
||||
public void writeStencilFunc(int func, int ref, int mask) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.STENCIL_FUNC);
|
||||
writeInt(func);
|
||||
writeInt(ref);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeStencilOp(int fail, int zfail, int zpass) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.STENCIL_OP);
|
||||
writeInt(fail);
|
||||
writeInt(zfail);
|
||||
writeInt(zpass);
|
||||
}
|
||||
|
||||
public void writeTexParameteri(int target, int pname, int param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.TEX_PARAMETERI);
|
||||
writeInt(target);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
// === Four int commands ===
|
||||
|
||||
public void writeViewport(int x, int y, int width, int height) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.VIEWPORT);
|
||||
writeInt(x);
|
||||
writeInt(y);
|
||||
writeInt(width);
|
||||
writeInt(height);
|
||||
}
|
||||
|
||||
public void writeBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.BLEND_FUNC);
|
||||
writeInt(srcRgb);
|
||||
writeInt(dstRgb);
|
||||
writeInt(srcAlpha);
|
||||
writeInt(dstAlpha);
|
||||
}
|
||||
|
||||
public void writeColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.COLOR_MASK);
|
||||
writeInt(red ? 1 : 0);
|
||||
writeInt(green ? 1 : 0);
|
||||
writeInt(blue ? 1 : 0);
|
||||
writeInt(alpha ? 1 : 0);
|
||||
}
|
||||
|
||||
public void writeStencilFuncSeparate(int face, int func, int ref, int mask) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.STENCIL_FUNC_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(func);
|
||||
writeInt(ref);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.STENCIL_OP_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(sfail);
|
||||
writeInt(dpfail);
|
||||
writeInt(dppass);
|
||||
}
|
||||
|
||||
// === Float commands ===
|
||||
|
||||
public void writePointSize(float size) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.POINT_SIZE);
|
||||
writeFloat(size);
|
||||
}
|
||||
|
||||
public void writeLineWidth(float width) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.LINE_WIDTH);
|
||||
writeFloat(width);
|
||||
}
|
||||
|
||||
public void writePolygonOffset(float factor, float units) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.POLYGON_OFFSET);
|
||||
writeFloat(factor);
|
||||
writeFloat(units);
|
||||
}
|
||||
|
||||
public void writeColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void writeClearColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.CLEAR_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void writeClearDepth(double depth) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.CLEAR_DEPTH);
|
||||
writeDouble(depth);
|
||||
}
|
||||
|
||||
public void writeBlendColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.BLEND_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
// === Mixed int+float commands ===
|
||||
|
||||
public void writeAlphaFunc(int func, float ref) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.ALPHA_FUNC);
|
||||
writeInt(func);
|
||||
writeFloat(ref);
|
||||
}
|
||||
|
||||
public void writeFogf(int pname, float param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.FOGF);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeLightf(int light, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.LIGHTF);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeLightModelf(int pname, float param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LIGHT_MODELF);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeLighti(int light, int pname, int param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.LIGHTI);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void writeLightModeli(int pname, int param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LIGHT_MODELI);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void writeMaterialf(int face, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.MATERIALF);
|
||||
writeInt(face);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeTexParameterf(int target, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.TEX_PARAMETERF);
|
||||
writeInt(target);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
// === Double commands ===
|
||||
|
||||
public void writeTranslate(double x, double y, double z) {
|
||||
ensureCapacity(28);
|
||||
writeInt(GLCommand.TRANSLATE);
|
||||
writeDouble(x);
|
||||
writeDouble(y);
|
||||
writeDouble(z);
|
||||
}
|
||||
|
||||
public void writeRotate(double angle, double x, double y, double z) {
|
||||
ensureCapacity(36);
|
||||
writeInt(GLCommand.ROTATE);
|
||||
writeDouble(angle);
|
||||
writeDouble(x);
|
||||
writeDouble(y);
|
||||
writeDouble(z);
|
||||
}
|
||||
|
||||
public void writeScale(double x, double y, double z) {
|
||||
ensureCapacity(28);
|
||||
writeInt(GLCommand.SCALE);
|
||||
writeDouble(x);
|
||||
writeDouble(y);
|
||||
writeDouble(z);
|
||||
}
|
||||
|
||||
public void writeOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
ensureCapacity(52);
|
||||
writeInt(GLCommand.ORTHO);
|
||||
writeDouble(left);
|
||||
writeDouble(right);
|
||||
writeDouble(bottom);
|
||||
writeDouble(top);
|
||||
writeDouble(zNear);
|
||||
writeDouble(zFar);
|
||||
}
|
||||
|
||||
public void writeFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
ensureCapacity(52);
|
||||
writeInt(GLCommand.FRUSTUM);
|
||||
writeDouble(left);
|
||||
writeDouble(right);
|
||||
writeDouble(bottom);
|
||||
writeDouble(top);
|
||||
writeDouble(zNear);
|
||||
writeDouble(zFar);
|
||||
}
|
||||
|
||||
// === Matrix commands ===
|
||||
|
||||
public void writeMultMatrix(Matrix4f matrix) {
|
||||
ensureCapacity(68);
|
||||
writeInt(GLCommand.MULT_MATRIX);
|
||||
writeMatrix4f(matrix);
|
||||
}
|
||||
|
||||
public void writeLoadMatrix(Matrix4f matrix) {
|
||||
ensureCapacity(68);
|
||||
writeInt(GLCommand.LOAD_MATRIX);
|
||||
writeMatrix4f(matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a Matrix4f to the buffer in column-major order (OpenGL convention).
|
||||
* Uses accessor methods instead of getToAddress() to avoid JOML MemUtil issues.
|
||||
*/
|
||||
private void writeMatrix4f(Matrix4f m) {
|
||||
// Column 0
|
||||
writeFloat(m.m00()); writeFloat(m.m01()); writeFloat(m.m02()); writeFloat(m.m03());
|
||||
// Column 1
|
||||
writeFloat(m.m10()); writeFloat(m.m11()); writeFloat(m.m12()); writeFloat(m.m13());
|
||||
// Column 2
|
||||
writeFloat(m.m20()); writeFloat(m.m21()); writeFloat(m.m22()); writeFloat(m.m23());
|
||||
// Column 3
|
||||
writeFloat(m.m30()); writeFloat(m.m31()); writeFloat(m.m32()); writeFloat(m.m33());
|
||||
}
|
||||
|
||||
// === Buffer commands (inline 4 floats) ===
|
||||
|
||||
public void writeFog(int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(12 + count * 4);
|
||||
writeInt(GLCommand.FOG);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLight(int light, int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(16 + count * 4);
|
||||
writeInt(GLCommand.LIGHT);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLightModel(int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(12 + count * 4);
|
||||
writeInt(GLCommand.LIGHT_MODEL);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMaterial(int face, int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(16 + count * 4);
|
||||
writeInt(GLCommand.MATERIAL);
|
||||
writeInt(face);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
// === Draw commands ===
|
||||
|
||||
public void writeDrawRange(int vboIndex) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DRAW_RANGE);
|
||||
writeInt(vboIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a DRAW_RANGE_RESTORE command that draws a VBO range and restores GL current state after.
|
||||
* Used for immediate mode VBOs to restore GL_CURRENT_COLOR, GL_CURRENT_NORMAL, GL_CURRENT_TEXTURE_COORDS.
|
||||
*
|
||||
* @param vboIndex Index into ownedVbos array
|
||||
*/
|
||||
public void writeDrawRangeRestore(
|
||||
int vboIndex, AccumulatedDraw.RestoreData restoreData
|
||||
) {
|
||||
ensureCapacity(28);
|
||||
writeInt(GLCommand.DRAW_RANGE_RESTORE);
|
||||
writeInt(vboIndex);
|
||||
writeInt(restoreData.flags);
|
||||
// Last color (4 bytes)
|
||||
writeInt(restoreData.lastColor);
|
||||
// Last normal (4 bytes)
|
||||
writeInt(restoreData.lastNormal);
|
||||
// Last texcoord (2 floats = 8 bytes)
|
||||
writeFloat(restoreData.lastTexCoordU);
|
||||
writeFloat(restoreData.lastTexCoordV);
|
||||
}
|
||||
|
||||
public void writeCallList(int listId) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CALL_LIST);
|
||||
writeInt(listId);
|
||||
}
|
||||
|
||||
public void writeDrawArrays(int mode, int start, int count) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.DRAW_ARRAYS);
|
||||
writeInt(mode);
|
||||
writeInt(start);
|
||||
writeInt(count);
|
||||
}
|
||||
|
||||
public void writeDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
ensureCapacity(24);
|
||||
writeInt(GLCommand.DRAW_ELEMENTS);
|
||||
writeInt(mode);
|
||||
writeInt(indices_count);
|
||||
writeInt(type);
|
||||
writeLong(indices_buffer_offset);
|
||||
}
|
||||
|
||||
public void writeBindVBO(int vbo) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.BIND_VBO);
|
||||
writeInt(vbo);
|
||||
}
|
||||
|
||||
public void writeBindVAO(int vao) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.BIND_VAO);
|
||||
writeInt(vao);
|
||||
}
|
||||
|
||||
public void writeDrawBuffer(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DRAW_BUFFER);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
private static final int MAX_DRAW_BUFFERS = 8;
|
||||
// cmd (4) + count (4) + buffers (4 * MAX_DRAW_BUFFERS)
|
||||
private static final int DRAW_BUFFERS_SIZE = 4 + 4 + 4 * MAX_DRAW_BUFFERS;
|
||||
|
||||
public void writeDrawBuffers(int count, int buffer) {
|
||||
ensureCapacity(DRAW_BUFFERS_SIZE);
|
||||
writeInt(GLCommand.DRAW_BUFFERS);
|
||||
writeInt(count);
|
||||
writeInt(buffer);
|
||||
for (int i = 1; i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeDrawBuffers(int count, IntBuffer bufs) {
|
||||
ensureCapacity(DRAW_BUFFERS_SIZE);
|
||||
writeInt(GLCommand.DRAW_BUFFERS);
|
||||
writeInt(count);
|
||||
final int pos = bufs.position();
|
||||
for (int i = 0; i < count && i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(bufs.get(pos + i));
|
||||
}
|
||||
for (int i = count; i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// === Complex object reference ===
|
||||
|
||||
public void writeComplexRef(DisplayListCommand cmd) {
|
||||
ensureCapacity(8);
|
||||
final int index = complexObjects.size();
|
||||
complexObjects.add(cmd);
|
||||
writeInt(GLCommand.COMPLEX_REF);
|
||||
writeInt(index);
|
||||
}
|
||||
|
||||
// === Reading methods for optimization pass ===
|
||||
|
||||
private long readPointer = 0;
|
||||
|
||||
/**
|
||||
* Reset read position to the beginning of the buffer.
|
||||
*/
|
||||
public void resetRead() {
|
||||
readPointer = basePointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's more data to read.
|
||||
*/
|
||||
public boolean hasRemaining() {
|
||||
return readPointer < writePointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an int from the current position.
|
||||
*/
|
||||
public int readInt() {
|
||||
final int value = memGetInt(readPointer);
|
||||
readPointer += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
public long readLong() {
|
||||
final long value = memGetLong(readPointer);
|
||||
readPointer += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a float from the current position.
|
||||
*/
|
||||
public float readFloat() {
|
||||
final float value = memGetFloat(readPointer);
|
||||
readPointer += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a double from the current position.
|
||||
*/
|
||||
public double readDouble() {
|
||||
final double value = memGetDouble(readPointer);
|
||||
readPointer += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a Matrix4f from the current position (16 floats in column-major order).
|
||||
*/
|
||||
public Matrix4f readMatrix4f(Matrix4f dest) {
|
||||
dest.m00(readFloat()); dest.m01(readFloat()); dest.m02(readFloat()); dest.m03(readFloat());
|
||||
dest.m10(readFloat()); dest.m11(readFloat()); dest.m12(readFloat()); dest.m13(readFloat());
|
||||
dest.m20(readFloat()); dest.m21(readFloat()); dest.m22(readFloat()); dest.m23(readFloat());
|
||||
dest.m30(readFloat()); dest.m31(readFloat()); dest.m32(readFloat()); dest.m33(readFloat());
|
||||
return dest;
|
||||
}
|
||||
|
||||
// === Lifecycle ===
|
||||
|
||||
public int size() {
|
||||
return (int) (writePointer - basePointer);
|
||||
}
|
||||
|
||||
public long address() {
|
||||
return basePointer;
|
||||
}
|
||||
|
||||
public ByteBuffer toBuffer() {
|
||||
buffer.limit(size());
|
||||
buffer.position(0);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Object[] getComplexObjects() {
|
||||
return complexObjects.toArray();
|
||||
}
|
||||
|
||||
public Object getComplexObject(int index) {
|
||||
return complexObjects.get(index);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0 && complexObjects.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the buffer for reuse. Does NOT free memory.
|
||||
*/
|
||||
public void clear() {
|
||||
writePointer = basePointer;
|
||||
complexObjects.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the underlying buffer. Must be called when done.
|
||||
*/
|
||||
public void free() {
|
||||
if (buffer != null) {
|
||||
memFree(buffer);
|
||||
buffer = null;
|
||||
basePointer = 0;
|
||||
writePointer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
/**
|
||||
* Executes commands from a CommandBuffer
|
||||
*/
|
||||
public final class CommandBufferExecutor {
|
||||
private CommandBufferExecutor() {}
|
||||
|
||||
// Static reusable buffer for matrix ops (render thread only, LWJGL2/3 compatible)
|
||||
private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer(16);
|
||||
// Static reusable buffer for fog/light/material params
|
||||
private static final FloatBuffer PARAMS_BUFFER = BufferUtils.createFloatBuffer(4);
|
||||
// Static reusable buffer for draw buffers
|
||||
private static final int MAX_DRAW_BUFFERS = 8;
|
||||
private static final IntBuffer DRAW_BUFFERS_BUFFER = BufferUtils.createIntBuffer(MAX_DRAW_BUFFERS);
|
||||
|
||||
/**
|
||||
* Execute all commands in the buffer.
|
||||
*
|
||||
* @param buffer The command buffer (position should be at 0, limit at size)
|
||||
* @param complexObjects Array of complex objects (TexImage2DCmd, etc.)
|
||||
* @param ownedVbos Array of VBOs owned by the display list (indexed by DrawRange commands)
|
||||
*/
|
||||
public static void execute(ByteBuffer buffer, Object[] complexObjects, DisplayListVBO ownedVbos) {
|
||||
long ptr = memAddress(buffer);
|
||||
final long end = ptr + buffer.limit();
|
||||
|
||||
while (ptr < end) {
|
||||
final int cmd = memGetInt(ptr);
|
||||
ptr += 4;
|
||||
|
||||
switch (cmd) {
|
||||
// === Single int commands ===
|
||||
case GLCommand.ENABLE -> {
|
||||
GLStateManager.glEnable(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.DISABLE -> {
|
||||
GLStateManager.glDisable(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.CLEAR -> {
|
||||
GLStateManager.glClear(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.CLEAR_STENCIL -> {
|
||||
GLStateManager.glClearStencil(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.CULL_FACE -> {
|
||||
GLStateManager.glCullFace(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.DEPTH_FUNC -> {
|
||||
GLStateManager.glDepthFunc(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.SHADE_MODEL -> {
|
||||
GLStateManager.glShadeModel(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.LOGIC_OP -> {
|
||||
GLStateManager.glLogicOp(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.MATRIX_MODE -> {
|
||||
GLStateManager.glMatrixMode(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.ACTIVE_TEXTURE -> {
|
||||
GLStateManager.glActiveTexture(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.USE_PROGRAM -> {
|
||||
GLStateManager.glUseProgram(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.PUSH_ATTRIB -> {
|
||||
GLStateManager.glPushAttrib(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.POP_ATTRIB -> {
|
||||
GLStateManager.glPopAttrib();
|
||||
ptr += 4; // Skip unused padding
|
||||
}
|
||||
case GLCommand.LOAD_IDENTITY -> {
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
case GLCommand.PUSH_MATRIX -> {
|
||||
GLStateManager.glPushMatrix();
|
||||
}
|
||||
case GLCommand.POP_MATRIX -> {
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
case GLCommand.STENCIL_MASK -> {
|
||||
GLStateManager.glStencilMask(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.DEPTH_MASK -> {
|
||||
GLStateManager.glDepthMask(memGetInt(ptr) != 0);
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.FRONT_FACE -> {
|
||||
GLStateManager.glFrontFace(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
// === Two int commands ===
|
||||
case GLCommand.BIND_TEXTURE -> {
|
||||
final int target = memGetInt(ptr);
|
||||
final int texture = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
}
|
||||
case GLCommand.POLYGON_MODE -> {
|
||||
final int face = memGetInt(ptr);
|
||||
final int mode = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glPolygonMode(face, mode);
|
||||
}
|
||||
case GLCommand.COLOR_MATERIAL -> {
|
||||
final int face = memGetInt(ptr);
|
||||
final int mode = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glColorMaterial(face, mode);
|
||||
}
|
||||
case GLCommand.LINE_STIPPLE -> {
|
||||
final int factor = memGetInt(ptr);
|
||||
final int pattern = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glLineStipple(factor, (short) pattern);
|
||||
}
|
||||
case GLCommand.STENCIL_MASK_SEPARATE -> {
|
||||
final int face = memGetInt(ptr);
|
||||
final int mask = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glStencilMaskSeparate(face, mask);
|
||||
}
|
||||
case GLCommand.FOGI -> {
|
||||
final int pname = memGetInt(ptr);
|
||||
final int param = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glFogi(pname, param);
|
||||
}
|
||||
case GLCommand.HINT -> {
|
||||
final int target = memGetInt(ptr);
|
||||
final int mode = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glHint(target, mode);
|
||||
}
|
||||
|
||||
// === Three int commands ===
|
||||
case GLCommand.STENCIL_FUNC -> {
|
||||
final int func = memGetInt(ptr);
|
||||
final int ref = memGetInt(ptr + 4);
|
||||
final int mask = memGetInt(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glStencilFunc(func, ref, mask);
|
||||
}
|
||||
case GLCommand.STENCIL_OP -> {
|
||||
final int fail = memGetInt(ptr);
|
||||
final int zfail = memGetInt(ptr + 4);
|
||||
final int zpass = memGetInt(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glStencilOp(fail, zfail, zpass);
|
||||
}
|
||||
case GLCommand.TEX_PARAMETERI -> {
|
||||
final int target = memGetInt(ptr);
|
||||
final int pname = memGetInt(ptr + 4);
|
||||
final int param = memGetInt(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glTexParameteri(target, pname, param);
|
||||
}
|
||||
|
||||
// === Four int commands ===
|
||||
case GLCommand.VIEWPORT -> {
|
||||
final int x = memGetInt(ptr);
|
||||
final int y = memGetInt(ptr + 4);
|
||||
final int width = memGetInt(ptr + 8);
|
||||
final int height = memGetInt(ptr + 12);
|
||||
ptr += 16;
|
||||
GLStateManager.glViewport(x, y, width, height);
|
||||
}
|
||||
case GLCommand.BLEND_FUNC -> {
|
||||
final int srcRgb = memGetInt(ptr);
|
||||
final int dstRgb = memGetInt(ptr + 4);
|
||||
final int srcAlpha = memGetInt(ptr + 8);
|
||||
final int dstAlpha = memGetInt(ptr + 12);
|
||||
ptr += 16;
|
||||
if (srcRgb == srcAlpha && dstRgb == dstAlpha) {
|
||||
GLStateManager.glBlendFunc(srcRgb, dstRgb);
|
||||
} else {
|
||||
GLStateManager.tryBlendFuncSeparate(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
}
|
||||
}
|
||||
case GLCommand.COLOR_MASK -> {
|
||||
final boolean r = memGetInt(ptr) != 0;
|
||||
final boolean g = memGetInt(ptr + 4) != 0;
|
||||
final boolean b = memGetInt(ptr + 8) != 0;
|
||||
final boolean a = memGetInt(ptr + 12) != 0;
|
||||
ptr += 16;
|
||||
GLStateManager.glColorMask(r, g, b, a);
|
||||
}
|
||||
case GLCommand.STENCIL_FUNC_SEPARATE -> {
|
||||
final int face = memGetInt(ptr);
|
||||
final int func = memGetInt(ptr + 4);
|
||||
final int ref = memGetInt(ptr + 8);
|
||||
final int mask = memGetInt(ptr + 12);
|
||||
ptr += 16;
|
||||
GLStateManager.glStencilFuncSeparate(face, func, ref, mask);
|
||||
}
|
||||
case GLCommand.STENCIL_OP_SEPARATE -> {
|
||||
final int face = memGetInt(ptr);
|
||||
final int sfail = memGetInt(ptr + 4);
|
||||
final int dpfail = memGetInt(ptr + 8);
|
||||
final int dppass = memGetInt(ptr + 12);
|
||||
ptr += 16;
|
||||
GLStateManager.glStencilOpSeparate(face, sfail, dpfail, dppass);
|
||||
}
|
||||
|
||||
// === Float commands ===
|
||||
case GLCommand.POINT_SIZE -> {
|
||||
GLStateManager.glPointSize(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.LINE_WIDTH -> {
|
||||
GLStateManager.glLineWidth(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.POLYGON_OFFSET -> {
|
||||
final float factor = memGetFloat(ptr);
|
||||
final float units = memGetFloat(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glPolygonOffset(factor, units);
|
||||
}
|
||||
case GLCommand.COLOR -> {
|
||||
final float r = memGetFloat(ptr);
|
||||
final float g = memGetFloat(ptr + 4);
|
||||
final float b = memGetFloat(ptr + 8);
|
||||
final float a = memGetFloat(ptr + 12);
|
||||
ptr += 16;
|
||||
GLStateManager.glColor4f(r, g, b, a);
|
||||
}
|
||||
case GLCommand.CLEAR_COLOR -> {
|
||||
final float r = memGetFloat(ptr);
|
||||
final float g = memGetFloat(ptr + 4);
|
||||
final float b = memGetFloat(ptr + 8);
|
||||
final float a = memGetFloat(ptr + 12);
|
||||
ptr += 16;
|
||||
GLStateManager.glClearColor(r, g, b, a);
|
||||
}
|
||||
case GLCommand.CLEAR_DEPTH -> {
|
||||
final double depth = memGetDouble(ptr);
|
||||
ptr += 8;
|
||||
GLStateManager.glClearDepth(depth);
|
||||
}
|
||||
case GLCommand.BLEND_COLOR -> {
|
||||
final float r = memGetFloat(ptr);
|
||||
final float g = memGetFloat(ptr + 4);
|
||||
final float b = memGetFloat(ptr + 8);
|
||||
final float a = memGetFloat(ptr + 12);
|
||||
ptr += 16;
|
||||
GLStateManager.glBlendColor(r, g, b, a);
|
||||
}
|
||||
|
||||
// === Mixed int+float commands ===
|
||||
case GLCommand.ALPHA_FUNC -> {
|
||||
final int func = memGetInt(ptr);
|
||||
final float ref = memGetFloat(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glAlphaFunc(func, ref);
|
||||
}
|
||||
case GLCommand.FOGF -> {
|
||||
final int pname = memGetInt(ptr);
|
||||
final float param = memGetFloat(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glFogf(pname, param);
|
||||
}
|
||||
case GLCommand.LIGHTF -> {
|
||||
final int light = memGetInt(ptr);
|
||||
final int pname = memGetInt(ptr + 4);
|
||||
final float param = memGetFloat(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glLightf(light, pname, param);
|
||||
}
|
||||
case GLCommand.LIGHT_MODELF -> {
|
||||
final int pname = memGetInt(ptr);
|
||||
final float param = memGetFloat(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glLightModelf(pname, param);
|
||||
}
|
||||
case GLCommand.LIGHTI -> {
|
||||
final int light = memGetInt(ptr);
|
||||
final int pname = memGetInt(ptr + 4);
|
||||
final int param = memGetInt(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glLighti(light, pname, param);
|
||||
}
|
||||
case GLCommand.LIGHT_MODELI -> {
|
||||
final int pname = memGetInt(ptr);
|
||||
final int param = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
GLStateManager.glLightModeli(pname, param);
|
||||
}
|
||||
case GLCommand.MATERIALF -> {
|
||||
final int face = memGetInt(ptr);
|
||||
final int pname = memGetInt(ptr + 4);
|
||||
final float param = memGetFloat(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glMaterialf(face, pname, param);
|
||||
}
|
||||
case GLCommand.TEX_PARAMETERF -> {
|
||||
final int target = memGetInt(ptr);
|
||||
final int pname = memGetInt(ptr + 4);
|
||||
final float param = memGetFloat(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glTexParameterf(target, pname, param);
|
||||
}
|
||||
|
||||
// === Double commands ===
|
||||
case GLCommand.TRANSLATE -> {
|
||||
final double x = memGetDouble(ptr);
|
||||
final double y = memGetDouble(ptr + 8);
|
||||
final double z = memGetDouble(ptr + 16);
|
||||
ptr += 24;
|
||||
GLStateManager.glTranslated(x, y, z);
|
||||
}
|
||||
case GLCommand.ROTATE -> {
|
||||
final double angle = memGetDouble(ptr);
|
||||
final double x = memGetDouble(ptr + 8);
|
||||
final double y = memGetDouble(ptr + 16);
|
||||
final double z = memGetDouble(ptr + 24);
|
||||
ptr += 32;
|
||||
GLStateManager.glRotated(angle, x, y, z);
|
||||
}
|
||||
case GLCommand.SCALE -> {
|
||||
final double x = memGetDouble(ptr);
|
||||
final double y = memGetDouble(ptr + 8);
|
||||
final double z = memGetDouble(ptr + 16);
|
||||
ptr += 24;
|
||||
GLStateManager.glScaled(x, y, z);
|
||||
}
|
||||
case GLCommand.ORTHO -> {
|
||||
final double left = memGetDouble(ptr);
|
||||
final double right = memGetDouble(ptr + 8);
|
||||
final double bottom = memGetDouble(ptr + 16);
|
||||
final double top = memGetDouble(ptr + 24);
|
||||
final double zNear = memGetDouble(ptr + 32);
|
||||
final double zFar = memGetDouble(ptr + 40);
|
||||
ptr += 48;
|
||||
GLStateManager.glOrtho(left, right, bottom, top, zNear, zFar);
|
||||
}
|
||||
case GLCommand.FRUSTUM -> {
|
||||
final double left = memGetDouble(ptr);
|
||||
final double right = memGetDouble(ptr + 8);
|
||||
final double bottom = memGetDouble(ptr + 16);
|
||||
final double top = memGetDouble(ptr + 24);
|
||||
final double zNear = memGetDouble(ptr + 32);
|
||||
final double zFar = memGetDouble(ptr + 40);
|
||||
ptr += 48;
|
||||
GLStateManager.glFrustum(left, right, bottom, top, zNear, zFar);
|
||||
}
|
||||
|
||||
// === Matrix commands ===
|
||||
case GLCommand.MULT_MATRIX -> {
|
||||
// Mode-agnostic: just multiply current matrix
|
||||
MATRIX_BUFFER.clear();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
MATRIX_BUFFER.put(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
MATRIX_BUFFER.flip();
|
||||
GLStateManager.glMultMatrix(MATRIX_BUFFER);
|
||||
}
|
||||
case GLCommand.LOAD_MATRIX -> {
|
||||
MATRIX_BUFFER.clear();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
MATRIX_BUFFER.put(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
MATRIX_BUFFER.flip();
|
||||
GLStateManager.glLoadMatrix(MATRIX_BUFFER);
|
||||
}
|
||||
|
||||
// === Buffer commands ===
|
||||
case GLCommand.FOG -> {
|
||||
final int pname = memGetInt(ptr);
|
||||
final int count = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
PARAMS_BUFFER.clear();
|
||||
for (int i = 0; i < count; i++) {
|
||||
PARAMS_BUFFER.put(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
PARAMS_BUFFER.flip();
|
||||
GLStateManager.glFog(pname, PARAMS_BUFFER);
|
||||
}
|
||||
case GLCommand.LIGHT -> {
|
||||
final int light = memGetInt(ptr);
|
||||
final int pname = memGetInt(ptr + 4);
|
||||
final int count = memGetInt(ptr + 8);
|
||||
ptr += 12;
|
||||
PARAMS_BUFFER.clear();
|
||||
for (int i = 0; i < count; i++) {
|
||||
PARAMS_BUFFER.put(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
PARAMS_BUFFER.flip();
|
||||
GLStateManager.glLight(light, pname, PARAMS_BUFFER);
|
||||
}
|
||||
case GLCommand.LIGHT_MODEL -> {
|
||||
final int pname = memGetInt(ptr);
|
||||
final int count = memGetInt(ptr + 4);
|
||||
ptr += 8;
|
||||
PARAMS_BUFFER.clear();
|
||||
for (int i = 0; i < count; i++) {
|
||||
PARAMS_BUFFER.put(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
PARAMS_BUFFER.flip();
|
||||
GLStateManager.glLightModel(pname, PARAMS_BUFFER);
|
||||
}
|
||||
case GLCommand.MATERIAL -> {
|
||||
final int face = memGetInt(ptr);
|
||||
final int pname = memGetInt(ptr + 4);
|
||||
final int count = memGetInt(ptr + 8);
|
||||
ptr += 12;
|
||||
PARAMS_BUFFER.clear();
|
||||
for (int i = 0; i < count; i++) {
|
||||
PARAMS_BUFFER.put(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
PARAMS_BUFFER.flip();
|
||||
GLStateManager.glMaterial(face, pname, PARAMS_BUFFER);
|
||||
}
|
||||
|
||||
// === Draw commands ===
|
||||
case GLCommand.DRAW_RANGE -> {
|
||||
final int vboIndex = memGetInt(ptr);
|
||||
ptr += 4;
|
||||
ownedVbos.render(vboIndex);
|
||||
}
|
||||
case GLCommand.DRAW_RANGE_RESTORE -> {
|
||||
// Draw VBO range then restore GL current state from last vertex attributes
|
||||
final int vboIndex = memGetInt(ptr);
|
||||
final int flags = memGetInt(ptr + 4);
|
||||
|
||||
// Draw the VBO
|
||||
ownedVbos.render(vboIndex);
|
||||
|
||||
// Restore attributes based on flags
|
||||
if ((flags & VertexFlags.COLOR_BIT) != 0) {
|
||||
final int color = memGetInt(ptr + 8);
|
||||
byte a = (byte) ((color >> 24) & 0xFF);
|
||||
byte r = (byte) ((color >> 16) & 0xFF);
|
||||
byte g = (byte) ((color >> 8) & 0xFF);
|
||||
byte b = (byte) ((color & 0xFF));
|
||||
GLStateManager.glColor4ub(r, g, b, a);
|
||||
}
|
||||
if ((flags & VertexFlags.NORMAL_BIT) != 0) {
|
||||
final int normal = memGetInt(ptr + 12);
|
||||
float nx = (byte)(normal) / 127.0f;
|
||||
float ny = (byte)(normal >> 8) / 127.0f;
|
||||
float nz = (byte)(normal >> 16) / 127.0f;
|
||||
GLStateManager.glNormal3f(nx, ny, nz);
|
||||
}
|
||||
if ((flags & VertexFlags.TEXTURE_BIT) != 0) {
|
||||
final float s = memGetFloat(ptr + 16);
|
||||
final float t = memGetFloat(ptr + 20);
|
||||
GLStateManager.glTexCoord2f(s, t);
|
||||
}
|
||||
ptr += 24; // Skip full command size (28 - 4 for cmd already read)
|
||||
}
|
||||
case GLCommand.CALL_LIST -> {
|
||||
final int listId = memGetInt(ptr);
|
||||
ptr += 4;
|
||||
DisplayListManager.glCallList(listId);
|
||||
}
|
||||
case GLCommand.DRAW_BUFFER -> {
|
||||
GLStateManager.glDrawBuffer(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.DRAW_BUFFERS -> {
|
||||
final int count = memGetInt(ptr);
|
||||
ptr += 4;
|
||||
DRAW_BUFFERS_BUFFER.clear();
|
||||
for (int i = 0; i < count; i++) {
|
||||
DRAW_BUFFERS_BUFFER.put(memGetInt(ptr + i * 4));
|
||||
}
|
||||
DRAW_BUFFERS_BUFFER.flip();
|
||||
ptr += 4 * MAX_DRAW_BUFFERS;
|
||||
GLStateManager.glDrawBuffers(DRAW_BUFFERS_BUFFER);
|
||||
}
|
||||
case GLCommand.DRAW_ARRAYS -> {
|
||||
GLStateManager.glDrawArrays(memGetInt(ptr), memGetInt(ptr + 4), memGetInt(ptr + 8));
|
||||
ptr += 12;
|
||||
}
|
||||
case GLCommand.DRAW_ELEMENTS -> {
|
||||
GLStateManager.glDrawElements(memGetInt(ptr), memGetInt(ptr + 4), memGetInt(ptr + 8), memGetLong(ptr + 12));
|
||||
ptr += 20;
|
||||
}
|
||||
case GLCommand.BIND_VBO -> {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.BIND_VAO -> {
|
||||
UniversalVAO.bindVertexArray(memGetInt(ptr));
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
// === Complex object reference ===
|
||||
case GLCommand.COMPLEX_REF -> {
|
||||
final int index = memGetInt(ptr);
|
||||
ptr += 4;
|
||||
((DisplayListCommand) complexObjects[index]).execute();
|
||||
}
|
||||
|
||||
default -> throw new IllegalStateException("Unknown command opcode: " + cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public final class CommandBufferProcessor {
|
||||
private static final Logger LOGGER = LogManager.getLogger("CommandBufferProcessor");
|
||||
private static final Matrix4f tempMatrix = new Matrix4f();
|
||||
|
||||
private CommandBufferProcessor() {}
|
||||
|
||||
/**
|
||||
* Copy a complete command (opcode already read) from raw to final buffer.
|
||||
*/
|
||||
public static void copyCommand(CommandBuffer raw, CommandBuffer out) {
|
||||
final int opcode = raw.readInt();
|
||||
copyCommandData(opcode, raw, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy command data based on opcode (opcode already consumed from raw).
|
||||
*/
|
||||
public static void copyCommandData(int opcode, CommandBuffer raw, CommandBuffer out) {
|
||||
switch (opcode) {
|
||||
// Single int commands
|
||||
case GLCommand.ENABLE -> out.writeEnable(raw.readInt());
|
||||
case GLCommand.DISABLE -> out.writeDisable(raw.readInt());
|
||||
case GLCommand.CLEAR -> out.writeClear(raw.readInt());
|
||||
case GLCommand.CLEAR_STENCIL -> out.writeClearStencil(raw.readInt());
|
||||
case GLCommand.CULL_FACE -> out.writeCullFace(raw.readInt());
|
||||
case GLCommand.DEPTH_FUNC -> out.writeDepthFunc(raw.readInt());
|
||||
case GLCommand.SHADE_MODEL -> out.writeShadeModel(raw.readInt());
|
||||
case GLCommand.LOGIC_OP -> out.writeLogicOp(raw.readInt());
|
||||
case GLCommand.MATRIX_MODE -> out.writeMatrixMode(raw.readInt());
|
||||
case GLCommand.ACTIVE_TEXTURE -> out.writeActiveTexture(raw.readInt());
|
||||
case GLCommand.USE_PROGRAM -> out.writeUseProgram(raw.readInt());
|
||||
case GLCommand.PUSH_ATTRIB -> out.writePushAttrib(raw.readInt());
|
||||
case GLCommand.POP_ATTRIB -> { raw.readInt(); out.writePopAttrib(); }
|
||||
case GLCommand.LOAD_IDENTITY -> out.writeLoadIdentity();
|
||||
case GLCommand.PUSH_MATRIX -> out.writePushMatrix();
|
||||
case GLCommand.POP_MATRIX -> out.writePopMatrix();
|
||||
case GLCommand.STENCIL_MASK -> out.writeStencilMask(raw.readInt());
|
||||
case GLCommand.DEPTH_MASK -> out.writeDepthMask(raw.readInt() != 0);
|
||||
case GLCommand.FRONT_FACE -> out.writeFrontFace(raw.readInt());
|
||||
|
||||
// Two int commands
|
||||
case GLCommand.BIND_TEXTURE -> out.writeBindTexture(raw.readInt(), raw.readInt());
|
||||
case GLCommand.POLYGON_MODE -> out.writePolygonMode(raw.readInt(), raw.readInt());
|
||||
case GLCommand.COLOR_MATERIAL -> out.writeColorMaterial(raw.readInt(), raw.readInt());
|
||||
case GLCommand.LINE_STIPPLE -> out.writeLineStipple(raw.readInt(), raw.readInt());
|
||||
case GLCommand.STENCIL_MASK_SEPARATE -> out.writeStencilMaskSeparate(raw.readInt(), raw.readInt());
|
||||
case GLCommand.FOGI -> out.writeFogi(raw.readInt(), raw.readInt());
|
||||
case GLCommand.HINT -> out.writeHint(raw.readInt(), raw.readInt());
|
||||
|
||||
// Three int commands
|
||||
case GLCommand.STENCIL_FUNC -> out.writeStencilFunc(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.STENCIL_OP -> out.writeStencilOp(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.TEX_PARAMETERI -> out.writeTexParameteri(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
|
||||
// Four int commands
|
||||
case GLCommand.VIEWPORT -> out.writeViewport(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.BLEND_FUNC -> out.writeBlendFunc(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.COLOR_MASK -> out.writeColorMask(raw.readInt() != 0, raw.readInt() != 0, raw.readInt() != 0, raw.readInt() != 0);
|
||||
case GLCommand.STENCIL_FUNC_SEPARATE -> out.writeStencilFuncSeparate(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.STENCIL_OP_SEPARATE -> out.writeStencilOpSeparate(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
|
||||
// Float commands
|
||||
case GLCommand.POINT_SIZE -> out.writePointSize(raw.readFloat());
|
||||
case GLCommand.LINE_WIDTH -> out.writeLineWidth(raw.readFloat());
|
||||
case GLCommand.POLYGON_OFFSET -> out.writePolygonOffset(raw.readFloat(), raw.readFloat());
|
||||
case GLCommand.COLOR -> out.writeColor(raw.readFloat(), raw.readFloat(), raw.readFloat(), raw.readFloat());
|
||||
case GLCommand.CLEAR_COLOR -> out.writeClearColor(raw.readFloat(), raw.readFloat(), raw.readFloat(), raw.readFloat());
|
||||
|
||||
// Single double commands
|
||||
case GLCommand.CLEAR_DEPTH -> out.writeClearDepth(raw.readDouble());
|
||||
|
||||
// Four float commands
|
||||
case GLCommand.BLEND_COLOR -> out.writeBlendColor(raw.readFloat(), raw.readFloat(), raw.readFloat(), raw.readFloat());
|
||||
|
||||
// Mixed int+float commands
|
||||
case GLCommand.ALPHA_FUNC -> out.writeAlphaFunc(raw.readInt(), raw.readFloat());
|
||||
case GLCommand.FOGF -> out.writeFogf(raw.readInt(), raw.readFloat());
|
||||
case GLCommand.LIGHTF -> out.writeLightf(raw.readInt(), raw.readInt(), raw.readFloat());
|
||||
case GLCommand.LIGHT_MODELF -> out.writeLightModelf(raw.readInt(), raw.readFloat());
|
||||
case GLCommand.LIGHTI -> out.writeLighti(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.LIGHT_MODELI -> out.writeLightModeli(raw.readInt(), raw.readInt());
|
||||
case GLCommand.MATERIALF -> out.writeMaterialf(raw.readInt(), raw.readInt(), raw.readFloat());
|
||||
case GLCommand.TEX_PARAMETERF -> out.writeTexParameterf(raw.readInt(), raw.readInt(), raw.readFloat());
|
||||
|
||||
// Double commands
|
||||
case GLCommand.TRANSLATE -> out.writeTranslate(raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.ROTATE -> out.writeRotate(raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.SCALE -> out.writeScale(raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.ORTHO -> out.writeOrtho(raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.FRUSTUM -> out.writeFrustum(raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
|
||||
// Matrix commands
|
||||
case GLCommand.MULT_MATRIX -> { out.writeMultMatrix(raw.readMatrix4f(tempMatrix)); }
|
||||
case GLCommand.LOAD_MATRIX -> { out.writeLoadMatrix(raw.readMatrix4f(tempMatrix)); }
|
||||
|
||||
// FloatBuffer commands (inline 4 floats max)
|
||||
case GLCommand.FOG -> {
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeFog(pname, fb);
|
||||
}
|
||||
case GLCommand.LIGHT -> {
|
||||
final int light = raw.readInt();
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeLight(light, pname, fb);
|
||||
}
|
||||
case GLCommand.LIGHT_MODEL -> {
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeLightModel(pname, fb);
|
||||
}
|
||||
case GLCommand.MATERIAL -> {
|
||||
final int face = raw.readInt();
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeMaterial(face, pname, fb);
|
||||
}
|
||||
|
||||
// Draw/call commands
|
||||
case GLCommand.DRAW_RANGE -> out.writeDrawRange(raw.readInt());
|
||||
case GLCommand.CALL_LIST -> out.writeCallList(raw.readInt());
|
||||
case GLCommand.DRAW_ARRAYS -> out.writeDrawArrays(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.DRAW_ELEMENTS -> out.writeDrawElements(raw.readInt(), raw.readInt(), raw.readInt(), raw.readLong());
|
||||
case GLCommand.BIND_VBO -> out.writeBindVBO(raw.readInt());
|
||||
case GLCommand.BIND_VAO -> out.writeBindVAO(raw.readInt());
|
||||
|
||||
// Complex object reference - transfer the object to output buffer
|
||||
case GLCommand.COMPLEX_REF -> {
|
||||
final int index = raw.readInt();
|
||||
final Object obj = raw.getComplexObject(index);
|
||||
out.writeComplexRef((DisplayListCommand) obj);
|
||||
}
|
||||
|
||||
default -> LOGGER.warn("[CommandBufferProcessor] Unknown opcode in buffer copy: {}", opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
/**
|
||||
* Records GL commands to a CommandBuffer during display list compilation.
|
||||
*/
|
||||
public final class CommandRecorder {
|
||||
private final CommandBuffer buffer;
|
||||
private int commandCount;
|
||||
|
||||
public CommandRecorder() {
|
||||
this.buffer = new CommandBuffer();
|
||||
this.commandCount = 0;
|
||||
}
|
||||
|
||||
public CommandBuffer getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public int getCommandCount() {
|
||||
return commandCount;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
buffer.free();
|
||||
}
|
||||
|
||||
// ==================== Recording Methods ====================
|
||||
|
||||
public void recordEnable(int cap) {
|
||||
buffer.writeEnable(cap);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDisable(int cap) {
|
||||
buffer.writeDisable(cap);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordClear(int mask) {
|
||||
buffer.writeClear(mask);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordClearColor(float r, float g, float b, float a) {
|
||||
buffer.writeClearColor(r, g, b, a);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordClearDepth(double depth) {
|
||||
buffer.writeClearDepth(depth);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordBlendColor(float r, float g, float b, float a) {
|
||||
buffer.writeBlendColor(r, g, b, a);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordClearStencil(int s) {
|
||||
buffer.writeClearStencil(s);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordColor(float r, float g, float b, float a) {
|
||||
buffer.writeColor(r, g, b, a);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordColorMask(boolean r, boolean g, boolean b, boolean a) {
|
||||
buffer.writeColorMask(r, g, b, a);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDepthMask(boolean flag) {
|
||||
buffer.writeDepthMask(flag);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordFrontFace(int mode) {
|
||||
buffer.writeFrontFace(mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDepthFunc(int func) {
|
||||
buffer.writeDepthFunc(func);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
buffer.writeBlendFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordAlphaFunc(int func, float ref) {
|
||||
buffer.writeAlphaFunc(func, ref);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordCullFace(int mode) {
|
||||
buffer.writeCullFace(mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordShadeModel(int mode) {
|
||||
buffer.writeShadeModel(mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordBindTexture(int target, int texture) {
|
||||
buffer.writeBindTexture(target, texture);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordTexParameteri(int target, int pname, int param) {
|
||||
buffer.writeTexParameteri(target, pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordTexParameterf(int target, int pname, float param) {
|
||||
buffer.writeTexParameterf(target, pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordMatrixMode(int mode) {
|
||||
buffer.writeMatrixMode(mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLoadIdentity() {
|
||||
buffer.writeLoadIdentity();
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPushMatrix() {
|
||||
buffer.writePushMatrix();
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPopMatrix() {
|
||||
buffer.writePopMatrix();
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordMultMatrix(Matrix4f matrix) {
|
||||
buffer.writeMultMatrix(matrix);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLoadMatrix(Matrix4f matrix) {
|
||||
buffer.writeLoadMatrix(matrix);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
buffer.writeOrtho(left, right, bottom, top, zNear, zFar);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
buffer.writeFrustum(left, right, bottom, top, zNear, zFar);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordViewport(int x, int y, int width, int height) {
|
||||
buffer.writeViewport(x, y, width, height);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPointSize(float size) {
|
||||
buffer.writePointSize(size);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLineWidth(float width) {
|
||||
buffer.writeLineWidth(width);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLineStipple(int factor, int pattern) {
|
||||
buffer.writeLineStipple(factor, pattern);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPolygonOffset(float factor, float units) {
|
||||
buffer.writePolygonOffset(factor, units);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPolygonMode(int face, int mode) {
|
||||
buffer.writePolygonMode(face, mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordColorMaterial(int face, int mode) {
|
||||
buffer.writeColorMaterial(face, mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLogicOp(int opcode) {
|
||||
buffer.writeLogicOp(opcode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordActiveTexture(int texture) {
|
||||
buffer.writeActiveTexture(texture);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordUseProgram(int program) {
|
||||
buffer.writeUseProgram(program);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPushAttrib(int mask) {
|
||||
buffer.writePushAttrib(mask);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPopAttrib() {
|
||||
buffer.writePopAttrib();
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordFogf(int pname, float param) {
|
||||
buffer.writeFogf(pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordFogi(int pname, int param) {
|
||||
buffer.writeFogi(pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordHint(int target, int mode) {
|
||||
buffer.writeHint(target, mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordFog(int pname, FloatBuffer params) {
|
||||
buffer.writeFog(pname, params);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLightf(int light, int pname, float param) {
|
||||
buffer.writeLightf(light, pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLighti(int light, int pname, int param) {
|
||||
buffer.writeLighti(light, pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLight(int light, int pname, FloatBuffer params) {
|
||||
buffer.writeLight(light, pname, params);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLightModelf(int pname, float param) {
|
||||
buffer.writeLightModelf(pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLightModeli(int pname, int param) {
|
||||
buffer.writeLightModeli(pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordLightModel(int pname, FloatBuffer params) {
|
||||
buffer.writeLightModel(pname, params);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordMaterialf(int face, int pname, float val) {
|
||||
buffer.writeMaterialf(face, pname, val);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordMaterial(int face, int pname, FloatBuffer params) {
|
||||
buffer.writeMaterial(face, pname, params);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordStencilFunc(int func, int ref, int mask) {
|
||||
buffer.writeStencilFunc(func, ref, mask);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordStencilFuncSeparate(int face, int func, int ref, int mask) {
|
||||
buffer.writeStencilFuncSeparate(face, func, ref, mask);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordStencilOp(int fail, int zfail, int zpass) {
|
||||
buffer.writeStencilOp(fail, zfail, zpass);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
||||
buffer.writeStencilOpSeparate(face, sfail, dpfail, dppass);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordStencilMask(int mask) {
|
||||
buffer.writeStencilMask(mask);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordStencilMaskSeparate(int face, int mask) {
|
||||
buffer.writeStencilMaskSeparate(face, mask);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordCallList(int listId) {
|
||||
buffer.writeCallList(listId);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDrawBuffer(int mode) {
|
||||
buffer.writeDrawBuffer(mode);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDrawBuffers(int count, int buf) {
|
||||
buffer.writeDrawBuffers(count, buf);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDrawBuffers(int count, IntBuffer bufs) {
|
||||
buffer.writeDrawBuffers(count, bufs);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDrawArrays(int mode, int start, int count) {
|
||||
buffer.writeDrawArrays(mode, start, count);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
buffer.writeDrawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordBindVBO(int vbo) {
|
||||
buffer.writeBindVBO(vbo);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordBindVAO(int vao) {
|
||||
buffer.writeBindVAO(vao);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordComplexCommand(DisplayListCommand cmd) {
|
||||
buffer.writeComplexRef(cmd);
|
||||
commandCount++;
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
|
||||
/**
|
||||
* Represents a compiled display list.
|
||||
*
|
||||
* <p>Contains commands serialized to a direct ByteBuffer for off-heap storage,
|
||||
* with complex objects (TexImage2DCmd, TexSubImage2DCmd) stored separately.
|
||||
*
|
||||
* <p>The ownedVbos array contains VBOs that are referenced by DrawRange commands.
|
||||
*/
|
||||
public final class CompiledDisplayList {
|
||||
/**
|
||||
* Empty display list singleton. Per OpenGL spec, an empty list is still valid.
|
||||
* This instance does nothing when rendered and is never deleted (shared singleton).
|
||||
*/
|
||||
public static final CompiledDisplayList EMPTY = new CompiledDisplayList(null, null, null);
|
||||
|
||||
private final ByteBuffer commandBuffer; // Off-heap command storage, must be freed
|
||||
private final Object[] complexObjects; // Complex commands (TexImage2D, etc.)
|
||||
private final DisplayListVBO ownedVbos; // GPU resources referenced by index
|
||||
|
||||
public CompiledDisplayList(ByteBuffer commandBuffer, Object[] complexObjects, DisplayListVBO ownedVbos) {
|
||||
this.commandBuffer = commandBuffer;
|
||||
this.complexObjects = complexObjects;
|
||||
this.ownedVbos = ownedVbos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this display list by executing all commands.
|
||||
*/
|
||||
public void render() {
|
||||
if (commandBuffer != null && commandBuffer.limit() > 0) {
|
||||
CommandBufferExecutor.execute(commandBuffer, complexObjects, ownedVbos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all resources held by this display list.
|
||||
*/
|
||||
public void delete() {
|
||||
// Delete complex objects that hold resources
|
||||
if (complexObjects != null) {
|
||||
for (Object obj : complexObjects) {
|
||||
if (obj instanceof DisplayListCommand cmd) {
|
||||
cmd.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close VBOs
|
||||
if (ownedVbos != null) {
|
||||
ownedVbos.delete();
|
||||
}
|
||||
|
||||
// Free the command buffer (off-heap memory)
|
||||
if (commandBuffer != null) {
|
||||
memFree(commandBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command buffer for debugging/inspection.
|
||||
*/
|
||||
public ByteBuffer getCommandBuffer() {
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get complex objects for inlining into another display list (nested lists).
|
||||
*/
|
||||
public Object[] getComplexObjects() {
|
||||
return complexObjects;
|
||||
}
|
||||
|
||||
public DisplayListVBO getOwnedVbos() {
|
||||
return ownedVbos;
|
||||
}
|
||||
|
||||
// === Test inspection methods ===
|
||||
|
||||
/**
|
||||
* Get counts of each command type in the buffer.
|
||||
* Used for testing to verify optimization results.
|
||||
*/
|
||||
public Int2IntMap getCommandCounts() {
|
||||
final Int2IntMap counts = new Int2IntOpenHashMap();
|
||||
if (commandBuffer == null || commandBuffer.limit() == 0) {
|
||||
return counts;
|
||||
}
|
||||
long ptr = memAddress(commandBuffer);
|
||||
final long end = ptr + commandBuffer.limit();
|
||||
while (ptr < end) {
|
||||
final int cmd = memGetInt(ptr);
|
||||
counts.mergeInt(cmd, 1, Integer::sum);
|
||||
ptr += GLCommand.getCommandSize(cmd, ptr);
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sequence of command opcodes in order.
|
||||
* Used for testing to verify command ordering.
|
||||
*/
|
||||
public IntList getCommandOpcodes() {
|
||||
final IntList opcodes = new IntArrayList();
|
||||
if (commandBuffer == null || commandBuffer.limit() == 0) {
|
||||
return opcodes;
|
||||
}
|
||||
long ptr = memAddress(commandBuffer);
|
||||
final long end = ptr + commandBuffer.limit();
|
||||
while (ptr < end) {
|
||||
final int cmd = memGetInt(ptr);
|
||||
opcodes.add(cmd);
|
||||
ptr += GLCommand.getCommandSize(cmd, ptr);
|
||||
}
|
||||
return opcodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DisplayListManager.getCompiledDisplayListString(0, this, null);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vbo.IVertexBuffer;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
/**
|
||||
* A class that stores multiple vertex formats & their corresponding buffers.
|
||||
*/
|
||||
public final class DisplayListVBO {
|
||||
|
||||
private final SubVBO[] vbos;
|
||||
|
||||
DisplayListVBO(SubVBO[] vbos) {
|
||||
this.vbos = vbos;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
for (SubVBO vbo : vbos) {
|
||||
vbo.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public SubVBO[] getVBOs() {
|
||||
return vbos;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
public SubVBO(IVertexArrayObject vao, int drawMode, int start, int count) {
|
||||
this.vao = vao;
|
||||
this.drawMode = drawMode;
|
||||
this.start = start;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
vao.delete();
|
||||
}
|
||||
|
||||
public void render() {
|
||||
vao.bind();
|
||||
vao.draw(drawMode, start, count);
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
public IVertexArrayObject getVAO() {
|
||||
return vao;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
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 org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
/**
|
||||
* 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 DisplayListVBO.SubVBO[] vbos = new DisplayListVBO.SubVBO[count];
|
||||
final ArrayList<ByteBuffer> allBuffers = new ArrayList<>();
|
||||
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];
|
||||
int start = 0;
|
||||
final IVertexArrayObject vao = VAOManager.createStorageVAO(format, -1, 0); // drawMode will be ignored
|
||||
final IVertexBuffer vbo = vao.getVBO();
|
||||
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.remaining();
|
||||
}
|
||||
vertexCount = format.getVertexCount(size);
|
||||
}
|
||||
if (data.drawMode == GL11.GL_QUADS) {
|
||||
IVertexArrayObject indexedVAO = new IndexedVAO(vbo, IndexBuffer.convertQuadsToTrigs(start, start + vertexCount));
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6);
|
||||
} else {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, data.drawMode, start, vertexCount);
|
||||
}
|
||||
|
||||
|
||||
start += vertexCount;
|
||||
}
|
||||
ByteBuffer bigBuffer = mergeAndDelete(allBuffers);
|
||||
vbo.allocate(bigBuffer, start);
|
||||
memFree(bigBuffer);
|
||||
|
||||
allBuffers.clear();
|
||||
}
|
||||
return new DisplayListVBO(vbos);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
|
||||
/**
|
||||
* Command opcodes for ByteBuffer-based display list serialization.
|
||||
* Each command has a fixed layout described in comments.
|
||||
* All values are 4-byte aligned.
|
||||
*/
|
||||
public final class GLCommand {
|
||||
private GLCommand() {}
|
||||
|
||||
// === Single int commands (8 bytes: opcode + param) ===
|
||||
public static final int ENABLE = 1; // [cmd:4][cap:4]
|
||||
public static final int DISABLE = 2; // [cmd:4][cap:4]
|
||||
public static final int CLEAR = 3; // [cmd:4][mask:4]
|
||||
public static final int CLEAR_STENCIL = 4; // [cmd:4][s:4]
|
||||
public static final int CULL_FACE = 5; // [cmd:4][mode:4]
|
||||
public static final int DEPTH_FUNC = 6; // [cmd:4][func:4]
|
||||
public static final int SHADE_MODEL = 7; // [cmd:4][mode:4]
|
||||
public static final int LOGIC_OP = 8; // [cmd:4][opcode:4]
|
||||
public static final int MATRIX_MODE = 9; // [cmd:4][mode:4]
|
||||
public static final int ACTIVE_TEXTURE = 10; // [cmd:4][texture:4]
|
||||
public static final int USE_PROGRAM = 11; // [cmd:4][program:4]
|
||||
public static final int PUSH_ATTRIB = 12; // [cmd:4][mask:4]
|
||||
public static final int POP_ATTRIB = 13; // [cmd:4][unused:4]
|
||||
public static final int LOAD_IDENTITY = 14; // [cmd:4][matrixMode:4]
|
||||
public static final int PUSH_MATRIX = 15; // [cmd:4][matrixMode:4]
|
||||
public static final int POP_MATRIX = 16; // [cmd:4][matrixMode:4]
|
||||
public static final int STENCIL_MASK = 17; // [cmd:4][mask:4]
|
||||
public static final int DEPTH_MASK = 18; // [cmd:4][flag:4] (0 or 1)
|
||||
public static final int FRONT_FACE = 19; // [cmd:4][mode:4]
|
||||
|
||||
// === Two int commands (12 bytes) ===
|
||||
public static final int BIND_TEXTURE = 20; // [cmd:4][target:4][texture:4]
|
||||
public static final int POLYGON_MODE = 21; // [cmd:4][face:4][mode:4]
|
||||
public static final int COLOR_MATERIAL = 22; // [cmd:4][face:4][mode:4]
|
||||
public static final int LINE_STIPPLE = 23; // [cmd:4][factor:4][pattern:4]
|
||||
public static final int STENCIL_MASK_SEPARATE = 24; // [cmd:4][face:4][mask:4]
|
||||
public static final int FOGI = 25; // [cmd:4][pname:4][param:4]
|
||||
public static final int HINT = 26; // [cmd:4][target:4][mode:4]
|
||||
|
||||
// === Three int commands (16 bytes) ===
|
||||
public static final int STENCIL_FUNC = 30; // [cmd:4][func:4][ref:4][mask:4]
|
||||
public static final int STENCIL_OP = 31; // [cmd:4][fail:4][zfail:4][zpass:4]
|
||||
public static final int TEX_PARAMETERI = 32; // [cmd:4][target:4][pname:4][param:4]
|
||||
|
||||
// === Four int commands (20 bytes) ===
|
||||
public static final int VIEWPORT = 40; // [cmd:4][x:4][y:4][width:4][height:4]
|
||||
public static final int BLEND_FUNC = 41; // [cmd:4][srcRgb:4][dstRgb:4][srcAlpha:4][dstAlpha:4]
|
||||
public static final int COLOR_MASK = 42; // [cmd:4][r:4][g:4][b:4][a:4] (0 or 1 each)
|
||||
public static final int STENCIL_FUNC_SEPARATE = 43; // [cmd:4][face:4][func:4][ref:4][mask:4]
|
||||
public static final int STENCIL_OP_SEPARATE = 44; // [cmd:4][face:4][sfail:4][dpfail:4][dppass:4]
|
||||
|
||||
// === Float commands ===
|
||||
public static final int POINT_SIZE = 50; // [cmd:4][size:4f]
|
||||
public static final int LINE_WIDTH = 51; // [cmd:4][width:4f]
|
||||
public static final int POLYGON_OFFSET = 52; // [cmd:4][factor:4f][units:4f]
|
||||
public static final int NORMAL = 53; // [cmd:4][x:4f][y:4f][z:4f]
|
||||
public static final int COLOR = 54; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int CLEAR_COLOR = 55; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int BLEND_COLOR = 56; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
|
||||
// === Mixed int+float commands ===
|
||||
public static final int ALPHA_FUNC = 60; // [cmd:4][func:4][ref:4f]
|
||||
public static final int FOGF = 61; // [cmd:4][pname:4][param:4f]
|
||||
public static final int LIGHTF = 62; // [cmd:4][light:4][pname:4][param:4f]
|
||||
public static final int LIGHT_MODELF = 63; // [cmd:4][pname:4][param:4f]
|
||||
public static final int LIGHTI = 64; // [cmd:4][light:4][pname:4][param:4]
|
||||
public static final int LIGHT_MODELI = 65; // [cmd:4][pname:4][param:4]
|
||||
public static final int MATERIALF = 66; // [cmd:4][face:4][pname:4][param:4f]
|
||||
public static final int TEX_PARAMETERF = 67; // [cmd:4][target:4][pname:4][param:4f]
|
||||
|
||||
// === Double commands ===
|
||||
public static final int TRANSLATE = 70; // [cmd:4][mode:4][x:8d][y:8d][z:8d] = 32 bytes
|
||||
public static final int ROTATE = 71; // [cmd:4][mode:4][angle:8d][x:8d][y:8d][z:8d] = 40 bytes
|
||||
public static final int SCALE = 72; // [cmd:4][mode:4][x:8d][y:8d][z:8d] = 32 bytes
|
||||
public static final int ORTHO = 73; // [cmd:4][left:8d][right:8d][bottom:8d][top:8d][zNear:8d][zFar:8d] = 52 bytes
|
||||
public static final int FRUSTUM = 74; // [cmd:4][left:8d][right:8d][bottom:8d][top:8d][zNear:8d][zFar:8d] = 52 bytes
|
||||
public static final int CLEAR_DEPTH = 75; // [cmd:4][depth:8d] = 12 bytes
|
||||
|
||||
// === Matrix commands (72 bytes) ===
|
||||
public static final int MULT_MATRIX = 80; // [cmd:4][mode:4][m00-m33:64f] = 72 bytes
|
||||
public static final int LOAD_MATRIX = 81; // [cmd:4][mode:4][m00-m33:64f] = 72 bytes
|
||||
|
||||
// === Buffer commands (inline 4 floats max) ===
|
||||
public static final int FOG = 90; // [cmd:4][pname:4][count:4][params:16f] = 28 bytes
|
||||
public static final int LIGHT = 91; // [cmd:4][light:4][pname:4][count:4][params:16f] = 32 bytes
|
||||
public static final int LIGHT_MODEL = 92; // [cmd:4][pname:4][count:4][params:16f] = 28 bytes
|
||||
public static final int MATERIAL = 93; // [cmd:4][face:4][pname:4][count:4][params:16f] = 32 bytes
|
||||
|
||||
// === Draw commands ===
|
||||
public static final int DRAW_RANGE = 100; // [cmd:4][vboIndex:4] = 8 bytes
|
||||
public static final int DRAW_RANGE_RESTORE = 101; // [cmd:4][vboIndex:4][flags:4][color:4][normal:4][lastTexCoord:8f] = 28 bytes
|
||||
public static final int CALL_LIST = 102; // [cmd:4][listId:4] = 8 bytes
|
||||
|
||||
|
||||
public static final int DRAW_ARRAYS = 110; // [cmd:4][drawMode:4][start:4][count:4] = 20 bytes
|
||||
public static final int DRAW_ELEMENTS = 111; // [cmd:4][mode:4][indices_count:4][type:4][indices_buffer_offset:8] = 28 bytes
|
||||
public static final int DRAW_BUFFER = 112; // [cmd:4][mode:4] = 8 bytes
|
||||
public static final int DRAW_BUFFERS = 113; // [cmd:4][count:4][bufs:4*8] = 40 bytes (up to 8 buffers)
|
||||
/**
|
||||
* Draw VBO range with attribute restoration after draw.
|
||||
* Used for immediate mode VBOs to restore GL current state (color, normal, texcoord).
|
||||
*/
|
||||
|
||||
|
||||
// === Bind commands ===
|
||||
public static final int BIND_VBO = 120;
|
||||
public static final int BIND_VAO = 121;
|
||||
|
||||
// === Complex object reference ===
|
||||
public static final int COMPLEX_REF = 255; // [cmd:4][index:4] = 8 bytes
|
||||
|
||||
/**
|
||||
* Get a human-readable name for an opcode (for debugging).
|
||||
*/
|
||||
public static String getName(int opcode) {
|
||||
return switch (opcode) {
|
||||
case ENABLE -> "ENABLE";
|
||||
case DISABLE -> "DISABLE";
|
||||
case CLEAR -> "CLEAR";
|
||||
case CLEAR_STENCIL -> "CLEAR_STENCIL";
|
||||
case CULL_FACE -> "CULL_FACE";
|
||||
case DEPTH_FUNC -> "DEPTH_FUNC";
|
||||
case SHADE_MODEL -> "SHADE_MODEL";
|
||||
case LOGIC_OP -> "LOGIC_OP";
|
||||
case MATRIX_MODE -> "MATRIX_MODE";
|
||||
case ACTIVE_TEXTURE -> "ACTIVE_TEXTURE";
|
||||
case USE_PROGRAM -> "USE_PROGRAM";
|
||||
case PUSH_ATTRIB -> "PUSH_ATTRIB";
|
||||
case POP_ATTRIB -> "POP_ATTRIB";
|
||||
case LOAD_IDENTITY -> "LOAD_IDENTITY";
|
||||
case PUSH_MATRIX -> "PUSH_MATRIX";
|
||||
case POP_MATRIX -> "POP_MATRIX";
|
||||
case STENCIL_MASK -> "STENCIL_MASK";
|
||||
case DEPTH_MASK -> "DEPTH_MASK";
|
||||
case FRONT_FACE -> "FRONT_FACE";
|
||||
case BIND_TEXTURE -> "BIND_TEXTURE";
|
||||
case POLYGON_MODE -> "POLYGON_MODE";
|
||||
case COLOR_MATERIAL -> "COLOR_MATERIAL";
|
||||
case LINE_STIPPLE -> "LINE_STIPPLE";
|
||||
case STENCIL_MASK_SEPARATE -> "STENCIL_MASK_SEPARATE";
|
||||
case FOGI -> "FOGI";
|
||||
case HINT -> "HINT";
|
||||
case STENCIL_FUNC -> "STENCIL_FUNC";
|
||||
case STENCIL_OP -> "STENCIL_OP";
|
||||
case TEX_PARAMETERI -> "TEX_PARAMETERI";
|
||||
case VIEWPORT -> "VIEWPORT";
|
||||
case BLEND_FUNC -> "BLEND_FUNC";
|
||||
case COLOR_MASK -> "COLOR_MASK";
|
||||
case STENCIL_FUNC_SEPARATE -> "STENCIL_FUNC_SEPARATE";
|
||||
case STENCIL_OP_SEPARATE -> "STENCIL_OP_SEPARATE";
|
||||
case POINT_SIZE -> "POINT_SIZE";
|
||||
case LINE_WIDTH -> "LINE_WIDTH";
|
||||
case POLYGON_OFFSET -> "POLYGON_OFFSET";
|
||||
case COLOR -> "COLOR";
|
||||
case CLEAR_COLOR -> "CLEAR_COLOR";
|
||||
case BLEND_COLOR -> "BLEND_COLOR";
|
||||
case ALPHA_FUNC -> "ALPHA_FUNC";
|
||||
case FOGF -> "FOGF";
|
||||
case LIGHTF -> "LIGHTF";
|
||||
case LIGHT_MODELF -> "LIGHT_MODELF";
|
||||
case LIGHTI -> "LIGHTI";
|
||||
case LIGHT_MODELI -> "LIGHT_MODELI";
|
||||
case MATERIALF -> "MATERIALF";
|
||||
case TEX_PARAMETERF -> "TEX_PARAMETERF";
|
||||
case TRANSLATE -> "TRANSLATE";
|
||||
case ROTATE -> "ROTATE";
|
||||
case SCALE -> "SCALE";
|
||||
case ORTHO -> "ORTHO";
|
||||
case FRUSTUM -> "FRUSTUM";
|
||||
case CLEAR_DEPTH -> "CLEAR_DEPTH";
|
||||
case MULT_MATRIX -> "MULT_MATRIX";
|
||||
case LOAD_MATRIX -> "LOAD_MATRIX";
|
||||
case FOG -> "FOG";
|
||||
case LIGHT -> "LIGHT";
|
||||
case LIGHT_MODEL -> "LIGHT_MODEL";
|
||||
case MATERIAL -> "MATERIAL";
|
||||
case DRAW_RANGE -> "DRAW_RANGE";
|
||||
case CALL_LIST -> "CALL_LIST";
|
||||
case DRAW_BUFFER -> "DRAW_BUFFER";
|
||||
case DRAW_BUFFERS -> "DRAW_BUFFERS";
|
||||
case COMPLEX_REF -> "COMPLEX_REF";
|
||||
case DRAW_RANGE_RESTORE -> "DRAW_RANGE_RESTORE";
|
||||
case BIND_VAO -> "BIND_VAO";
|
||||
case BIND_VBO -> "BIND_VBO";
|
||||
default -> "UNKNOWN(" + opcode + ")";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of a command in bytes (including the opcode).
|
||||
*/
|
||||
public static int getCommandSize(int cmd, long ptr) {
|
||||
return switch (cmd) {
|
||||
// Opcode-only commands (4 bytes)
|
||||
case GLCommand.LOAD_IDENTITY, GLCommand.PUSH_MATRIX, GLCommand.POP_MATRIX -> 4;
|
||||
|
||||
// Single int commands (8 bytes)
|
||||
case GLCommand.ENABLE, GLCommand.DISABLE, GLCommand.CLEAR, GLCommand.CLEAR_STENCIL,
|
||||
GLCommand.CULL_FACE, GLCommand.DEPTH_FUNC, GLCommand.SHADE_MODEL, GLCommand.LOGIC_OP,
|
||||
GLCommand.MATRIX_MODE, GLCommand.ACTIVE_TEXTURE, GLCommand.USE_PROGRAM,
|
||||
GLCommand.PUSH_ATTRIB, GLCommand.POP_ATTRIB, GLCommand.STENCIL_MASK,
|
||||
GLCommand.DEPTH_MASK, GLCommand.FRONT_FACE, GLCommand.POINT_SIZE, GLCommand.LINE_WIDTH,
|
||||
GLCommand.CALL_LIST, GLCommand.COMPLEX_REF, GLCommand.DRAW_RANGE, GLCommand.BIND_VBO,
|
||||
GLCommand.BIND_VAO -> 8;
|
||||
|
||||
// Two int commands (12 bytes)
|
||||
case GLCommand.BIND_TEXTURE, GLCommand.POLYGON_MODE, GLCommand.COLOR_MATERIAL,
|
||||
GLCommand.LINE_STIPPLE, GLCommand.STENCIL_MASK_SEPARATE, GLCommand.FOGI,
|
||||
GLCommand.HINT, GLCommand.POLYGON_OFFSET, GLCommand.ALPHA_FUNC, GLCommand.FOGF,
|
||||
GLCommand.LIGHT_MODELF, GLCommand.LIGHT_MODELI -> 12;
|
||||
|
||||
// Three int commands (16 bytes)
|
||||
case GLCommand.STENCIL_FUNC, GLCommand.STENCIL_OP, GLCommand.TEX_PARAMETERI,
|
||||
GLCommand.LIGHTF, GLCommand.LIGHTI,
|
||||
GLCommand.MATERIALF, GLCommand.TEX_PARAMETERF, GLCommand.DRAW_ARRAYS -> 16;
|
||||
|
||||
// Four int commands (20 bytes)
|
||||
case GLCommand.VIEWPORT, GLCommand.BLEND_FUNC, GLCommand.COLOR_MASK,
|
||||
GLCommand.STENCIL_FUNC_SEPARATE, GLCommand.STENCIL_OP_SEPARATE,
|
||||
GLCommand.COLOR, GLCommand.CLEAR_COLOR, GLCommand.BLEND_COLOR -> 20;
|
||||
|
||||
case GLCommand.DRAW_ELEMENTS -> 24;
|
||||
|
||||
// DRAW_RANGE_RESTORE: 28 bytes
|
||||
case GLCommand.DRAW_RANGE_RESTORE -> 28;
|
||||
|
||||
// DRAW_BUFFER: 8 bytes [cmd:4][mode:4]
|
||||
case GLCommand.DRAW_BUFFER -> 8;
|
||||
|
||||
// DRAW_BUFFERS: 40 bytes [cmd:4][count:4][bufs:4*8]
|
||||
case GLCommand.DRAW_BUFFERS -> 40;
|
||||
|
||||
// Double commands
|
||||
case GLCommand.TRANSLATE, GLCommand.SCALE -> 28; // cmd + 3 doubles
|
||||
case GLCommand.CLEAR_DEPTH -> 12; // cmd + 1 double
|
||||
case GLCommand.ROTATE -> 36; // cmd + 4 doubles
|
||||
case GLCommand.ORTHO, GLCommand.FRUSTUM -> 52; // cmd + 6 doubles
|
||||
|
||||
// Matrix commands
|
||||
case GLCommand.MULT_MATRIX, GLCommand.LOAD_MATRIX -> 68; // cmd + 16 floats
|
||||
|
||||
// Buffer commands (variable, read count)
|
||||
case GLCommand.FOG, GLCommand.LIGHT_MODEL -> {
|
||||
final int count = memGetInt(ptr + 8); // pname at +4, count at +8
|
||||
yield 12 + count * 4;
|
||||
}
|
||||
case GLCommand.LIGHT, GLCommand.MATERIAL -> {
|
||||
final int count = memGetInt(ptr + 12); // light/face at +4, pname at +8, count at +12
|
||||
yield 16 + count * 4;
|
||||
}
|
||||
|
||||
default -> throw new IllegalStateException("Unknown command: " + cmd);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClientArrayState;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
/**
|
||||
* Records immediate mode GL calls (glBegin/glEnd/glVertex) during display list compilation.
|
||||
* Converts immediate mode geometry to ByteBuffer directly via {@link DirectTessellator}
|
||||
* <p>
|
||||
* Currently only used for display list compilation, should be perfectly usable outside of display lists too though.
|
||||
*/
|
||||
public final class ImmediateModeRecorder {
|
||||
private static final DirectTessellator tessellator = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
|
||||
private ImmediateModeRecorder() {
|
||||
|
||||
}
|
||||
|
||||
public static DirectTessellator getInternalTessellator() {
|
||||
return tessellator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current texture coordinate.
|
||||
* Called by GLStateManager.glTexCoord*.
|
||||
*/
|
||||
public static void setTexCoord(float s, float t) {
|
||||
tessellator.setTextureUV(s, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current normal vector.
|
||||
* Called by GLStateManager.glNormal*.
|
||||
*/
|
||||
public static void setNormal(float x, float y, float z) {
|
||||
tessellator.setNormal(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording a new primitive.
|
||||
*/
|
||||
public static void begin(int primitiveType) {
|
||||
tessellator.startDrawing(primitiveType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if (!tessellator.isDrawing) {
|
||||
throw new IllegalStateException("glEnd called without glBegin");
|
||||
}
|
||||
|
||||
tessellator.isDrawing = false;
|
||||
|
||||
if (tessellator.isEmpty()) return null;
|
||||
|
||||
return tessellator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a vertex with current attributes.
|
||||
* Color is read from GLStateManager at call time.
|
||||
*/
|
||||
public static void vertex(float x, float y, float z) {
|
||||
if (!tessellator.isDrawing) {
|
||||
throw new IllegalStateException("glVertex called outside glBegin/glEnd");
|
||||
}
|
||||
tessellator.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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a glDrawArrays call during display list recording.
|
||||
* Reads vertex data from client arrays (tracked globally in GLStateManager) and converts to immediate mode geometry.
|
||||
*
|
||||
* @param mode Primitive type (GL_QUADS, GL_LINES, etc.)
|
||||
* @param first Starting index in arrays
|
||||
* @param count Number of vertices
|
||||
* @return Result containing converted geometry, or null if no geometry produced
|
||||
*/
|
||||
public static DirectTessellator processDrawArrays(int mode, int first, int count) {
|
||||
final ClientArrayState cas = GLStateManager.getClientArrayState();
|
||||
final Buffer vertexPointer = cas.isVertexArrayEnabled() ? cas.getVertexPointer() : null;
|
||||
final Buffer colorPointer = cas.isColorArrayEnabled() ? cas.getColorPointer() : null;
|
||||
|
||||
return convertClientArrays(
|
||||
mode, first, count,
|
||||
vertexPointer,
|
||||
cas.getVertexPointerType(), cas.getVertexPointerSize(), cas.getVertexPointerStride(),
|
||||
colorPointer,
|
||||
cas.getColorPointerType(), cas.getColorPointerSize(), cas.getColorPointerStride()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert client array data to immediate mode.
|
||||
* Called by glDrawArrays during display list recording.
|
||||
* Like Mesa's save_DrawArrays, reads vertex data from arrays and emits as immediate mode.
|
||||
*
|
||||
* @param mode Primitive type (GL_QUADS, GL_LINES, etc.)
|
||||
* @param first Starting index in arrays
|
||||
* @param count Number of vertices
|
||||
* @param vertexPointer Vertex position buffer (or null if not enabled)
|
||||
* @param vertexType GL type (GL_FLOAT, GL_DOUBLE, etc.)
|
||||
* @param vertexSize Size of vertex (2, 3, or 4)
|
||||
* @param vertexStride Stride in bytes (0 for tightly packed)
|
||||
* @param colorPointer Color buffer (or null if not enabled)
|
||||
* @param colorType GL type (GL_FLOAT, GL_UNSIGNED_BYTE, etc.)
|
||||
* @param colorSize Size of color (3 or 4)
|
||||
* @param colorStride Stride in bytes (0 for tightly packed)
|
||||
* @return Result containing converted geometry, or null if no geometry produced
|
||||
*/
|
||||
public static DirectTessellator convertClientArrays(
|
||||
int mode, int first, int count,
|
||||
java.nio.Buffer vertexPointer, int vertexType, int vertexSize, int vertexStride,
|
||||
java.nio.Buffer colorPointer, int colorType, int colorSize, int colorStride
|
||||
) {
|
||||
begin(mode);
|
||||
|
||||
// Hoist type checks and stride calculations outside the loop for performance.
|
||||
// Mesa's algorithm: offset_bytes = index * effective_stride
|
||||
// effective_stride = (stride == 0) ? (numComponents * sizeof(element)) : stride
|
||||
|
||||
// Determine color reader (hoisted out of loop)
|
||||
final ColorReader colorReader;
|
||||
if (colorPointer == null) {
|
||||
colorReader = null;
|
||||
} else if (colorType == GL11.GL_FLOAT && colorPointer instanceof FloatBuffer fb) {
|
||||
final int colorEffectiveStride = (colorStride == 0) ? colorSize * 4 : colorStride;
|
||||
final int strideInFloats = colorEffectiveStride / 4;
|
||||
colorReader = (idx) -> {
|
||||
final int offset = idx * strideInFloats;
|
||||
final float r = fb.get(offset);
|
||||
final float g = fb.get(offset + 1);
|
||||
final float b = fb.get(offset + 2);
|
||||
final float a = (colorSize == 4) ? fb.get(offset + 3) : 1.0f;
|
||||
GLStateManager.glColor4f(r, g, b, a);
|
||||
};
|
||||
} else if ((colorType == GL11.GL_UNSIGNED_BYTE || colorType == GL11.GL_BYTE) && colorPointer instanceof ByteBuffer bb) {
|
||||
final int colorEffectiveStride = (colorStride == 0) ? colorSize : colorStride;
|
||||
colorReader = (idx) -> {
|
||||
final int offset = idx * colorEffectiveStride;
|
||||
final float r = (bb.get(offset) & 0xFF) / 255.0f;
|
||||
final float g = (bb.get(offset + 1) & 0xFF) / 255.0f;
|
||||
final float b = (bb.get(offset + 2) & 0xFF) / 255.0f;
|
||||
final float a = (colorSize == 4) ? (bb.get(offset + 3) & 0xFF) / 255.0f : 1.0f;
|
||||
GLStateManager.glColor4f(r, g, b, a);
|
||||
};
|
||||
} else {
|
||||
// Unsupported color type - use white
|
||||
colorReader = (idx) -> GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Determine vertex reader (hoisted out of loop)
|
||||
final VertexReader vertexReader;
|
||||
if (vertexPointer == null) {
|
||||
vertexReader = null;
|
||||
} else if (vertexType == GL11.GL_FLOAT && vertexPointer instanceof FloatBuffer fb) {
|
||||
final int effectiveStride = (vertexStride == 0) ? vertexSize * 4 : vertexStride;
|
||||
final int strideInFloats = effectiveStride / 4;
|
||||
vertexReader = (idx) -> {
|
||||
final int offset = idx * strideInFloats;
|
||||
final float x = fb.get(offset);
|
||||
final float y = fb.get(offset + 1);
|
||||
final float z = (vertexSize >= 3) ? fb.get(offset + 2) : 0.0f;
|
||||
vertex(x, y, z);
|
||||
};
|
||||
} else if (vertexType == GL11.GL_DOUBLE && vertexPointer instanceof DoubleBuffer db) {
|
||||
final int effectiveStride = (vertexStride == 0) ? vertexSize * 8 : vertexStride;
|
||||
final int strideInDoubles = effectiveStride / 8;
|
||||
vertexReader = (idx) -> {
|
||||
final int offset = idx * strideInDoubles;
|
||||
final float x = (float) db.get(offset);
|
||||
final float y = (float) db.get(offset + 1);
|
||||
final float z = (vertexSize >= 3) ? (float) db.get(offset + 2) : 0.0f;
|
||||
vertex(x, y, z);
|
||||
};
|
||||
} else if (vertexType == GL11.GL_INT && vertexPointer instanceof IntBuffer ib) {
|
||||
final int effectiveStride = (vertexStride == 0) ? vertexSize * 4 : vertexStride;
|
||||
final int strideInInts = effectiveStride / 4;
|
||||
vertexReader = (idx) -> {
|
||||
final int offset = idx * strideInInts;
|
||||
final float x = ib.get(offset);
|
||||
final float y = ib.get(offset + 1);
|
||||
final float z = (vertexSize >= 3) ? ib.get(offset + 2) : 0.0f;
|
||||
vertex(x, y, z);
|
||||
};
|
||||
} else if (vertexType == GL11.GL_SHORT && vertexPointer instanceof ShortBuffer sb) {
|
||||
final int effectiveStride = (vertexStride == 0) ? vertexSize * 2 : vertexStride;
|
||||
final int strideInShorts = effectiveStride / 2;
|
||||
vertexReader = (idx) -> {
|
||||
final int offset = idx * strideInShorts;
|
||||
final float x = sb.get(offset);
|
||||
final float y = sb.get(offset + 1);
|
||||
final float z = (vertexSize >= 3) ? sb.get(offset + 2) : 0.0f;
|
||||
vertex(x, y, z);
|
||||
};
|
||||
} else {
|
||||
// Unsupported vertex type
|
||||
vertexReader = null;
|
||||
}
|
||||
|
||||
// Main loop - no type checks, just function calls
|
||||
for (int i = first; i < first + count; i++) {
|
||||
if (colorReader != null) {
|
||||
colorReader.read(i);
|
||||
}
|
||||
if (vertexReader != null) {
|
||||
vertexReader.read(i);
|
||||
}
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ColorReader {
|
||||
void read(int index);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface VertexReader {
|
||||
void read(int index);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 951 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user