Compare commits
207 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5072ed475 | |||
| ffee2141d4 | |||
| 9f4b6b8709 | |||
| b05f074f4c | |||
| 06a549983b | |||
| 0b96ca8509 | |||
| edd50096d6 | |||
| 607f3e8afe | |||
| 8b3404e5f8 | |||
| df6253af39 | |||
| b1f3b23ba1 | |||
| c4708ed173 | |||
| fcab0d3b20 | |||
| 1034360b88 | |||
| f92f656876 | |||
| e052a0c96f | |||
| 8bb8217c7b | |||
| 0563cde3c2 | |||
| 5fe192f4c5 | |||
| 428e12081c | |||
| ac102402cc | |||
| 966677b89e | |||
| 64e73b7d83 | |||
| 52ea2e96b7 | |||
| c658697ecd | |||
| a5b259f098 | |||
| e9da9c26f4 | |||
| 2f6ff1a3ea | |||
| 766e5a358d | |||
| a19189c2a8 | |||
| 0627f779d7 | |||
| 01bfb65d9e | |||
| cccad08a61 | |||
| 96be86cacf | |||
| b75791006c | |||
| 9047ebb970 | |||
| 2d1c2d6efb | |||
| 8577363438 | |||
| 4229ed75ae | |||
| b8408bc6fa | |||
| 56c4911316 | |||
| 244ead9451 | |||
| 5709a4c660 | |||
| 3913b955be | |||
| a295dcafd4 | |||
| f9914f9336 | |||
| 13e9df5b48 | |||
| 98c394bad1 | |||
| 444bf3b8bc | |||
| 455281a32d | |||
| 7b613ae8e3 | |||
| 443d6165fa | |||
| a4ebe3e3c1 | |||
| 6ac3edf280 | |||
| a1163dc340 | |||
| 38b7e66ef8 | |||
| a843a0ed65 | |||
| e0d2d2530f | |||
| 44e2936b68 | |||
| dfa717e0e3 | |||
| 69844417ce | |||
| 8370402dc1 | |||
| cbb32bc996 | |||
| ac876c0030 | |||
| 2176807a0a | |||
| 47732b7f57 | |||
| ff9afee5b4 | |||
| b5665a59c0 | |||
| 483ecf2e4d | |||
| 30eab27a49 | |||
| d1b5200fed | |||
| 4a4728d41e | |||
| c9204a2094 | |||
| c45f0b2414 | |||
| 97b2db84cd | |||
| c389f3b391 | |||
| 6049840aa6 | |||
| 2dee6b0326 | |||
| 27d9e6aeb7 | |||
| c751b6fcc6 | |||
| 31f173c8e8 | |||
| 90ca3bd394 | |||
| 30dd5526cd | |||
| a9e31b8133 | |||
| e71cd864b4 | |||
| 8efc7d54e7 | |||
| e323e8d62e | |||
| b970ad0ab8 | |||
| c811e6bad6 | |||
| 8ef0d40f0c | |||
| 227f7d0a23 | |||
| b385b018f1 | |||
| 4ece2de991 | |||
| a172961112 | |||
| 9e882951ef | |||
| 1c9fe23633 | |||
| 92b6a9695d | |||
| acc5e7af98 | |||
| aa9e49b3e7 | |||
| 7d86e24db5 | |||
| 71986d8818 | |||
| e948b4dac1 | |||
| 0a5f7d0c11 | |||
| 1c65ef8323 | |||
| 2c744dd22b | |||
| 009d7ede1b | |||
| 4199954843 | |||
| c7ab36c6b7 | |||
| 1da6544550 | |||
| c2896d1f73 | |||
| 302abb1e57 | |||
| 34c6f28173 | |||
| 75676df736 | |||
| 56b80f00e8 | |||
| c1651edb6a | |||
| c1375f7a10 | |||
| a82ef3dcde | |||
| 74a97dab0d | |||
| 16621773af | |||
| c8988ad1b7 | |||
| 1f3f432fef | |||
| 56032b6d6b | |||
| 6157c659d2 | |||
| d2cae841e7 | |||
| d1711be390 | |||
| 93332d6256 | |||
| 8292dc67c0 | |||
| 1b82acec9f | |||
| da4f423d10 | |||
| 5a4c04b5a3 | |||
| cd50be6531 | |||
| 5d4698621c | |||
| aba392ace4 | |||
| d1c0ea123a | |||
| 816efb2837 | |||
| 0221a39819 | |||
| cc08707f32 | |||
| 8db253f886 | |||
| f59bdf15d7 | |||
| ac0d439b3f | |||
| 4e652c7573 | |||
| 3686bfb4e0 | |||
| fe1b2c2683 | |||
| d5ed9a22fa | |||
| e3f9c974f8 | |||
| daead98102 | |||
| 5c31927d54 | |||
| f9fa1a5260 | |||
| d0472ee56d | |||
| e4e21d2dc8 | |||
| 79e4dce569 | |||
| 408f09c0f4 | |||
| 65bfedc942 | |||
| fab99cd4ec | |||
| 019f4b7c1e | |||
| f3b6b15bcb | |||
| 7c086cdc40 | |||
| 5617e1312c | |||
| 9dd51bfdde | |||
| c9dc998eef | |||
| 068df9d5e0 | |||
| 44dc7c96af | |||
| 89cc3513f3 | |||
| 7a94db77ef | |||
| f2fc669b37 | |||
| a3d4163b67 | |||
| aea4542616 | |||
| a489810d68 | |||
| 806a1e99db | |||
| 4843027e43 | |||
| af4ba453ca | |||
| adc5853f0b | |||
| c42ddd29a9 | |||
| 9b7d3c083f | |||
| 87bb4ae840 | |||
| b34f5e7f5f | |||
| a053a79d99 | |||
| 73c041e02f | |||
| f96a5fc794 | |||
| d591458cd6 | |||
| 88305d8db3 | |||
| 40b0517656 | |||
| 2289826363 | |||
| e1c2b2a0a9 | |||
| 70f7a2422b | |||
| 8ad6f184dd | |||
| 4a93bde7be | |||
| b9dfd93b56 | |||
| 5e78c98f17 | |||
| 31035ccb1e | |||
| e840a23d01 | |||
| 08b8f8778a | |||
| 044f87eef2 | |||
| 30cd7fd4e0 | |||
| fbf5dfaa9d | |||
| 58d4bc7f0f | |||
| 511a771351 | |||
| e806098544 | |||
| 4316cfbfd7 | |||
| 6be4c0303f | |||
| 7633c7bc70 | |||
| 0bc96a98cf | |||
| a0529a310b | |||
| ace3c03019 | |||
| 16b44695ec | |||
| 151ca3902f | |||
| 1ba24659bc |
@@ -1,65 +1,15 @@
|
||||
# Distant Horizons
|
||||
|
||||
This mod adds a Level Of Detail (LOD) system to Minecraft.\
|
||||
This implementation renders simplified chunks outside the normal render distance\
|
||||
allowing for an increased view distance without harming performance.
|
||||
|
||||
Or in other words: this mod lets you see farther without turning your game into a slide show.\
|
||||
If you want to see a quick demo, check out a video covering the mod here:
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=H2tnvEVbO1c" target="_blank"></a>
|
||||
|
||||
|
||||
Forge version: 1.16.5-36.1.0
|
||||
|
||||
Notes:\
|
||||
This version has been confirmed to work in Eclipse and Retail Minecraft.\
|
||||
(Retail running forge version 1.16.5-36.1.0)
|
||||
This repo is for the Distant Horizons mod.
|
||||
The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and potentially helping us port to different versions faster and easier.
|
||||
|
||||
Check out the mod's main GitLab page here:
|
||||
https://gitlab.com/jeseibel/minecraft-lod-mod
|
||||
|
||||
## source code installation
|
||||
|
||||
See the Forge Documentation online for more detailed instructions:\
|
||||
http://mcforge.readthedocs.io/en/latest/gettingstarted/
|
||||
|
||||
1. Create a system variable called "JAVA_MC_HOME" with the location of the JDK 1.8.0_251 (This is needed for gradle to work correctly)
|
||||
2. replace JAVA_HOME with JAVA_MC_HOME in gradle.bat
|
||||
3. open a command line in the project folder
|
||||
|
||||
**If using Ecplise:**
|
||||
1. run the command: `./gradlew geneclipseruns`
|
||||
2. run the command: `./gradlew eclipse`
|
||||
3. Make sure eclipse has the JDK 1.8.0_251 installed. (This is needed so that eclipse can run minecraft)
|
||||
4. Import the project into eclipse
|
||||
|
||||
**If using IntelliJ:**
|
||||
1. open IDEA and import the build.gradle
|
||||
2. run the command: `./gradlew genIntellijRuns`
|
||||
3. refresh the Gradle project in IDEA if required
|
||||
|
||||
|
||||
## Compiling
|
||||
|
||||
1. open a command line in the project folder
|
||||
2. run the command: `./gradlew build`
|
||||
3. the compiled jar file will be in the folder `build\libs`
|
||||
|
||||
|
||||
## Other commands
|
||||
|
||||
`./gradlew --refresh-dependencies` to refresh local dependencies.
|
||||
|
||||
`./gradlew clean` to reset everything (this does not affect your code) and then start the process again.
|
||||
|
||||
|
||||
## Note to self
|
||||
|
||||
The Minecraft source code is NOT added to your workspace in an editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only.
|
||||
|
||||
Source code uses Mojang mappings.
|
||||
|
||||
The source code can be 'created' with the `./eclipse` command and can be found in the following path:\
|
||||
`minecraft-lod-mod\build\fg_cache\mcp\ VERSION \joined\ RANDOM_STRING \patch\output.jar`
|
||||
You shouldn't download this repo directly.
|
||||
It should be automatically included when pulling the full mod.
|
||||
|
||||
|
||||
## Open Source Acknowledgements
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
@@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2021-12-22T02:14:44.485Z" agent="5.0 (Windows)" etag="8Lz4CpREcKLpQpROSPVl" version="16.0.3" type="gitlab"><diagram id="xLs7mM1S-vncSruOQYJG" name="Page-1">xZVNj5swEIZ/DcetAJdkc2yTbXvZVaQcuunNtSfgrsGRcRbor6+Jx4BDom3VSr1Enmc+7HnHOBFZl+1nTY/Fo+IgozTmbUQ2UZou71P724POAZIlDuRacIcmYCd+AsIY6UlwqINAo5Q04hhCpqoKmAkY1Vo1YdhByXDXI81hBnaMyjn9KrgpHL3P4pF/AZEXfuckRk9JfTCCuqBcNRNEHiKy1koZtyrbNcheO6+Ly/t0wzscTENlfichf/r2ohqemP2Pp+3dtn3s9tkdVnml8oQNCwOlJeioTeeV0OpUceiLxRH52BQ2cHekrPc2dvSWFaaU1krsEsuCNtDePG8yqGBvD6gSjO5sCCasUDe8OMl7tJtxDInXtpiMYIGM4uTzofIojl2gPn+gVXpLq/TvtDoIKddKKn3OJYcDLBizvDZavcDEw5er73H8b9QlSSjv4n+rS2bqzlSFin/oP2lrMUnrWrBQyFB1aIV5Rk+/3vf8XYbWpp2EbTpvVLaV56nhspaZt8e8s+UT3VmBzx6Ti3nYftRJM3j7kzRU52Deuo7z+U4GmF2Zn2caJDXiNTzutaHiDlslbCM3r89Q15dwbWLW9FW6LLS4+MxXF4WcDrNC5zs2tH3t2llzfFxd+PgPRR5+AQ==</diagram></mxfile>
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url = 'https://files.minecraftforge.net' }
|
||||
mavenCentral()
|
||||
// potential replacement in case of problems:
|
||||
// https://dist.creeper.host/Sponge/maven
|
||||
maven { url = 'https://repo.spongepowered.org/maven/' }
|
||||
// used to download and compile dependencies from git repos
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
dependencies {
|
||||
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
|
||||
classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT'
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
|
||||
}
|
||||
}
|
||||
apply plugin: 'net.minecraftforge.gradle'
|
||||
apply plugin: 'org.spongepowered.mixin'
|
||||
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
|
||||
version = 'a1.5.3'
|
||||
group = 'com.seibel.lod'
|
||||
archivesBaseName = 'Distant-Horizons_1.16.5'
|
||||
|
||||
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
|
||||
|
||||
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
|
||||
minecraft {
|
||||
// The mappings can be changed at any time, and must be in the following format.
|
||||
// snapshot_YYYYMMDD Snapshot are built nightly.
|
||||
// stable_# Stables are built at the discretion of the MCP team.
|
||||
// Use non-default mappings at your own risk. they may not always work.
|
||||
// Simply re-run your setup task after changing the mappings to update your workspace.
|
||||
mappings channel: 'official', version: '1.16.5'
|
||||
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
|
||||
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
|
||||
// Default run configurations.
|
||||
// These can be tweaked, removed, or duplicated as needed.
|
||||
runs {
|
||||
client {
|
||||
workingDirectory project.file('run')
|
||||
arg "-mixin.config=lod.mixins.json"
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
// The markers can be changed as needed.
|
||||
// "SCAN": For mods scan.
|
||||
// "REGISTRIES": For firing of registry events.
|
||||
// "REGISTRYDUMP": For getting the contents of all registries.
|
||||
property 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
// Recommended logging level for the console
|
||||
// You can set various levels here.
|
||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
mods {
|
||||
examplemod {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
workingDirectory project.file('run')
|
||||
arg "-mixin.config=lod.mixins.json"
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
// The markers can be changed as needed.
|
||||
// "SCAN": For mods scan.
|
||||
// "REGISTRIES": For firing of registry events.
|
||||
// "REGISTRYDUMP": For getting the contents of all registries.
|
||||
property 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
// Recommended logging level for the console
|
||||
// You can set various levels here.
|
||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
mods {
|
||||
examplemod {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data {
|
||||
workingDirectory project.file('run')
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
// The markers can be changed as needed.
|
||||
// "SCAN": For mods scan.
|
||||
// "REGISTRIES": For firing of registry events.
|
||||
// "REGISTRYDUMP": For getting the contents of all registries.
|
||||
property 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
// Recommended logging level for the console
|
||||
// You can set various levels here.
|
||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||
args '--mod', 'lod', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
||||
|
||||
mods {
|
||||
examplemod {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include resources generated by data generators.
|
||||
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
||||
|
||||
// this is required so that we can use
|
||||
// jitpack in the dependencies section below
|
||||
repositories {
|
||||
mavenCentral()
|
||||
// used to download and compile dependencies from git repos
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
|
||||
configurations {
|
||||
shadowMe
|
||||
compileOnly.extendsFrom(embed)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
|
||||
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
|
||||
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
|
||||
minecraft 'net.minecraftforge:forge:1.16.5-36.1.0'
|
||||
|
||||
|
||||
compile 'org.tukaani:xz:1.9'
|
||||
shadowMe 'org.tukaani:xz:1.9'
|
||||
compile 'org.apache.commons:commons-compress:1.21'
|
||||
shadowMe 'org.apache.commons:commons-compress:1.21'
|
||||
|
||||
// these were added to hopefully allow for cloning
|
||||
// configuredFeatures to allow for safe
|
||||
// multi threaded feature generation. Sadly I couldn't find
|
||||
// a way to duplicate lambda functions (which features use)
|
||||
// so for now I'm not sure what to do.
|
||||
//implementation 'io.github.kostaskougios:cloning:1.10.3'
|
||||
//
|
||||
//implementation ('com.esotericsoftware:kryo:5.1.1') {
|
||||
// exclude group: "org.objenesis"
|
||||
//}
|
||||
//implementation 'org.objenesis:objenesis:3.2'
|
||||
|
||||
|
||||
// You may put jars on which you depend on in ./libs or you may define them like so..
|
||||
// compile "some.group:artifact:version:classifier"
|
||||
// compile "some.group:artifact:version"
|
||||
|
||||
// Real examples
|
||||
// compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
|
||||
// compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
|
||||
|
||||
// The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
|
||||
// provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// These dependencies get remapped to your current MCP mappings
|
||||
// deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// For more info...
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
configurations = [project.configurations.getByName("shadowMe")]
|
||||
relocate 'org.tukaani', 'shaded.tukaani'
|
||||
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
|
||||
classifier = ''
|
||||
}
|
||||
|
||||
reobf {
|
||||
shadowJar {
|
||||
dependsOn tasks.createMcpToSrg
|
||||
mappings = tasks.createMcpToSrg.outputs.files.singleFile
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives tasks.shadowJar
|
||||
}
|
||||
|
||||
// Example for how to get properties into the manifest for reading by the runtime..
|
||||
jar {
|
||||
manifest {
|
||||
attributes([
|
||||
"Specification-Title": "LOD",
|
||||
"Specification-Version": "1", // We are version 1 of ourselves
|
||||
"Implementation-Title": project.name,
|
||||
"Implementation-Version": "1",
|
||||
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
"MixinConfigs": "lod.mixins.json",
|
||||
])
|
||||
}
|
||||
}
|
||||
// Example configuration to allow publishing using the maven-publish task
|
||||
// This is the preferred method to reobfuscate your jar file
|
||||
jar.finalizedBy('reobfJar')
|
||||
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing
|
||||
//publish.dependsOn('reobfJar')
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact jar
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "file:///${project.projectDir}/mcmodsrepo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixin {
|
||||
add sourceSets.main, "lod.refmap.json"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=false
|
||||
Vendored
BIN
Binary file not shown.
-5
@@ -1,5 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
|
||||
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
-84
@@ -1,84 +0,0 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_MC_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_MC_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_MC_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_MC_HOME=%JAVA_MC_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_MC_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_MC_HOME is set to an invalid directory: %JAVA_MC_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_MC_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
: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%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -28,7 +28,7 @@ package com.seibel.lod.core;
|
||||
* Pretty much all of the mod stems from there.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @version 11-29-2021
|
||||
*/
|
||||
public final class ModInfo
|
||||
{
|
||||
@@ -38,5 +38,5 @@ public final class ModInfo
|
||||
/** Human readable version of NAME */
|
||||
public static final String READABLE_NAME = "Distant Horizons";
|
||||
public static final String API = "LodAPI";
|
||||
public static final String VERSION = "a1.5.3";
|
||||
public static final String VERSION = "a1.5.4";
|
||||
}
|
||||
@@ -23,14 +23,12 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.builders.worldGeneration.LodGenWorker;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
@@ -42,7 +40,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
* Specifically for the client.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
* @version 12-8-2021
|
||||
*/
|
||||
public class ClientApi
|
||||
{
|
||||
@@ -63,6 +61,7 @@ public class ClientApi
|
||||
private boolean firstTimeSetupComplete = false;
|
||||
private boolean configOverrideReminderPrinted = false;
|
||||
|
||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||
|
||||
|
||||
private ClientApi()
|
||||
@@ -103,19 +102,32 @@ public class ClientApi
|
||||
lodDim.expandOrLoadRegionsAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
|
||||
|
||||
|
||||
// Note to self:
|
||||
// if "unspecified" shows up in the pie chart, it is
|
||||
// possibly because the amount of time between sections
|
||||
// is too small for the profiler to measure
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
if (CONFIG.client().advanced().debugging().getDrawLods())
|
||||
{
|
||||
// Note to self:
|
||||
// if "unspecified" shows up in the pie chart, it is
|
||||
// possibly because the amount of time between sections
|
||||
// is too small for the profiler to measure
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
if (!rendererDisabledBecauseOfExceptions) {
|
||||
try {
|
||||
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, MC.getProfiler());
|
||||
} catch (RuntimeException e) {
|
||||
rendererDisabledBecauseOfExceptions = true;
|
||||
try {
|
||||
//ClientApi.renderer.ma ();
|
||||
} catch (RuntimeException welpLookLikeWeWillLeakResource) {}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // go back into "terrain"
|
||||
}
|
||||
|
||||
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, MC.getProfiler());
|
||||
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // go back into "terrain"
|
||||
|
||||
|
||||
// these can't be set until after the buffers are built (in renderer.drawLODs)
|
||||
@@ -138,15 +150,26 @@ public class ClientApi
|
||||
{
|
||||
MC.sendChatMessage(ModInfo.READABLE_NAME + " experimental build " + ModInfo.VERSION);
|
||||
MC.sendChatMessage("You are running a unsupported version of the mod!");
|
||||
MC.sendChatMessage("==========================================");
|
||||
MC.sendChatMessage("SEIZURE WARNING: Flashing lights expected!"); // remove this line when the lighting shaders are fixed
|
||||
MC.sendChatMessage("==========================================");
|
||||
MC.sendChatMessage("Here be dragons!");
|
||||
|
||||
configOverrideReminderPrinted = true;
|
||||
}
|
||||
|
||||
// CONFIG.client().worldGenerator().setDistanceGenerationMode(DistanceGenerationMode.SURFACE);
|
||||
// CONFIG.client().worldGenerator().setDistanceGenerationMode(DistanceGenerationMode.FULL);
|
||||
|
||||
// CONFIG.client().worldGenerator().setGenerationPriority(GenerationPriority.AUTO);
|
||||
|
||||
// CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(GpuUploadMethod.BUFFER_STORAGE);
|
||||
// CONFIG.client().graphics().quality().setLodChunkRenderDistance(128);
|
||||
|
||||
// CONFIG.client().graphics().fogQuality().setFogDrawMode(FogDrawMode.FOG_ENABLED);
|
||||
// CONFIG.client().graphics().fogQuality().setFogDistance(FogDistance.FAR);
|
||||
// CONFIG.client().graphics().fogQuality().setDisableVanillaFog(true);
|
||||
|
||||
// CONFIG.client().advanced().buffers().setRebuildTimes(BufferRebuildTimes.FREQUENT);
|
||||
|
||||
|
||||
CONFIG.client().advanced().debugging().setDebugKeybindingsEnabled(true);
|
||||
@@ -159,6 +182,8 @@ public class ClientApi
|
||||
// Lod maintenance //
|
||||
//=================//
|
||||
|
||||
// FIXME: I need a onLastFrameCleanup() callback in Render Thread... Which calls renderer.cleanup()
|
||||
|
||||
/** This event is called once during the first frame Minecraft renders in the world. */
|
||||
public void firstFrameSetup()
|
||||
{
|
||||
@@ -168,14 +193,6 @@ public class ClientApi
|
||||
firstTimeSetupComplete = true;
|
||||
}
|
||||
|
||||
/** this method reset some static data every time we change world */
|
||||
private void resetMod()
|
||||
{
|
||||
// TODO when should this be used?
|
||||
ThreadMapUtil.clearMaps();
|
||||
LodGenWorker.restartExecutorService();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ package com.seibel.lod.core.api;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
@@ -80,7 +81,8 @@ public class EventApi
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ClientApi.renderer, ApiShared.lodBuilder);
|
||||
// FIXME: This is in server thread. We shouldn't be accessing the client's renderer!
|
||||
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +105,9 @@ public class EventApi
|
||||
/** This is also called when a new dimension loads */
|
||||
public void worldLoadEvent(IWorldWrapper world)
|
||||
{
|
||||
DataPointUtil.worldHeight = world.getHeight();
|
||||
DataPointUtil.WORLD_HEIGHT = world.getHeight();
|
||||
LodBuilder.MIN_WORLD_HEIGHT = world.getMinHeight(); // This updates the World height
|
||||
|
||||
//LodNodeGenWorker.restartExecutorService();
|
||||
//ThreadMapUtil.clearMaps();
|
||||
|
||||
@@ -115,13 +119,32 @@ public class EventApi
|
||||
ClientApi.renderer.regenerateLODsNextFrame();
|
||||
}
|
||||
|
||||
/** This is also called when the user disconnects from a server+ */
|
||||
public void worldUnloadEvent()
|
||||
{
|
||||
// the player just unloaded a world/dimension
|
||||
ThreadMapUtil.clearMaps();
|
||||
// ClientApi.renderer.markForCleanup();
|
||||
// ClientApi.renderer.destroyBuffers();
|
||||
|
||||
new Thread(() -> checkIfDisconnectedFromServer()).start();
|
||||
}
|
||||
private void checkIfDisconnectedFromServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
// world unloading events are called before disconnecting from the server,
|
||||
// so we need to wait a second for MC to disconnect
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
// this should never happen, but just in case
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
if (MC.getWrappedClientWorld() == null || (!MC.connectedToServer() && !MC.hasSinglePlayerServer()))
|
||||
{
|
||||
// the player just left the server
|
||||
|
||||
@@ -129,19 +152,20 @@ public class EventApi
|
||||
|
||||
// if this isn't done unfinished tasks may be left in the queue
|
||||
// preventing new LodChunks form being generated
|
||||
//LodNodeGenWorker.restartExecutorService(); // TODO why was this commented out? -James
|
||||
//ThreadMapUtil.clearMaps();
|
||||
LodWorldGenerator.INSTANCE.restartExecutorService();
|
||||
|
||||
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
|
||||
ApiShared.lodWorld.deselectWorld();
|
||||
|
||||
|
||||
// prevent issues related to the buffer builder
|
||||
// breaking when changing worlds.
|
||||
// breaking or retaining previous data when changing worlds.
|
||||
ClientApi.renderer.destroyBuffers();
|
||||
ClientApi.renderer.requestCleanup();
|
||||
recalculateWidths = true;
|
||||
// TODO: Check if after the refactoring, is this still needed
|
||||
ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||
|
||||
ClientApi.INSTANCE.rendererDisabledBecauseOfExceptions = false;
|
||||
|
||||
// make sure the nulled objects are freed.
|
||||
// (this prevents an out of memory error when
|
||||
@@ -167,14 +191,16 @@ public class EventApi
|
||||
{
|
||||
if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled())
|
||||
{
|
||||
if (key == GLFW.GLFW_KEY_F4 && keyAction == GLFW.GLFW_PRESS)
|
||||
if (key == GLFW.GLFW_KEY_F8 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
|
||||
MC.sendChatMessage("F8: Set debug mode " + CONFIG.client().advanced().debugging().getDebugMode());
|
||||
}
|
||||
|
||||
if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods());
|
||||
MC.sendChatMessage("F6: Set rendering " + CONFIG.client().advanced().debugging().getDrawLods());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,7 +231,7 @@ public class EventApi
|
||||
|
||||
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||
// make sure we have an odd number of regions
|
||||
newWidth += (newWidth & 1) == 0 ? 1 : 2;
|
||||
newWidth += (newWidth & 1) == 0 ? 1 : 0;
|
||||
|
||||
// do the dimensions need to change in size?
|
||||
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.bufferBuilding;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.VertexOptimizer;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
import static com.seibel.lod.core.builders.lodBuilding.LodBuilder.MIN_WORLD_HEIGHT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Builds LODs as rectangular prisms.
|
||||
* @author James Seibel
|
||||
* @version 12-8-2021
|
||||
*/
|
||||
public class CubicLodTemplate
|
||||
{
|
||||
//TODO make it a config
|
||||
static int cullingRange = 128;
|
||||
|
||||
public static void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerZ, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
if (vertexOptimizer == null)
|
||||
return;
|
||||
|
||||
// equivalent to 2^detailLevel
|
||||
int blockWidth = 1 << detailLevel;
|
||||
|
||||
int color;
|
||||
if (debugging != DebugMode.OFF)
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
|
||||
else
|
||||
color = DataPointUtil.getColor(data);
|
||||
|
||||
|
||||
generateBoundingBox(
|
||||
vertexOptimizer,
|
||||
DataPointUtil.getHeight(data),
|
||||
DataPointUtil.getDepth(data),
|
||||
blockWidth,
|
||||
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
|
||||
playerX,
|
||||
playerZ,
|
||||
adjData,
|
||||
color,
|
||||
DataPointUtil.getLightSkyAlt(data),
|
||||
DataPointUtil.getLightBlock(data),
|
||||
adjShadeDisabled);
|
||||
|
||||
addBoundingBoxToBuffer(buffer, vertexOptimizer);
|
||||
}
|
||||
|
||||
/** add the given position and color to the buffer */
|
||||
public static void addPosAndColor(LodBufferBuilder buffer,
|
||||
float x, float y, float z,
|
||||
int color, byte skyLightValue, byte blockLightValue)
|
||||
{
|
||||
// TODO transparency re-add by replacing the color 255 with "ColorUtil.getAlpha(color)"
|
||||
buffer.position(x, y, z)
|
||||
.color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255)
|
||||
.minecraftLightValue(skyLightValue).minecraftLightValue(blockLightValue)
|
||||
.endVertex();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void generateBoundingBox(VertexOptimizer vertexOptimizer,
|
||||
int height, int depth, int width,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
int playerX, int playerZ,
|
||||
Map<LodDirection, long[]> adjData,
|
||||
int color, byte skyLight, byte blockLight,
|
||||
boolean[] adjShadeDisabled)
|
||||
{
|
||||
// don't add an LOD if it is empty
|
||||
if (height == -1 && depth == -1)
|
||||
return;
|
||||
|
||||
if (depth == height)
|
||||
// if the top and bottom points are at the same height
|
||||
// render this LOD as 1 block thick
|
||||
height++;
|
||||
|
||||
// offset the AABB by its x/z position in the world since
|
||||
// it uses doubles to specify its location, unlike the model view matrix
|
||||
// which only uses floats
|
||||
double x = -playerX;
|
||||
double z = -playerZ;
|
||||
vertexOptimizer.reset();
|
||||
vertexOptimizer.setColor(color, adjShadeDisabled);
|
||||
vertexOptimizer.setLights(skyLight, blockLight);
|
||||
vertexOptimizer.setWidth(width, height - depth, width);
|
||||
vertexOptimizer.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
|
||||
vertexOptimizer.setAdjData(adjData);
|
||||
}
|
||||
|
||||
private static void addBoundingBoxToBuffer(LodBufferBuilder buffer, VertexOptimizer vertexOptimizer)
|
||||
{
|
||||
int color;
|
||||
byte skyLight;
|
||||
byte blockLight;
|
||||
|
||||
for (LodDirection lodDirection : VertexOptimizer.DIRECTIONS)
|
||||
{
|
||||
//if(vertexOptimizer.isCulled(lodDirection))
|
||||
// continue;
|
||||
// culling
|
||||
if (lodDirection == LodDirection.NORTH && vertexOptimizer.getZ(lodDirection, 0) < -cullingRange
|
||||
|| lodDirection == LodDirection.EAST && vertexOptimizer.getX(lodDirection, 0) > cullingRange
|
||||
|| lodDirection == LodDirection.SOUTH && vertexOptimizer.getZ(lodDirection, 0) > cullingRange
|
||||
|| lodDirection == LodDirection.WEST && vertexOptimizer.getX(lodDirection, 0) < -cullingRange)
|
||||
continue;
|
||||
|
||||
|
||||
int verticalFaceIndex = 0;
|
||||
while (vertexOptimizer.shouldRenderFace(lodDirection, verticalFaceIndex))
|
||||
{
|
||||
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
|
||||
{
|
||||
skyLight = vertexOptimizer.getSkyLight(lodDirection, verticalFaceIndex);
|
||||
blockLight = (byte) vertexOptimizer.getBlockLight();
|
||||
color = vertexOptimizer.getColor(lodDirection);
|
||||
addPosAndColor(buffer,
|
||||
vertexOptimizer.getX(lodDirection, vertexIndex),
|
||||
vertexOptimizer.getY(lodDirection, vertexIndex, verticalFaceIndex) + MIN_WORLD_HEIGHT,
|
||||
vertexOptimizer.getZ(lodDirection, vertexIndex),
|
||||
color, skyLight, blockLight );
|
||||
}
|
||||
verticalFaceIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+391
-349
File diff suppressed because it is too large
Load Diff
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* This is the abstract class used to create different
|
||||
* BufferBuilders.
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public abstract class AbstractLodTemplate
|
||||
{
|
||||
/** Uploads the given LOD to the buffer. */
|
||||
public abstract void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled);
|
||||
|
||||
/** add the given position and color to the buffer */
|
||||
protected void addPosAndColor(LodBufferBuilder buffer,
|
||||
float x, float y, float z,
|
||||
int color)
|
||||
{
|
||||
// TODO re-add transparency by replacing the 255 with "ColorUtil.getAlpha(color)"
|
||||
buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255).endVertex();
|
||||
}
|
||||
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* Builds LODs as rectangular prisms.
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class CubicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
|
||||
public CubicLodTemplate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
if (box == null)
|
||||
return;
|
||||
|
||||
// equivalent to 2^detailLevel
|
||||
int blockWidth = 1 << detailLevel;
|
||||
|
||||
int color;
|
||||
if (debugging != DebugMode.OFF)
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
|
||||
else
|
||||
color = DataPointUtil.getColor(data);
|
||||
|
||||
|
||||
generateBoundingBox(
|
||||
box,
|
||||
DataPointUtil.getHeight(data),
|
||||
DataPointUtil.getDepth(data),
|
||||
blockWidth,
|
||||
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
|
||||
bufferCenterBlockPos,
|
||||
adjData,
|
||||
color,
|
||||
DataPointUtil.getLightSkyAlt(data),
|
||||
DataPointUtil.getLightBlock(data),
|
||||
adjShadeDisabled);
|
||||
|
||||
addBoundingBoxToBuffer(buffer, box);
|
||||
}
|
||||
|
||||
private void generateBoundingBox(Box box,
|
||||
int height, int depth, int width,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
AbstractBlockPosWrapper bufferCenterBlockPos,
|
||||
Map<LodDirection, long[]> adjData,
|
||||
int color,
|
||||
int skyLight,
|
||||
int blockLight,
|
||||
boolean[] adjShadeDisabled)
|
||||
{
|
||||
// don't add an LOD if it is empty
|
||||
if (height == -1 && depth == -1)
|
||||
return;
|
||||
|
||||
if (depth == height)
|
||||
// if the top and bottom points are at the same height
|
||||
// render this LOD as 1 block thick
|
||||
height++;
|
||||
|
||||
// offset the AABB by its x/z position in the world since
|
||||
// it uses doubles to specify its location, unlike the model view matrix
|
||||
// which only uses floats
|
||||
double x = -bufferCenterBlockPos.getX();
|
||||
double z = -bufferCenterBlockPos.getZ();
|
||||
box.reset();
|
||||
box.setColor(color, adjShadeDisabled);
|
||||
box.setLights(skyLight, blockLight);
|
||||
box.setWidth(width, height - depth, width);
|
||||
box.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
|
||||
box.setUpCulling(32, bufferCenterBlockPos);
|
||||
box.setAdjData(adjData);
|
||||
}
|
||||
|
||||
private void addBoundingBoxToBuffer(LodBufferBuilder buffer, Box box)
|
||||
{
|
||||
int color;
|
||||
int skyLight;
|
||||
int blockLight;
|
||||
for (LodDirection lodDirection : Box.DIRECTIONS)
|
||||
{
|
||||
if(box.isCulled(lodDirection))
|
||||
continue;
|
||||
|
||||
int verticalFaceIndex = 0;
|
||||
while (box.shouldRenderFace(lodDirection, verticalFaceIndex))
|
||||
{
|
||||
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
|
||||
{
|
||||
color = box.getColor(lodDirection);
|
||||
skyLight = box.getSkyLight(lodDirection, verticalFaceIndex);
|
||||
blockLight = box.getBlockLight();
|
||||
color = ColorUtil.applyLightValue(color, skyLight, blockLight);
|
||||
addPosAndColor(buffer,
|
||||
box.getX(lodDirection, vertexIndex),
|
||||
box.getY(lodDirection, vertexIndex, verticalFaceIndex),
|
||||
box.getZ(lodDirection, vertexIndex),
|
||||
color);
|
||||
}
|
||||
verticalFaceIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* TODO DynamicLodTemplate
|
||||
* Chunks smoothly transition between
|
||||
* each other, unless a neighboring chunk
|
||||
* is at a significantly different height.
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class DynamicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* TODO #21 TriangularLodTemplate
|
||||
* Builds each LOD chunk as a singular rectangular prism.
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class TriangularLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,12 +35,9 @@ import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
@@ -54,18 +51,18 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
* @author Cola
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 10-22-2021
|
||||
* @version 12-11-2021
|
||||
*/
|
||||
@SuppressWarnings("GrazieInspection")
|
||||
public class LodBuilder
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IBlockColorSingletonWrapper BLOCK_COLOR = SingletonHandler.get(IBlockColorSingletonWrapper.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
private static final IBlockColorSingletonWrapper BLOCK_COLOR = SingletonHandler.get(IBlockColorSingletonWrapper.class);
|
||||
|
||||
/** If no blocks are found in the area in determineBottomPointForArea return this */
|
||||
public static final short DEFAULT_DEPTH = 0;
|
||||
/** If no blocks are found in the area in determineHeightPointForArea return this */
|
||||
public static final short DEFAULT_HEIGHT = 0;
|
||||
|
||||
/** This cannot be final! Different world have different height, and in menu, this causes Null Exceptions*/
|
||||
//public static final short MIN_WORLD_HEIGHT = MC.getWrappedClientWorld().getMinHeight();
|
||||
public static short MIN_WORLD_HEIGHT = 0; // Currently modified in EventApi.onWorldLoaded(...)
|
||||
/** Minecraft's max light value */
|
||||
public static final short DEFAULT_MAX_LIGHT = 15;
|
||||
|
||||
@@ -83,6 +80,7 @@ public class LodBuilder
|
||||
|
||||
//public static final boolean useExperimentalLighting = true;
|
||||
|
||||
private static int timesToEdgeDetect = 1;
|
||||
|
||||
|
||||
|
||||
@@ -109,8 +107,9 @@ public class LodBuilder
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
//noinspection GrazieInspection
|
||||
//try
|
||||
//{
|
||||
// we need a loaded client world in order to
|
||||
// get the textures for blocks
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
@@ -133,14 +132,14 @@ public class LodBuilder
|
||||
lodDim = lodWorld.getLodDimension(dim);
|
||||
}
|
||||
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode));
|
||||
}
|
||||
catch (IllegalArgumentException | NullPointerException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
}
|
||||
//}
|
||||
//catch (IllegalArgumentException | NullPointerException e)
|
||||
//{
|
||||
// e.printStackTrace();
|
||||
// // if the world changes while LODs are being generated
|
||||
// // they will throw errors as they try to access things that no longer
|
||||
// // exist.
|
||||
//}
|
||||
});
|
||||
lodGenThreadPool.execute(thread);
|
||||
}
|
||||
@@ -161,6 +160,7 @@ public class LodBuilder
|
||||
public void generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk, LodBuilderConfig config)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
//long executeTime = System.currentTimeMillis();
|
||||
if (chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
|
||||
@@ -168,7 +168,7 @@ public class LodBuilder
|
||||
int startZ;
|
||||
|
||||
|
||||
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
|
||||
LodRegion region = lodDim.getRegion(chunk.getRegionPosX(), chunk.getRegionPosZ());
|
||||
if (region == null)
|
||||
return;
|
||||
|
||||
@@ -196,18 +196,20 @@ public class LodBuilder
|
||||
|
||||
long[] data;
|
||||
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
|
||||
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
|
||||
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.WORLD_HEIGHT / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
|
||||
|
||||
|
||||
//lodDim.clear(detailLevel, posX, posZ);
|
||||
if (data != null && data.length != 0)
|
||||
{
|
||||
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().getX() * 16 + startX, detail.detailLevel);
|
||||
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().getZ() * 16 + startZ, detail.detailLevel);
|
||||
posX = LevelPosUtil.convert((byte) 0, chunk.getChunkPosX() * 16 + startX, detail.detailLevel);
|
||||
posZ = LevelPosUtil.convert((byte) 0, chunk.getChunkPosZ() * 16 + startZ, detail.detailLevel);
|
||||
lodDim.addVerticalData(detailLevel, posX, posZ, data, false);
|
||||
}
|
||||
}
|
||||
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ());
|
||||
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getChunkPosX(), chunk.getChunkPosZ());
|
||||
//executeTime = System.currentTimeMillis() - executeTime;
|
||||
//if (executeTime > 0) ClientApi.LOGGER.info("generateLodNodeFromChunk level: " + detailLevel + " time ms: " + executeTime);
|
||||
}
|
||||
|
||||
/** creates a vertical DataPoint */
|
||||
@@ -217,9 +219,7 @@ public class LodBuilder
|
||||
int size = 1 << detail.detailLevel;
|
||||
|
||||
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
|
||||
int verticalData = DataPointUtil.worldHeight / 2 + 1;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = chunk.getPos();
|
||||
int verticalData = DataPointUtil.WORLD_HEIGHT / 2 + 1;
|
||||
int height;
|
||||
int depth;
|
||||
int color;
|
||||
@@ -236,49 +236,42 @@ public class LodBuilder
|
||||
boolean hasCeiling = MC.getWrappedClientWorld().getDimensionType().hasCeiling();
|
||||
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
|
||||
boolean isDefault;
|
||||
AbstractBlockPosWrapper blockPos = FACTORY.createBlockPos();
|
||||
int index;
|
||||
|
||||
for (index = 0; index < size * size; index++)
|
||||
{
|
||||
xRel = startX + index % size;
|
||||
zRel = startZ + index / size;
|
||||
xAbs = chunkPos.getMinBlockX() + xRel;
|
||||
zAbs = chunkPos.getMinBlockZ() + zRel;
|
||||
xAbs = chunk.getMinX() + xRel;
|
||||
zAbs = chunk.getMinZ() + zRel;
|
||||
|
||||
//Calculate the height of the lod
|
||||
yAbs = DataPointUtil.worldHeight + 1;
|
||||
yAbs = chunk.getMaxY(xRel,zRel) - MIN_WORLD_HEIGHT;
|
||||
int count = 0;
|
||||
boolean topBlock = true;
|
||||
if (yAbs <= 0);
|
||||
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
|
||||
while (yAbs > 0)
|
||||
{
|
||||
height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||
height = determineHeightPointFrom(chunk, config, xAbs, yAbs, zAbs);
|
||||
|
||||
// If the lod is at the default height, it must be void data
|
||||
if (height == DEFAULT_HEIGHT)
|
||||
{
|
||||
if (topBlock)
|
||||
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
|
||||
if (height == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
yAbs = height - 1;
|
||||
// We search light on above air block
|
||||
depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||
depth = determineBottomPointFrom(chunk, config, xAbs, yAbs, zAbs, count < timesToEdgeDetect && !hasCeiling);
|
||||
if (hasCeiling && topBlock)
|
||||
{
|
||||
yAbs = depth;
|
||||
blockPos.set(xAbs, yAbs, zAbs);
|
||||
light = getLightValue(chunk, blockPos, true, hasSkyLight, true);
|
||||
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs, blockPos);
|
||||
blockPos.set(xAbs, yAbs - 1, zAbs);
|
||||
light = getLightValue(chunk, xAbs,yAbs + MIN_WORLD_HEIGHT, zAbs, true, hasSkyLight, true);
|
||||
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockPos.set(xAbs, yAbs, zAbs);
|
||||
light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock);
|
||||
color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||
blockPos.set(xAbs, yAbs + 1, zAbs);
|
||||
light = getLightValue(chunk, xAbs, yAbs + MIN_WORLD_HEIGHT, zAbs, hasCeiling, hasSkyLight, topBlock);
|
||||
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs);
|
||||
}
|
||||
lightBlock = light & 0b1111;
|
||||
lightSky = (light >> 4) & 0b1111;
|
||||
@@ -297,34 +290,56 @@ public class LodBuilder
|
||||
* Find the lowest valid point from the bottom.
|
||||
* Used when creating a vertical LOD.
|
||||
*/
|
||||
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, boolean strictEdge)
|
||||
{
|
||||
short depth = DEFAULT_DEPTH;
|
||||
short depth = 0;
|
||||
|
||||
for (int y = yAbs; y >= 0; y--)
|
||||
int colorOfBlock = 0;
|
||||
if (strictEdge)
|
||||
{
|
||||
blockPos.set(xAbs, y, zAbs);
|
||||
if (!isLayerValidLodPoint(chunk, blockPos))
|
||||
colorOfBlock = chunk.getBlockColorWrapper(xAbs, yAbs, zAbs).getColor();
|
||||
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(xAbs, yAbs + 1, zAbs);
|
||||
if (block != null && ((this.config.client().worldGenerator().getBlocksToAvoid().nonFull && block.isNonFull())
|
||||
|| (this.config.client().worldGenerator().getBlocksToAvoid().noCollision && block.hasNoCollision())))
|
||||
{
|
||||
int aboveColorInt = chunk.getBlockColorWrapper(xAbs, yAbs + 1, zAbs).getColor();
|
||||
if (aboveColorInt != 0)
|
||||
colorOfBlock = aboveColorInt;
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = yAbs - 1; y >= 0; y--)
|
||||
{
|
||||
|
||||
if (!isLayerValidLodPoint(chunk, xAbs, y, zAbs))
|
||||
{
|
||||
depth = (short) (y + 1);
|
||||
break;
|
||||
}
|
||||
if (strictEdge)
|
||||
{
|
||||
if (colorOfBlock != chunk.getBlockColorWrapper(xAbs, y, zAbs).getColor())
|
||||
{
|
||||
depth = (short) (y + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/** Find the highest valid point from the Top */
|
||||
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs)
|
||||
{
|
||||
short height = DEFAULT_HEIGHT;
|
||||
//TODO find a way to skip bottom of the world
|
||||
short height = 0;
|
||||
if (config.useHeightmap)
|
||||
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
|
||||
else
|
||||
{
|
||||
for (int y = yAbs; y >= 0; y--)
|
||||
{
|
||||
blockPos.set(xAbs, y, zAbs);
|
||||
if (isLayerValidLodPoint(chunk, blockPos))
|
||||
if (isLayerValidLodPoint(chunk, xAbs, y, zAbs))
|
||||
{
|
||||
height = (short) (y + 1);
|
||||
break;
|
||||
@@ -344,29 +359,27 @@ public class LodBuilder
|
||||
* Generate the color for the given chunk using biome water color, foliage
|
||||
* color, and grass color.
|
||||
*/
|
||||
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int xRel, int yAbs, int zRel, AbstractBlockPosWrapper blockPos)
|
||||
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int x, int y, int z)
|
||||
{
|
||||
int colorInt;
|
||||
if (builderConfig.useBiomeColors)
|
||||
{
|
||||
// I have no idea why I need to bit shift to the right, but
|
||||
// if I don't the biomes don't show up correctly.
|
||||
colorInt = chunk.getBiome(xRel, yAbs, zRel).getColorForBiome(xRel, zRel);
|
||||
colorInt = chunk.getBiome(x, y, z).getColorForBiome(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs, chunk.getPos().getMinBlockZ() + zRel);
|
||||
colorInt = getColorForBlock(chunk, blockPos);
|
||||
colorInt = getColorForBlock(chunk, x, y, z);
|
||||
|
||||
// if we are skipping non-full and non-solid blocks that means we ignore
|
||||
// snow, flowers, etc. Get the above block so we can still get the color
|
||||
// of the snow, flower, etc. that may be above this block
|
||||
int aboveColorInt = 0;
|
||||
if (config.client().worldGenerator().getBlocksToAvoid().nonFull || config.client().worldGenerator().getBlocksToAvoid().noCollision)
|
||||
{
|
||||
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs + 1, chunk.getPos().getMinBlockZ() + zRel);
|
||||
aboveColorInt = getColorForBlock(chunk, blockPos);
|
||||
}
|
||||
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(x, y + 1, z);
|
||||
if (block != null && ((config.client().worldGenerator().getBlocksToAvoid().nonFull && block.isNonFull())
|
||||
|| (config.client().worldGenerator().getBlocksToAvoid().noCollision && block.hasNoCollision())))
|
||||
aboveColorInt = getColorForBlock(chunk, x, y + 1, z);
|
||||
|
||||
//if (colorInt == 0 && yAbs > 0)
|
||||
// if this block is invisible, check the block below it
|
||||
@@ -382,7 +395,7 @@ public class LodBuilder
|
||||
}
|
||||
|
||||
/** Gets the light value for the given block position */
|
||||
private int getLightValue(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
|
||||
private int getLightValue(IChunkWrapper chunk, int x, int y, int z, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
|
||||
{
|
||||
int skyLight = 0;
|
||||
int blockLight;
|
||||
@@ -391,25 +404,25 @@ public class LodBuilder
|
||||
|
||||
IWorldWrapper world = MC.getWrappedServerWorld();
|
||||
|
||||
int blockBrightness = chunk.getEmittedBrightness(blockPos);
|
||||
int blockBrightness = chunk.getEmittedBrightness(x, y, z);
|
||||
// get the air block above or below this block
|
||||
if (hasCeiling && topBlock)
|
||||
blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
|
||||
y--;
|
||||
else
|
||||
blockPos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ());
|
||||
y++;
|
||||
|
||||
|
||||
|
||||
if (world != null && !world.isEmpty())
|
||||
if (world != null)
|
||||
{
|
||||
// server world sky light (always accurate)
|
||||
blockLight = world.getBlockLight(blockPos);
|
||||
blockLight = world.getBlockLight(x,y,z);
|
||||
if (topBlock && !hasCeiling && hasSkyLight)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
{
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(blockPos);
|
||||
skyLight = world.getSkyLight(x,y,z);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
}
|
||||
@@ -417,7 +430,7 @@ public class LodBuilder
|
||||
{
|
||||
// we are on predicted terrain, and we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
if (y >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
{
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
@@ -428,39 +441,42 @@ public class LodBuilder
|
||||
}
|
||||
else
|
||||
{
|
||||
world = MC.getWrappedServerWorld();
|
||||
if (world.isEmpty())
|
||||
return 0;
|
||||
// client world sky light (almost never accurate)
|
||||
blockLight = world.getBlockLight(blockPos);
|
||||
// estimate what the lighting should be
|
||||
if (hasSkyLight || !hasCeiling)
|
||||
world = MC.getWrappedClientWorld();
|
||||
if (world==null)
|
||||
{
|
||||
if (topBlock)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
blockLight = 0;
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// client world sky light (almost never accurate)
|
||||
blockLight = world.getBlockLight(x,y,z);
|
||||
// estimate what the lighting should be
|
||||
if (hasSkyLight || !hasCeiling)
|
||||
{
|
||||
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(blockPos);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
|
||||
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
|
||||
if (topBlock)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
{
|
||||
// we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(x,y,z);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
|
||||
{
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
// we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
if (y >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
{
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
}
|
||||
else
|
||||
skyLight = 0;
|
||||
}
|
||||
else
|
||||
skyLight = 0;
|
||||
}
|
||||
}
|
||||
if (hasSkyLight)
|
||||
skyLight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,34 +486,31 @@ public class LodBuilder
|
||||
}
|
||||
|
||||
/** Returns a color int for the given block. */
|
||||
private int getColorForBlock(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||
private int getColorForBlock(IChunkWrapper chunk, int x, int y, int z)
|
||||
{
|
||||
int colorOfBlock;
|
||||
int colorInt;
|
||||
|
||||
int xRel = blockPos.getX() - chunk.getPos().getMinBlockX();
|
||||
int zRel = blockPos.getZ() - chunk.getPos().getMinBlockZ();
|
||||
//int x = blockPos.getX();
|
||||
int y = blockPos.getY();
|
||||
//int z = blockPos.getZ();
|
||||
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(x, y, z);
|
||||
|
||||
if (blockShapeWrapper == null || blockShapeWrapper.isToAvoid())
|
||||
return 0;
|
||||
|
||||
IBlockColorWrapper blockColorWrapper;
|
||||
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
|
||||
|
||||
if (chunk.isWaterLogged(blockPos))
|
||||
if (chunk.isWaterLogged(x, y, z))
|
||||
blockColorWrapper = BLOCK_COLOR.getWaterColor();
|
||||
else
|
||||
blockColorWrapper = chunk.getBlockColorWrapper(blockPos);
|
||||
blockColorWrapper = chunk.getBlockColorWrapper(x, y, z);
|
||||
|
||||
|
||||
if (blockShapeWrapper.isToAvoid())
|
||||
return 0;
|
||||
|
||||
colorOfBlock = blockColorWrapper.getColor();
|
||||
|
||||
|
||||
if (blockColorWrapper.hasTint())
|
||||
{
|
||||
IBiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
|
||||
IBiomeWrapper biome = chunk.getBiome(x, y, z);
|
||||
int tintValue;
|
||||
if (blockColorWrapper.hasGrassTint())
|
||||
// grass and green plants
|
||||
@@ -517,15 +530,16 @@ public class LodBuilder
|
||||
|
||||
|
||||
/** Is the block at the given blockPos a valid LOD point? */
|
||||
private boolean isLayerValidLodPoint(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||
private boolean isLayerValidLodPoint(IChunkWrapper chunk, int x, int y, int z)
|
||||
{
|
||||
if (chunk.isWaterLogged(blockPos))
|
||||
if (chunk.isWaterLogged(x, y, z))
|
||||
return true;
|
||||
|
||||
boolean nonFullAvoidance = config.client().worldGenerator().getBlocksToAvoid().nonFull;
|
||||
boolean noCollisionAvoidance = config.client().worldGenerator().getBlocksToAvoid().noCollision;
|
||||
|
||||
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos);
|
||||
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(x, y, z);
|
||||
if (block == null) return false;
|
||||
return !block.isToAvoid()
|
||||
&& !(nonFullAvoidance && block.isNonFull())
|
||||
&& !(noCollisionAvoidance && block.hasNoCollision());
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.worldGeneration;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public class LodGenWorker
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
|
||||
private final LodChunkGenThread thread;
|
||||
|
||||
|
||||
|
||||
public LodGenWorker(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper serverWorld)
|
||||
{
|
||||
// just a few sanity checks
|
||||
if (newPos == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
|
||||
|
||||
if (newLodBuilder == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
|
||||
|
||||
if (newLodDimension == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension");
|
||||
|
||||
if (serverWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
|
||||
|
||||
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newGenerationMode,
|
||||
newLodBuilder,
|
||||
newLodDimension, serverWorld);
|
||||
}
|
||||
|
||||
public void queueWork()
|
||||
{
|
||||
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() == DistanceGenerationMode.FULL)
|
||||
{
|
||||
// if we are using FULL generation there is no reason
|
||||
// to queue up a bunch of generation requests,
|
||||
// because MC's internal server (as of 1.16.5) only
|
||||
// responds with a single thread. And we don't
|
||||
// want to cause more lag then necessary or queue up
|
||||
// requests that may end up being unneeded.
|
||||
thread.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static class LodChunkGenThread implements Runnable
|
||||
{
|
||||
private AbstractWorldGeneratorWrapper worldGenWrapper;
|
||||
|
||||
public final LodDimension lodDim;
|
||||
public final DistanceGenerationMode generationMode;
|
||||
|
||||
private final AbstractChunkPosWrapper pos;
|
||||
|
||||
public LodChunkGenThread(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper worldWrapper)
|
||||
{
|
||||
worldGenWrapper = FACTORY.createWorldGenerator(newLodBuilder, newLodDimension, worldWrapper);
|
||||
|
||||
pos = newPos;
|
||||
generationMode = newGenerationMode;
|
||||
lodDim = newLodDimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
|
||||
if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS))
|
||||
{
|
||||
switch (generationMode)
|
||||
{
|
||||
case NONE:
|
||||
// don't generate
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
worldGenWrapper.generateBiomesOnly(pos, generationMode);
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
worldGenWrapper.generateSurface(pos);
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
worldGenWrapper.generateFeatures(pos);
|
||||
break;
|
||||
case FULL:
|
||||
// very slow
|
||||
worldGenWrapper.generateFull(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z));
|
||||
// if (dataExistence)
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||
|
||||
// shows the pool size, active threads, queued tasks and completed tasks
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(endTime - startTime);
|
||||
|
||||
}// if in range
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// decrement how many threads are running
|
||||
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
|
||||
|
||||
// this position is no longer being generated
|
||||
LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos);
|
||||
}
|
||||
}// run
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stops the current genThreads if they are running
|
||||
* and then recreates the Executor service. <br><br>
|
||||
* <p>
|
||||
* This is done to clear any outstanding tasks
|
||||
* that may exist after the player leaves their current world.
|
||||
* If this isn't done unfinished tasks may be left in the queue
|
||||
* preventing new LodChunks form being generated.
|
||||
*/
|
||||
public static void restartExecutorService()
|
||||
{
|
||||
if (genThreads != null && !genThreads.isShutdown())
|
||||
{
|
||||
genThreads.shutdownNow();
|
||||
}
|
||||
genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
}
|
||||
|
||||
}
|
||||
+198
-34
@@ -25,50 +25,47 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractExperimentalWorldGeneratorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
/**
|
||||
* A singleton that handles all long distance LOD world generation.
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 9-25-2021
|
||||
* @version 12-11-2021
|
||||
*/
|
||||
public class LodWorldGenerator
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
private static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class);
|
||||
|
||||
|
||||
/** This holds the thread used to create LOD generation requests off the main thread. */
|
||||
private final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
|
||||
private ExecutorService genSubThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(),
|
||||
new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
|
||||
|
||||
/** we only want to queue up one generator thread at a time */
|
||||
private boolean generatorThreadRunning = false;
|
||||
|
||||
/**
|
||||
* How many chunks to generate outside the player's view distance at one
|
||||
* time. (or more specifically how many requests to make at one time). I
|
||||
* multiply by 8 to make sure there is always a buffer of chunk requests, to
|
||||
* make sure the CPU is always busy, and we can generate LODs as quickly as
|
||||
* possible.
|
||||
*/
|
||||
public int maxChunkGenRequests;
|
||||
|
||||
/**
|
||||
* This keeps track of how many chunk generation requests are on going. This is
|
||||
* to limit how many chunks are queued at once. To prevent chunks from being
|
||||
@@ -82,32 +79,64 @@ public class LodWorldGenerator
|
||||
* Singleton copy of this object
|
||||
*/
|
||||
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
|
||||
public AbstractExperimentalWorldGeneratorWrapper experimentalWorldGenerator;
|
||||
|
||||
|
||||
|
||||
private LodWorldGenerator()
|
||||
{
|
||||
|
||||
}
|
||||
private LodWorldGenerator() {}
|
||||
|
||||
/**
|
||||
* Queues up LodNodeGenWorkers for the given lodDimension.
|
||||
* @param renderer needed so the LodNodeGenWorkers can flag that the
|
||||
* renderer needed so the LodNodeGenWorkers can flag that the
|
||||
* buffers need to be rebuilt.
|
||||
*/
|
||||
public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder)
|
||||
public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder)
|
||||
{
|
||||
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() != DistanceGenerationMode.NONE
|
||||
|
||||
IWorldWrapper world = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
// TODO: Rename the config option
|
||||
if (CONFIG.client().worldGenerator().getAllowUnstableFeatureGeneration()) {
|
||||
if (experimentalWorldGenerator == null) {
|
||||
experimentalWorldGenerator = WRAPPER_FACTORY.createExperimentalWorldGenerator(lodBuilder, lodDim, world);
|
||||
if (experimentalWorldGenerator == null) CONFIG.client().worldGenerator().setAllowUnstableFeatureGeneration(false);
|
||||
}
|
||||
} else {
|
||||
if (experimentalWorldGenerator != null) {
|
||||
experimentalWorldGenerator.stop();
|
||||
experimentalWorldGenerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (experimentalWorldGenerator != null) {
|
||||
experimentalWorldGenerator.queueGenerationRequests(lodDim, lodBuilder);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This currently doesn't use the DetailDistanceUtil.getDistanceGenerationMode(int detail) to get the mode.
|
||||
// This is fine currently since DistanceGenerationMode doesn't care about the detail level for now.
|
||||
// However, If that was to be changed, This will need to be fixed.
|
||||
DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
|
||||
if (mode != DistanceGenerationMode.NONE
|
||||
&& !generatorThreadRunning
|
||||
&& MC.hasSinglePlayerServer())
|
||||
{
|
||||
// the thread is now running, don't queue up another thread
|
||||
generatorThreadRunning = true;
|
||||
|
||||
/**
|
||||
* How many chunks to generate outside the player's view distance at one
|
||||
* time. (or more specifically how many requests to make at one time). I
|
||||
* multiply by 8 to make sure there is always a buffer of chunk requests, to
|
||||
* make sure the CPU is always busy, and we can generate LODs as quickly as
|
||||
* possible.
|
||||
*/
|
||||
int genRequestPerThread = VERSION_CONSTANTS.getWorldGenerationCountPerThread();
|
||||
int maxChunkGenRequests;
|
||||
if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(mode))
|
||||
maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * genRequestPerThread;
|
||||
else maxChunkGenRequests = genRequestPerThread;
|
||||
|
||||
// just in case the config changed
|
||||
maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * 8;
|
||||
|
||||
Thread generatorThread = new Thread(() ->
|
||||
Runnable generatorFunc = (() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -127,7 +156,6 @@ public class LodWorldGenerator
|
||||
playerPosX,
|
||||
playerPosZ);
|
||||
|
||||
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
@@ -140,7 +168,7 @@ public class LodWorldGenerator
|
||||
// an easy way to do so.
|
||||
|
||||
// add the near positions
|
||||
if (posToGenerate.getNthDetail(nearIndex, true) != 0 && nearIndex < posToGenerate.getNumberOfNearPos())
|
||||
if (nearIndex < posToGenerate.getNumberOfNearPos() && posToGenerate.getNthDetail(nearIndex, true) != 0)
|
||||
{
|
||||
detailLevel = (byte) (posToGenerate.getNthDetail(nearIndex, true) - 1);
|
||||
posX = posToGenerate.getNthPosX(nearIndex, true);
|
||||
@@ -159,13 +187,12 @@ public class LodWorldGenerator
|
||||
|
||||
positionsWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||
genWorker.queueWork();
|
||||
queueWork(chunkPos, mode, lodBuilder, lodDim, serverWorld);
|
||||
}
|
||||
|
||||
|
||||
// add the far positions
|
||||
if (posToGenerate.getNthDetail(farIndex, false) != 0 && farIndex < posToGenerate.getNumberOfFarPos())
|
||||
if (farIndex < posToGenerate.getNumberOfFarPos() && posToGenerate.getNthDetail(farIndex, false) != 0)
|
||||
{
|
||||
detailLevel = (byte) (posToGenerate.getNthDetail(farIndex, false) - 1);
|
||||
posX = posToGenerate.getNthPosX(farIndex, false);
|
||||
@@ -185,13 +212,12 @@ public class LodWorldGenerator
|
||||
|
||||
positionsWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||
genWorker.queueWork();
|
||||
queueWork(chunkPos, mode, lodBuilder, lodDim, serverWorld);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
// this shouldn't ever happen, but just in case
|
||||
e.printStackTrace();
|
||||
@@ -202,8 +228,146 @@ public class LodWorldGenerator
|
||||
}
|
||||
});
|
||||
|
||||
mainGenThread.execute(generatorThread);
|
||||
if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(mode))
|
||||
{
|
||||
generatorFunc.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
mainGenThread.execute(generatorFunc);
|
||||
}
|
||||
} // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning
|
||||
} // queueGenerationRequests
|
||||
|
||||
private void queueWork(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper serverWorld)
|
||||
{
|
||||
// just a few sanity checks
|
||||
if (newPos == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
|
||||
|
||||
if (newLodBuilder == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
|
||||
|
||||
if (newLodDimension == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension");
|
||||
|
||||
if (serverWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
|
||||
|
||||
Runnable method = (() -> {generateChunk(newPos, newGenerationMode,
|
||||
newLodBuilder, newLodDimension, serverWorld);});
|
||||
|
||||
if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(newGenerationMode))
|
||||
{
|
||||
// --Note: This is now using version constants--
|
||||
// if we are using FULL generation there is no reason
|
||||
// to queue up a bunch of generation requests,
|
||||
// because MC's internal server (as of 1.16.5) only
|
||||
// responds with a single thread. And we don't
|
||||
// want to cause more lag than necessary or queue up
|
||||
// requests that may end up being unneeded.
|
||||
// In 1.17+, world generation becomes completely single
|
||||
// threaded. So to allow that, we check the boolean for
|
||||
// whether the wrapper requires single thread
|
||||
method.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genSubThreads.execute(method);
|
||||
}
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
}
|
||||
|
||||
private void generateChunk(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode,
|
||||
LodBuilder newLodBuilder, LodDimension lodDim, IWorldWrapper worldWrapper)
|
||||
{
|
||||
// try
|
||||
{
|
||||
AbstractWorldGeneratorWrapper worldGenWrapper = WRAPPER_FACTORY.createWorldGenerator(newLodBuilder, lodDim, worldWrapper);
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
|
||||
if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS))
|
||||
{
|
||||
switch (generationMode)
|
||||
{
|
||||
case NONE:
|
||||
// don't generate
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
worldGenWrapper.generateBiomesOnly(pos, generationMode);
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
worldGenWrapper.generateSurface(pos);
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
worldGenWrapper.generateFeatures(pos);
|
||||
break;
|
||||
case FULL:
|
||||
// very slow
|
||||
worldGenWrapper.generateFull(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
// boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z));
|
||||
// if (dataExistence)
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||
|
||||
// shows the pool size, active threads, queued tasks and completed tasks
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
|
||||
}// if in range
|
||||
}
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// ClientApi.LOGGER.error(LodWorldGenerator.class.getSimpleName() + ": ran into an error: " + e.getMessage());
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// finally
|
||||
{
|
||||
// decrement how many threads are running
|
||||
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
|
||||
|
||||
// this position is no longer being generated
|
||||
LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos);
|
||||
}
|
||||
}// run
|
||||
|
||||
/**
|
||||
* Stops the current genThreads if they are running
|
||||
* and then recreates the Executor service. <br><br>
|
||||
* <p>
|
||||
* This is done to clear any outstanding tasks
|
||||
* that may exist after the player leaves their current world.
|
||||
* If this isn't done unfinished tasks may be left in the queue
|
||||
* preventing new LodChunks form being generated.
|
||||
*/
|
||||
public void restartExecutorService()
|
||||
{
|
||||
if (experimentalWorldGenerator != null) {
|
||||
experimentalWorldGenerator.stop();
|
||||
experimentalWorldGenerator = null;
|
||||
}
|
||||
|
||||
if (genSubThreads != null && !genSubThreads.isShutdown())
|
||||
{
|
||||
genSubThreads.shutdownNow();
|
||||
}
|
||||
genSubThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(),
|
||||
new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.seibel.lod.core.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Where the annotations for the config are defined
|
||||
*
|
||||
* @author coolGi2007
|
||||
* @version 12-28-2021
|
||||
*/
|
||||
public class ConfigAnnotations {
|
||||
/** a textField, button, etc. that can be interacted with */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Entry
|
||||
{
|
||||
String name() default "";
|
||||
|
||||
int width() default 150;
|
||||
|
||||
double minValue() default Double.MIN_NORMAL;
|
||||
|
||||
double maxValue() default Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ScreenEntry
|
||||
{
|
||||
String name() default "";
|
||||
|
||||
int width() default 100;
|
||||
}
|
||||
|
||||
|
||||
/** Used when sorting the configs in the menu */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Category
|
||||
{
|
||||
String value();
|
||||
}
|
||||
|
||||
|
||||
/** Makes text (looks like @Entry but dosnt save and has no button */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Comment
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
package com.seibel.lod.core.dataFormat;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
|
||||
import static com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.skyLightPlayer;
|
||||
|
||||
public class BlockDataFormat
|
||||
{
|
||||
/*
|
||||
|a |a |a |a |r |r |r |r |
|
||||
|
||||
|r |r |r |r |g |g |g |g |
|
||||
|
||||
|g |g |g |g |b |b |b |b |
|
||||
|
||||
|b |b |b |b |h |h |h |h |
|
||||
|
||||
|h |h |h |h |h |h |d |d |
|
||||
|
||||
|d |d |d |d |d |d |d |d |
|
||||
|
||||
|bl |bl |bl |bl |sl |sl |sl |sl |
|
||||
|
||||
|l |l |f |g |g |g |v |e |
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// Reminder: bytes have range of [-128, 127].
|
||||
// When converting to or from an int a 128 should be added or removed.
|
||||
// If there is a bug with color then it's probably caused by this.
|
||||
|
||||
//To be used in the future for negative value
|
||||
//public final static int MIN_DEPTH = -64;
|
||||
//public final static int MIN_HEIGHT = -64;
|
||||
public final static int EMPTY_DATA = 0;
|
||||
public static final short VERTICAL_OFFSET = -64;
|
||||
public static int WORLD_HEIGHT = 1024;
|
||||
|
||||
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
|
||||
|
||||
//public final static int BLUE_COLOR_SHIFT = 0;
|
||||
//public final static int GREEN_COLOR_SHIFT = 8;
|
||||
//public final static int RED_COLOR_SHIFT = 16;
|
||||
//public final static int ALPHA_COLOR_SHIFT = 24;
|
||||
|
||||
public final static int BLUE_SHIFT = 36;
|
||||
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
|
||||
public final static int RED_SHIFT = BLUE_SHIFT + 16;
|
||||
public final static int ALPHA_SHIFT = BLUE_SHIFT + 24;
|
||||
|
||||
public final static int COLOR_SHIFT = 36;
|
||||
|
||||
public final static int HEIGHT_SHIFT = 26;
|
||||
public final static int DEPTH_SHIFT = 16;
|
||||
public final static int BLOCK_LIGHT_SHIFT = 12;
|
||||
public final static int SKY_LIGHT_SHIFT = 8;
|
||||
//public final static int LIGHTS_SHIFT = SKY_LIGHT_SHIFT;
|
||||
//public final static int VERTICAL_INDEX_SHIFT = 6;
|
||||
public final static int FLAG_SHIFT = 5;
|
||||
public final static int GEN_TYPE_SHIFT = 2;
|
||||
public final static int VOID_SHIFT = 1;
|
||||
public final static int EXISTENCE_SHIFT = 0;
|
||||
|
||||
public final static long ALPHA_MASK = 0b1111;
|
||||
public final static long RED_MASK = 0b1111_1111;
|
||||
public final static long GREEN_MASK = 0b1111_1111;
|
||||
public final static long BLUE_MASK = 0b1111_1111;
|
||||
public final static long COLOR_MASK = 0b11111111_11111111_11111111;
|
||||
public final static long HEIGHT_MASK = 0b11_1111_1111;
|
||||
public final static long DEPTH_MASK = 0b11_1111_1111;
|
||||
//public final static long LIGHTS_MASK = 0b1111_1111;
|
||||
public final static long BLOCK_LIGHT_MASK = 0b1111;
|
||||
public final static long SKY_LIGHT_MASK = 0b1111;
|
||||
//public final static long VERTICAL_INDEX_MASK = 0b11;
|
||||
public final static long FLAG_MASK = 0b1;
|
||||
public final static long GEN_TYPE_MASK = 0b111;
|
||||
public final static long VOID_MASK = 1;
|
||||
public final static long EXISTENCE_MASK = 1;
|
||||
|
||||
|
||||
public static long createVoidDataPoint(int generationMode)
|
||||
{
|
||||
long dataPoint = 0;
|
||||
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
dataPoint += VOID_MASK << VOID_SHIFT;
|
||||
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode, boolean flag)
|
||||
{
|
||||
return createDataPoint(
|
||||
ColorUtil.getAlpha(color),
|
||||
ColorUtil.getRed(color),
|
||||
ColorUtil.getGreen(color),
|
||||
ColorUtil.getBlue(color),
|
||||
height, depth, lightSky, lightBlock, generationMode, flag);
|
||||
}
|
||||
|
||||
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag)
|
||||
{
|
||||
long dataPoint = 0;
|
||||
dataPoint += (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << ALPHA_SHIFT;
|
||||
dataPoint += (red & RED_MASK) << RED_SHIFT;
|
||||
dataPoint += (green & GREEN_MASK) << GREEN_SHIFT;
|
||||
dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT;
|
||||
dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT;
|
||||
dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT;
|
||||
dataPoint += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT;
|
||||
dataPoint += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT;
|
||||
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
if (flag)
|
||||
dataPoint += FLAG_MASK << FLAG_SHIFT;
|
||||
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
public static short getHeight(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK);
|
||||
}
|
||||
|
||||
public static short getDepth(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK);
|
||||
}
|
||||
|
||||
public static short getAlpha(long dataPoint)
|
||||
{
|
||||
return (short) ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111);
|
||||
}
|
||||
|
||||
public static short getRed(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
|
||||
}
|
||||
|
||||
public static short getGreen(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
|
||||
}
|
||||
|
||||
public static short getBlue(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
|
||||
}
|
||||
|
||||
public static byte getLightSky(long dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static byte getLightSkyAlt(long dataPoint)
|
||||
{
|
||||
if (skyLightPlayer == 0 && ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1)
|
||||
return 0;
|
||||
else
|
||||
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static byte getLightBlock(long dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static boolean getFlag(long dataPoint)
|
||||
{
|
||||
return ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1;
|
||||
}
|
||||
|
||||
public static byte getGenerationMode(long dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isVoid(long dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
|
||||
}
|
||||
|
||||
public static boolean doesItExist(long dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
|
||||
}
|
||||
|
||||
public static int getColor(long dataPoint)
|
||||
{
|
||||
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24));
|
||||
}
|
||||
|
||||
/** This is used to convert a dataPoint to string (useful for the print function) */
|
||||
@SuppressWarnings("unused")
|
||||
public static String toString(long dataPoint)
|
||||
{
|
||||
return getHeight(dataPoint) + " " +
|
||||
getDepth(dataPoint) + " " +
|
||||
getAlpha(dataPoint) + " " +
|
||||
getRed(dataPoint) + " " +
|
||||
getBlue(dataPoint) + " " +
|
||||
getGreen(dataPoint) + " " +
|
||||
getLightBlock(dataPoint) + " " +
|
||||
getLightSky(dataPoint) + " " +
|
||||
getGenerationMode(dataPoint) + " " +
|
||||
isVoid(dataPoint) + " " +
|
||||
doesItExist(dataPoint) + '\n';
|
||||
}
|
||||
|
||||
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
|
||||
{
|
||||
start *= packetSize;
|
||||
length *= packetSize;
|
||||
arraySize *= packetSize;
|
||||
for (int i = 0; i < arraySize - start; i++)
|
||||
{
|
||||
array[start + i] = array[start + length + i];
|
||||
//remove comment to not leave garbage at the end
|
||||
//array[start + packetSize + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
|
||||
{
|
||||
start *= packetSize;
|
||||
length *= packetSize;
|
||||
arraySize *= packetSize;
|
||||
for (int i = arraySize - start - 1; i >= 0; i--)
|
||||
{
|
||||
array[start + length + i] = array[start + i];
|
||||
array[start + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merge column of multiple data together
|
||||
* @param dataToMerge one or more columns of data
|
||||
* @param inputVerticalData vertical size of an input data
|
||||
* @param maxVerticalData max vertical size of the merged data
|
||||
* @return one column of correctly parsed data
|
||||
*/
|
||||
public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData)
|
||||
{
|
||||
int size = dataToMerge.length / inputVerticalData;
|
||||
|
||||
// We initialize the arrays that are going to be used
|
||||
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((WORLD_HEIGHT / 2 + 1) * 2);
|
||||
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
|
||||
|
||||
|
||||
int genMode = DistanceGenerationMode.FULL.complexity;
|
||||
boolean allEmpty = true;
|
||||
boolean allVoid = true;
|
||||
boolean allDefault;
|
||||
long singleData;
|
||||
|
||||
|
||||
short depth;
|
||||
short height;
|
||||
int count = 0;
|
||||
int i;
|
||||
int ii;
|
||||
int dataIndex;
|
||||
//We collect the indexes of the data, ordered by the depth
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData))
|
||||
{
|
||||
genMode = Math.min(genMode, getGenerationMode(singleData));
|
||||
allEmpty = false;
|
||||
if (!isVoid(singleData))
|
||||
{
|
||||
allVoid = false;
|
||||
depth = getDepth(singleData);
|
||||
height = getHeight(singleData);
|
||||
|
||||
int botPos = -1;
|
||||
int topPos = -1;
|
||||
//values fall in between and possibly require extension of array
|
||||
boolean botExtend = false;
|
||||
boolean topExtend = false;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (depth <= heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1])
|
||||
{
|
||||
botPos = i;
|
||||
break;
|
||||
}
|
||||
else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
{
|
||||
botPos = i;
|
||||
botExtend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (height <= heightAndDepth[i * 2] && height >= heightAndDepth[i * 2 + 1])
|
||||
{
|
||||
topPos = i;
|
||||
break;
|
||||
}
|
||||
else if (height < heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
{
|
||||
topPos = i;
|
||||
topExtend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (topPos == -1)
|
||||
{
|
||||
if (botPos == -1)
|
||||
{
|
||||
//whole block falls above
|
||||
extendArray(heightAndDepth, 2, 0, 1, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count++;
|
||||
}
|
||||
else if (!botExtend)
|
||||
{
|
||||
//only top falls above extending it there, while bottom is inside existing
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
count -= botPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//top falls between some blocks, extending those as well
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count -= botPos;
|
||||
}
|
||||
}
|
||||
else if (!topExtend)
|
||||
{
|
||||
if (!botExtend)
|
||||
//both top and bottom are within some exiting blocks, possibly merging them
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
else
|
||||
//top falls between some blocks, extending it there
|
||||
heightAndDepth[topPos * 2 + 1] = depth;
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!botExtend)
|
||||
{
|
||||
//only top is within some exiting block, extending it
|
||||
topPos++; //to make it easier
|
||||
heightAndDepth[topPos * 2] = height;
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//both top and bottom are outside existing blocks
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
extendArray(heightAndDepth, 2, topPos + 1, 1, count);
|
||||
count++;
|
||||
heightAndDepth[topPos * 2 + 2] = height;
|
||||
heightAndDepth[topPos * 2 + 3] = depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//We check if there is any data that's not empty or void
|
||||
if (allEmpty)
|
||||
return dataPoint;
|
||||
if (allVoid)
|
||||
{
|
||||
dataPoint[0] = createVoidDataPoint(genMode);
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
//we limit the vertical portion to maxVerticalData
|
||||
int j = 0;
|
||||
while (count > maxVerticalData)
|
||||
{
|
||||
ii = WORLD_HEIGHT - VERTICAL_OFFSET;
|
||||
for (i = 0; i < count - 1; i++)
|
||||
{
|
||||
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii)
|
||||
{
|
||||
ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2];
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1];
|
||||
for (i = j + 1; i < count - 1; i++)
|
||||
{
|
||||
heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2];
|
||||
heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1];
|
||||
}
|
||||
//System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1);
|
||||
count--;
|
||||
}
|
||||
//As standard the vertical lods are ordered from top to bottom
|
||||
for (j = count - 1; j >= 0; j--)
|
||||
{
|
||||
height = heightAndDepth[j * 2];
|
||||
depth = heightAndDepth[j * 2 + 1];
|
||||
|
||||
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
|
||||
break;
|
||||
|
||||
int numberOfChildren = 0;
|
||||
int tempAlpha = 0;
|
||||
int tempRed = 0;
|
||||
int tempGreen = 0;
|
||||
int tempBlue = 0;
|
||||
int tempLightBlock = 0;
|
||||
int tempLightSky = 0;
|
||||
byte tempGenMode = DistanceGenerationMode.FULL.complexity;
|
||||
allEmpty = true;
|
||||
allVoid = true;
|
||||
allDefault = true;
|
||||
long data = 0;
|
||||
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData) && !isVoid(singleData))
|
||||
{
|
||||
|
||||
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|
||||
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
|
||||
{
|
||||
if (getHeight(singleData) > getHeight(data))
|
||||
data = singleData;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!doesItExist(data))
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData];
|
||||
data = createVoidDataPoint(getGenerationMode(singleData));
|
||||
}
|
||||
|
||||
if (doesItExist(data))
|
||||
{
|
||||
allEmpty = false;
|
||||
if (!isVoid(data))
|
||||
{
|
||||
numberOfChildren++;
|
||||
allVoid = false;
|
||||
tempAlpha += getAlpha(data);
|
||||
tempRed += getRed(data);
|
||||
tempGreen += getGreen(data);
|
||||
tempBlue += getBlue(data);
|
||||
tempLightBlock += getLightBlock(data);
|
||||
tempLightSky += getLightSky(data);
|
||||
if (!getFlag(data))
|
||||
allDefault = false;
|
||||
}
|
||||
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
|
||||
}
|
||||
else
|
||||
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
|
||||
}
|
||||
|
||||
if (allEmpty)
|
||||
//no child has been initialized
|
||||
dataPoint[j] = EMPTY_DATA;
|
||||
else if (allVoid)
|
||||
//all the children are void
|
||||
dataPoint[j] = createVoidDataPoint(tempGenMode);
|
||||
else
|
||||
{
|
||||
//we have at least 1 child
|
||||
tempAlpha = tempAlpha / numberOfChildren;
|
||||
tempRed = tempRed / numberOfChildren;
|
||||
tempGreen = tempGreen / numberOfChildren;
|
||||
tempBlue = tempBlue / numberOfChildren;
|
||||
tempLightBlock = tempLightBlock / numberOfChildren;
|
||||
tempLightSky = tempLightSky / numberOfChildren;
|
||||
dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
|
||||
}
|
||||
}
|
||||
return dataPoint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.seibel.lod.core.dataFormat;
|
||||
|
||||
public class ColorFormat
|
||||
{
|
||||
public final static int BLUE_SHIFT = 0;
|
||||
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
|
||||
public final static int RED_SHIFT = BLUE_SHIFT + 16;
|
||||
public final static int ALPHA_SHIFT = BLUE_SHIFT + 24;
|
||||
|
||||
public final static long ALPHA_MASK = 0b1111;
|
||||
public final static long RED_MASK = 0b1111_1111;
|
||||
public final static long GREEN_MASK = 0b1111_1111;
|
||||
public final static long BLUE_MASK = 0b1111_1111;
|
||||
|
||||
public static int createColorData(int alpha, int red, int green, int blue)
|
||||
{
|
||||
int colorData = 0;
|
||||
colorData += (alpha & ALPHA_MASK) << ALPHA_SHIFT;
|
||||
colorData += (red & RED_MASK) << RED_SHIFT;
|
||||
colorData += (green & GREEN_MASK) << GREEN_SHIFT;
|
||||
colorData += (blue & BLUE_MASK) << BLUE_SHIFT;
|
||||
|
||||
return colorData;
|
||||
}
|
||||
|
||||
public static short getAlpha(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK);
|
||||
}
|
||||
|
||||
public static short getRed(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
|
||||
}
|
||||
|
||||
public static short getGreen(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
|
||||
}
|
||||
|
||||
public static short getBlue(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.seibel.lod.core.dataFormat;
|
||||
|
||||
public class DataMergeUtil
|
||||
{
|
||||
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
|
||||
{
|
||||
start *= packetSize;
|
||||
length *= packetSize;
|
||||
arraySize *= packetSize;
|
||||
for (int i = 0; i < arraySize - start; i++)
|
||||
{
|
||||
array[start + i] = array[start + length + i];
|
||||
//remove comment to not leave garbage at the end
|
||||
//array[start + packetSize + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
|
||||
{
|
||||
start *= packetSize;
|
||||
length *= packetSize;
|
||||
arraySize *= packetSize;
|
||||
for (int i = arraySize - start - 1; i >= 0; i--)
|
||||
{
|
||||
array[start + length + i] = array[start + i];
|
||||
array[start + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.seibel.lod.core.dataFormat;
|
||||
|
||||
public class LightFormat
|
||||
{
|
||||
public final static byte INT_BLOCK_LIGHT_SHIFT = 16;
|
||||
public final static byte INT_SKY_LIGHT_SHIFT = 0;
|
||||
|
||||
public final static byte BYTE_BLOCK_LIGHT_SHIFT = 4;
|
||||
public final static byte BYTE_SKY_LIGHT_SHIFT = 0;
|
||||
|
||||
public final static byte BLOCK_LIGHT_MASK = 0b1111;
|
||||
public final static byte SKY_LIGHT_MASK = 0b1111;
|
||||
|
||||
|
||||
public static byte formatLightAsByte(byte skyLight, byte blockLight)
|
||||
{
|
||||
return (byte) (((skyLight & SKY_LIGHT_MASK) << (BYTE_SKY_LIGHT_SHIFT + 4)) | ((blockLight & BLOCK_LIGHT_MASK) << (BYTE_BLOCK_LIGHT_SHIFT + 4)));
|
||||
}
|
||||
|
||||
public static int formatLightAsInt(byte skyLight, byte blockLight)
|
||||
{
|
||||
return ((skyLight & SKY_LIGHT_MASK) << INT_SKY_LIGHT_SHIFT) | ((blockLight & BLOCK_LIGHT_MASK) << INT_BLOCK_LIGHT_SHIFT);
|
||||
}
|
||||
|
||||
public static int convertByteToIntFormat(byte lights)
|
||||
{
|
||||
return formatLightAsInt((byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK), (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK));
|
||||
}
|
||||
|
||||
public static byte getSkyLight(byte lights)
|
||||
{
|
||||
return (byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static byte getBlockLight(byte lights)
|
||||
{
|
||||
return (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.seibel.lod.core.dataFormat;
|
||||
|
||||
public class PositionDataFormat
|
||||
{
|
||||
public final static byte LOD_COUNT_SHIFT = 6;
|
||||
public final static byte CORRECT_LIGHT_SHIFT = 5;
|
||||
public final static byte GEN_TYPE_SHIFT = 2;
|
||||
public final static byte VOID_SHIFT = 1;
|
||||
public final static byte EXISTENCE_SHIFT = 0;
|
||||
|
||||
//We are able to count up to 64 different lods in a column
|
||||
public final static short LOD_COUNT_MASK = 0b11_1111;
|
||||
public final static short CORRECT_LIGHT_MASK = 0b1;
|
||||
public final static short GEN_TYPE_MASK = 0b111;
|
||||
public final static short VOID_MASK = 0b1;
|
||||
public final static short EXISTENCE_MASK = 0b1;
|
||||
|
||||
public final static int EMPTY_DATA = 0;
|
||||
public final static int VOID_DATA = VOID_MASK<<VOID_SHIFT + EXISTENCE_MASK<<EXISTENCE_SHIFT;
|
||||
|
||||
public static short createVoidPositionData(byte generationMode)
|
||||
{
|
||||
short positionData = 0;
|
||||
positionData |= (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
positionData |= VOID_MASK << VOID_SHIFT;
|
||||
positionData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
|
||||
return positionData;
|
||||
}
|
||||
|
||||
public static short createPositionData(int lodCount, boolean correctLight, byte generationMode)
|
||||
{
|
||||
short positionData = 0;
|
||||
positionData |= (lodCount & LOD_COUNT_MASK) << LOD_COUNT_SHIFT;
|
||||
positionData |= (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
if (correctLight)
|
||||
positionData |= CORRECT_LIGHT_MASK << CORRECT_LIGHT_SHIFT;
|
||||
positionData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
|
||||
return positionData;
|
||||
}
|
||||
|
||||
public static byte getLodCount(short dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> LOD_COUNT_SHIFT) & LOD_COUNT_MASK);
|
||||
}
|
||||
public static boolean getFlag(short dataPoint)
|
||||
{
|
||||
return ((dataPoint >>> CORRECT_LIGHT_SHIFT) & CORRECT_LIGHT_MASK) == 1;
|
||||
}
|
||||
public static byte getGenerationMode(short dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
|
||||
}
|
||||
public static boolean isVoid(short dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
|
||||
}
|
||||
public static boolean doesItExist(short dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
|
||||
}
|
||||
|
||||
public static short setLodCount(short dataPoint, short lodCount)
|
||||
{
|
||||
return (short) (dataPoint | ((lodCount & LOD_COUNT_MASK) << LOD_COUNT_SHIFT));
|
||||
}
|
||||
public static short setFlag(short dataPoint)
|
||||
{
|
||||
return (short) (dataPoint | ((CORRECT_LIGHT_MASK) << CORRECT_LIGHT_SHIFT));
|
||||
}
|
||||
public static short setGenerationMode(short dataPoint, byte generationMode)
|
||||
{
|
||||
return (short) (dataPoint | ((generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT));
|
||||
}
|
||||
public static short setVoid(short dataPoint)
|
||||
{
|
||||
return (short) (dataPoint | (VOID_MASK << VOID_SHIFT));
|
||||
}
|
||||
public static short setExistence(short dataPoint)
|
||||
{
|
||||
return (short) (dataPoint | (EXISTENCE_MASK << EXISTENCE_SHIFT));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.seibel.lod.core.dataFormat;
|
||||
|
||||
public class VerticalDataFormat
|
||||
{
|
||||
public final static short MIN_WORLD_HEIGHT = -2048;
|
||||
public final static short MAX_WORLD_HEIGHT = 2047;
|
||||
|
||||
public final static byte HEIGHT_SHIFT = 20;
|
||||
public final static byte DEPTH_SHIFT = 8;
|
||||
public final static byte LEVEL_SHIFT = 3;
|
||||
public final static byte BOTTOM_TYPE_SHIFT = 2;
|
||||
public final static byte TRANSPARENCY_SHIFT = 1;
|
||||
public final static byte EXISTENCE_SHIFT = 0;
|
||||
|
||||
|
||||
public final static int FULL_MASK = ~0;
|
||||
|
||||
public final static int HEIGHT_MASK = 0b1111_1111_1111;
|
||||
public final static int DEPTH_MASK = 0b1111_1111_1111;
|
||||
public final static int LEVEL_MASK = 0b111;
|
||||
public final static int TRANSPARENCY_MASK = 0b1;
|
||||
public final static int BOTTOM_TYPE_MASK = 0b1;
|
||||
public final static int EXISTENCE_MASK = 0b1;
|
||||
|
||||
|
||||
public final static int HEIGHT_RESET = ~(HEIGHT_MASK << HEIGHT_SHIFT);
|
||||
public final static int DEPTH_RESET = ~(DEPTH_MASK << DEPTH_SHIFT);
|
||||
public final static int LEVEL_RESET = ~(LEVEL_MASK << LEVEL_SHIFT);
|
||||
public final static int TRANSPARENCY_RESET = ~(TRANSPARENCY_MASK << BOTTOM_TYPE_SHIFT);
|
||||
public final static int BOTTOM_TYPE_RESET = ~(BOTTOM_TYPE_MASK << TRANSPARENCY_SHIFT);
|
||||
public final static int EXISTENCE_RESET = ~(EXISTENCE_MASK << EXISTENCE_SHIFT);
|
||||
|
||||
public final static int EMPTY_LOD = 0;
|
||||
|
||||
|
||||
public static int createVerticalData(int height, int depth, int level, boolean transparent, boolean bottom)
|
||||
{
|
||||
int verticalData = 0;
|
||||
verticalData |= (height & HEIGHT_MASK) << HEIGHT_SHIFT;
|
||||
verticalData |= (depth & DEPTH_MASK) << DEPTH_SHIFT;
|
||||
verticalData |= (level & LEVEL_MASK) << LEVEL_SHIFT;
|
||||
if (bottom)
|
||||
verticalData |= BOTTOM_TYPE_MASK << BOTTOM_TYPE_SHIFT;
|
||||
if (transparent)
|
||||
verticalData |= TRANSPARENCY_MASK << TRANSPARENCY_SHIFT;
|
||||
verticalData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
|
||||
return verticalData;
|
||||
}
|
||||
|
||||
public static short getHeight(int verticalData)
|
||||
{
|
||||
return (short) ((verticalData >>> HEIGHT_SHIFT) & HEIGHT_MASK);
|
||||
}
|
||||
|
||||
public static short getDepth(int verticalData)
|
||||
{
|
||||
return (short) ((verticalData >>> DEPTH_SHIFT) & DEPTH_MASK);
|
||||
}
|
||||
|
||||
public static byte getLevel(int verticalData)
|
||||
{
|
||||
return (byte) ((verticalData >>> LEVEL_SHIFT) & LEVEL_MASK);
|
||||
}
|
||||
|
||||
public static boolean isTransparent(int verticalData)
|
||||
{
|
||||
return ((verticalData >>> TRANSPARENCY_SHIFT) & TRANSPARENCY_MASK) == 1;
|
||||
}
|
||||
|
||||
public static boolean isBottom(int verticalData)
|
||||
{
|
||||
return ((verticalData >>> BOTTOM_TYPE_SHIFT) & BOTTOM_TYPE_MASK) == 1;
|
||||
}
|
||||
|
||||
public static boolean doesItExist(int verticalData)
|
||||
{
|
||||
return (((verticalData >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
|
||||
}
|
||||
|
||||
|
||||
public static int setHeight(int verticalData, int height)
|
||||
{
|
||||
return verticalData | ((height & HEIGHT_MASK) << HEIGHT_SHIFT);
|
||||
}
|
||||
|
||||
public static int setDepth(int verticalData, int depth)
|
||||
{
|
||||
return verticalData | ((depth & DEPTH_MASK) << DEPTH_SHIFT);
|
||||
}
|
||||
|
||||
public static int setLevel(int verticalData, int level)
|
||||
{
|
||||
return verticalData | ((level & LEVEL_MASK) << LEVEL_SHIFT);
|
||||
}
|
||||
|
||||
public static int setTransparency(int verticalData)
|
||||
{
|
||||
return verticalData | ((TRANSPARENCY_MASK) << TRANSPARENCY_SHIFT);
|
||||
}
|
||||
|
||||
public static int setBottom(int verticalData)
|
||||
{
|
||||
return verticalData | ((BOTTOM_TYPE_MASK) << BOTTOM_TYPE_SHIFT);
|
||||
}
|
||||
|
||||
public static int setExistence(int verticalData)
|
||||
{
|
||||
return verticalData | ((EXISTENCE_MASK) << EXISTENCE_SHIFT);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Vec3i;
|
||||
|
||||
/**
|
||||
@@ -73,7 +71,7 @@ public enum LodDirection
|
||||
|
||||
|
||||
|
||||
private LodDirection(int p_i46016_3_, int p_i46016_4_, int p_i46016_5_, String p_i46016_6_, LodDirection.AxisDirection p_i46016_7_, LodDirection.Axis p_i46016_8_, Vec3i p_i46016_9_)
|
||||
LodDirection(int p_i46016_3_, int p_i46016_4_, int p_i46016_5_, String p_i46016_6_, LodDirection.AxisDirection p_i46016_7_, LodDirection.Axis p_i46016_8_, Vec3i p_i46016_9_)
|
||||
{
|
||||
// this.data3d = p_i46016_3_;
|
||||
// this.data2d = p_i46016_5_;
|
||||
@@ -227,9 +225,8 @@ public enum LodDirection
|
||||
{
|
||||
return this.axis;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LodDirection byName(@Nullable String name)
|
||||
|
||||
public static LodDirection byName(String name)
|
||||
{
|
||||
return name == null ? null : BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
@@ -328,7 +325,7 @@ public enum LodDirection
|
||||
// return this.normal.getX() * f1 + this.normal.getZ() * f2 > 0.0F;
|
||||
// }
|
||||
|
||||
public static enum Axis implements Predicate<LodDirection>
|
||||
public enum Axis implements Predicate<LodDirection>
|
||||
{
|
||||
X("x")
|
||||
{
|
||||
@@ -381,12 +378,11 @@ public enum LodDirection
|
||||
}));
|
||||
private final String name;
|
||||
|
||||
private Axis(String name)
|
||||
Axis(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
public static LodDirection.Axis byName(String name)
|
||||
{
|
||||
return BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||
@@ -419,7 +415,7 @@ public enum LodDirection
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable LodDirection p_test_1_)
|
||||
public boolean test(LodDirection p_test_1_)
|
||||
{
|
||||
return p_test_1_ != null && p_test_1_.getAxis() == this;
|
||||
}
|
||||
@@ -443,7 +439,7 @@ public enum LodDirection
|
||||
public abstract double choose(double p_196051_1_, double p_196051_3_, double p_196051_5_);
|
||||
}
|
||||
|
||||
public static enum AxisDirection
|
||||
public enum AxisDirection
|
||||
{
|
||||
POSITIVE(1, "Towards positive"),
|
||||
NEGATIVE(-1, "Towards negative");
|
||||
@@ -451,7 +447,7 @@ public enum LodDirection
|
||||
private final int step;
|
||||
private final String name;
|
||||
|
||||
private AxisDirection(int newStep, String newName)
|
||||
AxisDirection(int newStep, String newName)
|
||||
{
|
||||
this.step = newStep;
|
||||
this.name = newName;
|
||||
|
||||
@@ -31,20 +31,22 @@ package com.seibel.lod.core.enums.config;
|
||||
*/
|
||||
public enum BufferRebuildTimes
|
||||
{
|
||||
FREQUENT(1000, 500, 2500),
|
||||
FREQUENT(1000, 500, 2500, 1),
|
||||
|
||||
NORMAL(2000, 1000, 5000),
|
||||
NORMAL(2000, 1000, 5000, 4),
|
||||
|
||||
RARE(5000, 2000, 10000);
|
||||
RARE(5000, 2000, 10000, 16);
|
||||
|
||||
public final int playerMoveTimeout;
|
||||
public final int renderedChunkTimeout;
|
||||
public final int chunkChangeTimeout;
|
||||
public final int playerMoveDistance;
|
||||
|
||||
BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout)
|
||||
BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout, int playerMoveDistance)
|
||||
{
|
||||
this.playerMoveTimeout = playerMoveTimeout;
|
||||
this.renderedChunkTimeout = renderedChunkTimeout;
|
||||
this.chunkChangeTimeout = chunkChangeTimeout;
|
||||
this.playerMoveDistance = playerMoveDistance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* NONE <br>
|
||||
* BIOME_ONLY <br>
|
||||
@@ -92,4 +94,46 @@ public enum DistanceGenerationMode
|
||||
{
|
||||
this.complexity = complexity;
|
||||
}
|
||||
|
||||
// Note: return null if out of range
|
||||
@Nullable
|
||||
public static DistanceGenerationMode previous(DistanceGenerationMode mode) {
|
||||
switch (mode) {
|
||||
case FULL:
|
||||
return DistanceGenerationMode.FEATURES;
|
||||
case FEATURES:
|
||||
return DistanceGenerationMode.SURFACE;
|
||||
case SURFACE:
|
||||
return DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
return DistanceGenerationMode.BIOME_ONLY;
|
||||
case BIOME_ONLY:
|
||||
return DistanceGenerationMode.NONE;
|
||||
case NONE:
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: return null if out of range
|
||||
@Nullable
|
||||
public static DistanceGenerationMode next(DistanceGenerationMode mode) {
|
||||
switch (mode) {
|
||||
case FULL:
|
||||
return null;
|
||||
case FEATURES:
|
||||
return DistanceGenerationMode.FULL;
|
||||
case SURFACE:
|
||||
return DistanceGenerationMode.FEATURES;
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
return DistanceGenerationMode.SURFACE;
|
||||
case BIOME_ONLY:
|
||||
return DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
case NONE:
|
||||
return DistanceGenerationMode.BIOME_ONLY;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
/**
|
||||
* AUTO <br>
|
||||
* Near_First <br>
|
||||
* Far_First <br>
|
||||
* <br>
|
||||
@@ -27,10 +28,13 @@ package com.seibel.lod.core.enums.config;
|
||||
* outside the normal view distance.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
* @version 12-1-2021
|
||||
*/
|
||||
public enum GenerationPriority
|
||||
{
|
||||
/** NEAR_FIRST when connected to servers and FAR_FIRST when on single player */
|
||||
AUTO,
|
||||
|
||||
NEAR_FIRST,
|
||||
|
||||
FAR_FIRST
|
||||
|
||||
@@ -20,22 +20,40 @@
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
/**
|
||||
* Buffer_Storage, Sub_Data, Buffer_Mapping
|
||||
* Auto, Buffer_Storage, Sub_Data, Buffer_Mapping, Data
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
* @version 12-1-2021
|
||||
*/
|
||||
public enum GpuUploadMethod
|
||||
{
|
||||
/** Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. */
|
||||
/** Picks the best option based on the GPU the user has. */
|
||||
AUTO,
|
||||
|
||||
/**
|
||||
* Default for NVIDIA if OpenGL 4.5 is supported. <br>
|
||||
* Fast rendering, no stuttering.
|
||||
*/
|
||||
BUFFER_STORAGE,
|
||||
|
||||
/** Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. */
|
||||
/**
|
||||
* Backup option for NVIDIA. <br>
|
||||
* Fast rendering but may stutter when uploading.
|
||||
*/
|
||||
SUB_DATA,
|
||||
|
||||
/** Fast rendering but will stutter when uploading. */
|
||||
/**
|
||||
* Default option for AMD/Intel. <br>
|
||||
* May end up storing buffers in System memory. <br>
|
||||
* Fast rending if in GPU memory, slow if in system memory, <br>
|
||||
* but won't stutter when uploading.
|
||||
*/
|
||||
BUFFER_MAPPING,
|
||||
|
||||
/**
|
||||
* Backup option for AMD/Intel. <br>
|
||||
* Fast rendering but may stutter when uploading.
|
||||
*/
|
||||
DATA,
|
||||
|
||||
/** May end up storing buffers in System memory. Slower rendering but won't stutter when uploading. */
|
||||
BUFFER_MAPPING,
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.AbstractLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.CubicLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.DynamicLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.TriangularLodTemplate;
|
||||
|
||||
/**
|
||||
* Cubic, Triangular, Dynamic
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-10-2021
|
||||
*/
|
||||
public enum LodTemplate
|
||||
{
|
||||
/**
|
||||
* LODs are rendered as
|
||||
* rectangular prisms.
|
||||
*/
|
||||
CUBIC(new CubicLodTemplate()),
|
||||
|
||||
/**
|
||||
* LODs smoothly transition between
|
||||
* each other.
|
||||
*/
|
||||
TRIANGULAR(new TriangularLodTemplate()),
|
||||
|
||||
/**
|
||||
* LODs smoothly transition between
|
||||
* each other, unless a neighboring LOD
|
||||
* is at a significantly different height.
|
||||
*/
|
||||
DYNAMIC(new DynamicLodTemplate());
|
||||
|
||||
|
||||
public final AbstractLodTemplate template;
|
||||
|
||||
LodTemplate(AbstractLodTemplate newTemplate)
|
||||
{
|
||||
template = newTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* heightmap <br>
|
||||
* multi_lod <br>
|
||||
@@ -77,4 +79,34 @@ public enum VerticalQuality
|
||||
{
|
||||
this.maxVerticalData = maxVerticalData;
|
||||
}
|
||||
|
||||
// Note: return null if out of range
|
||||
@Nullable
|
||||
public static VerticalQuality previous(VerticalQuality mode) {
|
||||
switch (mode) {
|
||||
case HIGH:
|
||||
return VerticalQuality.MEDIUM;
|
||||
case MEDIUM:
|
||||
return VerticalQuality.LOW;
|
||||
case LOW:
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: return null if out of range
|
||||
@Nullable
|
||||
public static VerticalQuality next(VerticalQuality mode) {
|
||||
switch (mode) {
|
||||
case HIGH:
|
||||
return null;
|
||||
case MEDIUM:
|
||||
return VerticalQuality.HIGH;
|
||||
case LOW:
|
||||
return VerticalQuality.MEDIUM;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-6
@@ -20,14 +20,23 @@
|
||||
package com.seibel.lod.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* fast, fancy, or off
|
||||
* USE_DEFAULT_FOG_COLOR, <br>
|
||||
* USE_SKY_COLOR, <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
* @version 11-27-2021
|
||||
*/
|
||||
public enum FogQuality
|
||||
public enum FogColorMode
|
||||
{
|
||||
FAST,
|
||||
FANCY,
|
||||
OFF
|
||||
/** Fog uses Minecraft's fog color. */
|
||||
USE_WORLD_FOG_COLOR,
|
||||
|
||||
/**
|
||||
* Replicates the effect of the clear sky mod.
|
||||
* Making the fog blend in with the sky better
|
||||
* https://www.curseforge.com/minecraft/mc-mods/clear-skies
|
||||
* https://www.curseforge.com/minecraft/mc-mods/clear-skies-forge-port
|
||||
* For it to look good you need one of those mods
|
||||
*/
|
||||
USE_SKY_COLOR,
|
||||
}
|
||||
@@ -23,16 +23,11 @@ package com.seibel.lod.core.enums.rendering;
|
||||
* NEAR, FAR, or NEAR_AND_FAR.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
* @version 11-26-2021
|
||||
*/
|
||||
public enum FogDistance
|
||||
{
|
||||
/** good for fast or fancy fog qualities. */
|
||||
NEAR,
|
||||
|
||||
/** good for fast or fancy fog qualities. */
|
||||
FAR,
|
||||
|
||||
/** only looks good if the fog quality is set to Fancy. */
|
||||
NEAR_AND_FAR
|
||||
}
|
||||
+8
-15
@@ -21,27 +21,20 @@ package com.seibel.lod.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* USE_OPTIFINE_FOG_SETTING, <br>
|
||||
* NEVER_DRAW_FOG, <br>
|
||||
* ALWAYS_DRAW_FOG_FAST, <br>
|
||||
* ALWAYS_DRAW_FOG_FANCY <br>
|
||||
* FOG_ENABLED, <br>
|
||||
* FOG_DISABLED <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-3-2021
|
||||
* @version 11-27-2021
|
||||
*/
|
||||
public enum FogDrawOverride
|
||||
public enum FogDrawMode
|
||||
{
|
||||
/**
|
||||
* Use whatever Fog setting optifine is using.
|
||||
* If optifine isn't installed this defaults to ALWAYS_DRAW_FOG.
|
||||
*/
|
||||
OPTIFINE_SETTING,
|
||||
USE_OPTIFINE_SETTING,
|
||||
|
||||
/** Never draw fog on the LODs */
|
||||
NO_FOG,
|
||||
|
||||
/** Always draw fast fog on the LODs */
|
||||
FAST,
|
||||
|
||||
/** Always draw fancy fog on the LODs */
|
||||
FANCY
|
||||
}
|
||||
FOG_ENABLED,
|
||||
FOG_DISABLED
|
||||
}
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
package com.seibel.lod.core.handlers;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
@@ -35,24 +34,16 @@ import com.seibel.lod.core.objects.math.Mat4f;
|
||||
* different MC versions.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public interface IReflectionHandler
|
||||
{
|
||||
/** @returns the type of fog optifine is currently set to render. */
|
||||
public FogQuality getFogQuality();
|
||||
/** @returns Whether Optifine is set to render fog or not. */
|
||||
FogDrawMode getFogDrawMode();
|
||||
|
||||
/** @returns if Vivecraft is present. Attempts to find the "VRRenderer" class. */
|
||||
public boolean vivecraftPresent();
|
||||
boolean vivecraftPresent();
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane);
|
||||
/** @returns if Sodium (or a sodium like) mod is present. Attempts to find the "SodiumWorldRenderer" class. */
|
||||
boolean sodiumPresent();
|
||||
}
|
||||
|
||||
@@ -23,11 +23,13 @@ import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
@@ -77,7 +79,7 @@ public class LodDimensionFileHandler
|
||||
* file handler, older versions (smaller numbers) will be deleted and overwritten,
|
||||
* newer versions (larger numbers) will be ignored and won't be read.
|
||||
*/
|
||||
public static final int LOD_SAVE_FILE_VERSION = 6;
|
||||
public static final int LOD_SAVE_FILE_VERSION = 8;
|
||||
|
||||
/**
|
||||
* Allow saving asynchronously, but never try to save multiple regions
|
||||
@@ -99,6 +101,8 @@ public class LodDimensionFileHandler
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// read from file //
|
||||
//================//
|
||||
@@ -115,111 +119,65 @@ public class LodDimensionFileHandler
|
||||
|
||||
for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel, verticalQuality);
|
||||
File file = getBestMatchingRegionFile(tempDetailLevel, regionX, regionZ, generationMode, verticalQuality);
|
||||
if (file == null) continue; // Failed to find the file for this detail level. continue and try next one
|
||||
|
||||
try
|
||||
long fileSize = file.length();
|
||||
if (fileSize == 0) continue; // file is empty. Let's not try parsing empty files
|
||||
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(file)))
|
||||
{
|
||||
// if the fileName was null that means the folder is inaccessible
|
||||
// for some reason
|
||||
if (fileName == null)
|
||||
throw new IllegalArgumentException("Unable to read region [" + regionX + ", " + regionZ + "] file, no fileName.");
|
||||
int fileVersion;
|
||||
fileVersion = inputStream.read();
|
||||
|
||||
File file = new File(fileName);
|
||||
if (!file.exists())
|
||||
// check if this file can be read by this file handler
|
||||
if (fileVersion < 6)
|
||||
{
|
||||
//there is no file for current gen mode
|
||||
//search others above current from the most to the least detailed
|
||||
DistanceGenerationMode tempGenMode = DistanceGenerationMode.FULL;
|
||||
while (tempGenMode != generationMode)
|
||||
{
|
||||
fileName = getFileNameAndPathForRegion(regionX, regionZ, tempGenMode, tempDetailLevel, verticalQuality);
|
||||
if (fileName != null)
|
||||
{
|
||||
file = new File(fileName);
|
||||
if (file.exists())
|
||||
break;
|
||||
}
|
||||
//decrease gen mode
|
||||
if (tempGenMode == DistanceGenerationMode.FULL)
|
||||
tempGenMode = DistanceGenerationMode.FEATURES;
|
||||
else if (tempGenMode == DistanceGenerationMode.FEATURES)
|
||||
tempGenMode = DistanceGenerationMode.SURFACE;
|
||||
else if (tempGenMode == DistanceGenerationMode.SURFACE)
|
||||
tempGenMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT)
|
||||
tempGenMode = DistanceGenerationMode.BIOME_ONLY;
|
||||
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY)
|
||||
tempGenMode = DistanceGenerationMode.NONE;
|
||||
}
|
||||
if (!file.exists())
|
||||
//there wasn't a file, don't return anything
|
||||
continue;
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
inputStream.close();
|
||||
file.delete();
|
||||
ClientApi.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ ". File has been deleted.");
|
||||
// This should not break, but be continue to see whether other detail levels can be loaded or updated
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// don't try parsing empty files
|
||||
long dataSize = file.length();
|
||||
dataSize -= 1;
|
||||
if (dataSize > 0)
|
||||
else if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(file)))
|
||||
{
|
||||
int fileVersion;
|
||||
fileVersion = inputStream.read();
|
||||
|
||||
// check if this file can be read by this file handler
|
||||
if (fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
inputStream.close();
|
||||
file.delete();
|
||||
ClientApi.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ ". File was been deleted.");
|
||||
|
||||
break;
|
||||
}
|
||||
else if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// close the reader and ignore the file, we don't
|
||||
// want to accidentally delete anything the user may want.
|
||||
inputStream.close();
|
||||
ClientApi.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ " this region will not be written to in order to protect the newer file.");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// this file is a readable version,
|
||||
// read the file
|
||||
byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel);
|
||||
inputStream.read(data);
|
||||
inputStream.close();
|
||||
|
||||
|
||||
// add the data to our region
|
||||
region.addLevelContainer(new VerticalLevelContainer(data));
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||
ioEx.printStackTrace();
|
||||
}
|
||||
// the file we are reading is a newer version,
|
||||
// close the reader and ignore the file, we don't
|
||||
// want to accidentally delete anything the user may want.
|
||||
inputStream.close();
|
||||
ClientApi.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ " this region will not be written to in order to protect the newer file.");
|
||||
// This should not break, but be continue to see whether other detail levels can be loaded or updated
|
||||
continue;
|
||||
}
|
||||
else if (fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
ClientApi.LOGGER.debug("Old LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ ". File will be loaded and updated to new format in next save.");
|
||||
// this is old, but readable version
|
||||
// read and add the data to our region
|
||||
region.addLevelContainer(new VerticalLevelContainer(new DataInputStream(inputStream), fileVersion));
|
||||
inputStream.close();
|
||||
} else
|
||||
{
|
||||
// this file is a readable version,
|
||||
// read and add the data to our region
|
||||
region.addLevelContainer(new VerticalLevelContainer(new DataInputStream(inputStream), LOD_SAVE_FILE_VERSION));
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
// the buffered reader encountered a
|
||||
// problem reading the file
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read xz compressed file [" + file + "] error [" + ioEx.getMessage() + "]: ");
|
||||
ioEx.printStackTrace();
|
||||
}
|
||||
}// for each detail level
|
||||
|
||||
@@ -274,121 +232,111 @@ public class LodDimensionFileHandler
|
||||
{
|
||||
for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality());
|
||||
// Get the old file
|
||||
File oldFile = getRegionFile(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality());
|
||||
ClientApi.LOGGER.debug("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
|
||||
|
||||
// if the fileName was null that means the folder is inaccessible
|
||||
// for some reason
|
||||
if (fileName == null)
|
||||
boolean isFileFullyGened = false;
|
||||
// make sure the file and folder exists
|
||||
if (!oldFile.exists())
|
||||
{
|
||||
ClientApi.LOGGER.warn("Unable to save region [" + region.regionPosX + ", " + region.regionPosZ + "] to file, file is inaccessible.");
|
||||
return;
|
||||
}
|
||||
File oldFile = new File(fileName);
|
||||
//ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
|
||||
byte[] temp = region.getLevel(detailLevel).toDataString();
|
||||
|
||||
try
|
||||
{
|
||||
// make sure the file and folder exists
|
||||
if (!oldFile.exists())
|
||||
{
|
||||
// the file doesn't exist,
|
||||
// create it and the folder if need be
|
||||
if (!oldFile.getParentFile().exists())
|
||||
oldFile.getParentFile().mkdirs();
|
||||
// the file doesn't exist,
|
||||
// create it and the folder if need be
|
||||
if (!oldFile.getParentFile().exists())
|
||||
oldFile.getParentFile().mkdirs();
|
||||
try {
|
||||
oldFile.createNewFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
// the file exists, make sure it
|
||||
// is the correct version.
|
||||
// (to make sure we don't overwrite a newer
|
||||
// version file if it exists)
|
||||
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||
int isFull = 0;
|
||||
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(oldFile)))
|
||||
{
|
||||
fileVersion = inputStream.read();
|
||||
inputStream.skip(1);
|
||||
isFull = inputStream.read() & 0b10000000;
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
// check if this file can be written to by the file handler
|
||||
if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// don't write anything, we don't want to accidentally
|
||||
// delete anything the user may want.
|
||||
return;
|
||||
}
|
||||
if ((temp[1] & 0b10000000) != 0b10000000 && isFull == 0b10000000)
|
||||
{
|
||||
// existing file is complete while new one is only partially generate
|
||||
// this can happen is for some reason loading failed
|
||||
// this doesn't fix the bug, but at least protects old data
|
||||
ClientApi.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + fileName + "]");
|
||||
return;
|
||||
}
|
||||
// if we got this far then we are good
|
||||
// to overwrite the old file
|
||||
}
|
||||
// the old file is good, now create a new temporary save file
|
||||
File newFile = new File(fileName + TMP_FILE_EXTENSION);
|
||||
try (XZCompressorOutputStream outputStream = new XZCompressorOutputStream(new FileOutputStream(newFile), 3))
|
||||
{
|
||||
// add the version of this file
|
||||
outputStream.write(LOD_SAVE_FILE_VERSION);
|
||||
|
||||
// add each LodChunk to the file
|
||||
outputStream.write(temp);
|
||||
outputStream.close();
|
||||
|
||||
// overwrite the old file with the new one
|
||||
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
ClientApi.LOGGER.error("LOD file write error. Unable to create parent directory for [" + oldFile + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
else
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
// the file exists, make sure it
|
||||
// is the correct version.
|
||||
// (to make sure we don't overwrite a newer
|
||||
// version file if it exists)
|
||||
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(oldFile)))
|
||||
{
|
||||
fileVersion = inputStream.read();
|
||||
inputStream.skip(1);
|
||||
isFileFullyGened = (inputStream.read() & 0b10000000) != 0;
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ClientApi.LOGGER.warn("LOD file write warning. Unable to read existing file [" + oldFile + "] version. Treating it as latest version. [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// check if this file can be written to by the file handler
|
||||
if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// don't write anything, we don't want to accidentally
|
||||
// delete anything the user may want.
|
||||
continue;
|
||||
}
|
||||
// if we got this far then we are good
|
||||
// to overwrite the old file
|
||||
}
|
||||
|
||||
// Now create a new temporary save file
|
||||
File tempFile;
|
||||
try {
|
||||
tempFile = File.createTempFile(oldFile.getName(), TMP_FILE_EXTENSION);
|
||||
} catch (IOException e) {
|
||||
ClientApi.LOGGER.error("LOD file write error. Unable to create temp file for [" + oldFile + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
tempFile.deleteOnExit(); // Mark it to be deleted on exit if any unexcepted terminations happen
|
||||
try (XZCompressorOutputStream outputStream = new XZCompressorOutputStream(new FileOutputStream(tempFile), 3))
|
||||
{
|
||||
// add the version of this file
|
||||
outputStream.write(LOD_SAVE_FILE_VERSION);
|
||||
// add each LodChunk to the file
|
||||
boolean isNewDataFullyGened = region.getLevel(detailLevel).writeData(new DataOutputStream(outputStream));
|
||||
outputStream.close();
|
||||
|
||||
if (!isNewDataFullyGened && isFileFullyGened)
|
||||
{
|
||||
// existing file is complete while new one is only partially generate
|
||||
// this can happen is for some reason loading failed
|
||||
// this doesn't fix the bug, but at least protects old data
|
||||
ClientApi.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + oldFile + "]");
|
||||
try {
|
||||
tempFile.delete();
|
||||
} catch (SecurityException e) {
|
||||
// Failed to delete temp file... just continue.
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file write error. Unable to write to temp file [" + tempFile + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
// overwrite the old file with the new one
|
||||
try {
|
||||
Files.move(tempFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
ClientApi.LOGGER.error("LOD file write error. Unable to update file [" + oldFile + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
public byte[] getHashFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||
{
|
||||
int regionX = regionPos.x;
|
||||
int regionZ = regionPos.z;
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
|
||||
try (InputStream is = Files.newInputStream(Paths.get(fileName))) {
|
||||
return org.apache.commons.codec.digest.DigestUtils.md5(is);
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||
ioEx.printStackTrace();
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
@@ -398,26 +346,40 @@ public class LodDimensionFileHandler
|
||||
* <p>
|
||||
* Returns null if there is an IO or security Exception.
|
||||
*/
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||
{
|
||||
try
|
||||
{
|
||||
// saveFolder is something like
|
||||
// ".\Super Flat\DIM-1\data\"
|
||||
// or
|
||||
// ".\Super Flat\data\"
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
|
||||
verticalQuality + File.separatorChar +
|
||||
generationMode.toString() + File.separatorChar +
|
||||
DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
}
|
||||
catch (IOException | SecurityException e)
|
||||
{
|
||||
ClientApi.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
|
||||
private String getFileBasePath() {
|
||||
try {
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar;
|
||||
} catch (IOException e) {
|
||||
ClientApi.LOGGER.warn("Unable to get the base save file path. One possible cause is that"
|
||||
+ " the process failed to read the current path location due to security configs.");
|
||||
throw new RuntimeException("DistantHorizons Get Save File Path Failure");
|
||||
}
|
||||
}
|
||||
|
||||
private File getRegionFile(int regionX, int regionZ, DistanceGenerationMode genMode, byte detail, VerticalQuality vertQuality) {
|
||||
return new File(getFileBasePath() + vertQuality + File.separatorChar +
|
||||
genMode + File.separatorChar +
|
||||
DETAIL_FOLDER_NAME_PREFIX + detail + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION);
|
||||
}
|
||||
|
||||
// Return null if no file found
|
||||
@Nullable
|
||||
private File getBestMatchingRegionFile(byte detailLevel, int regionX, int regionZ, DistanceGenerationMode targetGenMode, VerticalQuality targetVertQuality) {
|
||||
DistanceGenerationMode genMode = targetGenMode;
|
||||
// Search from least GenMode to max GenMode, than least vertQuality to max vertQuality
|
||||
do {
|
||||
File file = getRegionFile(regionX, regionZ, genMode, detailLevel, targetVertQuality);
|
||||
if (file.exists()) return file; // Found target file.
|
||||
targetGenMode = DistanceGenerationMode.next(targetGenMode);
|
||||
if (targetGenMode == null) { // Failed to find any files for this vertQuality. Try next one up.
|
||||
targetGenMode = genMode;
|
||||
targetVertQuality = VerticalQuality.next(targetVertQuality);
|
||||
}
|
||||
} while (targetVertQuality != null);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,27 +25,29 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
* where they are private or potentially absent.
|
||||
* Specifically the fog setting in Optifine or the
|
||||
* presence/absence of other mods.
|
||||
* For example: the fog setting in Optifine or the
|
||||
* presence/absence of Vivecraft.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public class ReflectionHandler implements IReflectionHandler
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + ReflectionHandler.class.getSimpleName());
|
||||
|
||||
private static ReflectionHandler instance;
|
||||
public static ReflectionHandler instance;
|
||||
|
||||
private Field ofFogField = null;
|
||||
private final Object mcOptionsObject;
|
||||
|
||||
private Boolean sodiumPresent = null;
|
||||
|
||||
|
||||
|
||||
|
||||
private ReflectionHandler(Field[] optionFields, Object newMcOptionsObject)
|
||||
@@ -75,6 +77,8 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** finds the Optifine fog type field */
|
||||
private void setupFogField(Field[] optionFields)
|
||||
{
|
||||
@@ -100,14 +104,14 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
* @return the fog quality
|
||||
*/
|
||||
@Override
|
||||
public FogQuality getFogQuality()
|
||||
public FogDrawMode getFogDrawMode()
|
||||
{
|
||||
if (ofFogField == null)
|
||||
{
|
||||
// either optifine isn't installed,
|
||||
// the variable name was changed, or
|
||||
// the setup method wasn't called yet.
|
||||
return FogQuality.FANCY;
|
||||
return FogDrawMode.FOG_ENABLED;
|
||||
}
|
||||
|
||||
int returnNum = 0;
|
||||
@@ -129,12 +133,11 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
// it should never be called in this case
|
||||
|
||||
// normal options
|
||||
case 1:
|
||||
return FogQuality.FAST;
|
||||
case 2:
|
||||
return FogQuality.FANCY;
|
||||
case 3:
|
||||
return FogQuality.OFF;
|
||||
case 1: // fast
|
||||
case 2: // fancy
|
||||
return FogDrawMode.FOG_ENABLED;
|
||||
case 3: // off
|
||||
return FogDrawMode.FOG_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,41 +159,29 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane)
|
||||
public boolean sodiumPresent()
|
||||
{
|
||||
// find the matrix values.
|
||||
float nearMatrixValue = -((newFarClipPlane + newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
float farMatrixValue = -((2 * newFarClipPlane * newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
// we don't want to run a potentially expensive
|
||||
// reflection search operation every time this method is called
|
||||
if (sodiumPresent == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName("me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer");
|
||||
|
||||
sodiumPresent = true;
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
sodiumPresent = false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO this was originally created before we had the Mat4f object,
|
||||
// so this doesn't need to be done with reflection anymore.
|
||||
// And should be moved to RenderUtil
|
||||
|
||||
// get the fields of the projectionMatrix
|
||||
Field[] fields = projectionMatrix.getClass().getDeclaredFields();
|
||||
// bypass the security protections on the fields that encode near and far plane values.
|
||||
fields[10].setAccessible(true);
|
||||
fields[11].setAccessible(true);
|
||||
// Change the values of the near and far plane.
|
||||
fields[10].set(projectionMatrix, nearMatrixValue);
|
||||
fields[11].set(projectionMatrix, farMatrixValue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return projectionMatrix;
|
||||
return sodiumPresent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.seibel.lod.core.objects;
|
||||
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class BlockBiomeCouple
|
||||
{
|
||||
public static ConcurrentMap<IBlockColorWrapper, BlockBiomeCouple> noBiomeIstanceCache = new ConcurrentHashMap<>();
|
||||
public static ConcurrentMap<IBiomeWrapper, ConcurrentMap<IBlockColorWrapper, BlockBiomeCouple>> withBiomeIstanceCache = new ConcurrentHashMap<>();
|
||||
|
||||
String blockName;
|
||||
String biomeName;
|
||||
String coupleName;
|
||||
|
||||
IBiomeWrapper biomeColor;
|
||||
IBlockColorWrapper blockColor;
|
||||
|
||||
public static void addBlockBiomeToCache(IBlockColorWrapper blockColor){
|
||||
}
|
||||
|
||||
public static BlockBiomeCouple getBlockBiomeCouple(IBlockColorWrapper blockColor){
|
||||
if(noBiomeIstanceCache.containsKey(blockColor))
|
||||
{
|
||||
return noBiomeIstanceCache.get(blockColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockBiomeCouple couple = new BlockBiomeCouple(blockColor);
|
||||
noBiomeIstanceCache.put(blockColor,couple);
|
||||
return couple;
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockBiomeCouple getBlockBiomeCouple(IBiomeWrapper biomeColor, IBlockColorWrapper blockColor){
|
||||
if(biomeColor == null)
|
||||
{
|
||||
return getBlockBiomeCouple(blockColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(withBiomeIstanceCache.containsKey(biomeColor))
|
||||
{
|
||||
withBiomeIstanceCache.put(biomeColor, new ConcurrentHashMap<>());
|
||||
}
|
||||
ConcurrentMap<IBlockColorWrapper, BlockBiomeCouple> blockToCoupleMap = withBiomeIstanceCache.get(biomeColor);
|
||||
if(blockToCoupleMap.containsKey(blockColor))
|
||||
{
|
||||
return blockToCoupleMap.get(blockColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockBiomeCouple couple = new BlockBiomeCouple(blockColor,biomeColor);
|
||||
blockToCoupleMap.put(blockColor,couple);
|
||||
return couple;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BlockBiomeCouple(IBlockColorWrapper blockColor)
|
||||
{
|
||||
this.biomeColor = null;
|
||||
this.blockColor = blockColor;
|
||||
biomeName = "";
|
||||
blockName = blockColor.getName();
|
||||
coupleName = blockName;
|
||||
}
|
||||
|
||||
public BlockBiomeCouple(IBlockColorWrapper blockColor, IBiomeWrapper biomeColor)
|
||||
{
|
||||
this.biomeColor = biomeColor;
|
||||
this.blockColor = blockColor;
|
||||
|
||||
if(biomeColor == null)
|
||||
biomeName = biomeColor.getName();
|
||||
else
|
||||
biomeName = "";
|
||||
|
||||
blockName = blockColor.getName();
|
||||
|
||||
coupleName = blockName + biomeName;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof BlockBiomeCouple))
|
||||
return false;
|
||||
BlockBiomeCouple that = (BlockBiomeCouple) o;
|
||||
return Objects.equals(blockName, that.blockName) && Objects.equals(biomeName, that.biomeName);
|
||||
}
|
||||
|
||||
@Override public int hashCode()
|
||||
{
|
||||
return Objects.hash(blockName, biomeName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -28,9 +28,9 @@ package com.seibel.lod.core.objects;
|
||||
*/
|
||||
public class MinDefaultMax<T>
|
||||
{
|
||||
public T minValue;
|
||||
public T defaultValue;
|
||||
public T maxValue;
|
||||
public final T minValue;
|
||||
public final T defaultValue;
|
||||
public final T maxValue;
|
||||
|
||||
public MinDefaultMax(T newMinValue, T newDefaultValue, T newMaxValue)
|
||||
{
|
||||
|
||||
+25
-62
@@ -35,12 +35,11 @@ import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
* Similar to Minecraft's AxisAlignedBoundingBox.
|
||||
*
|
||||
* This class handles all the vertex optimization that's needed for a column of lods. W
|
||||
* @author Leonardo Amato
|
||||
* @version 10-2-2021
|
||||
*/
|
||||
public class Box
|
||||
public class VertexOptimizer
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
@@ -201,13 +200,14 @@ public class Box
|
||||
public final Map<LodDirection, byte[]> skyLights;
|
||||
public byte blockLight;
|
||||
|
||||
/** Holds if the given direction should be culled or not */
|
||||
public final boolean[] culling;
|
||||
boolean skipTop;
|
||||
boolean skipBot;
|
||||
|
||||
|
||||
|
||||
/** creates an empty box */
|
||||
@SuppressWarnings("serial")
|
||||
public Box()
|
||||
public VertexOptimizer()
|
||||
{
|
||||
boxOffset = new int[3];
|
||||
boxWidth = new int[3];
|
||||
@@ -236,8 +236,6 @@ public class Box
|
||||
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
}};
|
||||
|
||||
culling = new boolean[6];
|
||||
}
|
||||
|
||||
/** Set the light of the columns */
|
||||
@@ -310,31 +308,6 @@ public class Box
|
||||
}
|
||||
}
|
||||
|
||||
/** determine which faces should be culled */
|
||||
public void setUpCulling(int cullingDistance, AbstractBlockPosWrapper playerPos)
|
||||
{
|
||||
for (LodDirection lodDirection : DIRECTIONS)
|
||||
{
|
||||
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.WEST || lodDirection == LodDirection.NORTH)
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) > getFacePos(lodDirection) + cullingDistance;
|
||||
|
||||
else if (lodDirection == LodDirection.UP || lodDirection == LodDirection.EAST || lodDirection == LodDirection.SOUTH)
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) < getFacePos(lodDirection) - cullingDistance;
|
||||
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection direction that we want to check if it's culled
|
||||
* @return true if and only if the face of the direction is culled
|
||||
*/
|
||||
public boolean isCulled(LodDirection lodDirection)
|
||||
{
|
||||
return culling[DIRECTION_INDEX.get(lodDirection)];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method create all the shared face culling based on the adjacent data
|
||||
* @param adjData data adjacent to the column we are going to render
|
||||
@@ -347,15 +320,13 @@ public class Box
|
||||
int maxY = getMaxY();
|
||||
long singleAdjDataPoint;
|
||||
|
||||
/* TODO implement attached vertical face culling
|
||||
// TODO transparency uncomment final condition to see ocean floor
|
||||
//Up direction case
|
||||
if(DataPointUtil.doesItExist(adjData.get(Direction.UP)))
|
||||
{
|
||||
height = DataPointUtil.getHeight(singleAdjDataPoint);
|
||||
depth = DataPointUtil.getDepth(singleAdjDataPoint);
|
||||
}*/
|
||||
singleAdjDataPoint = adjData.get(LodDirection.UP)[0];
|
||||
skipTop = DataPointUtil.doesItExist(singleAdjDataPoint) && DataPointUtil.getDepth(singleAdjDataPoint) == maxY;// && DataPointUtil.getAlpha(singleAdjDataPoint) == 255;
|
||||
//Down direction case
|
||||
singleAdjDataPoint = adjData.get(LodDirection.DOWN)[0];
|
||||
skipBot = DataPointUtil.doesItExist(singleAdjDataPoint) && DataPointUtil.getHeight(singleAdjDataPoint) == minY;// && DataPointUtil.getAlpha(singleAdjDataPoint) == 255;
|
||||
if(DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
else
|
||||
@@ -364,9 +335,6 @@ public class Box
|
||||
//TODO clean some similar cases
|
||||
for (LodDirection lodDirection : ADJ_DIRECTIONS)
|
||||
{
|
||||
if (isCulled(lodDirection))
|
||||
continue;
|
||||
|
||||
long[] dataPoint = adjData.get(lodDirection);
|
||||
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
|
||||
{
|
||||
@@ -374,7 +342,7 @@ public class Box
|
||||
adjDepth.get(lodDirection)[0] = minY;
|
||||
adjHeight.get(lodDirection)[1] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[1] = VOID_FACE;
|
||||
skyLights.get(lodDirection)[0] = 15; //in void set full sky light
|
||||
skyLights.get(lodDirection)[0] = 15; //in void set full skylight
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -384,17 +352,22 @@ public class Box
|
||||
boolean toFinish = false;
|
||||
int toFinishIndex = 0;
|
||||
boolean allAbove = true;
|
||||
// TODO transparency ocean floor fix
|
||||
//boolean isOpaque = ((colorMap[0] >> 24) & 0xFF) == 255;
|
||||
for (i = 0; i < dataPoint.length; i++)
|
||||
{
|
||||
singleAdjDataPoint = dataPoint[i];
|
||||
|
||||
if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||
break;
|
||||
|
||||
// TODO transparency ocean floor fix
|
||||
//if (isOpaque && DataPointUtil.getAlpha(singleAdjDataPoint) != 255)
|
||||
// continue;
|
||||
|
||||
height = DataPointUtil.getHeight(singleAdjDataPoint);
|
||||
depth = DataPointUtil.getDepth(singleAdjDataPoint);
|
||||
|
||||
if (depth <= maxY)
|
||||
if (depth < maxY)
|
||||
{
|
||||
allAbove = false;
|
||||
if (height < minY)
|
||||
@@ -448,7 +421,7 @@ public class Box
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (height >= maxY)//depth > minY &&
|
||||
else if (height >= maxY)// && depth > minY
|
||||
{
|
||||
// the adj data intersects the higher part of the current data
|
||||
// we start the creation of a new face
|
||||
@@ -521,24 +494,14 @@ public class Box
|
||||
boxOffset[Z] = zOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method return the position of a face in the axis of the face
|
||||
* This is useful for the face culling
|
||||
* @param lodDirection that we want to check
|
||||
* @return position in the axis of the face
|
||||
*/
|
||||
public int getFacePos(LodDirection lodDirection)
|
||||
{
|
||||
return boxOffset[FACE_DIRECTION.get(lodDirection)[0]] + boxWidth[FACE_DIRECTION.get(lodDirection)[0]] * FACE_DIRECTION.get(lodDirection)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the given direction should be rendered.
|
||||
*/
|
||||
/** returns true if the given direction should be rendered. */
|
||||
public boolean shouldRenderFace(LodDirection lodDirection, int adjIndex)
|
||||
{
|
||||
if (lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
|
||||
return adjIndex == 0;
|
||||
if (lodDirection == LodDirection.UP)
|
||||
return adjIndex == 0 && !skipTop;
|
||||
if (lodDirection == LodDirection.DOWN)
|
||||
return adjIndex == 0 && !skipBot;
|
||||
|
||||
return !(adjHeight.get(lodDirection)[adjIndex] == VOID_FACE && adjDepth.get(lodDirection)[adjIndex] == VOID_FACE);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
package com.seibel.lod.core.objects.lod;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* A level container is a quad tree level
|
||||
*/
|
||||
@@ -81,7 +85,7 @@ public interface LevelContainer
|
||||
byte getDetailLevel();
|
||||
|
||||
|
||||
int getMaxVerticalData();
|
||||
int getVerticalSize();
|
||||
|
||||
/** Clears the dataPoint at the given array index */
|
||||
void clear(int posX, int posZ);
|
||||
@@ -99,13 +103,13 @@ public interface LevelContainer
|
||||
* @param posZ z position in the detail level to update
|
||||
*/
|
||||
void updateData(LevelContainer lowerLevelContainer, int posX, int posZ);
|
||||
|
||||
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
* @return data as a String
|
||||
* This will write the raw data with metadata to the output stream
|
||||
* @return isAllGenerated whether the data is all generated
|
||||
* @throws IOException
|
||||
*/
|
||||
byte[] toDataString();
|
||||
|
||||
boolean writeData(DataOutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
|
||||
@@ -128,7 +128,7 @@ public class LodDimension
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(MC.getGameDirectory().getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + MC.getCurrentDimensionId());
|
||||
File.separatorChar + "Distant_Horizons_server_data" + File.separatorChar + MC.getCurrentDimensionId());
|
||||
}
|
||||
|
||||
fileHandler = new LodDimensionFileHandler(saveDir, this);
|
||||
@@ -310,6 +310,32 @@ public class LodDimension
|
||||
|
||||
regions[xIndex][zIndex] = newRegion;
|
||||
}
|
||||
public interface PosComsumer {
|
||||
void run(int x, int z);
|
||||
}
|
||||
|
||||
public void iterateWithSpiral(PosComsumer r) {
|
||||
int ox,oy,dx,dy;
|
||||
ox = oy = dx = 0;
|
||||
dy = -1;
|
||||
int len = regions.length;
|
||||
int maxI = len*len;
|
||||
int halfLen = len/2;
|
||||
for(int i =0; i < maxI; i++){
|
||||
if ((-halfLen <= ox) && (ox <= halfLen) && (-halfLen <= oy) && (oy <= halfLen)){
|
||||
int x = ox+halfLen;
|
||||
int z = oy+halfLen;
|
||||
r.run(x, z);
|
||||
}
|
||||
if( (ox == oy) || ((ox < 0) && (ox == -oy)) || ((ox > 0) && (ox == 1-oy))){
|
||||
int temp = dx;
|
||||
dx = -dy;
|
||||
dy = temp;
|
||||
}
|
||||
ox += dx;
|
||||
oy += dy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -325,116 +351,99 @@ public class LodDimension
|
||||
|
||||
// don't run the tree cutter multiple times
|
||||
// for the same location
|
||||
if (newPlayerChunk.getX() != lastCutChunk.getX() || newPlayerChunk.getZ() != lastCutChunk.getZ())
|
||||
{
|
||||
if (newPlayerChunk.getX() != lastCutChunk.getX() || newPlayerChunk.getZ() != lastCutChunk.getZ()) {
|
||||
lastCutChunk = newPlayerChunk;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte minAllowedDetailLevel;
|
||||
|
||||
|
||||
Runnable thread = () -> {
|
||||
|
||||
// go over every region in the dimension
|
||||
for (int x = 0; x < regions.length; x++)
|
||||
{
|
||||
for (int z = 0; z < regions.length; z++)
|
||||
{
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
|
||||
if (regions[x][z] != null)
|
||||
{
|
||||
// check what detail level this region should be
|
||||
// and cut it if it is higher then that
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
|
||||
minAllowedDetailLevel = DetailDistanceUtil.getCutLodDetail(detail);
|
||||
|
||||
if (regions[x][z].getMinDetailLevel() > minAllowedDetailLevel)
|
||||
{
|
||||
regions[x][z].cutTree(minAllowedDetailLevel);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
iterateWithSpiral((int x, int z) -> {
|
||||
int regionX;
|
||||
int regionZ;
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte minAllowedDetailLevel;
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
|
||||
if (regions[x][z] != null) {
|
||||
// check what detail level this region should be
|
||||
// and cut it if it is higher then that
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ,
|
||||
playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
|
||||
minAllowedDetailLevel = DetailDistanceUtil.getCutLodDetail(detail);
|
||||
|
||||
if (regions[x][z].getMinDetailLevel() > minAllowedDetailLevel) {
|
||||
regions[x][z].cutTree(minAllowedDetailLevel);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
}// region z
|
||||
}// region z
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
cutAndExpandThread.execute(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/** Either expands or loads all regions in the rendered LOD area */
|
||||
public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ)
|
||||
{
|
||||
public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ) {
|
||||
DistanceGenerationMode generationMode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||
AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX),
|
||||
LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||
VerticalQuality verticalQuality = CONFIG.client().graphics().quality().getVerticalQuality();
|
||||
|
||||
|
||||
|
||||
if (lastExpandedChunk == null)
|
||||
lastExpandedChunk = FACTORY.createChunkPos(newPlayerChunk.getX() + 1, newPlayerChunk.getZ() - 1);
|
||||
|
||||
|
||||
// don't run the expander multiple times
|
||||
// for the same location
|
||||
if (newPlayerChunk.getX() != lastExpandedChunk.getX() || newPlayerChunk.getZ() != lastExpandedChunk.getZ())
|
||||
{
|
||||
if (newPlayerChunk.getX() != lastExpandedChunk.getX() || newPlayerChunk.getZ() != lastExpandedChunk.getZ()) {
|
||||
lastExpandedChunk = newPlayerChunk;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
LodRegion region;
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte levelToGen;
|
||||
|
||||
for (int x = 0; x < regions.length; x++)
|
||||
{
|
||||
for (int z = 0; z < regions.length; z++)
|
||||
{
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
final RegionPos regionPos = new RegionPos(regionX, regionZ);
|
||||
region = regions[x][z];
|
||||
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance);
|
||||
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
|
||||
|
||||
// check that the region isn't null and at least this detail level
|
||||
if (region == null || region.getGenerationMode() != generationMode)
|
||||
{
|
||||
// First case, region has to be created
|
||||
|
||||
// try to get the region from file
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
|
||||
// if there is no region file create an empty region
|
||||
if (regions[x][z] == null)
|
||||
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality);
|
||||
|
||||
regenRegionBuffer[x][z] = true;
|
||||
regenDimensionBuffers = true;
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
else if (region.getMinDetailLevel() > levelToGen)
|
||||
{
|
||||
// Second case, the region exists at a higher detail level.
|
||||
|
||||
// Expand the region by introducing the missing layer
|
||||
region.growTree(levelToGen);
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
|
||||
Runnable thread = () -> {
|
||||
|
||||
iterateWithSpiral((int x, int z) -> {
|
||||
int regionX;
|
||||
int regionZ;
|
||||
LodRegion region;
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte levelToGen;
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
final RegionPos regionPos = new RegionPos(regionX, regionZ);
|
||||
region = regions[x][z];
|
||||
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX,
|
||||
playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance);
|
||||
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
|
||||
|
||||
// check that the region isn't null and at least this detail level
|
||||
if (region == null || region.getGenerationMode() != generationMode) {
|
||||
// First case, region has to be created
|
||||
|
||||
// try to get the region from file
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
|
||||
// if there is no region file create an empty region
|
||||
if (regions[x][z] == null)
|
||||
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality);
|
||||
|
||||
regenRegionBuffer[x][z] = true;
|
||||
regenDimensionBuffers = true;
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
} else if (region.getMinDetailLevel() > levelToGen) {
|
||||
// Second case, the region exists at a higher detail level.
|
||||
|
||||
// Expand the region by introducing the missing layer
|
||||
region.growTree(levelToGen);
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
cutAndExpandThread.execute(thread);
|
||||
}
|
||||
}
|
||||
@@ -551,7 +560,6 @@ public class LodDimension
|
||||
// We can use two type of generation scheduling
|
||||
switch (CONFIG.client().worldGenerator().getGenerationPriority())
|
||||
{
|
||||
default:
|
||||
case NEAR_FIRST:
|
||||
//in the NEAR_FIRST generation scheduling we prioritize the nearest un-generated position to the player
|
||||
//the chunk position to generate will be stored in a posToGenerate object
|
||||
@@ -637,7 +645,7 @@ public class LodDimension
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
case FAR_FIRST:
|
||||
//in the FAR_FIRST generation we dedicate part of the generation process to the far region with really
|
||||
//low detail quality.
|
||||
@@ -680,8 +688,16 @@ public class LodDimension
|
||||
int playerPosZ)
|
||||
{
|
||||
LodRegion region = getRegion(regionPos.x, regionPos.z);
|
||||
|
||||
// use FAR_FIRST on local worlds and NEAR_FIRST on servers
|
||||
GenerationPriority generationPriority = CONFIG.client().worldGenerator().getGenerationPriority();
|
||||
if (generationPriority == GenerationPriority.AUTO)
|
||||
generationPriority = MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST;
|
||||
|
||||
boolean requireCorrectDetailLevel = generationPriority == GenerationPriority.NEAR_FIRST;
|
||||
|
||||
if (region != null)
|
||||
region.getPosToRender(posToRender, playerPosX, playerPosZ, CONFIG.client().worldGenerator().getGenerationPriority() == GenerationPriority.NEAR_FIRST);
|
||||
region.getPosToRender(posToRender, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -162,9 +162,7 @@ public class LodRegion
|
||||
// The dataContainer could have null entries if the
|
||||
// detailLevel changes.
|
||||
if (this.dataContainer[detailLevel] == null)
|
||||
{
|
||||
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
|
||||
}
|
||||
|
||||
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
|
||||
|
||||
@@ -179,9 +177,8 @@ public class LodRegion
|
||||
*/
|
||||
public boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data)
|
||||
{
|
||||
//position is already relative
|
||||
//posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
//posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
|
||||
// The dataContainer could have null entries if the
|
||||
// detailLevel changes.
|
||||
@@ -198,6 +195,8 @@ public class LodRegion
|
||||
*/
|
||||
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
|
||||
}
|
||||
|
||||
@@ -208,6 +207,8 @@ public class LodRegion
|
||||
*/
|
||||
public long getSingleData(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
return dataContainer[detailLevel].getSingleData(posX, posZ);
|
||||
}
|
||||
|
||||
@@ -216,6 +217,8 @@ public class LodRegion
|
||||
*/
|
||||
public void clear(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
dataContainer[detailLevel].clear(posX, posZ);
|
||||
}
|
||||
|
||||
@@ -450,6 +453,8 @@ public class LodRegion
|
||||
*/
|
||||
private void update(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ);
|
||||
}
|
||||
|
||||
@@ -459,15 +464,12 @@ public class LodRegion
|
||||
*/
|
||||
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if (detailLevel < minDetailLevel)
|
||||
if (detailLevel < minDetailLevel || dataContainer[detailLevel] == null)
|
||||
return false;
|
||||
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
|
||||
if (dataContainer[detailLevel] == null)
|
||||
return false;
|
||||
|
||||
return dataContainer[detailLevel].doesItExist(posX, posZ);
|
||||
}
|
||||
|
||||
@@ -599,7 +601,7 @@ public class LodRegion
|
||||
|
||||
public int getMaxVerticalData(byte detailLevel)
|
||||
{
|
||||
return dataContainer[detailLevel].getMaxVerticalData();
|
||||
return dataContainer[detailLevel].getVerticalSize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
package com.seibel.lod.core.objects.lod.quadtree;
|
||||
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
|
||||
public class LodQuadTree
|
||||
{
|
||||
public LodSection[][][] quadTreeStructure;
|
||||
public QuadTreeProperties properties;
|
||||
|
||||
public LodQuadTree()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.seibel.lod.core.objects.lod.quadtree;
|
||||
|
||||
|
||||
/**
|
||||
A lod section rappresent a distinct section in a precise level of the quadtree.
|
||||
The section holds all the lods information as color (computed with the blockBiome object referenced by the ID), size, light...
|
||||
The save and load of a section is handled by the section itself together with a handler class.
|
||||
*/
|
||||
public class LodSection
|
||||
{
|
||||
//level of detail of this section
|
||||
public final int detail;
|
||||
|
||||
//level position of this section
|
||||
public final int sectionPosX;
|
||||
public final int sectionPosZ;
|
||||
|
||||
//horizontal size of this section
|
||||
public final int horizontalSize;
|
||||
//vertical size of this section
|
||||
public final int verticalSize;
|
||||
|
||||
//how many id we save for each lod
|
||||
public final int idPerLod;
|
||||
|
||||
//What generation mode should be used for chunk in this section
|
||||
public final byte generationMode;
|
||||
|
||||
//if present in region file, use pregenerated chunk in lod creation
|
||||
public boolean usePregeneratedChunk;
|
||||
|
||||
//Position data hold information about each level position like generation mode used, number of vertical lods in that area...
|
||||
private byte[] positionData;
|
||||
|
||||
//Position data hold vertical information about each lod, like minY and maxY...
|
||||
private int[] verticalData;
|
||||
|
||||
//Lights data hold lights information about each lod, like skylight and block light values...
|
||||
private byte[] lightsData;
|
||||
|
||||
//BlockBiomeId hold a unique ID for each lod relative to a block-biome couple. This couple is capable of generating color
|
||||
//We could just reference
|
||||
private int[] BlockBiomeId;
|
||||
|
||||
//BlockBiomeFrequency for each lod BlockBiomeId
|
||||
private byte[] BlockBiomeFrequency;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param detail
|
||||
* @param horizontalSize
|
||||
* @param verticalSize
|
||||
* @param levelPosX
|
||||
* @param levelPosZ
|
||||
* @param generationMode
|
||||
* @param avoidPregeneratedChunk
|
||||
* @param idPerLod
|
||||
*/
|
||||
public LodSection(int detail, int horizontalSize, int verticalSize, int levelPosX, int levelPosZ, byte generationMode, boolean avoidPregeneratedChunk, int idPerLod)
|
||||
{
|
||||
this.detail = detail;
|
||||
this.sectionPosX = levelPosX;
|
||||
this.sectionPosZ = levelPosZ;
|
||||
this.horizontalSize = horizontalSize;
|
||||
this.verticalSize = verticalSize;
|
||||
this.generationMode = generationMode;
|
||||
this.usePregeneratedChunk = avoidPregeneratedChunk;
|
||||
this.idPerLod = idPerLod;
|
||||
//Now we should search if there is a file with this values
|
||||
//if so we load it and end the process
|
||||
|
||||
//Otherwise we initialize this section
|
||||
positionData = new byte[horizontalSize*horizontalSize];
|
||||
verticalData = new int[horizontalSize*horizontalSize*verticalSize];
|
||||
lightsData = new byte[horizontalSize*horizontalSize*verticalSize];
|
||||
BlockBiomeId = new int[horizontalSize*horizontalSize*verticalSize*idPerLod];
|
||||
BlockBiomeFrequency = new byte[horizontalSize*horizontalSize*verticalSize*idPerLod];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param otherSection
|
||||
* @param inputStartX
|
||||
* @param inputStartZ
|
||||
* @param inputEndX
|
||||
* @param inputEndZ
|
||||
* @param targetStartX
|
||||
* @param targetStartZ
|
||||
* @param targetEndX
|
||||
* @param targetEndZ
|
||||
*/
|
||||
public void mergeAndAdd(LodSection otherSection,
|
||||
int inputStartX, int inputStartZ, int inputEndX, int inputEndZ,
|
||||
int targetStartX, int targetStartZ, int targetEndX, int targetEndZ)
|
||||
{
|
||||
}
|
||||
|
||||
public short getPositionData(int posX, int posZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long[] getData(int posX, int posZ)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
}
|
||||
|
||||
public void tryload()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.seibel.lod.core.objects.lod.quadtree;
|
||||
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
|
||||
public class QuadTreeMover
|
||||
{
|
||||
public static void move(LodQuadTree lqt, RenderQuadTree rqt, int centerX, int centerZ)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.seibel.lod.core.objects.lod.quadtree;
|
||||
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
|
||||
public class QuadTreeProperties
|
||||
{
|
||||
public int MAX_NUMBER_OF_DETAIL=23;
|
||||
public int SECTION_SIZE=128;
|
||||
public int[] absoluteDetailCircleSize = new int[MAX_NUMBER_OF_DETAIL];
|
||||
public int[] relativeDetailCircleSize = new int[MAX_NUMBER_OF_DETAIL];
|
||||
public int[] generationModeOfDetail = new int[MAX_NUMBER_OF_DETAIL];
|
||||
|
||||
public LodQuadTree lodQuadTree;
|
||||
public RenderQuadTree renderQuadTree;
|
||||
|
||||
public void update()
|
||||
{
|
||||
for(int detail = 0; detail < MAX_NUMBER_OF_DETAIL; detail++)
|
||||
{
|
||||
//Compute circle distance for this detail in term of blocks
|
||||
absoluteDetailCircleSize[detail] = DetailDistanceUtil.getDrawDistanceFromDetail(detail);
|
||||
//Compute circle distance in terms of number of section
|
||||
relativeDetailCircleSize[detail] = (int) Math.ceil(absoluteDetailCircleSize[detail]/(SECTION_SIZE*2^detail));
|
||||
}
|
||||
updateGridSize();
|
||||
}
|
||||
|
||||
private void updateGridSize()
|
||||
{
|
||||
for(int detail = 0; detail < MAX_NUMBER_OF_DETAIL; detail++)
|
||||
{
|
||||
//Use this value to change to update the size of the matrices
|
||||
//relativeDetailCircleSize[detail];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.seibel.lod.core.objects.lod.quadtree;
|
||||
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
|
||||
public class RenderQuadTree
|
||||
{
|
||||
//public RenderSection[][][] quadTreeStructure;
|
||||
public QuadTreeProperties properties;
|
||||
|
||||
public RenderQuadTree()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -161,41 +161,11 @@ public class Mat4f
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder stringbuilder = new StringBuilder();
|
||||
stringbuilder.append("Matrix4f:\n");
|
||||
stringbuilder.append(this.m00);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m01);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m02);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m03);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m10);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m11);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m12);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m13);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m20);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m21);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m22);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m23);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m30);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m31);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m32);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m33);
|
||||
stringbuilder.append("\n");
|
||||
return stringbuilder.toString();
|
||||
return "Matrix4f:\n" +
|
||||
this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" +
|
||||
this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" +
|
||||
this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" +
|
||||
this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -553,4 +523,21 @@ public class Mat4f
|
||||
this.m13 = y;
|
||||
this.m23 = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the values that store the clipping planes.
|
||||
* Formula for calculating matrix values is the same that OpenGL uses when making matrices.
|
||||
*
|
||||
* @param nearClip New near clipping plane value.
|
||||
* @param farClip New far clipping plane value.
|
||||
*/
|
||||
public void setClipPlanes(float nearClip,float farClip)
|
||||
{
|
||||
//convert to matrix values, formula copied from a textbook / openGL specification.
|
||||
float matNearClip = -((farClip + nearClip) / (farClip - nearClip));
|
||||
float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip));
|
||||
//set new values for the clip planes.
|
||||
this.m22 = matNearClip;
|
||||
this.m23 = matFarClip;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.google.common.collect.ImmutableList;
|
||||
* DefaultVertexFormats class.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @version 12-8-2021
|
||||
*/
|
||||
public class DefaultLodVertexFormats
|
||||
{
|
||||
@@ -37,6 +37,8 @@ public class DefaultLodVertexFormats
|
||||
public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3);
|
||||
public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1);
|
||||
|
||||
public static final LodVertexFormatElement ELEMENT_BLOCK_LIGHT = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 1);
|
||||
|
||||
|
||||
public static final LodVertexFormat POSITION = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).build());
|
||||
public static final LodVertexFormat POSITION_COLOR = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).build());
|
||||
@@ -45,4 +47,5 @@ public class DefaultLodVertexFormats
|
||||
public static final LodVertexFormat POSITION_COLOR_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).build());
|
||||
public static final LodVertexFormat POSITION_COLOR_TEX_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).add(ELEMENT_LIGHT_MAP_UV).build());
|
||||
|
||||
public static final LodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_BLOCK_LIGHT).add(ELEMENT_BLOCK_LIGHT).build());
|
||||
}
|
||||
|
||||
@@ -19,14 +19,11 @@
|
||||
|
||||
package com.seibel.lod.core.objects.opengl;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -40,7 +37,7 @@ import com.google.common.collect.Lists;
|
||||
* OpenGL buffers.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @version 12-9-2021
|
||||
*/
|
||||
public class LodBufferBuilder
|
||||
{
|
||||
@@ -53,7 +50,6 @@ public class LodBufferBuilder
|
||||
private int nextElementByte = 0;
|
||||
private int totalUploadedBytes = 0;
|
||||
private int vertices;
|
||||
@Nullable
|
||||
private LodVertexFormatElement currentElement;
|
||||
private int elementIndex;
|
||||
private int mode;
|
||||
@@ -87,7 +83,7 @@ public class LodBufferBuilder
|
||||
/** make sure the buffer doesn't overflow when inserting new elements */
|
||||
private void ensureVertexCapacity()
|
||||
{
|
||||
this.ensureCapacity(this.format.getVertexSize());
|
||||
this.ensureCapacity(this.format.getByteSize());
|
||||
}
|
||||
private void ensureCapacity(int vertexSizeInBytes)
|
||||
{
|
||||
@@ -97,9 +93,9 @@ public class LodBufferBuilder
|
||||
int j = i + roundUp(vertexSizeInBytes);
|
||||
//LOGGER.debug("Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.", i, j);
|
||||
ByteBuffer bytebuffer = allocateByteBuffer(j);
|
||||
((Buffer) this.buffer).position(0);
|
||||
this.buffer.position(0);
|
||||
bytebuffer.put(this.buffer);
|
||||
((Buffer) bytebuffer).rewind();
|
||||
bytebuffer.rewind();
|
||||
this.buffer = bytebuffer;
|
||||
}
|
||||
}
|
||||
@@ -272,7 +268,7 @@ public class LodBufferBuilder
|
||||
this.switchFormat(LodVertexFormat);
|
||||
this.currentElement = LodVertexFormat.getElements().get(0);
|
||||
this.elementIndex = 0;
|
||||
((Buffer) this.buffer).clear();
|
||||
this.buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +282,7 @@ public class LodBufferBuilder
|
||||
{
|
||||
this.building = false;
|
||||
this.vertexCounts.add(new LodBufferBuilder.DrawState(this.format, this.vertices, this.mode));
|
||||
this.totalRenderedBytes += this.vertices * this.format.getVertexSize();
|
||||
this.totalRenderedBytes += this.vertices * this.format.getByteSize();
|
||||
this.vertices = 0;
|
||||
this.currentElement = null;
|
||||
this.elementIndex = 0;
|
||||
@@ -326,8 +322,7 @@ public class LodBufferBuilder
|
||||
ImmutableList<LodVertexFormatElement> immutablelist = this.format.getElements();
|
||||
this.elementIndex = (this.elementIndex + 1) % immutablelist.size();
|
||||
this.nextElementByte += this.currentElement.getByteSize();
|
||||
LodVertexFormatElement LodVertexFormatelement = immutablelist.get(this.elementIndex);
|
||||
this.currentElement = LodVertexFormatelement;
|
||||
this.currentElement = immutablelist.get(this.elementIndex);
|
||||
// if (LodVertexFormatelement.getUsage() == LodVertexFormatElement.Usage.PADDING)
|
||||
// {
|
||||
// this.nextElement();
|
||||
@@ -358,7 +353,22 @@ public class LodBufferBuilder
|
||||
}
|
||||
}
|
||||
|
||||
public LodBufferBuilder vertex(float x, float y, float z)
|
||||
public LodBufferBuilder minecraftLightValue(byte lightValue)
|
||||
{
|
||||
LodVertexFormatElement LodVertexFormatelement = this.currentElement();
|
||||
if (LodVertexFormatelement.getType() != LodVertexFormatElement.DataType.UBYTE)
|
||||
{
|
||||
throw new IllegalStateException("Light Color must be stored as a UBYTE");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.putByte(0, lightValue);
|
||||
this.nextElement();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public LodBufferBuilder position(float x, float y, float z)
|
||||
{
|
||||
if (this.currentElement().getType() != LodVertexFormatElement.DataType.FLOAT)
|
||||
{
|
||||
@@ -439,17 +449,17 @@ public class LodBufferBuilder
|
||||
public ByteBuffer getCleanedByteBuffer()
|
||||
{
|
||||
LodBufferBuilder.DrawState bufferbuilder$drawstate = this.vertexCounts.get(this.lastRenderedCountIndex++);
|
||||
((Buffer) this.buffer).position(this.totalUploadedBytes);
|
||||
this.totalUploadedBytes += bufferbuilder$drawstate.vertexCount() * bufferbuilder$drawstate.format().getVertexSize();
|
||||
((Buffer) this.buffer).limit(this.totalUploadedBytes);
|
||||
this.buffer.position(this.totalUploadedBytes);
|
||||
this.totalUploadedBytes += bufferbuilder$drawstate.vertexCount() * bufferbuilder$drawstate.format().getByteSize();
|
||||
this.buffer.limit(this.totalUploadedBytes);
|
||||
if (this.lastRenderedCountIndex == this.vertexCounts.size() && this.vertices == 0)
|
||||
{
|
||||
this.clear();
|
||||
}
|
||||
|
||||
ByteBuffer bytebuffer = this.buffer.slice();
|
||||
bytebuffer.order(this.buffer.order()); // FORGE: Fix incorrect byte order
|
||||
((Buffer) this.buffer).clear();
|
||||
//bytebuffer.order(this.buffer.order()); // FORGE: Fix incorrect byte order
|
||||
this.buffer.clear();
|
||||
return bytebuffer; // the original method also returned bufferbuilder$drawstate
|
||||
}
|
||||
|
||||
@@ -533,10 +543,10 @@ public class LodBufferBuilder
|
||||
// Forge added methods
|
||||
public void putBulkData(ByteBuffer buffer)
|
||||
{
|
||||
ensureCapacity(buffer.limit() + this.format.getVertexSize());
|
||||
((Buffer) this.buffer).position(this.vertices * this.format.getVertexSize());
|
||||
ensureCapacity(buffer.limit() + this.format.getByteSize());
|
||||
this.buffer.position(this.vertices * this.format.getByteSize());
|
||||
this.buffer.put(buffer);
|
||||
this.vertices += buffer.limit() / this.format.getVertexSize();
|
||||
this.vertices += buffer.limit() / this.format.getByteSize();
|
||||
this.nextElementByte += buffer.limit();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.lod.core.objects.opengl;
|
||||
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.GLProxyContext;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
@@ -41,7 +41,7 @@ public class LodVertexBuffer implements AutoCloseable
|
||||
if (GLProxy.getInstance().getGlContext() == GLProxyContext.NONE)
|
||||
throw new IllegalStateException("Thread [" +Thread.currentThread().getName() + "] tried to create a [" + LodVertexBuffer.class.getSimpleName() + "] outside a OpenGL contex.");
|
||||
|
||||
this.id = GL15.glGenBuffers();
|
||||
this.id = GL32.glGenBuffers();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public class LodVertexBuffer implements AutoCloseable
|
||||
{
|
||||
if (this.id >= 0)
|
||||
{
|
||||
GLProxy.getInstance().recordOpenGlCall(() -> GL15.glDeleteBuffers(this.id));
|
||||
GLProxy.getInstance().recordOpenGlCall(() -> GL32.glDeleteBuffers(this.id));
|
||||
this.id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,13 @@ import it.unimi.dsi.fastutil.ints.IntList;
|
||||
* were commented out since we didn't need them.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @version 12-9-2021
|
||||
*/
|
||||
public class LodVertexFormat
|
||||
{
|
||||
private final ImmutableList<LodVertexFormatElement> elements;
|
||||
private final IntList offsets = new IntArrayList();
|
||||
private final int vertexSize;
|
||||
private final int byteSize;
|
||||
|
||||
public LodVertexFormat(ImmutableList<LodVertexFormatElement> elementList)
|
||||
{
|
||||
@@ -54,17 +54,12 @@ public class LodVertexFormat
|
||||
i += LodVertexFormatElement.getByteSize();
|
||||
}
|
||||
|
||||
this.vertexSize = i;
|
||||
this.byteSize = i;
|
||||
}
|
||||
|
||||
public int getIntegerSize()
|
||||
public int getByteSize()
|
||||
{
|
||||
return this.getVertexSize() / 4;
|
||||
}
|
||||
|
||||
public int getVertexSize()
|
||||
{
|
||||
return this.vertexSize;
|
||||
return this.byteSize;
|
||||
}
|
||||
|
||||
public ImmutableList<LodVertexFormatElement> getElements()
|
||||
@@ -98,7 +93,7 @@ public class LodVertexFormat
|
||||
else if (obj != null && this.getClass() == obj.getClass())
|
||||
{
|
||||
LodVertexFormat vertexformat = (LodVertexFormat) obj;
|
||||
return this.vertexSize != vertexformat.vertexSize ? false : this.elements.equals(vertexformat.elements);
|
||||
return this.byteSize == vertexformat.byteSize && this.elements.equals(vertexformat.elements);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@ public class LodVertexFormatElement
|
||||
|
||||
|
||||
|
||||
public static enum DataType
|
||||
public enum DataType
|
||||
{
|
||||
FLOAT(4, "Float", GL11.GL_FLOAT),
|
||||
UBYTE(1, "Unsigned Byte", GL11.GL_UNSIGNED_BYTE),
|
||||
@@ -85,7 +85,7 @@ public class LodVertexFormatElement
|
||||
private final String name;
|
||||
private final int glType;
|
||||
|
||||
private DataType(int sizeInBytes, String newName, int openGlDataType)
|
||||
DataType(int sizeInBytes, String newName, int openGlDataType)
|
||||
{
|
||||
this.size = sizeInBytes;
|
||||
this.name = newName;
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.objects.rending;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
|
||||
/**
|
||||
* This object is just a replacement for an array
|
||||
* to make things easier to understand in the LodRenderer.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-03-2021
|
||||
*/
|
||||
public class NearFarFogSettings
|
||||
{
|
||||
public final NearOrFarSetting near = new NearOrFarSetting(FogDistance.NEAR);
|
||||
public final NearOrFarSetting far = new NearOrFarSetting(FogDistance.FAR);
|
||||
|
||||
/**
|
||||
* If true that means Minecraft is
|
||||
* rendering fog
|
||||
*/
|
||||
public boolean vanillaIsRenderingFog = true;
|
||||
|
||||
public NearFarFogSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This holds all relevant data to rendering fog at either
|
||||
* near or far distances.
|
||||
*/
|
||||
public static class NearOrFarSetting
|
||||
{
|
||||
public FogQuality quality = FogQuality.FANCY;
|
||||
public FogDistance distance;
|
||||
|
||||
public NearOrFarSetting(FogDistance newFogDistance)
|
||||
{
|
||||
distance = newFogDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2021 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,23 +19,28 @@
|
||||
|
||||
package com.seibel.lod.core.render;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GLCapabilities;
|
||||
import org.lwjgl.opengl.GLUtil;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.GLProxyContext;
|
||||
import com.seibel.lod.core.render.shader.LodShader;
|
||||
import com.seibel.lod.core.render.shader.LodShaderProgram;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
@@ -43,21 +48,26 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
* and GPU capabilities.
|
||||
*
|
||||
* <p>
|
||||
* Helpful OpenGL resources: <br><br>
|
||||
* Helpful OpenGL resources:
|
||||
* <p>
|
||||
* https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf <br>
|
||||
* https://learnopengl.com/Advanced-OpenGL/Advanced-Data <br>
|
||||
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br><br>
|
||||
* https://www.slideshare.net/CassEveritt/approaching-zero-driver-overhead <br><br>
|
||||
*
|
||||
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br>
|
||||
* https://stackoverflow.com/questions/63509735/massive-performance-loss-with-glmapbuffer <br><br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
* @version 12-9-2021
|
||||
*/
|
||||
public class GLProxy
|
||||
{
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
|
||||
private static ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build());
|
||||
|
||||
private static final ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build());
|
||||
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
private static GLProxy instance = null;
|
||||
|
||||
@@ -76,33 +86,94 @@ public class GLProxy
|
||||
/** the proxyWorker's GL capabilities */
|
||||
public final GLCapabilities proxyWorkerGlCapabilities;
|
||||
|
||||
|
||||
|
||||
/** This program contains all shaders required when rendering LODs */
|
||||
public LodShaderProgram lodShaderProgram;
|
||||
/** This is the VAO that is used when rendering */
|
||||
public final int vertexArrayObjectId;
|
||||
|
||||
|
||||
/** Requires OpenGL 4.5, and offers the best buffer uploading */
|
||||
/** Requires OpenGL 4.4, and offers the best buffer uploading */
|
||||
public final boolean bufferStorageSupported;
|
||||
|
||||
/** Requires OpenGL 3.0 */
|
||||
public final boolean mapBufferRangeSupported;
|
||||
/** Requires OpenGL 4.5 */
|
||||
public final boolean namedObjectSupported;
|
||||
|
||||
/** Requires OpenGL 4.3 */
|
||||
public final boolean VertexAttributeBufferBindingSupported;
|
||||
|
||||
/** Requires OpenGL 3.0, which will current min requirement as 3.3, should always be true */
|
||||
@Deprecated
|
||||
public final boolean mapBufferRangeSupported = true;
|
||||
|
||||
private final GpuUploadMethod preferredUploadMethod;
|
||||
|
||||
|
||||
private String getFailedVersionInfo(GLCapabilities c) {
|
||||
StringBuilder str = new StringBuilder("Your supported OpenGL version:\n");
|
||||
|
||||
str.append("1.1: "+c.OpenGL11+"\n");
|
||||
str.append("1.2: "+c.OpenGL12+"\n");
|
||||
str.append("1.3: "+c.OpenGL13+"\n");
|
||||
str.append("1.4: "+c.OpenGL14+"\n");
|
||||
str.append("1.5: "+c.OpenGL15+"\n");
|
||||
str.append("2.0: "+c.OpenGL20+"\n");
|
||||
str.append("2.1: "+c.OpenGL21+"\n");
|
||||
str.append("3.0: "+c.OpenGL30+"\n");
|
||||
str.append("3.1: "+c.OpenGL31+"\n");
|
||||
str.append("3.2: "+c.OpenGL32+" <- REQUIRED\n");
|
||||
str.append("3.3: "+c.OpenGL33+"\n");
|
||||
str.append("4.0: "+c.OpenGL40+"\n");
|
||||
str.append("4.1: "+c.OpenGL41+"\n");
|
||||
str.append("4.2: "+c.OpenGL42+"\n");
|
||||
str.append("4.3: "+c.OpenGL43+" <- optional improvement\n");
|
||||
str.append("4.4: "+c.OpenGL44+" <- optional improvement\n");
|
||||
str.append("4.5: "+c.OpenGL45+"\n");
|
||||
str.append("4.6: "+c.OpenGL46+"\n");
|
||||
|
||||
str.append("If you noticed that your computer supports higher OpenGL versions"
|
||||
+ " but not the required version, try running the game in compatibility mode."
|
||||
+ " (How you turn that on, I have no clue~)");
|
||||
return str.toString();
|
||||
}
|
||||
private String getVersionInfo(GLCapabilities c) {
|
||||
StringBuilder str = new StringBuilder("Your supported OpenGL version:\n");
|
||||
|
||||
str.append("1.1: "+c.OpenGL11+"\n");
|
||||
str.append("1.2: "+c.OpenGL12+"\n");
|
||||
str.append("1.3: "+c.OpenGL13+"\n");
|
||||
str.append("1.4: "+c.OpenGL14+"\n");
|
||||
str.append("1.5: "+c.OpenGL15+"\n");
|
||||
str.append("2.0: "+c.OpenGL20+"\n");
|
||||
str.append("2.1: "+c.OpenGL21+"\n");
|
||||
str.append("3.0: "+c.OpenGL30+"\n");
|
||||
str.append("3.1: "+c.OpenGL31+"\n");
|
||||
str.append("3.2: "+c.OpenGL32+" <- REQUIRED\n");
|
||||
str.append("3.3: "+c.OpenGL33+"\n");
|
||||
str.append("4.0: "+c.OpenGL40+"\n");
|
||||
str.append("4.1: "+c.OpenGL41+"\n");
|
||||
str.append("4.2: "+c.OpenGL42+"\n");
|
||||
str.append("4.3: "+c.OpenGL43+" <- optional improvement\n");
|
||||
str.append("4.4: "+c.OpenGL44+" <- optional improvement\n");
|
||||
str.append("4.5: "+c.OpenGL45+"\n");
|
||||
str.append("4.6: "+c.OpenGL46+"\n");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException
|
||||
* @throws RuntimeException
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
private GLProxy()
|
||||
{
|
||||
ClientApi.LOGGER.error("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
|
||||
|
||||
boolean enableDebugLogging = CONFIG.client().advanced().debugging().getDebugMode() == DebugMode.SHOW_DETAIL;
|
||||
|
||||
// this must be created on minecraft's render context to work correctly
|
||||
|
||||
ClientApi.LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
|
||||
|
||||
ClientApi.LOGGER.info("Lod Render OpenGL version [" + GL11.glGetString(GL11.GL_VERSION) + "].");
|
||||
|
||||
// getting Minecraft's context has to be done on the render thread,
|
||||
// where the GL context is
|
||||
if (GLFW.glfwGetCurrentContext() == 0L)
|
||||
throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!");
|
||||
|
||||
|
||||
|
||||
//============================//
|
||||
// create the builder context //
|
||||
@@ -111,7 +182,20 @@ public class GLProxy
|
||||
// get Minecraft's context
|
||||
minecraftGlContext = GLFW.glfwGetCurrentContext();
|
||||
minecraftGlCapabilities = GL.getCapabilities();
|
||||
|
||||
|
||||
// crash the game if the GPU doesn't support OpenGL 3.2
|
||||
if (!minecraftGlCapabilities.OpenGL32)
|
||||
{
|
||||
String supportedVersionInfo = getFailedVersionInfo(minecraftGlCapabilities);
|
||||
|
||||
// Note: as of MC 1.17 this shouldn't happen since MC
|
||||
// requires OpenGL 3.2, but for older MC version this will warn the player.
|
||||
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName()
|
||||
+ " and discovered this GPU doesn't support OpenGL 3.2." + " Sorry I couldn't tell you sooner :(\n"+
|
||||
"Additional info:\n"+supportedVersionInfo;
|
||||
MC.crashMinecraft(errorMessage, new UnsupportedOperationException("This GPU doesn't support OpenGL 3.2."));
|
||||
}
|
||||
ClientApi.LOGGER.info("minecraftGlCapabilities:\n"+getVersionInfo(minecraftGlCapabilities));
|
||||
|
||||
// context creation setup
|
||||
GLFW.glfwDefaultWindowHints();
|
||||
@@ -122,21 +206,24 @@ public class GLProxy
|
||||
// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 5);
|
||||
|
||||
|
||||
// create the LodBuilder context
|
||||
lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext);
|
||||
GLFW.glfwMakeContextCurrent(lodBuilderGlContext);
|
||||
lodBuilderGlCapabilities = GL.createCapabilities();
|
||||
|
||||
ClientApi.LOGGER.info("lodBuilderGlCapabilities:\n"+getVersionInfo(lodBuilderGlCapabilities));
|
||||
|
||||
// create the proxyWorker's context
|
||||
proxyWorkerGlContext = GLFW.glfwCreateWindow(64, 48, "LOD proxy worker Window", 0L, minecraftGlContext);
|
||||
GLFW.glfwMakeContextCurrent(proxyWorkerGlContext);
|
||||
proxyWorkerGlCapabilities = GL.createCapabilities();
|
||||
|
||||
|
||||
|
||||
|
||||
ClientApi.LOGGER.info("proxyWorkerGlCapabilities:\n"+getVersionInfo(lodBuilderGlCapabilities));
|
||||
|
||||
// Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after
|
||||
VertexAttributeBufferBindingSupported = minecraftGlCapabilities.glBindVertexBuffer != 0L; // Nullptr
|
||||
|
||||
// UNUSED currently
|
||||
// Check if we can use the named version of all calls, which is available in GL4.5 or after
|
||||
namedObjectSupported = minecraftGlCapabilities.glNamedBufferData != 0L; //Nullptr
|
||||
|
||||
|
||||
//==================================//
|
||||
@@ -144,53 +231,62 @@ public class GLProxy
|
||||
//==================================//
|
||||
|
||||
setGlContext(GLProxyContext.LOD_BUILDER);
|
||||
|
||||
ClientApi.LOGGER.info("Lod Render OpenGL version [" + GL11.glGetString(GL11.GL_VERSION) + "].");
|
||||
|
||||
// crash the game if the GPU doesn't support OpenGL 2.0
|
||||
if (!minecraftGlCapabilities.OpenGL20)
|
||||
{
|
||||
// Note: as of MC 1.17 this shouldn't happen since MC
|
||||
// requires OpenGL 3.3, but just in case.
|
||||
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 2.0 or greater.";
|
||||
MC.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 2.0 or greater."));
|
||||
// TODO: Enable this but disable INFO logging
|
||||
|
||||
File proxyLog = new File("OpenGL-Lod-ProxyContext.log");
|
||||
try {
|
||||
proxyLog.createNewFile();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
if (enableDebugLogging)
|
||||
try {
|
||||
GLUtil.setupDebugMessageCallback(new PrintStream(proxyLog));
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// get specific capabilities
|
||||
// TODO re-add buffer storage support
|
||||
bufferStorageSupported = lodBuilderGlCapabilities.glBufferStorage != 0;
|
||||
mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0;
|
||||
|
||||
|
||||
// Check if we can use the Buffer Storage, which is available in GL4.4 or after
|
||||
bufferStorageSupported = minecraftGlCapabilities.glBufferStorage != 0L && lodBuilderGlCapabilities.glBufferStorage != 0L; // Nullptr
|
||||
//bufferStorageSupported = true;
|
||||
// display the capabilities
|
||||
if (!bufferStorageSupported)
|
||||
{
|
||||
String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5";
|
||||
ClientApi.LOGGER.error("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
|
||||
ClientApi.LOGGER.warn("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods.");
|
||||
}
|
||||
|
||||
String vendor = GL32.glGetString(GL32.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION"
|
||||
if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE"))
|
||||
{
|
||||
// NVIDIA card
|
||||
preferredUploadMethod = bufferStorageSupported ? GpuUploadMethod.BUFFER_STORAGE : GpuUploadMethod.SUB_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
// AMD or Intel card
|
||||
preferredUploadMethod = GpuUploadMethod.BUFFER_MAPPING;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// shader setup //
|
||||
//==============//
|
||||
|
||||
//setGlContext(GLProxyContext.LOD_RENDER);
|
||||
setGlContext(GLProxyContext.MINECRAFT);
|
||||
|
||||
createShaderProgram();
|
||||
|
||||
// Note: VAO objects can not be shared between contexts,
|
||||
// this must be created on minecraft's render context to work correctly
|
||||
vertexArrayObjectId = GL30.glGenVertexArrays();
|
||||
|
||||
|
||||
|
||||
|
||||
ClientApi.LOGGER.info("GPU Vendor [" + vendor + "], Preferred upload method is [" + preferredUploadMethod + "].");
|
||||
|
||||
setGlContext(GLProxyContext.PROXY_WORKER);
|
||||
File workerLog = new File("OpenGL-Lod-WorkerContext.log");
|
||||
try {
|
||||
workerLog.createNewFile();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (enableDebugLogging)
|
||||
try {
|
||||
GLUtil.setupDebugMessageCallback(new PrintStream(workerLog));
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
//==========//
|
||||
// clean up //
|
||||
@@ -199,55 +295,10 @@ public class GLProxy
|
||||
// Since this is created on the render thread, make sure the Minecraft context is used in the end
|
||||
setGlContext(GLProxyContext.MINECRAFT);
|
||||
|
||||
|
||||
// GLProxy creation success
|
||||
ClientApi.LOGGER.error(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
|
||||
ClientApi.LOGGER.info(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
|
||||
}
|
||||
|
||||
/** Creates all required shaders */
|
||||
public void createShaderProgram()
|
||||
{
|
||||
LodShader vertexShader = null;
|
||||
LodShader fragmentShader = null;
|
||||
|
||||
try
|
||||
{
|
||||
// get the shaders from the resource folder
|
||||
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/unshaded.vert", false);
|
||||
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/unshaded.frag", false);
|
||||
|
||||
// this can be used when testing shaders,
|
||||
// since we can't hot swap the files in the resource folder
|
||||
// vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "C:/Users/James Seibel/Desktop/shaders/unshaded.vert", true);
|
||||
// fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/unshaded.frag", true);
|
||||
|
||||
|
||||
// create the shaders
|
||||
|
||||
lodShaderProgram = new LodShaderProgram();
|
||||
|
||||
// Attach the compiled shaders to the program
|
||||
lodShaderProgram.attachShader(vertexShader);
|
||||
lodShaderProgram.attachShader(fragmentShader);
|
||||
|
||||
// activate the fragment shader output
|
||||
GL30.glBindFragDataLocation(lodShaderProgram.id, 0, "fragColor");
|
||||
|
||||
// attach the shader program to the OpenGL context
|
||||
lodShaderProgram.link();
|
||||
|
||||
// after the shaders have been attached to the program
|
||||
// we don't need their OpenGL references anymore
|
||||
GL20.glDeleteShader(vertexShader.id);
|
||||
GL20.glDeleteShader(fragmentShader.id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error("Unable to compile shaders. Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper function to make switching contexts easier. <br>
|
||||
* Does nothing if the calling thread is already using newContext.
|
||||
@@ -317,21 +368,29 @@ public class GLProxy
|
||||
+ "no context [0].");
|
||||
}
|
||||
|
||||
public static boolean hasInstance() {
|
||||
return instance != null;
|
||||
}
|
||||
|
||||
public static GLProxy getInstance()
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new GLProxy();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public GpuUploadMethod getGpuUploadMethod() {
|
||||
GpuUploadMethod method = CONFIG.client().advanced().buffers().getGpuUploadMethod();
|
||||
|
||||
if (!bufferStorageSupported && method == GpuUploadMethod.BUFFER_STORAGE)
|
||||
{
|
||||
// if buffer storage isn't supported
|
||||
// default to SUB_DATA
|
||||
method = GpuUploadMethod.SUB_DATA;
|
||||
}
|
||||
|
||||
return method == GpuUploadMethod.AUTO ? preferredUploadMethod : method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously calls the given runnable on proxy's OpenGL context.
|
||||
@@ -364,6 +423,31 @@ public class GLProxy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If called from a legacy OpenGL context this will
|
||||
* set the fog end to infinity with a density of 0.
|
||||
* Effectively removing the fog.
|
||||
* <p>
|
||||
* This only works with Legacy OpenGL because James hasn't
|
||||
* looking into a way for it to work with Modern OpenGL.
|
||||
*/
|
||||
public boolean disableLegacyFog()
|
||||
{
|
||||
// make sure this is a legacy OpenGL context
|
||||
if (minecraftGlCapabilities.glFogf != 0)
|
||||
{
|
||||
// glFogf should only have an address if the current OpenGL
|
||||
// context can call it, and it should only be able to call it in
|
||||
// legacy OpenGL contexts; since it is disabled in Modern
|
||||
// OpenGL.
|
||||
|
||||
GL11.glFogf(GL11.GL_FOG_START, 0.0f);
|
||||
GL11.glFogf(GL11.GL_FOG_END, Float.MAX_VALUE);
|
||||
GL11.glFogf(GL11.GL_FOG_DENSITY, 0.0f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.render;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
import com.seibel.lod.core.handlers.IReflectionHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
|
||||
/**
|
||||
* This object is just a replacement for an array
|
||||
* to make things easier to understand in the LodRenderer.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-26-2021
|
||||
*/
|
||||
public class LodFogConfig
|
||||
{
|
||||
public FogDrawMode fogDrawMode;
|
||||
public FogDistance fogDistance;
|
||||
|
||||
public float nearFogStart = 0;
|
||||
public float nearFogEnd = 0;
|
||||
|
||||
public float farFogStart = 0;
|
||||
public float farFogEnd = 0;
|
||||
|
||||
public LodFogConfig(ILodConfigWrapperSingleton config, IReflectionHandler reflectionHandler, int farPlaneBlockDistance, int vanillaBlockRenderedDistance) {
|
||||
|
||||
fogDrawMode = config.client().graphics().fogQuality().getFogDrawMode();
|
||||
if (fogDrawMode == FogDrawMode.USE_OPTIFINE_SETTING)
|
||||
fogDrawMode = reflectionHandler.getFogDrawMode();
|
||||
|
||||
// how different distances are drawn depends on the quality set
|
||||
fogDistance = config.client().graphics().fogQuality().getFogDistance();
|
||||
|
||||
// far fog //
|
||||
|
||||
if (config.client().graphics().fogQuality().getFogDistance() == FogDistance.NEAR_AND_FAR)
|
||||
farFogStart = farPlaneBlockDistance * 0.9f;
|
||||
else
|
||||
// for more realistic fog when using FAR
|
||||
farFogStart = Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f);
|
||||
|
||||
farFogEnd = farPlaneBlockDistance;
|
||||
|
||||
|
||||
// near fog //
|
||||
|
||||
// the reason that I wrote fogEnd then fogStart backwards
|
||||
// is because we are using fog backwards to how
|
||||
// it is normally used, hiding near objects
|
||||
// instead of far objects.
|
||||
nearFogEnd = vanillaBlockRenderedDistance * 1.41f;
|
||||
nearFogStart = vanillaBlockRenderedDistance * 1.6f;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2021 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.render;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.objects.math.Vec3f;
|
||||
import com.seibel.lod.core.render.objects.ShaderProgram;
|
||||
import com.seibel.lod.core.render.objects.VertexAttribute;
|
||||
import com.seibel.lod.core.render.objects.VertexAttributePostGL43;
|
||||
import com.seibel.lod.core.render.objects.VertexAttributePreGL43;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
public class LodRenderProgram extends ShaderProgram {
|
||||
public static final String VERTEX_SHADER_PATH = "shaders/standard.vert";
|
||||
public static final String FRAGMENT_SHADER_PATH = "shaders/flat_shaded.frag";
|
||||
|
||||
public final VertexAttribute vao;
|
||||
|
||||
// Attributes
|
||||
public final int posAttrib;
|
||||
public final int colAttrib;
|
||||
public final int blockSkyLightAttrib;
|
||||
public final int blockLightAttrib;
|
||||
// Uniforms
|
||||
public final int mvmUniform;
|
||||
public final int projUniform;
|
||||
public final int cameraUniform;
|
||||
public final int fogColorUniform;
|
||||
// public final int skyLightUniform; worldSkyLight is currently not used
|
||||
public final int lightMapUniform;
|
||||
// Fog Uniforms
|
||||
public final int fogEnabledUniform;
|
||||
public final int nearFogEnabledUniform;
|
||||
public final int farFogEnabledUniform;
|
||||
public final int nearFogStartUniform;
|
||||
public final int nearFogEndUniform;
|
||||
public final int farFogStartUniform;
|
||||
public final int farFogEndUniform;
|
||||
|
||||
// This will bind VertexAttribute
|
||||
public LodRenderProgram() {
|
||||
super(VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH, "fragColor");
|
||||
|
||||
posAttrib = getAttributeLocation("vPosition");
|
||||
colAttrib = getAttributeLocation("color");
|
||||
blockSkyLightAttrib = getAttributeLocation("blockSkyLight");
|
||||
blockLightAttrib = getAttributeLocation("blockLight");
|
||||
|
||||
mvmUniform = getUniformLocation("modelViewMatrix");
|
||||
projUniform = getUniformLocation("projectionMatrix");
|
||||
cameraUniform = getUniformLocation("cameraPos");
|
||||
fogColorUniform = getUniformLocation("fogColor");
|
||||
// skyLightUniform = getUniformLocation("worldSkyLight");
|
||||
lightMapUniform = getUniformLocation("lightMap");
|
||||
|
||||
// Fog uniforms
|
||||
fogEnabledUniform = getUniformLocation("fogEnabled");
|
||||
nearFogEnabledUniform = getUniformLocation("nearFogEnabled");
|
||||
farFogEnabledUniform = getUniformLocation("farFogEnabled");
|
||||
// near
|
||||
nearFogStartUniform = getUniformLocation("nearFogStart");
|
||||
nearFogEndUniform = getUniformLocation("nearFogEnd");
|
||||
// far
|
||||
farFogStartUniform = getUniformLocation("farFogStart");
|
||||
farFogEndUniform = getUniformLocation("farFogEnd");
|
||||
|
||||
// TODO: Add better use of the LODFormat thing
|
||||
int vertexByteCount = LodUtil.LOD_VERTEX_FORMAT.getByteSize();
|
||||
if (GLProxy.getInstance().VertexAttributeBufferBindingSupported)
|
||||
vao = new VertexAttributePostGL43(); // also binds VertexAttribute
|
||||
else
|
||||
vao = new VertexAttributePreGL43(); // also binds VertexAttribute
|
||||
//vao.bind();
|
||||
vao.setVertexAttribute(0, posAttrib, VertexAttribute.VertexPointer.addVec3Pointer(false));
|
||||
vao.setVertexAttribute(0, colAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(4, true));
|
||||
vao.setVertexAttribute(0, blockSkyLightAttrib, VertexAttribute.VertexPointer.addUnsignedBytePointer(false));
|
||||
vao.setVertexAttribute(0, blockLightAttrib, VertexAttribute.VertexPointer.addUnsignedBytePointer(false));
|
||||
vao.completeAndCheck(vertexByteCount);
|
||||
}
|
||||
|
||||
// Override ShaderProgram.bind()
|
||||
public void bind() {
|
||||
super.bind();
|
||||
vao.bind();
|
||||
}
|
||||
// Override ShaderProgram.unbind()
|
||||
public void unbind() {
|
||||
super.unbind();
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
// Override ShaderProgram.free()
|
||||
public void free() {
|
||||
vao.free();
|
||||
super.free();
|
||||
}
|
||||
|
||||
public void bindVertexBuffer(int vbo) {
|
||||
vao.bindBufferToAllBindingPoint(vbo);
|
||||
}
|
||||
|
||||
public void unbindVertexBuffer() {
|
||||
vao.unbindBuffersFromAllBindingPoint();
|
||||
}
|
||||
|
||||
public void fillUniformData(Mat4f modelViewMatrix, Mat4f projectionMatrix, Vec3f cameraPos, Color fogColor, int skyLight, int lightmapBindPoint) {
|
||||
super.bind();
|
||||
// uniforms
|
||||
setUniform(mvmUniform, modelViewMatrix);
|
||||
setUniform(projUniform, projectionMatrix);
|
||||
setUniform(cameraUniform, cameraPos);
|
||||
setUniform(fogColorUniform, fogColor);
|
||||
// setUniform(skyLightUniform, skyLight);
|
||||
setUniform(lightMapUniform, lightmapBindPoint);
|
||||
}
|
||||
|
||||
public void fillUniformDataForFog(LodFogConfig fogSettings) {
|
||||
super.bind();
|
||||
if (fogSettings.fogDrawMode != FogDrawMode.FOG_DISABLED) {
|
||||
setUniform(fogEnabledUniform, true);
|
||||
setUniform(nearFogEnabledUniform, fogSettings.fogDistance != FogDistance.FAR);
|
||||
setUniform(farFogEnabledUniform, fogSettings.fogDistance != FogDistance.NEAR);
|
||||
// near
|
||||
setUniform(nearFogStartUniform, fogSettings.nearFogStart);
|
||||
setUniform(nearFogEndUniform, fogSettings.nearFogEnd);
|
||||
// far
|
||||
setUniform(farFogStartUniform, fogSettings.farFogStart);
|
||||
setUniform(farFogEndUniform, fogSettings.farFogEnd);
|
||||
} else {
|
||||
setUniform(fogEnabledUniform, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,9 +21,10 @@ package com.seibel.lod.core.render;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Vec3f;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
|
||||
/**
|
||||
* This holds miscellaneous helper code
|
||||
@@ -34,7 +35,7 @@ import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
|
||||
*/
|
||||
public class RenderUtil
|
||||
{
|
||||
private static final MinecraftRenderWrapper MC_RENDER = MinecraftRenderWrapper.INSTANCE;
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
|
||||
/**
|
||||
@@ -84,24 +85,23 @@ public class RenderUtil
|
||||
* Returns true if one of the region's 4 corners is in front
|
||||
* of the camera.
|
||||
*/
|
||||
public static boolean isRegionInViewFrustum(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, AbstractBlockPosWrapper vboCenterPos)
|
||||
public static boolean isRegionInViewFrustum(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, int vboCenterPosX, int vboCenterPosZ)
|
||||
{
|
||||
// convert the vbo position into a direction vector
|
||||
// starting from the player's position
|
||||
Vec3f vboVec = new Vec3f(vboCenterPos.getX(), 0, vboCenterPos.getZ());
|
||||
Vec3f vboVec = new Vec3f(vboCenterPosX, 0, vboCenterPosZ);
|
||||
Vec3f playerVec = new Vec3f(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ());
|
||||
|
||||
vboVec.subtract(playerVec);
|
||||
Vec3f vboCenterVec = vboVec;
|
||||
|
||||
|
||||
int halfRegionWidth = LodUtil.REGION_WIDTH / 2;
|
||||
|
||||
// calculate the 4 corners
|
||||
Vec3f vboSeVec = new Vec3f(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);
|
||||
Vec3f vboSwVec = new Vec3f(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);
|
||||
Vec3f vboNwVec = new Vec3f(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
|
||||
Vec3f vboNeVec = new Vec3f(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
|
||||
Vec3f vboSeVec = new Vec3f(vboVec.x + halfRegionWidth, vboVec.y, vboVec.z + halfRegionWidth);
|
||||
Vec3f vboSwVec = new Vec3f(vboVec.x - halfRegionWidth, vboVec.y, vboVec.z + halfRegionWidth);
|
||||
Vec3f vboNwVec = new Vec3f(vboVec.x - halfRegionWidth, vboVec.y, vboVec.z - halfRegionWidth);
|
||||
Vec3f vboNeVec = new Vec3f(vboVec.x + halfRegionWidth, vboVec.y, vboVec.z - halfRegionWidth);
|
||||
|
||||
// if any corner is visible, this region should be rendered
|
||||
return isNormalizedVectorInViewFrustum(vboSeVec, cameraDir) ||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.seibel.lod.core.render.objects;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
public class LightmapTexture {
|
||||
public int id;
|
||||
|
||||
public LightmapTexture() {
|
||||
id = GL32.glGenTextures();
|
||||
bind();
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, id);
|
||||
}
|
||||
public void unbind() {
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
public void free() {
|
||||
GL32.glDeleteTextures(id);
|
||||
}
|
||||
|
||||
// private int[] testArray;
|
||||
|
||||
public void fillData(int lightMapWidth, int lightMapHeight, int[] pixels) {
|
||||
GL32.glDeleteTextures(id);
|
||||
id = GL32.glGenTextures();
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, id);
|
||||
if (pixels.length != lightMapWidth*lightMapHeight)
|
||||
throw new RuntimeException("Lightmap Width*Height not equal to pixels provided!");
|
||||
|
||||
// comment me out to see when the lightmap is changing
|
||||
/*
|
||||
boolean same = true;
|
||||
int badIndex = 0;
|
||||
if (testArray != null && pixels != null)
|
||||
for (int i = 0; i < pixels.length; i++)
|
||||
{
|
||||
if(pixels[i] != testArray[i])
|
||||
{
|
||||
same = false;
|
||||
badIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
testArray = pixels;
|
||||
MC.sendChatMessage(same + " " + badIndex);
|
||||
*/
|
||||
// comment this line out to prevent uploading the new lightmap
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA, lightMapWidth,
|
||||
lightMapHeight, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_BYTE, pixels);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_BORDER);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_BORDER);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
}
|
||||
|
||||
}
|
||||
+43
-55
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2021 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,15 +17,16 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.render.shader;
|
||||
package com.seibel.lod.core.render.objects;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
|
||||
@@ -36,37 +37,57 @@ import com.seibel.lod.core.api.ClientApi;
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class LodShader
|
||||
public class Shader
|
||||
{
|
||||
/** OpenGL shader ID */
|
||||
public final int id;
|
||||
|
||||
|
||||
|
||||
/** Creates a shader with specified type. */
|
||||
public LodShader(int type)
|
||||
{
|
||||
id = GL20.glCreateShader(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Loads a shader from file.
|
||||
*
|
||||
/** Creates a shader with specified type.
|
||||
* @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
|
||||
* @param path File path of the shader
|
||||
* @param absoluteFilePath If false the file path is relative to the resource jar folder.
|
||||
* @throws Exception if the shader fails to compile
|
||||
* @throws RuntimeException if the shader fails to compile
|
||||
*/
|
||||
public static LodShader loadShader(int type, String path, boolean absoluteFilePath) throws Exception
|
||||
public Shader(int type, String path, boolean absoluteFilePath)
|
||||
{
|
||||
ClientApi.LOGGER.info("Loading shader at "+path);
|
||||
// Create an empty shader object
|
||||
id = GL32.glCreateShader(type);
|
||||
StringBuilder source = loadFile(path, absoluteFilePath);
|
||||
GL32.glShaderSource(id, source);
|
||||
|
||||
GL32.glCompileShader(id);
|
||||
// check if the shader compiled
|
||||
int status = GL32.glGetShaderi(id, GL32.GL_COMPILE_STATUS);
|
||||
if (status != GL32.GL_TRUE) {
|
||||
String message = "Shader compiler error. Details: "+GL32.glGetShaderInfoLog(id);
|
||||
free(); // important!
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
ClientApi.LOGGER.info("Shader at "+path+" loaded sucessfully.");
|
||||
}
|
||||
|
||||
// REMEMBER to always free the resource!
|
||||
public void free() {
|
||||
GL32.glDeleteShader(id);
|
||||
}
|
||||
|
||||
private StringBuilder loadFile(String path, boolean absoluteFilePath) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
// open the file
|
||||
InputStream in = absoluteFilePath ? new FileInputStream(path) : LodShader.class.getClassLoader().getResourceAsStream(path);
|
||||
InputStream in;
|
||||
if (absoluteFilePath) {
|
||||
// Throws FileNotFoundException
|
||||
in = new FileInputStream(path); // Note: this should use OS path seperator
|
||||
} else {
|
||||
in = Shader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/'
|
||||
if (in == null) {
|
||||
throw new FileNotFoundException("Shader file not found in resource: "+path);
|
||||
}
|
||||
}
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
|
||||
// read in the file
|
||||
@@ -76,41 +97,8 @@ public class LodShader
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ClientApi.LOGGER.error("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
|
||||
throw new RuntimeException("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
|
||||
}
|
||||
CharSequence shaderFileSource = stringBuilder.toString();
|
||||
|
||||
return createShader(type, shaderFileSource);
|
||||
return stringBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shader with the specified type and source.
|
||||
*
|
||||
* @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
|
||||
* @param source Source of the shader
|
||||
* @throws Exception if the shader fails to compile
|
||||
*/
|
||||
public static LodShader createShader(int type, CharSequence source) throws Exception
|
||||
{
|
||||
LodShader shader = new LodShader(type);
|
||||
GL20.glShaderSource(shader.id, source);
|
||||
shader.compile();
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the shader and checks it's status afterwards.
|
||||
* @throws Exception if the shader fails to compile
|
||||
*/
|
||||
public void compile() throws Exception
|
||||
{
|
||||
GL20.glCompileShader(id);
|
||||
|
||||
// check if the shader compiled
|
||||
int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS);
|
||||
if (status != GL20.GL_TRUE)
|
||||
throw new Exception(GL20.glGetShaderInfoLog(id));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.render.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.objects.math.Vec3d;
|
||||
import com.seibel.lod.core.objects.math.Vec3f;
|
||||
|
||||
|
||||
/**
|
||||
* This object holds the reference to a OpenGL shader program
|
||||
* and contains a few methods that can be used with OpenGL shader programs.
|
||||
* The reason for many of these simple wrapper methods is as reminders of what
|
||||
* can (and needs to be) done with a shader program.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-26-2021
|
||||
*/
|
||||
public class ShaderProgram
|
||||
{
|
||||
/** Stores the handle of the program. */
|
||||
public final int id;
|
||||
|
||||
// TODO: A better way to set the fragData output name
|
||||
/** Creates a shader program.
|
||||
* This will bind ShaderProgram */
|
||||
public ShaderProgram(String vert, String frag, String fragDataOutputName)
|
||||
{
|
||||
Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, vert, false);
|
||||
Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, frag, false);
|
||||
|
||||
id = GL32.glCreateProgram();
|
||||
|
||||
GL32.glAttachShader(this.id, vertShader.id);
|
||||
GL32.glAttachShader(this.id, fragShader.id);
|
||||
//GL32.glBindFragDataLocation(id, 0, fragDataOutputName);
|
||||
GL32.glLinkProgram(this.id);
|
||||
|
||||
vertShader.free(); // important!
|
||||
fragShader.free(); // important!
|
||||
|
||||
int status = GL32.glGetProgrami(this.id, GL32.GL_LINK_STATUS);
|
||||
if (status != GL32.GL_TRUE) {
|
||||
String message = "Shader Link Error. Details: "+GL32.glGetProgramInfoLog(this.id);
|
||||
free(); // important!
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
GL32.glUseProgram(id); // This HAVE to be a direct call to prevent calling the overloaded version
|
||||
}
|
||||
|
||||
/** This will bind ShaderProgram */
|
||||
public void bind()
|
||||
{
|
||||
GL32.glUseProgram(id);
|
||||
}
|
||||
/** This will unbind ShaderProgram */
|
||||
public void unbind() {
|
||||
GL32.glUseProgram(0);
|
||||
}
|
||||
|
||||
// REMEMBER to always free the resource!
|
||||
public void free()
|
||||
{
|
||||
GL32.glDeleteProgram(id);
|
||||
}
|
||||
|
||||
/** WARNING: Slow native call! Cache it if possible!
|
||||
* Gets the location of an attribute variable with specified name.
|
||||
* Calls GL20.glGetAttribLocation(id, name)
|
||||
*
|
||||
* @param name Attribute name
|
||||
* @throws RuntimeException if attribute not found
|
||||
* @return Location of the attribute
|
||||
*/
|
||||
public int getAttributeLocation(CharSequence name)
|
||||
{
|
||||
int i = GL32.glGetAttribLocation(id, name);
|
||||
if (i==-1) throw new RuntimeException("Attribute name not found: "+name);
|
||||
return i;
|
||||
}
|
||||
|
||||
/** WARNING: Slow native call! Cache it if possible!
|
||||
* Gets the location of a uniform variable with specified name.
|
||||
* Calls GL20.glGetUniformLocation(id, name)
|
||||
*
|
||||
* @param name Uniform name
|
||||
* @throws RuntimeException if uniform not found
|
||||
* @return Location of the Uniform
|
||||
*/
|
||||
public int getUniformLocation(CharSequence name)
|
||||
{
|
||||
int i = GL32.glGetUniformLocation(id, name);
|
||||
if (i==-1) throw new RuntimeException("Uniform name not found: "+name);
|
||||
return i;
|
||||
}
|
||||
|
||||
/** Requires ShaderProgram binded. */
|
||||
public void setUniform(int location, boolean value)
|
||||
{
|
||||
// This use -1 for false as that equals all one set
|
||||
GL32.glUniform1i(location, value ? 1 : 0);
|
||||
}
|
||||
|
||||
/** Requires ShaderProgram binded. */
|
||||
public void setUniform(int location, int value)
|
||||
{
|
||||
GL32.glUniform1i(location, value);
|
||||
}
|
||||
|
||||
/** Requires ShaderProgram binded. */
|
||||
public void setUniform(int location, float value)
|
||||
{
|
||||
GL32.glUniform1f(location, value);
|
||||
}
|
||||
|
||||
/** Requires ShaderProgram binded. */
|
||||
public void setUniform(int location, Vec3f value)
|
||||
{
|
||||
GL32.glUniform3f(location, value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
/** Requires ShaderProgram binded. */
|
||||
public void setUniform(int location, Vec3d value)
|
||||
{
|
||||
GL32.glUniform3f(location, (float) value.x, (float) value.y, (float) value.z);
|
||||
}
|
||||
|
||||
/** Requires ShaderProgram binded. */
|
||||
public void setUniform(int location, Mat4f value)
|
||||
{
|
||||
try (MemoryStack stack = MemoryStack.stackPush())
|
||||
{
|
||||
FloatBuffer buffer = stack.mallocFloat(4 * 4);
|
||||
value.store(buffer);
|
||||
GL32.glUniformMatrix4fv(location, false, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts the color's RGBA values into values between 0 and 1.
|
||||
* Requires ShaderProgram binded. */
|
||||
public void setUniform(int location, Color value)
|
||||
{
|
||||
GL32.glUniform4f(location, value.getRed() / 256.0f, value.getGreen() / 256.0f, value.getBlue() / 256.0f, value.getAlpha() / 256.0f);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2021 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.render.objects;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
public abstract class VertexAttribute {
|
||||
|
||||
public static final class VertexPointer {
|
||||
public final int elementCount;
|
||||
public final int glType;
|
||||
public final boolean normalized;
|
||||
public final int byteSize;
|
||||
public VertexPointer(int elementCount, int glType, boolean normalized, int byteSize) {
|
||||
this.elementCount = elementCount;
|
||||
this.glType = glType;
|
||||
this.normalized = normalized;
|
||||
this.byteSize = byteSize;
|
||||
}
|
||||
public static VertexPointer addFloatPointer(boolean normalized) {
|
||||
return new VertexPointer(1, GL32.GL_FLOAT, normalized, 4);
|
||||
}
|
||||
public static VertexPointer addVec2Pointer(boolean normalized) {
|
||||
return new VertexPointer(2, GL32.GL_FLOAT, normalized, 8);
|
||||
}
|
||||
public static VertexPointer addVec3Pointer(boolean normalized) {
|
||||
return new VertexPointer(3, GL32.GL_FLOAT, normalized, 12);
|
||||
}
|
||||
public static VertexPointer addVec4Pointer(boolean normalized) {
|
||||
return new VertexPointer(1, GL32.GL_FLOAT, normalized, 16);
|
||||
}
|
||||
public static VertexPointer addUnsignedBytePointer(boolean normalized) {
|
||||
return new VertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 1);
|
||||
}
|
||||
public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized) {
|
||||
return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, elementCount);
|
||||
}
|
||||
public static VertexPointer addIntPointer(boolean normalized) {
|
||||
return new VertexPointer(1, GL32.GL_INT, normalized, 4);
|
||||
}
|
||||
public static VertexPointer addIvec2Pointer(boolean normalized) {
|
||||
return new VertexPointer(2, GL32.GL_INT, normalized, 8);
|
||||
}
|
||||
public static VertexPointer addIvec3Pointer(boolean normalized) {
|
||||
return new VertexPointer(3, GL32.GL_INT, normalized, 12);
|
||||
}
|
||||
public static VertexPointer addIvec4Pointer(boolean normalized) {
|
||||
return new VertexPointer(4, GL32.GL_INT, normalized, 16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Stores the handle of the VertexAttribute. */
|
||||
public final int id;
|
||||
|
||||
// This will bind VertexAttribute
|
||||
protected VertexAttribute() {
|
||||
id = GL32.glGenVertexArrays();
|
||||
GL32.glBindVertexArray(id);
|
||||
}
|
||||
|
||||
// This will bind VertexAttribute
|
||||
public void bind() {
|
||||
GL32.glBindVertexArray(id);
|
||||
}
|
||||
|
||||
// This will unbind VertexAttribute
|
||||
public void unbind() {
|
||||
GL32.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// REMEMBER to always free the resource!
|
||||
public void free() {
|
||||
GL32.glDeleteVertexArrays(id);
|
||||
}
|
||||
|
||||
// Requires VertexAttribute binded, VertexBuffer binded
|
||||
public abstract void bindBufferToAllBindingPoint(int buffer);
|
||||
// Requires VertexAttribute binded, VertexBuffer binded
|
||||
public abstract void bindBufferToBindingPoint(int buffer, int bindingPoint);
|
||||
// Requires VertexAttribute binded
|
||||
public abstract void unbindBuffersFromAllBindingPoint();
|
||||
// Requires VertexAttribute binded
|
||||
public abstract void unbindBuffersFromBindingPoint(int bindingPoint);
|
||||
// Requires VertexAttribute binded
|
||||
public abstract void setVertexAttribute(int bindingPoint, int attributeIndex, VertexPointer attribute);
|
||||
// Requires VertexAttribute binded
|
||||
public abstract void completeAndCheck(int expectedStrideSize);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.seibel.lod.core.render.objects;
|
||||
|
||||
import org.lwjgl.opengl.GL43;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
|
||||
// In OpenGL 4.3 and later, Vertex Attribute got a make-over.
|
||||
// Now it provides support for buffer binding points natively.
|
||||
// This means that setting up the VAO just use ONE native call when
|
||||
// binding to a buffer.
|
||||
//
|
||||
// Since I no longer needs to implement binding points, I also no
|
||||
// longer needs to keep track of Pointers.
|
||||
|
||||
public final class VertexAttributePostGL43 extends VertexAttribute {
|
||||
|
||||
int numberOfBindingPoints = 0;
|
||||
int strideSize = 0;
|
||||
|
||||
// This will bind VertexAttribute
|
||||
public VertexAttributePostGL43() {
|
||||
super(); // also bind VertexAttribute
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded, VertexBuffer binded
|
||||
public void bindBufferToAllBindingPoint(int buffer) {
|
||||
for (int i=0; i<numberOfBindingPoints; i++)
|
||||
GL43.glBindVertexBuffer(i, buffer, 0, strideSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded, VertexBuffer binded
|
||||
public void bindBufferToBindingPoint(int buffer, int bindingPoint) {
|
||||
GL43.glBindVertexBuffer(bindingPoint, buffer, 0, strideSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void unbindBuffersFromAllBindingPoint() {
|
||||
for (int i=0; i<numberOfBindingPoints; i++)
|
||||
GL43.glBindVertexBuffer(i, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void unbindBuffersFromBindingPoint(int bindingPoint) {
|
||||
GL43.glBindVertexBuffer(bindingPoint, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void setVertexAttribute(int bindingPoint, int attributeIndex, VertexPointer attribute) {
|
||||
GL43.glVertexAttribFormat(attributeIndex, attribute.elementCount, attribute.glType,
|
||||
attribute.normalized, strideSize); // Here strideSize is new attrib offset
|
||||
strideSize += attribute.byteSize;
|
||||
if (numberOfBindingPoints <= bindingPoint) numberOfBindingPoints = bindingPoint+1;
|
||||
GL43.glVertexAttribBinding(attributeIndex, bindingPoint);
|
||||
GL43.glEnableVertexAttribArray(attributeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void completeAndCheck(int expectedStrideSize) {
|
||||
if (strideSize != expectedStrideSize) {
|
||||
ClientApi.LOGGER.error("Vertex Attribute calculated stride size " + strideSize +
|
||||
" does not match the provided expected stride size " + expectedStrideSize + "!");
|
||||
}
|
||||
ClientApi.LOGGER.info("Vertex Attribute (GL43+) completed. It contains "+numberOfBindingPoints
|
||||
+" binding points and a stride size of "+strideSize);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.seibel.lod.core.render.objects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
|
||||
|
||||
public final class VertexAttributePreGL43 extends VertexAttribute {
|
||||
|
||||
// I tried to use as much raw arrays as possible as those lookups
|
||||
// happens every frame, and the speed directly effects fps
|
||||
int strideSize = 0;
|
||||
int[][] bindingPointsToIndex;
|
||||
VertexPointer[] pointers;
|
||||
int[] pointersOffset;
|
||||
|
||||
|
||||
TreeMap<Integer, TreeSet<Integer>> bindingPointsToIndexBuilder;
|
||||
ArrayList<VertexPointer> pointersBuilder;
|
||||
|
||||
// This will bind VertexAttribute
|
||||
public VertexAttributePreGL43() {
|
||||
super(); // also bind VertexAttribute
|
||||
bindingPointsToIndexBuilder = new TreeMap<Integer, TreeSet<Integer>>();
|
||||
pointersBuilder = new ArrayList<VertexPointer>();
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded, VertexBuffer binded
|
||||
public void bindBufferToAllBindingPoint(int buffer) {
|
||||
for (int i=0; i<pointers.length; i++)
|
||||
GL32.glEnableVertexAttribArray(i);
|
||||
|
||||
for (int i=0; i< pointers.length; i++) {
|
||||
VertexPointer pointer = pointers[i];
|
||||
if (pointer==null) continue;
|
||||
GL32.glVertexAttribPointer(i, pointer.elementCount, pointer.glType,
|
||||
pointer.normalized, strideSize, pointersOffset[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded, VertexBuffer binded
|
||||
public void bindBufferToBindingPoint(int buffer, int bindingPoint) {
|
||||
int[] toBind = bindingPointsToIndex[bindingPoint];
|
||||
|
||||
for (int i=0; i<toBind.length; i++)
|
||||
GL32.glEnableVertexAttribArray(toBind[i]);
|
||||
|
||||
for (int i=0; i< toBind.length; i++) {
|
||||
VertexPointer pointer = pointers[toBind[i]];
|
||||
if (pointer==null) continue;
|
||||
GL32.glVertexAttribPointer(toBind[i], pointer.elementCount, pointer.glType,
|
||||
pointer.normalized, strideSize, pointersOffset[toBind[i]]);
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void unbindBuffersFromAllBindingPoint() {
|
||||
for (int i=0; i<pointers.length; i++)
|
||||
GL32.glDisableVertexAttribArray(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void unbindBuffersFromBindingPoint(int bindingPoint) {
|
||||
int[] toBind = bindingPointsToIndex[bindingPoint];
|
||||
|
||||
for (int i=0; i<toBind.length; i++)
|
||||
GL32.glDisableVertexAttribArray(toBind[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void setVertexAttribute(int bindingPoint, int attributeIndex, VertexPointer attribute) {
|
||||
TreeSet<Integer> intArray = bindingPointsToIndexBuilder.get(bindingPoint);
|
||||
if (intArray == null) {
|
||||
intArray = new TreeSet<Integer>();
|
||||
bindingPointsToIndexBuilder.put(bindingPoint, intArray);
|
||||
}
|
||||
intArray.add(attributeIndex);
|
||||
|
||||
while (pointersBuilder.size() <= attributeIndex) {
|
||||
// This is dumb, but ArrayList doesn't have a resize, And this code
|
||||
// should only be ran when it's building the Vertex Attribute anyways.
|
||||
pointersBuilder.add(null);
|
||||
}
|
||||
pointersBuilder.set(attributeIndex, attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
// Requires VertexAttribute binded
|
||||
public void completeAndCheck(int expectedStrideSize) {
|
||||
int maxBindPointNumber = bindingPointsToIndexBuilder.lastKey();
|
||||
bindingPointsToIndex = new int[maxBindPointNumber+1][];
|
||||
|
||||
bindingPointsToIndexBuilder.forEach((Integer i, TreeSet<Integer> set) -> {
|
||||
bindingPointsToIndex[i] = new int[set.size()];
|
||||
Iterator<Integer> iter = set.iterator();
|
||||
for (int j = 0; j<set.size(); j++) {
|
||||
bindingPointsToIndex[i][j] = iter.next();
|
||||
}
|
||||
});
|
||||
|
||||
pointers = pointersBuilder.toArray(new VertexPointer[pointersBuilder.size()]);
|
||||
pointersOffset = new int[pointers.length];
|
||||
pointersBuilder = null; // Release the builder
|
||||
bindingPointsToIndexBuilder = null; // Release the builder
|
||||
|
||||
// Check if all pointers are valid
|
||||
int currentOffset = 0;
|
||||
for (int i = 0; i < pointers.length; i++) {
|
||||
VertexPointer pointer = pointers[i];
|
||||
if (pointer == null) {
|
||||
ClientApi.LOGGER.warn("Vertex Attribute index "+i+" is not set! No index should be skipped normally!");
|
||||
continue;
|
||||
}
|
||||
pointersOffset[i] = currentOffset;
|
||||
currentOffset += pointer.byteSize;
|
||||
}
|
||||
if (currentOffset != expectedStrideSize)
|
||||
ClientApi.LOGGER.error("Vertex Attribute calculated stride size " + currentOffset +
|
||||
" does not match the provided expected stride size " + expectedStrideSize + "!");
|
||||
strideSize = currentOffset;
|
||||
ClientApi.LOGGER.info("Vertex Attribute (pre GL43) completed.");
|
||||
|
||||
// Debug logging
|
||||
ClientApi.LOGGER.info("Vertex Attribute Debug Data:");
|
||||
ClientApi.LOGGER.info("AttributeIndex: ElementCount, glType, normalized, strideSize, offset");
|
||||
|
||||
for (int i=0; i< pointers.length; i++) {
|
||||
VertexPointer pointer = pointers[i];
|
||||
if (pointer==null) {
|
||||
ClientApi.LOGGER.warn(i + ": Null!!!!");
|
||||
continue;
|
||||
}
|
||||
ClientApi.LOGGER.info(i + ": "+pointer.elementCount+", "+
|
||||
pointer.glType+", "+pointer.normalized+", "+strideSize+", "+pointersOffset[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.render.shader;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
|
||||
|
||||
/**
|
||||
* This object holds the reference to a OpenGL shader program
|
||||
* and contains a few methods that can be used with OpenGL shader programs.
|
||||
* The reason for many of these simple wrapper methods is as reminders of what
|
||||
* can (and needs to be) done with a shader program.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class LodShaderProgram
|
||||
{
|
||||
/** Stores the handle of the program. */
|
||||
public final int id;
|
||||
|
||||
/** Creates a shader program. */
|
||||
public LodShaderProgram()
|
||||
{
|
||||
id = GL20.glCreateProgram();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Calls GL20.glUseProgram(this.id) */
|
||||
public void use()
|
||||
{
|
||||
GL20.glUseProgram(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls GL20.glAttachShader(this.id, shader.id)
|
||||
*
|
||||
* @param shader Shader to get attached
|
||||
*/
|
||||
public void attachShader(LodShader shader)
|
||||
{
|
||||
GL20.glAttachShader(this.id, shader.id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Links the shader program to the current OpenGL context.
|
||||
* @throws Exception Exception if the program failed to link
|
||||
*/
|
||||
public void link() throws Exception
|
||||
{
|
||||
GL20.glLinkProgram(this.id);
|
||||
checkLinkStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the program was linked successfully.
|
||||
* @throws Exception if the program failed to link
|
||||
*/
|
||||
public void checkLinkStatus() throws Exception
|
||||
{
|
||||
int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS);
|
||||
if (status != GL20.GL_TRUE)
|
||||
throw new Exception(GL20.glGetProgramInfoLog(this.id));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the location of an attribute variable with specified name.
|
||||
* Calls GL20.glGetAttribLocation(id, name)
|
||||
*
|
||||
* @param name Attribute name
|
||||
*
|
||||
* @return Location of the attribute
|
||||
*/
|
||||
public int getAttributeLocation(CharSequence name)
|
||||
{
|
||||
return GL20.glGetAttribLocation(id, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls GL20.glEnableVertexAttribArray(location)
|
||||
*
|
||||
* @param location Location of the vertex attribute
|
||||
*/
|
||||
public void enableVertexAttribute(int location)
|
||||
{
|
||||
GL20.glEnableVertexAttribArray(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls GL20.glDisableVertexAttribArray(location)
|
||||
*
|
||||
* @param location Location of the vertex attribute
|
||||
*/
|
||||
public void disableVertexAttribute(int location)
|
||||
{
|
||||
GL20.glDisableVertexAttribArray(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the vertex attribute pointer.
|
||||
* Calls GL20.glVertexAttribPointer(...)
|
||||
*
|
||||
* @param location Location of the vertex attribute
|
||||
* @param size Number of values per vertex
|
||||
* @param stride Offset between consecutive generic vertex attributes in
|
||||
* bytes
|
||||
* @param offset Offset of the first component of the first generic vertex
|
||||
* attribute in bytes
|
||||
*/
|
||||
public void pointVertexAttribute(int location, int size, int stride, int offset)
|
||||
{
|
||||
GL20.glVertexAttribPointer(location, size, GL20.GL_FLOAT, false, stride, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of an uniform variable with specified name.
|
||||
* Calls GL20.glGetUniformLocation(id, name)
|
||||
*
|
||||
* @param name Uniform name
|
||||
*
|
||||
* @return -1 = error value, 0 = first value, 1 = second value, etc.
|
||||
*/
|
||||
public int getUniformLocation(CharSequence name)
|
||||
{
|
||||
return GL20.glGetUniformLocation(id, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the uniform variable for specified location.
|
||||
*
|
||||
* @param location Uniform location
|
||||
* @param value Value to set
|
||||
*/
|
||||
public void setUniform(int location, int value)
|
||||
{
|
||||
GL20.glUniform1i(location, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the uniform variable for specified location.
|
||||
*
|
||||
* @param location Uniform location
|
||||
* @param value Value to set
|
||||
*/
|
||||
public void setUniform(int location, Mat4f value)
|
||||
{
|
||||
try (MemoryStack stack = MemoryStack.stackPush())
|
||||
{
|
||||
FloatBuffer buffer = stack.mallocFloat(4 * 4);
|
||||
value.store(buffer);
|
||||
GL20.glUniformMatrix4fv(location, false, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import static com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactor
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
@@ -58,7 +59,8 @@ public class DataPointUtil
|
||||
//public final static int MIN_DEPTH = -64;
|
||||
//public final static int MIN_HEIGHT = -64;
|
||||
public final static int EMPTY_DATA = 0;
|
||||
public static int worldHeight = 256;
|
||||
public static final short VERTICAL_OFFSET = -64;
|
||||
public static int WORLD_HEIGHT = 1024;
|
||||
|
||||
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
|
||||
|
||||
@@ -101,6 +103,9 @@ public class DataPointUtil
|
||||
public final static long VOID_MASK = 1;
|
||||
public final static long EXISTENCE_MASK = 1;
|
||||
|
||||
public final static long HEIGHT_SHIFTED_MASK = HEIGHT_MASK << HEIGHT_SHIFT;
|
||||
public final static long DEPTH_SHIFTED_MASK = DEPTH_MASK << DEPTH_SHIFT;
|
||||
|
||||
|
||||
public static long createVoidDataPoint(int generationMode)
|
||||
{
|
||||
@@ -139,6 +144,12 @@ public class DataPointUtil
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
public static long shiftHeightAndDepth(long dataPoint, short offset) {
|
||||
long height = (dataPoint + (offset << HEIGHT_SHIFT)) & HEIGHT_SHIFTED_MASK;
|
||||
long depth = (dataPoint + (offset << DEPTH_SHIFT)) & DEPTH_SHIFTED_MASK;
|
||||
return dataPoint & ~(HEIGHT_SHIFTED_MASK | DEPTH_SHIFTED_MASK) | height | depth;
|
||||
}
|
||||
|
||||
public static short getHeight(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK);
|
||||
@@ -210,7 +221,8 @@ public class DataPointUtil
|
||||
|
||||
public static int getColor(long dataPoint)
|
||||
{
|
||||
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24));
|
||||
// TODO re-add transparency by replacing the color 255 with what is in comment
|
||||
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111) << 24);
|
||||
}
|
||||
|
||||
/** This is used to convert a dataPoint to string (useful for the print function) */
|
||||
@@ -267,13 +279,13 @@ public class DataPointUtil
|
||||
int size = dataToMerge.length / inputVerticalData;
|
||||
|
||||
// We initialize the arrays that are going to be used
|
||||
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight / 2 + 1) * 2);
|
||||
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((WORLD_HEIGHT + 1) * 2);
|
||||
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
|
||||
|
||||
|
||||
int genMode = DistanceGenerationMode.FULL.complexity;
|
||||
boolean allEmpty = true;
|
||||
boolean allVoid = true;
|
||||
boolean limited = false;
|
||||
boolean allDefault;
|
||||
long singleData;
|
||||
|
||||
@@ -287,115 +299,139 @@ public class DataPointUtil
|
||||
//We collect the indexes of the data, ordered by the depth
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
if (index == 0)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData))
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
genMode = Math.min(genMode, getGenerationMode(singleData));
|
||||
allEmpty = false;
|
||||
if (!isVoid(singleData))
|
||||
singleData = dataToMerge[dataIndex];
|
||||
if (doesItExist(singleData))
|
||||
{
|
||||
allVoid = false;
|
||||
depth = getDepth(singleData);
|
||||
height = getHeight(singleData);
|
||||
|
||||
int botPos = -1;
|
||||
int topPos = -1;
|
||||
//values fall in between and possibly require extension of array
|
||||
boolean botExtend = false;
|
||||
boolean topExtend = false;
|
||||
for (i = 0; i < count; i++)
|
||||
genMode = Math.min(genMode, getGenerationMode(singleData));
|
||||
allEmpty = false;
|
||||
if (!isVoid(singleData))
|
||||
{
|
||||
if (depth <= heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1])
|
||||
{
|
||||
botPos = i;
|
||||
break;
|
||||
}
|
||||
else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
{
|
||||
botPos = i;
|
||||
botExtend = true;
|
||||
break;
|
||||
}
|
||||
allVoid = false;
|
||||
count++;
|
||||
heightAndDepth[dataIndex * 2] = getHeight(singleData);
|
||||
heightAndDepth[dataIndex * 2 +1] = getDepth(singleData);
|
||||
}
|
||||
for (i = 0; i < count; i++)
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData))
|
||||
{
|
||||
genMode = Math.min(genMode, getGenerationMode(singleData));
|
||||
allEmpty = false;
|
||||
if (!isVoid(singleData))
|
||||
{
|
||||
if (height <= heightAndDepth[i * 2] && height >= heightAndDepth[i * 2 + 1])
|
||||
allVoid = false;
|
||||
depth = getDepth(singleData);
|
||||
height = getHeight(singleData);
|
||||
|
||||
int botPos = -1;
|
||||
int topPos = -1;
|
||||
//values fall in between and possibly require extension of array
|
||||
boolean botExtend = false;
|
||||
boolean topExtend = false;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
topPos = i;
|
||||
break;
|
||||
if (depth < heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1])
|
||||
{
|
||||
botPos = i;
|
||||
break;
|
||||
}
|
||||
else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth >= heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
{
|
||||
botPos = i;
|
||||
botExtend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (height < heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
topPos = i;
|
||||
topExtend = true;
|
||||
break;
|
||||
if (height <= heightAndDepth[i * 2] && height > heightAndDepth[i * 2 + 1])
|
||||
{
|
||||
topPos = i;
|
||||
break;
|
||||
}
|
||||
else if (height <= heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
{
|
||||
topPos = i;
|
||||
topExtend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (topPos == -1)
|
||||
{
|
||||
if (botPos == -1)
|
||||
if (topPos == -1)
|
||||
{
|
||||
//whole block falls above
|
||||
extendArray(heightAndDepth, 2, 0, 1, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count++;
|
||||
if (botPos == -1)
|
||||
{
|
||||
//whole block falls above
|
||||
extendArray(heightAndDepth, 2, 0, 1, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count++;
|
||||
}
|
||||
else if (!botExtend)
|
||||
{
|
||||
//only top falls above extending it there, while bottom is inside existing
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
count -= botPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//top falls between some blocks, extending those as well
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count -= botPos;
|
||||
}
|
||||
}
|
||||
else if (!botExtend)
|
||||
else if (!topExtend)
|
||||
{
|
||||
//only top falls above extending it there, while bottom is inside existing
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
count -= botPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//top falls between some blocks, extending those as well
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count -= botPos;
|
||||
}
|
||||
}
|
||||
else if (!topExtend)
|
||||
{
|
||||
if (!botExtend)
|
||||
//both top and bottom are within some exiting blocks, possibly merging them
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
else
|
||||
//top falls between some blocks, extending it there
|
||||
heightAndDepth[topPos * 2 + 1] = depth;
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!botExtend)
|
||||
{
|
||||
//only top is within some exiting block, extending it
|
||||
topPos++; //to make it easier
|
||||
heightAndDepth[topPos * 2] = height;
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
if (!botExtend)
|
||||
//both top and bottom are within some exiting blocks, possibly merging them
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
else
|
||||
//top falls between some blocks, extending it there
|
||||
heightAndDepth[topPos * 2 + 1] = depth;
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//both top and bottom are outside existing blocks
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
extendArray(heightAndDepth, 2, topPos + 1, 1, count);
|
||||
count++;
|
||||
heightAndDepth[topPos * 2 + 2] = height;
|
||||
heightAndDepth[topPos * 2 + 3] = depth;
|
||||
if (!botExtend)
|
||||
{
|
||||
//only top is within some exiting block, extending it
|
||||
topPos++; //to make it easier
|
||||
heightAndDepth[topPos * 2] = height;
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//both top and bottom are outside existing blocks
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
extendArray(heightAndDepth, 2, topPos + 1, 1, count);
|
||||
count++;
|
||||
heightAndDepth[topPos * 2 + 2] = height;
|
||||
heightAndDepth[topPos * 2 + 3] = depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +448,8 @@ public class DataPointUtil
|
||||
int j = 0;
|
||||
while (count > maxVerticalData)
|
||||
{
|
||||
ii = worldHeight;
|
||||
limited = true;
|
||||
ii = WORLD_HEIGHT;
|
||||
for (i = 0; i < count - 1; i++)
|
||||
{
|
||||
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii)
|
||||
@@ -431,88 +468,104 @@ public class DataPointUtil
|
||||
count--;
|
||||
}
|
||||
//As standard the vertical lods are ordered from top to bottom
|
||||
for (j = count - 1; j >= 0; j--)
|
||||
if (!limited && size == 1)
|
||||
{
|
||||
height = heightAndDepth[j * 2];
|
||||
depth = heightAndDepth[j * 2 + 1];
|
||||
|
||||
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
|
||||
break;
|
||||
|
||||
int numberOfChildren = 0;
|
||||
int tempAlpha = 0;
|
||||
int tempRed = 0;
|
||||
int tempGreen = 0;
|
||||
int tempBlue = 0;
|
||||
int tempLightBlock = 0;
|
||||
int tempLightSky = 0;
|
||||
byte tempGenMode = DistanceGenerationMode.FULL.complexity;
|
||||
allEmpty = true;
|
||||
allVoid = true;
|
||||
allDefault = true;
|
||||
long data = 0;
|
||||
|
||||
for (int index = 0; index < size; index++)
|
||||
for (j = 0; j < count; j++)
|
||||
dataPoint[j] = dataToMerge[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
height = heightAndDepth[j * 2];
|
||||
depth = heightAndDepth[j * 2 + 1];
|
||||
|
||||
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
|
||||
break;
|
||||
|
||||
int numberOfChildren = 0;
|
||||
int tempAlpha = 0;
|
||||
int tempRed = 0;
|
||||
int tempGreen = 0;
|
||||
int tempBlue = 0;
|
||||
int tempLightBlock = 0;
|
||||
int tempLightSky = 0;
|
||||
byte tempGenMode = DistanceGenerationMode.FULL.complexity;
|
||||
allEmpty = true;
|
||||
allVoid = true;
|
||||
allDefault = true;
|
||||
long data = 0;
|
||||
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData) && !isVoid(singleData))
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
|
||||
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|
||||
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData) && !isVoid(singleData))
|
||||
{
|
||||
if (getHeight(singleData) > getHeight(data))
|
||||
if ((depth <= getDepth(singleData) && getDepth(singleData) < height)
|
||||
|| (depth < getHeight(singleData) && getHeight(singleData) <= height))
|
||||
{
|
||||
data = singleData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!doesItExist(data))
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData];
|
||||
data = createVoidDataPoint(getGenerationMode(singleData));
|
||||
}
|
||||
|
||||
if (doesItExist(data))
|
||||
{
|
||||
allEmpty = false;
|
||||
if (!isVoid(data))
|
||||
{
|
||||
numberOfChildren++;
|
||||
allVoid = false;
|
||||
tempAlpha += getAlpha(data);
|
||||
tempRed += getRed(data);
|
||||
tempGreen += getGreen(data);
|
||||
tempBlue += getBlue(data);
|
||||
tempLightBlock += getLightBlock(data);
|
||||
tempLightSky += getLightSky(data);
|
||||
if (!getFlag(data))
|
||||
allDefault = false;
|
||||
}
|
||||
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!doesItExist(data))
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData];
|
||||
data = createVoidDataPoint(getGenerationMode(singleData));
|
||||
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
|
||||
}
|
||||
|
||||
if (doesItExist(data))
|
||||
{
|
||||
allEmpty = false;
|
||||
if (!isVoid(data))
|
||||
{
|
||||
numberOfChildren++;
|
||||
allVoid = false;
|
||||
tempAlpha += getAlpha(data);
|
||||
tempRed += getRed(data);
|
||||
tempGreen += getGreen(data);
|
||||
tempBlue += getBlue(data);
|
||||
tempLightBlock += getLightBlock(data);
|
||||
tempLightSky += getLightSky(data);
|
||||
if (!getFlag(data)) allDefault = false;
|
||||
}
|
||||
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
|
||||
}
|
||||
if (allEmpty)
|
||||
//no child has been initialized
|
||||
dataPoint[j] = EMPTY_DATA;
|
||||
else if (allVoid)
|
||||
//all the children are void
|
||||
dataPoint[j] = createVoidDataPoint(tempGenMode);
|
||||
else
|
||||
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
|
||||
}
|
||||
|
||||
if (allEmpty)
|
||||
//no child has been initialized
|
||||
dataPoint[j] = EMPTY_DATA;
|
||||
else if (allVoid)
|
||||
//all the children are void
|
||||
dataPoint[j] = createVoidDataPoint(tempGenMode);
|
||||
else
|
||||
{
|
||||
//we have at least 1 child
|
||||
tempAlpha = tempAlpha / numberOfChildren;
|
||||
tempRed = tempRed / numberOfChildren;
|
||||
tempGreen = tempGreen / numberOfChildren;
|
||||
tempBlue = tempBlue / numberOfChildren;
|
||||
tempLightBlock = tempLightBlock / numberOfChildren;
|
||||
tempLightSky = tempLightSky / numberOfChildren;
|
||||
dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
|
||||
{
|
||||
//we have at least 1 child
|
||||
if (size != 1)
|
||||
{
|
||||
tempAlpha = tempAlpha / numberOfChildren;
|
||||
tempRed = tempRed / numberOfChildren;
|
||||
tempGreen = tempGreen / numberOfChildren;
|
||||
tempBlue = tempBlue / numberOfChildren;
|
||||
tempLightBlock = tempLightBlock / numberOfChildren;
|
||||
tempLightSky = tempLightSky / numberOfChildren;
|
||||
}
|
||||
//data = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
|
||||
//if (j > 0 && getColor(data) == getColor(dataPoint[j]))
|
||||
//{
|
||||
// add simplification at the end due to color
|
||||
//}
|
||||
dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataPoint;
|
||||
|
||||
@@ -19,11 +19,10 @@
|
||||
|
||||
package com.seibel.lod.core.util;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.HorizontalQuality;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -33,13 +32,13 @@ import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
|
||||
public class DetailDistanceUtil
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final MinecraftRenderWrapper MC_RENDER = MinecraftRenderWrapper.INSTANCE;
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
private static final double genMultiplier = 1.0;
|
||||
private static final double treeGenMultiplier = 1.0;
|
||||
private static final double treeCutMultiplier = 1.0;
|
||||
private static byte minGenDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel;
|
||||
private static byte minDrawDetail = (byte) Math.max(CONFIG.client().graphics().quality().getDrawResolution().detailLevel, CONFIG.client().graphics().quality().getDrawResolution().detailLevel);
|
||||
private static byte minDrawDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel;
|
||||
private static final int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
|
||||
private static final int minDistance = 0;
|
||||
private static int minDetailDistance = (int) (MC_RENDER.getRenderDistance()*16 * 1.42f);
|
||||
@@ -79,7 +78,7 @@ public class DetailDistanceUtil
|
||||
if (CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality())
|
||||
return detail * 0x10000; //if you want more you are doing wrong
|
||||
|
||||
int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale().distanceUnit;
|
||||
int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale() * 16;
|
||||
if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST)
|
||||
return (detail * distanceUnit);
|
||||
else
|
||||
@@ -96,14 +95,14 @@ public class DetailDistanceUtil
|
||||
|
||||
public static byte baseInverseFunction(int distance, byte minDetail, boolean useRenderMinDistance)
|
||||
{
|
||||
int detail;
|
||||
byte detail;
|
||||
if (distance == 0
|
||||
|| (distance < minDetailDistance && useRenderMinDistance)
|
||||
|| CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality())
|
||||
return minDetail;
|
||||
int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale().distanceUnit;
|
||||
int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale() * 16;
|
||||
if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST)
|
||||
detail = (byte) distance / distanceUnit;
|
||||
detail = (byte) (distance / distanceUnit);
|
||||
else
|
||||
{
|
||||
double base = CONFIG.client().graphics().quality().getHorizontalQuality().quadraticBase;
|
||||
@@ -134,17 +133,21 @@ public class DetailDistanceUtil
|
||||
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail, true);
|
||||
}
|
||||
|
||||
|
||||
// NOTE: The recent LodWorldGenerator changes assumes that this value doesn't change with 'detail'.
|
||||
// If this is changed, LodWorldGenerator needs to be fixed!
|
||||
/*
|
||||
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
|
||||
{
|
||||
return CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
}
|
||||
}*/
|
||||
|
||||
public static byte getLodDrawDetail(int detail)
|
||||
public static byte getLodDrawDetail(byte detail)
|
||||
{
|
||||
if (detail < minDrawDetail)
|
||||
return minDrawDetail;
|
||||
else
|
||||
return (byte) detail;
|
||||
detail += minDrawDetail;
|
||||
if (detail > 10)
|
||||
detail = 10;
|
||||
return detail;
|
||||
}
|
||||
|
||||
public static HorizontalResolution getLodGenDetail(int detail)
|
||||
|
||||
@@ -26,7 +26,8 @@ import java.util.HashSet;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.enums.config.VanillaOverdraw;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.handlers.IReflectionHandler;
|
||||
import com.seibel.lod.core.objects.VertexOptimizer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.opengl.DefaultLodVertexFormats;
|
||||
@@ -35,23 +36,24 @@ import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
|
||||
|
||||
/**
|
||||
* This class holds methods and constants that may be used in multiple places.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public class LodUtil
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final MinecraftRenderWrapper MC_RENDER = MinecraftRenderWrapper.INSTANCE;
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
private static final IReflectionHandler REFLECTION_HANDLER = SingletonHandler.get(IReflectionHandler.class);
|
||||
|
||||
/**
|
||||
* Vanilla render distances less than or equal to this will not allow partial
|
||||
@@ -140,7 +142,7 @@ public class LodUtil
|
||||
public static final int MAX_ALLOCATABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
|
||||
|
||||
/** the format of data stored in the GPU buffers */
|
||||
public static final LodVertexFormat LOD_VERTEX_FORMAT = DefaultLodVertexFormats.POSITION_COLOR;
|
||||
public static final LodVertexFormat LOD_VERTEX_FORMAT = DefaultLodVertexFormats.POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT;
|
||||
|
||||
|
||||
|
||||
@@ -342,18 +344,17 @@ public class LodUtil
|
||||
return new HashSet<>();
|
||||
|
||||
case DYNAMIC:
|
||||
|
||||
if (chunkRenderDist > MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW
|
||||
&& chunkRenderDist <= MINIMUM_RENDER_DISTANCE_FOR_FAR_OVERDRAW)
|
||||
{
|
||||
// This is a small render distance (but greater than the minimum partial
|
||||
// distance), skip positions that are greater than 2/3 the render distance
|
||||
// This is a small render distance (but greater than the minimum partial distance)
|
||||
// skip positions that are greater than 2/3 the render distance
|
||||
skipRadius = (int) Math.ceil(chunkRenderDist * (2.0/3.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a large render distance. Skip positions that are greater than
|
||||
// 4/5ths the render distance
|
||||
// This is a large render distance.
|
||||
// Skip positions that are greater than 4/5ths the render distance
|
||||
skipRadius = (int) Math.ceil(chunkRenderDist * (4.0 / 5.0));
|
||||
}
|
||||
break;
|
||||
@@ -369,7 +370,7 @@ public class LodUtil
|
||||
|
||||
|
||||
// get the chunks that are going to be rendered by Minecraft
|
||||
HashSet<AbstractChunkPosWrapper> posToSkip = MC_RENDER.getRenderedChunks();
|
||||
HashSet<AbstractChunkPosWrapper> posToSkip = REFLECTION_HANDLER.sodiumPresent() ? MC_RENDER.getSodiumRenderedChunks() : MC_RENDER.getVanillaRenderedChunks();
|
||||
|
||||
|
||||
// remove everything outside the skipRadius,
|
||||
@@ -382,7 +383,9 @@ public class LodUtil
|
||||
{
|
||||
if (x <= centerChunk.getX() - skipRadius || x >= centerChunk.getX() + skipRadius
|
||||
|| z <= centerChunk.getZ() - skipRadius || z >= centerChunk.getZ() + skipRadius)
|
||||
{
|
||||
posToSkip.remove(FACTORY.createChunkPos(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,10 +406,10 @@ public class LodUtil
|
||||
return false;
|
||||
int tempX;
|
||||
int tempZ;
|
||||
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
|
||||
{
|
||||
tempX = x + Box.DIRECTION_NORMAL_MAP.get(lodDirection).x;
|
||||
tempZ = z + Box.DIRECTION_NORMAL_MAP.get(lodDirection).z;
|
||||
tempX = x + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).x;
|
||||
tempZ = z + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).z;
|
||||
if (vanillaRenderedChunks[x][z] || (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length)
|
||||
&& !vanillaRenderedChunks[tempX][tempZ]))
|
||||
return true;
|
||||
|
||||
@@ -40,7 +40,7 @@ public class SingletonHandler
|
||||
/**
|
||||
* Adds the given singleton so it can be referenced later.
|
||||
*
|
||||
* @param objectClass
|
||||
* @param interfaceClass
|
||||
* @param singletonReference
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.VertexOptimizer;
|
||||
|
||||
// FIXME: Nuke this whole thing and use ThreadLocal instead. And no more redundant get() please!
|
||||
|
||||
/**
|
||||
* Holds data used by specific threads so
|
||||
@@ -42,7 +44,6 @@ public class ThreadMapUtil
|
||||
public static final ConcurrentMap<String, long[][]> threadBuilderArrayMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[][]> threadBuilderVerticalArrayMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[]> threadVerticalAddDataMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, byte[][]> saveContainer = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, short[]> projectionArrayMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, short[]> heightAndDepthMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[]> singleDataToMergeMap = new ConcurrentHashMap<>();
|
||||
@@ -55,7 +56,7 @@ public class ThreadMapUtil
|
||||
|
||||
public static final ConcurrentMap<String, boolean[]> adjShadeDisabled = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, Map<LodDirection, long[]>> adjDataMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, Box> boxMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, VertexOptimizer> boxMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +66,7 @@ public class ThreadMapUtil
|
||||
if (!adjShadeDisabled.containsKey(Thread.currentThread().getName())
|
||||
|| (adjShadeDisabled.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
adjShadeDisabled.put(Thread.currentThread().getName(), new boolean[Box.DIRECTIONS.length]);
|
||||
adjShadeDisabled.put(Thread.currentThread().getName(), new boolean[VertexOptimizer.DIRECTIONS.length]);
|
||||
}
|
||||
Arrays.fill(adjShadeDisabled.get(Thread.currentThread().getName()), false);
|
||||
return adjShadeDisabled.get(Thread.currentThread().getName());
|
||||
@@ -82,24 +83,24 @@ public class ThreadMapUtil
|
||||
adjDataMap.put(Thread.currentThread().getName(), new HashMap<>());
|
||||
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new long[1]);
|
||||
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new long[1]);
|
||||
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
|
||||
adjDataMap.get(Thread.currentThread().getName()).put(lodDirection, new long[verticalData]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
|
||||
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(lodDirection), DataPointUtil.EMPTY_DATA);
|
||||
}
|
||||
return adjDataMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static Box getBox()
|
||||
public static VertexOptimizer getBox()
|
||||
{
|
||||
if (!boxMap.containsKey(Thread.currentThread().getName())
|
||||
|| (boxMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
boxMap.put(Thread.currentThread().getName(), new Box());
|
||||
boxMap.put(Thread.currentThread().getName(), new VertexOptimizer());
|
||||
}
|
||||
boxMap.get(Thread.currentThread().getName()).reset();
|
||||
return boxMap.get(Thread.currentThread().getName());
|
||||
@@ -117,7 +118,8 @@ public class ThreadMapUtil
|
||||
//________________________//
|
||||
|
||||
|
||||
|
||||
|
||||
//TODO: Maybe use actual valid total world height instead of always assuming the worse and alloc 1024 blocks.
|
||||
/** returns the array filled with 0's */
|
||||
public static long[] getBuilderVerticalArray(int detailLevel)
|
||||
{
|
||||
@@ -128,7 +130,7 @@ public class ThreadMapUtil
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
size = 1 << i;
|
||||
array[i] = new long[size * size * (DataPointUtil.worldHeight / 2 + 1)];
|
||||
array[i] = new long[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
|
||||
}
|
||||
threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
@@ -136,41 +138,22 @@ public class ThreadMapUtil
|
||||
return threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel];
|
||||
}
|
||||
|
||||
/** returns the array NOT cleared every time */
|
||||
public static byte[] getSaveContainer(int detailLevel)
|
||||
{
|
||||
if (!saveContainer.containsKey(Thread.currentThread().getName()) || (saveContainer.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
byte[][] array = new byte[LodUtil.DETAIL_OPTIONS][];
|
||||
int size = 1;
|
||||
for (int i = LodUtil.DETAIL_OPTIONS - 1; i >= 0; i--)
|
||||
{
|
||||
array[i] = new byte[2 + 8 * size * size * DetailDistanceUtil.getMaxVerticalData(i)];
|
||||
size = size << 1;
|
||||
}
|
||||
saveContainer.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
//Arrays.fill(threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel], 0);
|
||||
return saveContainer.get(Thread.currentThread().getName())[detailLevel];
|
||||
}
|
||||
|
||||
|
||||
/** returns the array filled with 0's */
|
||||
public static long[] getVerticalDataArray(int arrayLength)
|
||||
{
|
||||
if (!threadVerticalAddDataMap.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMap.get(Thread.currentThread().getName()) == null))
|
||||
long[] array = threadVerticalAddDataMap.get(Thread.currentThread().getName());
|
||||
if (array == null || array.length != arrayLength)
|
||||
{
|
||||
threadVerticalAddDataMap.put(Thread.currentThread().getName(), new long[arrayLength]);
|
||||
array = new long[arrayLength];
|
||||
threadVerticalAddDataMap.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
else
|
||||
{
|
||||
Arrays.fill(threadVerticalAddDataMap.get(Thread.currentThread().getName()), 0);
|
||||
}
|
||||
return threadVerticalAddDataMap.get(Thread.currentThread().getName());
|
||||
Arrays.fill(array, 0);
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//FIXME: If the arrayLength change, this may return incorrect sized array
|
||||
/** returns the array NOT cleared every time */
|
||||
public static short[] getHeightAndDepth(int arrayLength)
|
||||
{
|
||||
@@ -185,18 +168,19 @@ public class ThreadMapUtil
|
||||
/** returns the array filled with 0's */
|
||||
public static long[] getVerticalUpdateArray(int detailLevel)
|
||||
{
|
||||
if (!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null))
|
||||
long[][] arrays = verticalUpdate.get(Thread.currentThread().getName());
|
||||
if (arrays == null)
|
||||
{
|
||||
long[][] array = new long[LodUtil.DETAIL_OPTIONS][];
|
||||
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
|
||||
array[i] = new long[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
|
||||
verticalUpdate.put(Thread.currentThread().getName(), array);
|
||||
arrays = new long[LodUtil.DETAIL_OPTIONS][];
|
||||
verticalUpdate.put(Thread.currentThread().getName(), arrays);
|
||||
}
|
||||
long[] array = arrays[detailLevel];
|
||||
int arrayLength = DetailDistanceUtil.getMaxVerticalData(detailLevel) * 4;
|
||||
if (array == null || array.length != arrayLength)
|
||||
array = new long[arrayLength];
|
||||
else
|
||||
{
|
||||
Arrays.fill(verticalUpdate.get(Thread.currentThread().getName())[detailLevel], 0);
|
||||
}
|
||||
return verticalUpdate.get(Thread.currentThread().getName())[detailLevel];
|
||||
Arrays.fill(array, 0);
|
||||
return array;
|
||||
}
|
||||
|
||||
/** clears all arrays so they will have to be rebuilt */
|
||||
@@ -209,7 +193,6 @@ public class ThreadMapUtil
|
||||
threadBuilderArrayMap.clear();
|
||||
threadBuilderVerticalArrayMap.clear();
|
||||
threadVerticalAddDataMap.clear();
|
||||
saveContainer.clear();
|
||||
projectionArrayMap.clear();
|
||||
heightAndDepthMap.clear();
|
||||
singleDataToMergeMap.clear();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.seibel.lod.core.wrapperInterfaces;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
|
||||
/**
|
||||
* A singleton that contains variables specific to each version of Minecraft
|
||||
* which can be used to change how DH-Core runs.
|
||||
* For example: After MC 1.17 blocks can be negative, which changes how we generate LODs.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 12-11-2021
|
||||
*/
|
||||
public interface IVersionConstants
|
||||
{
|
||||
/** @returns the minimum height blocks can be generated */
|
||||
int getMinimumWorldHeight();
|
||||
|
||||
|
||||
/**
|
||||
* @Returns True if the given DistanceGenerationMode can be run on our own thread. <br>
|
||||
* False if the generation must be run on Minecraft's server thread.
|
||||
*/
|
||||
boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode);
|
||||
|
||||
/**
|
||||
* @Returns the number of generations call per thread.
|
||||
*/
|
||||
default int getWorldGenerationCountPerThread() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,25 +24,37 @@ import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractExperimentalWorldGeneratorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
/**
|
||||
* This handles creating abstract wrapper objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-18-2021
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public interface IWrapperFactory
|
||||
{
|
||||
public AbstractBlockPosWrapper createBlockPos();
|
||||
public AbstractBlockPosWrapper createBlockPos(int x, int y, int z);
|
||||
AbstractBlockPosWrapper createBlockPos();
|
||||
AbstractBlockPosWrapper createBlockPos(int x, int y, int z);
|
||||
|
||||
|
||||
public AbstractChunkPosWrapper createChunkPos();
|
||||
public AbstractChunkPosWrapper createChunkPos(int x, int z);
|
||||
public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos);
|
||||
public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos);
|
||||
AbstractChunkPosWrapper createChunkPos();
|
||||
public default AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined)
|
||||
{
|
||||
int x = (int) (xAndZPositionCombined & Integer.MAX_VALUE);
|
||||
int z = (int) (xAndZPositionCombined >> Long.SIZE / 2) & Integer.MAX_VALUE;
|
||||
|
||||
return createChunkPos(x, z);
|
||||
}
|
||||
AbstractChunkPosWrapper createChunkPos(int x, int z);
|
||||
AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos);
|
||||
AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
|
||||
public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper);
|
||||
AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper);
|
||||
// Return null to signal that there is no AbstractWorldGenerator
|
||||
public default AbstractExperimentalWorldGeneratorWrapper createExperimentalWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ package com.seibel.lod.core.wrapperInterfaces.block;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
|
||||
/**
|
||||
* BlockPos needs to be abstract instead of a interfaces
|
||||
* BlockPos needs to be abstract instead of an interfaces
|
||||
* so that we can define its constructors.
|
||||
*
|
||||
* @author James Seibel
|
||||
|
||||
+1
-1
@@ -30,6 +30,6 @@ package com.seibel.lod.core.wrapperInterfaces.block;
|
||||
public interface IBlockColorSingletonWrapper
|
||||
{
|
||||
/** @returns the base color of water (grey) */
|
||||
public IBlockColorWrapper getWaterColor();
|
||||
IBlockColorWrapper getWaterColor();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,22 +29,24 @@ public interface IBlockColorWrapper
|
||||
//Colors getters//
|
||||
//--------------//
|
||||
|
||||
public boolean hasColor();
|
||||
boolean hasColor();
|
||||
|
||||
public int getColor();
|
||||
String getName();
|
||||
|
||||
int getColor();
|
||||
|
||||
|
||||
//------------//
|
||||
//Tint getters//
|
||||
//------------//
|
||||
|
||||
public boolean hasTint();
|
||||
boolean hasTint();
|
||||
|
||||
public boolean hasGrassTint();
|
||||
boolean hasGrassTint();
|
||||
|
||||
public boolean hasFolliageTint();
|
||||
boolean hasFolliageTint();
|
||||
|
||||
public boolean hasWaterTint();
|
||||
boolean hasWaterTint();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -25,15 +25,15 @@ package com.seibel.lod.core.wrapperInterfaces.block;
|
||||
*/
|
||||
public interface IBlockShapeWrapper
|
||||
{
|
||||
public boolean ofBlockToAvoid();
|
||||
boolean ofBlockToAvoid();
|
||||
|
||||
//-----------------//
|
||||
//Avoidance getters//
|
||||
//-----------------//
|
||||
|
||||
public boolean isNonFull();
|
||||
boolean isNonFull();
|
||||
|
||||
public boolean hasNoCollision();
|
||||
boolean hasNoCollision();
|
||||
|
||||
public boolean isToAvoid();
|
||||
boolean isToAvoid();
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
|
||||
/**
|
||||
* This is abstract instead of a interface so
|
||||
* This is abstract instead of an interface, so
|
||||
* we can define its constructors.
|
||||
*
|
||||
* @author James Seibel
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.seibel.lod.core.wrapperInterfaces.chunk;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
|
||||
import com.seibel.lod.forge.wrappers.world.BiomeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
@@ -30,23 +30,29 @@ import com.seibel.lod.forge.wrappers.world.BiomeWrapper;
|
||||
*/
|
||||
public interface IChunkWrapper
|
||||
{
|
||||
public int getHeight();
|
||||
int getHeight();
|
||||
|
||||
public boolean isPositionInWater(AbstractBlockPosWrapper blockPos);
|
||||
boolean isPositionInWater(int x, int y, int z);
|
||||
|
||||
public int getHeightMapValue(int xRel, int zRel);
|
||||
int getHeightMapValue(int xRel, int zRel);
|
||||
|
||||
public BiomeWrapper getBiome(int xRel, int yAbs, int zRel);
|
||||
IBiomeWrapper getBiome(int x, int y, int z);
|
||||
IBlockColorWrapper getBlockColorWrapper(int x, int y, int z);
|
||||
IBlockShapeWrapper getBlockShapeWrapper(int x, int y, int z);
|
||||
|
||||
public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos);
|
||||
int getChunkPosX();
|
||||
int getChunkPosZ();
|
||||
int getRegionPosX();
|
||||
int getRegionPosZ();
|
||||
int getMaxY(int x, int z);
|
||||
int getMaxX();
|
||||
int getMaxZ();
|
||||
int getMinX();
|
||||
int getMinZ();
|
||||
|
||||
public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos);
|
||||
boolean isLightCorrect();
|
||||
|
||||
public AbstractChunkPosWrapper getPos();
|
||||
boolean isWaterLogged(int x, int y, int z);
|
||||
|
||||
public boolean isLightCorrect();
|
||||
|
||||
public boolean isWaterLogged(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public int getEmittedBrightness(AbstractBlockPosWrapper blockPos);
|
||||
int getEmittedBrightness(int x, int y, int z);
|
||||
}
|
||||
|
||||
+269
-182
@@ -26,15 +26,15 @@ import com.seibel.lod.core.enums.config.GenerationPriority;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.config.HorizontalQuality;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.enums.config.HorizontalScale;
|
||||
import com.seibel.lod.core.enums.config.LodTemplate;
|
||||
import com.seibel.lod.core.enums.config.VanillaOverdraw;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogColorMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawOverride;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
import com.seibel.lod.core.objects.MinDefaultMax;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
|
||||
|
||||
/**
|
||||
* This holds the config defaults, setters/getters
|
||||
@@ -42,192 +42,196 @@ import com.seibel.lod.core.util.LodUtil;
|
||||
* the options that should be implemented in a configWrapperSingleton.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public interface ILodConfigWrapperSingleton
|
||||
{
|
||||
public IClient client();
|
||||
IClient client();
|
||||
|
||||
|
||||
public interface IClient
|
||||
interface IClient
|
||||
{
|
||||
public IGraphics graphics();
|
||||
public IWorldGenerator worldGenerator();
|
||||
public IAdvanced advanced();
|
||||
IGraphics graphics();
|
||||
IWorldGenerator worldGenerator();
|
||||
IAdvanced advanced();
|
||||
|
||||
|
||||
//==================//
|
||||
// Graphics Configs //
|
||||
//==================//
|
||||
public interface IGraphics
|
||||
interface IGraphics
|
||||
{
|
||||
public static final String DESC = "These settings control how the mod will look in game";
|
||||
String DESC = "These settings control how the mod will look in game";
|
||||
|
||||
public IQuality quality();
|
||||
public IFogQuality fogQuality();
|
||||
public IAdvancedGraphics advancedGraphics();
|
||||
IQuality quality();
|
||||
IFogQuality fogQuality();
|
||||
IAdvancedGraphics advancedGraphics();
|
||||
|
||||
|
||||
public interface IQuality
|
||||
interface IQuality
|
||||
{
|
||||
public static final String DESC = "These settings control how detailed the fake chunks will be.";
|
||||
String DESC = "These settings control how detailed the fake chunks will be.";
|
||||
|
||||
HorizontalResolution DRAW_RESOLUTION_DEFAULT = HorizontalResolution.BLOCK;
|
||||
public static final String DRAW_RESOLUTION_DESC = ""
|
||||
String DRAW_RESOLUTION_DESC = ""
|
||||
+ " What is the maximum detail fake chunks should be drawn at? \n"
|
||||
+ " This setting will only affect closer chunks.\n"
|
||||
+ " Higher settings will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n";
|
||||
public HorizontalResolution getDrawResolution();
|
||||
public void setDrawResolution(HorizontalResolution newHorizontalResolution);
|
||||
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk (width of one block). \n"
|
||||
+ "\n"
|
||||
+ " Lowest Quality: " + HorizontalResolution.CHUNK
|
||||
+ " Highest Quality: " + HorizontalResolution.BLOCK;
|
||||
HorizontalResolution getDrawResolution();
|
||||
void setDrawResolution(HorizontalResolution newHorizontalResolution);
|
||||
|
||||
MinDefaultMax<Integer> LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX = new MinDefaultMax<Integer>(16, 64, 1024);
|
||||
String LOD_CHUNK_RENDER_DISTANCE_DESC = ""
|
||||
+ " The mod's render distance, measured in chunks. \n";
|
||||
public int getLodChunkRenderDistance();
|
||||
public void setLodChunkRenderDistance(int newLodChunkRenderDistance);
|
||||
+ " The radius of the mod's render distance. (measured in chunks) \n";
|
||||
int getLodChunkRenderDistance();
|
||||
void setLodChunkRenderDistance(int newLodChunkRenderDistance);
|
||||
|
||||
VerticalQuality VERTICAL_QUALITY_DEFAULT = VerticalQuality.MEDIUM;
|
||||
String VERTICAL_QUALITY_DESC = ""
|
||||
+ " This indicates how detailed fake chunks will represent \n"
|
||||
+ " overhangs, caves, floating islands, ect. \n"
|
||||
+ " Higher options will use more memory and increase GPU usage. \n"
|
||||
+ " Higher options will make the world more accurate, but"
|
||||
+ " will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ " " + VerticalQuality.LOW + ": uses at max 2 columns per position. \n"
|
||||
+ " " + VerticalQuality.MEDIUM + ": uses at max 4 columns per position. \n"
|
||||
+ " " + VerticalQuality.HIGH + ": uses at max 8 columns per position. \n";
|
||||
public VerticalQuality getVerticalQuality();
|
||||
public void setVerticalQuality(VerticalQuality newVerticalQuality);
|
||||
+ " " + VerticalQuality.HIGH + ": uses at max 8 columns per position. \n"
|
||||
+ "\n"
|
||||
+ " Lowest Quality: " + VerticalQuality.LOW
|
||||
+ " Highest Quality: " + VerticalQuality.HIGH;
|
||||
VerticalQuality getVerticalQuality();
|
||||
void setVerticalQuality(VerticalQuality newVerticalQuality);
|
||||
|
||||
HorizontalScale HORIZONTAL_SCALE_DEFAULT = HorizontalScale.MEDIUM;
|
||||
MinDefaultMax<Integer> HORIZONTAL_SCALE_MIN_DEFAULT_MAX = new MinDefaultMax<Integer>(2, 8, 32);
|
||||
String HORIZONTAL_SCALE_DESC = ""
|
||||
+ " This indicates how quickly fake chunks drop off in quality. \n"
|
||||
+ " " + HorizontalScale.LOW + ": quality drops every " + HorizontalScale.LOW.distanceUnit / 16 + " chunks. \n"
|
||||
+ " " + HorizontalScale.MEDIUM + ": quality drops every " + HorizontalScale.MEDIUM.distanceUnit / 16 + " chunks. \n"
|
||||
+ " " + HorizontalScale.HIGH + ": quality drops every " + HorizontalScale.HIGH.distanceUnit / 16 + " chunks. \n";
|
||||
public HorizontalScale getHorizontalScale();
|
||||
public void setHorizontalScale(HorizontalScale newHorizontalScale);
|
||||
+ " This indicates how quickly fake chunks decrease in quality the further away they are. \n"
|
||||
+ " Higher settings will render higher quality fake chunks farther away, \n"
|
||||
+ " but will increase memory and GPU usage.";
|
||||
int getHorizontalScale();
|
||||
void setHorizontalScale(int newHorizontalScale);
|
||||
|
||||
HorizontalQuality HORIZONTAL_QUALITY_DEFAULT = HorizontalQuality.MEDIUM;
|
||||
String HORIZONTAL_QUALITY_DESC = ""
|
||||
+ " This indicates the exponential base of the quadratic drop-off \n"
|
||||
+ " " + HorizontalQuality.LOWEST + ": base " + HorizontalQuality.LOWEST.quadraticBase + ". \n"
|
||||
+ " " + HorizontalQuality.LOW + ": base " + HorizontalQuality.LOW.quadraticBase + ". \n"
|
||||
+ " " + HorizontalQuality.MEDIUM + ": base " + HorizontalQuality.MEDIUM.quadraticBase + ". \n"
|
||||
+ " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \n";
|
||||
public HorizontalQuality getHorizontalQuality();
|
||||
public void setHorizontalQuality(HorizontalQuality newHorizontalQuality);
|
||||
+ " This indicates how much farther away each drop in quality is. \n"
|
||||
+ "\n"
|
||||
+ " " + HorizontalQuality.LOWEST + ": each drop in quality is the same distance away. \n"
|
||||
+ " " + HorizontalQuality.LOW + ": each drop in quality is " + HorizontalQuality.LOW.quadraticBase + " times farther away. \n"
|
||||
+ " " + HorizontalQuality.MEDIUM + ": each drop in quality is " + HorizontalQuality.MEDIUM.quadraticBase + " times farther away. \n"
|
||||
+ " " + HorizontalQuality.HIGH + ": each drop in quality is " + HorizontalQuality.HIGH.quadraticBase + " times farther away. \n"
|
||||
+ "\n"
|
||||
+ " Lowest Quality: " + HorizontalQuality.LOWEST
|
||||
+ " Highest Quality: " + HorizontalQuality.HIGH;
|
||||
HorizontalQuality getHorizontalQuality();
|
||||
void setHorizontalQuality(HorizontalQuality newHorizontalQuality);
|
||||
}
|
||||
|
||||
public interface IFogQuality
|
||||
interface IFogQuality
|
||||
{
|
||||
String DESC = "These settings control the fog quality.";
|
||||
|
||||
FogDistance FOG_DISTANCE_DEFAULT = FogDistance.FAR;
|
||||
String FOG_DISTANCE_DESC = ""
|
||||
+ " At what distance should Fog be drawn on the fake chunks? \n"
|
||||
+ " If the fog cuts off abruptly or you are using Optifine's \"fast\" fog option \n"
|
||||
+ " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n";
|
||||
public FogDistance getFogDistance();
|
||||
public void setFogDistance(FogDistance newFogDistance);
|
||||
+ "\n"
|
||||
+ " This setting shouldn't affect performance.";
|
||||
FogDistance getFogDistance();
|
||||
void setFogDistance(FogDistance newFogDistance);
|
||||
|
||||
FogDrawOverride FOG_DRAW_OVERRIDE_DEFAULT = FogDrawOverride.FANCY;
|
||||
String FOG_DRAW_OVERRIDE_DESC = ""
|
||||
FogDrawMode FOG_DRAW_MODE_DEFAULT = FogDrawMode.FOG_ENABLED;
|
||||
String FOG_DRAW_MODE_DESC = ""
|
||||
+ " When should fog be drawn? \n"
|
||||
+ " " + FogDrawOverride.OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.FANCY + ". \n"
|
||||
+ " " + FogDrawOverride.NO_FOG + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.FAST + ": Always draw fast fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n";
|
||||
public FogDrawOverride getFogDrawOverride();
|
||||
public void setFogDrawOverride(FogDrawOverride newFogDrawOverride);
|
||||
+ "\n"
|
||||
+ " " + FogDrawMode.USE_OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawMode.FOG_ENABLED + ". \n"
|
||||
+ " " + FogDrawMode.FOG_ENABLED + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawMode.FOG_DISABLED + ": Always draw fast fog on the LODs \n"
|
||||
+ "\n"
|
||||
+ " Disabling fog will improve GPU performance.";
|
||||
FogDrawMode getFogDrawMode();
|
||||
void setFogDrawMode(FogDrawMode newFogDrawMode);
|
||||
|
||||
FogColorMode FOG_COLOR_MODE_DEFAULT = FogColorMode.USE_WORLD_FOG_COLOR;
|
||||
String FOG_COLOR_MODE_DESC = ""
|
||||
+ " What color should fog use? \n"
|
||||
+ "\n"
|
||||
+ " " + FogColorMode.USE_WORLD_FOG_COLOR + ": Use the world's fog color. \n"
|
||||
+ " " + FogColorMode.USE_SKY_COLOR + ": Use the sky's color. \n"
|
||||
+ "\n"
|
||||
+ " This setting doesn't affect performance.";
|
||||
FogColorMode getFogColorMode();
|
||||
void setFogColorMode(FogColorMode newFogColorMode);
|
||||
|
||||
boolean DISABLE_VANILLA_FOG_DEFAULT = false;
|
||||
String DISABLE_VANILLA_FOG_DESC = ""
|
||||
+ " If true disable Minecraft's fog. \n\n"
|
||||
+ ""
|
||||
+ " Experimental! May cause issues with Sodium. \n\n"
|
||||
+ ""
|
||||
+ " Unlike Optifine or Sodium's fog disabling option this won't change \n"
|
||||
+ " performance (we don't actually disable the fog, we just tell it to render a infinite distance away). \n"
|
||||
+ " May or may not play nice with other mods that edit fog. \n";
|
||||
public boolean getDisableVanillaFog();
|
||||
public void setDisableVanillaFog(boolean newDisableVanillaFog);
|
||||
+ " If true disable Minecraft's fog. \n"
|
||||
+ "\n"
|
||||
+ " Experimental! Will cause issues with Sodium and \n"
|
||||
+ " may not play nice with other mods that edit fog. \n";
|
||||
boolean getDisableVanillaFog();
|
||||
void setDisableVanillaFog(boolean newDisableVanillaFog);
|
||||
}
|
||||
|
||||
public interface IAdvancedGraphics
|
||||
interface IAdvancedGraphics
|
||||
{
|
||||
String DESC = "Graphics options that are a bit more technical.";
|
||||
|
||||
LodTemplate LOD_TEMPLATE_DEFAULT = LodTemplate.CUBIC;
|
||||
String LOD_TEMPLATE_DESC = ""
|
||||
+ " How should the LODs be drawn? \n"
|
||||
+ " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n"
|
||||
+ " \n"
|
||||
+ " " + LodTemplate.CUBIC + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
|
||||
+ " " + LodTemplate.TRIANGULAR + ": LOD Chunks smoothly transition between other. \n"
|
||||
+ " " + LodTemplate.DYNAMIC + ": LOD Chunks smoothly transition between each other, \n"
|
||||
+ " " + " unless a neighboring chunk is at a significantly different height. \n";
|
||||
public LodTemplate getLodTemplate();
|
||||
public void setLodTemplate(LodTemplate newLodTemplate);
|
||||
|
||||
boolean DISABLE_DIRECTIONAL_CULLING_DEFAULT = false;
|
||||
String DISABLE_DIRECTIONAL_CULLING_DESC = ""
|
||||
+ " If false fake chunks behind the player's camera \n"
|
||||
+ " aren't drawn, increasing performance. \n\n"
|
||||
+ ""
|
||||
+ " aren't drawn, increasing GPU performance. \n"
|
||||
+ "\n"
|
||||
+ " If true all LODs are drawn, even those behind \n"
|
||||
+ " the player's camera, decreasing performance. \n\n"
|
||||
+ ""
|
||||
+ " Disable this if you see LODs disappearing. \n"
|
||||
+ " (Which may happen if you are using a camera mod) \n";
|
||||
public boolean getDisableDirectionalCulling();
|
||||
public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling);
|
||||
+ " the player's camera, decreasing GPU performance. \n"
|
||||
+ "\n"
|
||||
+ " Disable this if you see LODs disappearing at the corners of your vision. \n";
|
||||
boolean getDisableDirectionalCulling();
|
||||
void setDisableDirectionalCulling(boolean newDisableDirectionalCulling);
|
||||
|
||||
boolean ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT = false;
|
||||
String ALWAYS_DRAW_AT_MAD_QUALITY_DESC = ""
|
||||
+ " Disable quality falloff, \n"
|
||||
+ " all fake chunks will be drawn at the highest \n"
|
||||
+ " available detail level. \n\n"
|
||||
+ " "
|
||||
+ " available detail level. \n"
|
||||
+ "\n"
|
||||
+ " WARNING: \n"
|
||||
+ " This could cause a Out Of Memory crash on render \n"
|
||||
+ " distances higher than 128 \n";
|
||||
public boolean getAlwaysDrawAtMaxQuality();
|
||||
public void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality);
|
||||
+ " This could cause an Out Of Memory crash when using render \n"
|
||||
+ " distances higher than 128 and will drastically increase GPU usage. \n";
|
||||
boolean getAlwaysDrawAtMaxQuality();
|
||||
void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality);
|
||||
|
||||
VanillaOverdraw VANILLA_OVERDRAW_DEFAULT = VanillaOverdraw.DYNAMIC;
|
||||
String VANILLA_OVERDRAW_DESC = ""
|
||||
+ " How often should LODs be drawn on top of regular chunks? \n"
|
||||
+ " HALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves. \n\n"
|
||||
+ " HALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves. \n"
|
||||
+ "\n"
|
||||
+ " " + VanillaOverdraw.NEVER + ": LODs won't render on top of vanilla chunks. \n"
|
||||
+ " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks preventing only some holes in the world. \n"
|
||||
+ " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks, preventing some holes in the world. \n"
|
||||
+ " " + VanillaOverdraw.DYNAMIC + ": LODs will render on top of distant vanilla chunks to hide delayed loading. \n"
|
||||
+ " " + " More effective on higher render distances. \n"
|
||||
+ " " + " For vanilla render distances less than or equal to " + LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW + " \n"
|
||||
+ " " + " " + VanillaOverdraw.NEVER + " or " + VanillaOverdraw.ALWAYS + " may be used depending on the dimension. \n"
|
||||
+ " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n";
|
||||
public VanillaOverdraw getVanillaOverdraw();
|
||||
public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw);
|
||||
|
||||
GpuUploadMethod GPU_UPLOAD_METHOD_DEFAULT = GpuUploadMethod.BUFFER_STORAGE;
|
||||
String GPU_UPLOAD_METHOD_DESC = ""
|
||||
+ " What method should be used to upload geometry to the GPU? \n"
|
||||
+ " Listed in the suggested order of best to worst. \n\n"
|
||||
+ ""
|
||||
+ " " + GpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. \n"
|
||||
+ " " + GpuUploadMethod.SUB_DATA + ": Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. \n"
|
||||
+ " " + GpuUploadMethod.DATA + ": Fast rendering but will stutter when uploading. \n"
|
||||
+ " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly better than " + GpuUploadMethod.SUB_DATA + " if using a integrated GPU. \n";
|
||||
public GpuUploadMethod getGpuUploadMethod();
|
||||
public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog);
|
||||
+ " " + " " + VanillaOverdraw.NEVER + " or " + VanillaOverdraw.ALWAYS + " will be used depending on the dimension. \n"
|
||||
+ " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing all holes in the world. \n"
|
||||
+ "\n"
|
||||
+ " This setting shouldn't affect performance. \n";
|
||||
VanillaOverdraw getVanillaOverdraw();
|
||||
void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw);
|
||||
|
||||
boolean USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT = false;
|
||||
String USE_EXTENDED_NEAR_CLIP_PLANE_DESC = ""
|
||||
+ " Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly \n"
|
||||
+ " especially when in/near an ocean. \n";
|
||||
public boolean getUseExtendedNearClipPlane();
|
||||
public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane);
|
||||
+ " especially when in/near an ocean. \n"
|
||||
+ "\n"
|
||||
+ " This setting shouldn't affect performance. \n";
|
||||
boolean getUseExtendedNearClipPlane();
|
||||
void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,65 +241,85 @@ public interface ILodConfigWrapperSingleton
|
||||
//========================//
|
||||
// WorldGenerator Configs //
|
||||
//========================//
|
||||
public interface IWorldGenerator
|
||||
interface IWorldGenerator
|
||||
{
|
||||
String DESC = "These settings control how fake chunks outside your normal view range are generated.";
|
||||
|
||||
GenerationPriority GENERATION_PRIORITY_DEFAULT = GenerationPriority.FAR_FIRST;
|
||||
GenerationPriority GENERATION_PRIORITY_DEFAULT = GenerationPriority.AUTO;
|
||||
String GENERATION_PRIORITY_DESC = ""
|
||||
+ " In what order should fake chunks be generated outside the vanilla render distance? \n"
|
||||
+ "\n"
|
||||
+ " " + GenerationPriority.FAR_FIRST + " \n"
|
||||
+ " LODs are generated from low to high detail \n"
|
||||
+ " Fake chunks are generated from lowest to highest detail \n"
|
||||
+ " with a small priority for far away regions. \n"
|
||||
+ " This fills in the world fastest. \n\n"
|
||||
+ ""
|
||||
+ " This fills in the world fastest, but you will have large low detail \n"
|
||||
+ " blocks for a while while the generation happens. \n"
|
||||
+ "\n"
|
||||
+ " " + GenerationPriority.NEAR_FIRST + " \n"
|
||||
+ " LODs are generated around the player \n"
|
||||
+ " in a spiral, similar to vanilla minecraft. \n";
|
||||
public GenerationPriority getGenerationPriority();
|
||||
public void setGenerationPriority(GenerationPriority newGenerationPriority);
|
||||
+ " Fake chunks are generated around the player \n"
|
||||
+ " in a spiral, similar to vanilla minecraft. \n"
|
||||
+ " Best used when on a server since we can't generate \n"
|
||||
+ " fake chunks. \n"
|
||||
+ "\n"
|
||||
+ " " + GenerationPriority.AUTO + " \n"
|
||||
+ " Uses " + GenerationPriority.FAR_FIRST + " when on a single player world \n"
|
||||
+ " and " + GenerationPriority.NEAR_FIRST + " when connected to a server. \n"
|
||||
+ "\n"
|
||||
+ " This shouldn't affect performance.";
|
||||
GenerationPriority getGenerationPriority();
|
||||
void setGenerationPriority(GenerationPriority newGenerationPriority);
|
||||
|
||||
DistanceGenerationMode DISTANCE_GENERATION_MODE_DEFAULT = DistanceGenerationMode.SURFACE;
|
||||
String DISTANCE_GENERATION_MODE_DESC = ""
|
||||
+ " Note: The times listed here are the amount of time it took \n"
|
||||
+ " one of the developer's PC to generate 1 chunk, \n"
|
||||
+ " and are included so you can compare the \n"
|
||||
+ " different generation options. Your mileage may vary. \n\n"
|
||||
+ ""
|
||||
public static String getDistanceGenerationModeDesc(IVersionConstants versionConstants)
|
||||
{
|
||||
return ""
|
||||
+ " How detailed should fake chunks be generated outside the vanilla render distance? \n"
|
||||
+ "\n"
|
||||
+ " The times are the amount of time it took one of the developer's PC to generate \n"
|
||||
+ " one chunk in Minecraft 1.16.5 and may be inaccurate for different Minecraft versions. \n"
|
||||
+ " They are included to give a rough estimate as to how the different options \n"
|
||||
+ " may perform in comparison to each other. \n"
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.NONE + " \n"
|
||||
+ " Don't run the distance generator. \n\n"
|
||||
+ ""
|
||||
+ " Don't run the distance generator. \n"
|
||||
+ " No CPU usage - Fastest \n"
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY + " \n"
|
||||
+ " Only generate the biomes and use the biome's \n"
|
||||
+ " grass color, water color, or snow color. \n"
|
||||
+ " Doesn't generate height, everything is shown at sea level. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n\n"
|
||||
+ ""
|
||||
+ " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.BIOME_ONLY) + " - Fastest (2-5 ms) \n"
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT + " \n"
|
||||
+ " Same as BIOME_ONLY, except instead \n"
|
||||
+ " Same as " + DistanceGenerationMode.BIOME_ONLY + ", except instead \n"
|
||||
+ " of always using sea level as the LOD height \n"
|
||||
+ " different biome types (mountain, ocean, forest, etc.) \n"
|
||||
+ " use predetermined heights to simulate having height data. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n\n"
|
||||
+ ""
|
||||
+ " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT) + " - Fastest (2-5 ms) \n"
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SURFACE + " \n"
|
||||
+ " Generate the world surface, \n"
|
||||
+ " this does NOT include trees, \n"
|
||||
+ " or structures. \n"
|
||||
+ " Multithreaded - Faster (10-20 ms) \n\n"
|
||||
+ ""
|
||||
+ " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.SURFACE) + " - Faster (10-20 ms) \n"
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.FEATURES + " \n"
|
||||
+ " Generate everything except structures. \n"
|
||||
+ " WARNING: This may cause world generation bugs or instability! \n"
|
||||
+ " Multithreaded - Fast (15-20 ms) \n\n"
|
||||
+ ""
|
||||
+ " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.FEATURES) + " - Fast (15-20 ms) \n"
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.FULL + " \n"
|
||||
+ " Ask the local server to generate/load each chunk. \n"
|
||||
+ " This will show player made structures, which can \n"
|
||||
+ " be useful if you are adding the mod to a pre-existing world. \n"
|
||||
+ " This is the most compatible, but causes server/simulation lag. \n"
|
||||
+ " SingleThreaded - Slow (15-50 ms, with spikes up to 200 ms) \n";
|
||||
public DistanceGenerationMode getDistanceGenerationMode();
|
||||
public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode);
|
||||
+ " " + multiOrSingleThreadText(versionConstants, DistanceGenerationMode.FULL) + " - Slow (15-50 ms, with spikes up to 200 ms) \n"
|
||||
+ "\n"
|
||||
+ " The multithreaded options may increase CPU load significantly (while generating) \n"
|
||||
+ " depending on how many world generation threads you have allocated. \n";
|
||||
}
|
||||
DistanceGenerationMode getDistanceGenerationMode();
|
||||
void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode);
|
||||
|
||||
boolean ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT = false;
|
||||
String ALLOW_UNSTABLE_FEATURE_GENERATION_DESC = ""
|
||||
@@ -309,25 +333,36 @@ public interface ILodConfigWrapperSingleton
|
||||
+ " so some trees may not be generated.) \n"
|
||||
+ " By setting this to true, all features will be generated, \n"
|
||||
+ " but your game will be more unstable and crashes may occur. \n"
|
||||
+ " \n"
|
||||
+ "\n"
|
||||
+ " I would love to remove this option and always generate everything, \n"
|
||||
+ " but I'm not sure how to do that. \n"
|
||||
+ " If you are a Java wizard, check out the git issue here: \n"
|
||||
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n";
|
||||
public boolean getAllowUnstableFeatureGeneration();
|
||||
public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration);
|
||||
boolean getAllowUnstableFeatureGeneration();
|
||||
void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration);
|
||||
|
||||
BlocksToAvoid BLOCKS_TO_AVOID_DEFAULT = BlocksToAvoid.BOTH;
|
||||
String BLOCKS_TO_AVOID_DESC = ""
|
||||
+ " " + BlocksToAvoid.NONE + ": Use all blocks when generating fake chunks \n\n"
|
||||
+ ""
|
||||
+ " " + BlocksToAvoid.NON_FULL + ": Only use full blocks when generating fake chunks (ignores slabs, lanterns, torches, grass, etc.) \n\n"
|
||||
+ ""
|
||||
+ " " + BlocksToAvoid.NO_COLLISION + ": Only use solid blocks when generating fake chunks (ignores grass, torches, etc.) \n"
|
||||
+ ""
|
||||
+ " " + BlocksToAvoid.BOTH + ": Only use full solid blocks when generating fake chunks \n";
|
||||
public BlocksToAvoid getBlocksToAvoid();
|
||||
public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid);
|
||||
+ " When generating fake chunks, what blocks should be ignored? \n"
|
||||
+ " Ignored blocks don't affect the height of the fake chunk, but might affect the color. \n"
|
||||
+ " So using " + BlocksToAvoid.BOTH + " will prevent snow covered blocks from appearing one block too tall, \n"
|
||||
+ " but will still show the snow's color.\n"
|
||||
+ "\n"
|
||||
+ " " + BlocksToAvoid.NONE + ": Use all blocks when generating fake chunks \n"
|
||||
+ " " + BlocksToAvoid.NON_FULL + ": Only use full blocks when generating fake chunks (ignores slabs, lanterns, torches, tall grass, etc.) \n"
|
||||
+ " " + BlocksToAvoid.NO_COLLISION + ": Only use solid blocks when generating fake chunks (ignores tall grass, torches, etc.) \n"
|
||||
+ " " + BlocksToAvoid.BOTH + ": Only use full solid blocks when generating fake chunks \n"
|
||||
+ "\n"
|
||||
+ " This wont't affect performance.";
|
||||
BlocksToAvoid getBlocksToAvoid();
|
||||
void setBlockToAvoid(BlocksToAvoid newBlockToAvoid);
|
||||
|
||||
|
||||
/** description helper method */
|
||||
static String multiOrSingleThreadText(IVersionConstants versionConstants, DistanceGenerationMode distanceGenerationMode)
|
||||
{
|
||||
return versionConstants.isWorldGeneratorSingleThreaded(distanceGenerationMode) ? "Singlethreaded" : "Multithreaded";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -336,16 +371,16 @@ public interface ILodConfigWrapperSingleton
|
||||
//============================//
|
||||
// AdvancedModOptions Configs //
|
||||
//============================//
|
||||
public interface IAdvanced
|
||||
interface IAdvanced
|
||||
{
|
||||
public static final String DESC = "Advanced mod settings";
|
||||
String DESC = "Advanced mod settings";
|
||||
|
||||
public IThreading threading();
|
||||
public IDebugging debugging();
|
||||
public IBuffers buffers();
|
||||
IThreading threading();
|
||||
IDebugging debugging();
|
||||
IBuffers buffers();
|
||||
|
||||
|
||||
public interface IThreading
|
||||
interface IThreading
|
||||
{
|
||||
String DESC = "These settings control how many CPU threads the mod uses for different tasks.";
|
||||
|
||||
@@ -354,34 +389,45 @@ public interface ILodConfigWrapperSingleton
|
||||
Runtime.getRuntime().availableProcessors() / 2,
|
||||
Runtime.getRuntime().availableProcessors());
|
||||
String NUMBER_OF_WORLD_GENERATION_THREADS_DESC = ""
|
||||
+ " This is how many threads are used when generating LODs outside \n"
|
||||
+ " the normal render distance. \n"
|
||||
+ " How many threads should be used when generating fake chunks outside \n"
|
||||
+ " the normal render distance? \n"
|
||||
+ "\n"
|
||||
+ " If you experience stuttering when generating distant LODs, decrease \n"
|
||||
+ " this number. If you want to increase LOD generation speed, \n"
|
||||
+ " increase this number. \n\n"
|
||||
+ ""
|
||||
+ " increase this number. \n"
|
||||
+ "\n"
|
||||
+ " This and the number of buffer builder threads are independent, \n"
|
||||
+ " so if they add up to more threads than your CPU has cores, \n"
|
||||
+ " that shouldn't cause an issue. \n"
|
||||
+ "\n"
|
||||
+ " The maximum value is the number of logical processors on your CPU. \n"
|
||||
+ " Requires a restart to take effect. \n";
|
||||
public int getNumberOfWorldGenerationThreads();
|
||||
public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads);
|
||||
int getNumberOfWorldGenerationThreads();
|
||||
void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads);
|
||||
|
||||
MinDefaultMax<Integer> NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX
|
||||
= new MinDefaultMax<Integer>(1,
|
||||
Runtime.getRuntime().availableProcessors() / 2,
|
||||
Runtime.getRuntime().availableProcessors());
|
||||
String NUMBER_OF_BUFFER_BUILDER_THREADS_DESC = ""
|
||||
+ " This is how many threads are used when building vertex buffers \n"
|
||||
+ " How many threads are used when building vertex buffers? \n"
|
||||
+ " (The things sent to your GPU to draw the fake chunks). \n"
|
||||
+ "\n"
|
||||
+ " If you experience high CPU usage when NOT generating distant \n"
|
||||
+ " fake chunks, lower this number. \n"
|
||||
+ " \n"
|
||||
+ " fake chunks, lower this number. A higher number will make fake\n"
|
||||
+ " fake chunks' transition faster when moving around the world. \n"
|
||||
+ "\n"
|
||||
+ " This and the number of world generator threads are independent, \n"
|
||||
+ " so if they add up to more threads than your CPU has cores, \n"
|
||||
+ " that shouldn't cause an issue. \n"
|
||||
+ "\n"
|
||||
+ " The maximum value is the number of logical processors on your CPU. \n"
|
||||
+ " Requires a restart to take effect. \n";
|
||||
public int getNumberOfBufferBuilderThreads();
|
||||
public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads);
|
||||
int getNumberOfBufferBuilderThreads();
|
||||
void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads);
|
||||
}
|
||||
|
||||
public interface IDebugging
|
||||
interface IDebugging
|
||||
{
|
||||
String DESC = "These settings can be used to look for bugs, or see how certain aspects of the mod work.";
|
||||
|
||||
@@ -389,36 +435,77 @@ public interface ILodConfigWrapperSingleton
|
||||
String DRAW_LODS_DESC = ""
|
||||
+ " If true, the mod is enabled and fake chunks will be drawn. \n"
|
||||
+ " If false, the mod will still generate fake chunks, \n"
|
||||
+ " but they won't be rendered. \n";
|
||||
public boolean getDrawLods();
|
||||
public void setDrawLods(boolean newDrawLods);
|
||||
+ " but they won't be rendered. \n"
|
||||
+ "\n"
|
||||
+ " Disabling rendering will reduce GPU usage \n";
|
||||
boolean getDrawLods();
|
||||
void setDrawLods(boolean newDrawLods);
|
||||
|
||||
DebugMode DEBUG_MODE_DEFAULT = DebugMode.OFF;
|
||||
String DEBUG_MODE_DESC = ""
|
||||
+ " Should specialized colors/rendering modes be used? \n"
|
||||
+ "\n"
|
||||
+ " " + DebugMode.OFF + ": Fake chunks will be drawn with their normal colors. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL + ": Fake chunks color will be based on their detail level. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME + ": Fake chunks color will be based on their detail level, drawn as a wireframe. \n";
|
||||
public DebugMode getDebugMode();
|
||||
public void setDebugMode(DebugMode newDebugMode);
|
||||
DebugMode getDebugMode();
|
||||
void setDebugMode(DebugMode newDebugMode);
|
||||
|
||||
boolean DEBUG_KEYBINDINGS_ENABLED_DEFAULT = true;
|
||||
String DEBUG_KEYBINDINGS_ENABLED_DESC = ""
|
||||
+ " If true the F4 key can be used to cycle through the different debug modes. \n"
|
||||
+ " If true the F8 key can be used to cycle through the different debug modes. \n"
|
||||
+ " and the F6 key can be used to enable and disable LOD rendering.";
|
||||
public boolean getDebugKeybindingsEnabled();
|
||||
public void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings);
|
||||
boolean getDebugKeybindingsEnabled();
|
||||
void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings);
|
||||
}
|
||||
|
||||
public interface IBuffers
|
||||
interface IBuffers
|
||||
{
|
||||
String DESC = "These settings affect how often geometry is rebuilt.";
|
||||
|
||||
GpuUploadMethod GPU_UPLOAD_METHOD_DEFAULT = GpuUploadMethod.AUTO;
|
||||
String GPU_UPLOAD_METHOD_DESC = ""
|
||||
+ " What method should be used to upload geometry to the GPU? \n"
|
||||
+ "\n"
|
||||
+ " " + GpuUploadMethod.AUTO + ": Picks the best option based on the GPU you have. \n"
|
||||
+ " " + GpuUploadMethod.BUFFER_STORAGE + ": Default for NVIDIA if OpenGL 4.5 is supported. \n"
|
||||
+ " Fast rendering, no stuttering. \n"
|
||||
+ " " + GpuUploadMethod.SUB_DATA + ": Backup option for NVIDIA. \n"
|
||||
+ " Fast rendering but may stutter when uploading. \n"
|
||||
+ " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly the best option for integrated GPUs. \n"
|
||||
+ " Default option for AMD/Intel. \n"
|
||||
+ " May end up storing buffers in System memory. \n"
|
||||
+ " Fast rendering if in GPU memory, slow if in system memory, \n"
|
||||
+ " but won't stutter when uploading. \n"
|
||||
+ " " + GpuUploadMethod.DATA + ": Fast rendering but will stutter when uploading. \n"
|
||||
+ " Backup option for AMD/Intel. \n"
|
||||
+ " Fast rendering but may stutter when uploading. \n"
|
||||
+ "\n"
|
||||
+ " If you don't see any difference when changing these settings, or the world looks corrupted: \n"
|
||||
+ " Restart the game to clear the old buffers. \n";
|
||||
GpuUploadMethod getGpuUploadMethod();
|
||||
void setGpuUploadMethod(GpuUploadMethod newGpuUploadMethod);
|
||||
|
||||
MinDefaultMax<Integer> GPU_UPLOAD_PER_MEGABYTE_IN_MILLISECONDS_DEFAULT = new MinDefaultMax<Integer>(0, 10, 5000);
|
||||
String GPU_UPLOAD_PER_MEGABYTE_IN_MILLISECONDS_DESC = ""
|
||||
+ " How long should a buffer wait per Megabyte of data uploaded?\n"
|
||||
+ " Helpful resource for frame times: https://fpstoms.com \n"
|
||||
+ "\n"
|
||||
+ " Longer times may reduce stuttering but will make fake chunks \n"
|
||||
+ " transition and load slower. Change this to [0] for no timeout.\n"
|
||||
+ "\n"
|
||||
+ " NOTE:\n"
|
||||
+ " Before changing this config, try changing \"GPU Upload methods\"\n"
|
||||
+ " and determined the best method for your hardware first. \n";
|
||||
int getGpuUploadPerMegabyteInMilliseconds();
|
||||
void setGpuUploadPerMegabyteInMilliseconds(int newMilliseconds);
|
||||
|
||||
String REBUILD_TIMES_DESC = ""
|
||||
+ " How frequently should geometry be rebuilt and sent to the GPU? \n"
|
||||
+ " How frequently should vertex buffers (geometry) be rebuilt and sent to the GPU? \n"
|
||||
+ " Higher settings may cause stuttering, but will prevent holes in the world \n";
|
||||
BufferRebuildTimes REBUILD_TIMES_DEFAULT = BufferRebuildTimes.NORMAL;
|
||||
public BufferRebuildTimes getRebuildTimes();
|
||||
public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes);
|
||||
BufferRebuildTimes getRebuildTimes();
|
||||
void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+82
-12
@@ -19,11 +19,14 @@
|
||||
|
||||
package com.seibel.lod.core.wrapperInterfaces.minecraft;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.objects.math.Vec3d;
|
||||
import com.seibel.lod.core.objects.math.Vec3f;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
|
||||
@@ -32,33 +35,100 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
* rendering in Minecraft.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-18-2021
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public interface IMinecraftRenderWrapper
|
||||
{
|
||||
public Vec3f getLookAtVector();
|
||||
Vec3f getLookAtVector();
|
||||
|
||||
public AbstractBlockPosWrapper getCameraBlockPosition();
|
||||
AbstractBlockPosWrapper getCameraBlockPosition();
|
||||
|
||||
public boolean playerHasBlindnessEffect();
|
||||
boolean playerHasBlindnessEffect();
|
||||
|
||||
public Vec3d getCameraExactPosition();
|
||||
Vec3d getCameraExactPosition();
|
||||
|
||||
public Mat4f getDefaultProjectionMatrix(float partialTicks);
|
||||
Mat4f getDefaultProjectionMatrix(float partialTicks);
|
||||
|
||||
public double getGamma();
|
||||
double getGamma();
|
||||
|
||||
public double getFov(float partialTicks);
|
||||
Color getFogColor();
|
||||
|
||||
Color getSkyColor();
|
||||
|
||||
double getFov(float partialTicks);
|
||||
|
||||
/** Measured in chunks */
|
||||
public int getRenderDistance();
|
||||
int getRenderDistance();
|
||||
|
||||
public int getScreenWidth();
|
||||
public int getScreenHeight();
|
||||
int getScreenWidth();
|
||||
int getScreenHeight();
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of all chunks that Minecraft
|
||||
* is going to render this frame.
|
||||
* <br>
|
||||
* If not implemented this calls {@link #getMaximumRenderedChunks()}.
|
||||
*/
|
||||
public HashSet<AbstractChunkPosWrapper> getRenderedChunks();
|
||||
public default HashSet<AbstractChunkPosWrapper> getVanillaRenderedChunks()
|
||||
{
|
||||
return getMaximumRenderedChunks();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of every chunk that
|
||||
* Sodium is going to render this frame.
|
||||
* <br>
|
||||
* If not implemented this calls {@link #getMaximumRenderedChunks()}.
|
||||
*/
|
||||
public default HashSet<AbstractChunkPosWrapper> getSodiumRenderedChunks()
|
||||
{
|
||||
return getMaximumRenderedChunks();
|
||||
}
|
||||
|
||||
/**
|
||||
* <strong>Doesn't need to be implemented.</strong> <br>
|
||||
* Returns every chunk position within the vanilla render distance.
|
||||
*/
|
||||
public default HashSet<AbstractChunkPosWrapper> getMaximumRenderedChunks()
|
||||
{
|
||||
IMinecraftWrapper mcWrapper = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
IWrapperFactory factory = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
int chunkRenderDist = this.getRenderDistance();
|
||||
// if we have a odd render distance, we'll have a empty gap. This way we'll overlap by 1 instead,
|
||||
// which is preferable to having a hole in the world
|
||||
chunkRenderDist = chunkRenderDist % 2 == 0 ? chunkRenderDist : chunkRenderDist - 1;
|
||||
|
||||
AbstractChunkPosWrapper centerChunkPos = mcWrapper.getPlayerChunkPos();
|
||||
int startChunkX = centerChunkPos.getX() - chunkRenderDist;
|
||||
int startChunkZ = centerChunkPos.getZ() - chunkRenderDist;
|
||||
|
||||
// add every position within render distance
|
||||
HashSet<AbstractChunkPosWrapper> renderedPos = new HashSet<AbstractChunkPosWrapper>();
|
||||
for (int chunkX = 0; chunkX < (chunkRenderDist * 2); chunkX++)
|
||||
{
|
||||
for(int chunkZ = 0; chunkZ < (chunkRenderDist * 2); chunkZ++)
|
||||
{
|
||||
renderedPos.add(factory.createChunkPos(startChunkX + chunkX, startChunkZ + chunkZ));
|
||||
}
|
||||
}
|
||||
|
||||
return renderedPos;
|
||||
}
|
||||
|
||||
/** @returns null if there was a issue getting the lightmap */
|
||||
int[] getLightmapPixels();
|
||||
|
||||
/** @returns -1 if there was an issue getting the lightmap */
|
||||
int getLightmapTextureHeight();
|
||||
/** @returns -1 if there was an issue getting the lightmap */
|
||||
int getLightmapTextureWidth();
|
||||
/** @returns -1 if there was an issue getting the lightmap */
|
||||
public int getLightmapGLFormat();
|
||||
|
||||
/** Try and disable vanilla fog. Return true if successful, or false if not able to.
|
||||
* If we are still using legacy fog, this method will not be called. */
|
||||
public default boolean tryDisableVanillaFog() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-26
@@ -34,7 +34,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
* Contains everything related to the Minecraft object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 9-16-2021
|
||||
* @version 12-8-2021
|
||||
*/
|
||||
public interface IMinecraftWrapper
|
||||
{
|
||||
@@ -50,7 +50,7 @@ public interface IMinecraftWrapper
|
||||
* <p>
|
||||
* This doesn't affect OpenGL objects in any way.
|
||||
*/
|
||||
public void clearFrameObjectCache();
|
||||
void clearFrameObjectCache();
|
||||
|
||||
|
||||
|
||||
@@ -58,18 +58,18 @@ public interface IMinecraftWrapper
|
||||
// method wrappers //
|
||||
//=================//
|
||||
|
||||
public float getShade(LodDirection lodDirection);
|
||||
float getShade(LodDirection lodDirection);
|
||||
|
||||
public boolean hasSinglePlayerServer();
|
||||
boolean hasSinglePlayerServer();
|
||||
|
||||
public String getCurrentServerName();
|
||||
public String getCurrentServerIp();
|
||||
public String getCurrentServerVersion();
|
||||
String getCurrentServerName();
|
||||
String getCurrentServerIp();
|
||||
String getCurrentServerVersion();
|
||||
|
||||
/** Returns the dimension the player is currently in */
|
||||
public IDimensionTypeWrapper getCurrentDimension();
|
||||
IDimensionTypeWrapper getCurrentDimension();
|
||||
|
||||
public String getCurrentDimensionId();
|
||||
String getCurrentDimensionId();
|
||||
|
||||
/** This texture changes every frame */
|
||||
ILightMapWrapper getCurrentLightMap();
|
||||
@@ -77,18 +77,18 @@ public interface IMinecraftWrapper
|
||||
/**
|
||||
* Returns the color int at the given pixel coordinates
|
||||
* from the current lightmap.
|
||||
* @param u x location in texture space
|
||||
* @param v z location in texture space
|
||||
* @param blockLight x location in texture space
|
||||
* @param skyLight z location in texture space
|
||||
*/
|
||||
public int getColorIntFromLightMap(int u, int v);
|
||||
int getColorIntFromLightMap(int blockLight, int skyLight);
|
||||
|
||||
/**
|
||||
* Returns the Color at the given pixel coordinates
|
||||
* from the current lightmap.
|
||||
* @param u x location in texture space
|
||||
* @param v z location in texture space
|
||||
* @param blockLight x location in texture space
|
||||
* @param skyLight z location in texture space
|
||||
*/
|
||||
public Color getColorFromLightMap(int u, int v);
|
||||
Color getColorFromLightMap(int blockLight, int skyLight);
|
||||
|
||||
|
||||
|
||||
@@ -97,35 +97,35 @@ public interface IMinecraftWrapper
|
||||
// Simple gets //
|
||||
//=============//
|
||||
|
||||
public boolean playerExists();
|
||||
boolean playerExists();
|
||||
|
||||
public AbstractBlockPosWrapper getPlayerBlockPos();
|
||||
AbstractBlockPosWrapper getPlayerBlockPos();
|
||||
|
||||
public AbstractChunkPosWrapper getPlayerChunkPos();
|
||||
AbstractChunkPosWrapper getPlayerChunkPos();
|
||||
|
||||
/**
|
||||
* Attempts to get the ServerWorld for the dimension
|
||||
* the user is currently in.
|
||||
* @returns null if no ServerWorld is available
|
||||
*/
|
||||
public IWorldWrapper getWrappedServerWorld();
|
||||
IWorldWrapper getWrappedServerWorld();
|
||||
|
||||
public IWorldWrapper getWrappedClientWorld();
|
||||
IWorldWrapper getWrappedClientWorld();
|
||||
|
||||
public File getGameDirectory();
|
||||
File getGameDirectory();
|
||||
|
||||
public IProfilerWrapper getProfiler();
|
||||
IProfilerWrapper getProfiler();
|
||||
|
||||
public float getSkyDarken(float partialTicks);
|
||||
float getSkyDarken(float partialTicks);
|
||||
|
||||
boolean connectedToServer();
|
||||
|
||||
/** Returns all worlds available to the server */
|
||||
public ArrayList<IWorldWrapper> getAllServerWorlds();
|
||||
ArrayList<IWorldWrapper> getAllServerWorlds();
|
||||
|
||||
|
||||
|
||||
public void sendChatMessage(String string);
|
||||
void sendChatMessage(String string);
|
||||
|
||||
/**
|
||||
* Crashes Minecraft, displaying the given errorMessage <br> <br>
|
||||
@@ -135,7 +135,7 @@ public interface IMinecraftWrapper
|
||||
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
|
||||
* Exit Code: -1 <br>
|
||||
*/
|
||||
public void crashMinecraft(String errorMessage, Throwable exception);
|
||||
void crashMinecraft(String errorMessage, Throwable exception);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ package com.seibel.lod.core.wrapperInterfaces.minecraft;
|
||||
*/
|
||||
public interface IProfilerWrapper
|
||||
{
|
||||
public void push(String newSection);
|
||||
void push(String newSection);
|
||||
|
||||
public void popPush(String newSection);
|
||||
void popPush(String newSection);
|
||||
|
||||
public void pop();
|
||||
void pop();
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ package com.seibel.lod.core.wrapperInterfaces.misc;
|
||||
*/
|
||||
public interface ILightMapWrapper
|
||||
{
|
||||
public int getLightValue(int skyLight, int blockLight);
|
||||
int getLightValue(int skyLight, int blockLight);
|
||||
}
|
||||
|
||||
+4
-4
@@ -30,9 +30,9 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
*/
|
||||
public interface IBiomeColorWrapperSingleton
|
||||
{
|
||||
public IBiomeColorWrapperSingleton getInstance();
|
||||
IBiomeColorWrapperSingleton getInstance();
|
||||
|
||||
public int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
public int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
public int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,14 @@ package com.seibel.lod.core.wrapperInterfaces.world;
|
||||
public interface IBiomeWrapper
|
||||
{
|
||||
/** Returns a color int for the given biome. */
|
||||
public int getColorForBiome(int x, int z);
|
||||
int getColorForBiome(int x, int z);
|
||||
|
||||
public int getGrassTint(int x, int z);
|
||||
String getName();
|
||||
|
||||
public int getFolliageTint();
|
||||
int getGrassTint(int x, int z);
|
||||
|
||||
public int getWaterTint();
|
||||
int getFolliageTint();
|
||||
|
||||
int getWaterTint();
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -25,9 +25,9 @@ package com.seibel.lod.core.wrapperInterfaces.world;
|
||||
*/
|
||||
public interface IDimensionTypeWrapper
|
||||
{
|
||||
public String getDimensionName();
|
||||
String getDimensionName();
|
||||
|
||||
public boolean hasCeiling();
|
||||
boolean hasCeiling();
|
||||
|
||||
public boolean hasSkyLight();
|
||||
boolean hasSkyLight();
|
||||
}
|
||||
|
||||
@@ -32,28 +32,33 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
*/
|
||||
public interface IWorldWrapper
|
||||
{
|
||||
public IDimensionTypeWrapper getDimensionType();
|
||||
IDimensionTypeWrapper getDimensionType();
|
||||
|
||||
public WorldType getWorldType();
|
||||
WorldType getWorldType();
|
||||
|
||||
public int getBlockLight(AbstractBlockPosWrapper blockPos);
|
||||
int getBlockLight(int x, int y, int z);
|
||||
|
||||
public int getSkyLight(AbstractBlockPosWrapper blockPos);
|
||||
int getSkyLight(int x, int y, int z);
|
||||
|
||||
public IBiomeWrapper getBiome(AbstractBlockPosWrapper blockPos);
|
||||
boolean hasCeiling();
|
||||
|
||||
public boolean hasCeiling();
|
||||
boolean hasSkyLight();
|
||||
|
||||
public boolean hasSkyLight();
|
||||
// Pls don't use this
|
||||
// If the world is null then this can't be called and gives an error
|
||||
boolean isEmpty();
|
||||
|
||||
public boolean isEmpty();
|
||||
int getHeight();
|
||||
|
||||
public int getHeight();
|
||||
int getSeaLevel();
|
||||
|
||||
public int getSeaLevel();
|
||||
default short getMinHeight()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
|
||||
public File getSaveFolder() throws UnsupportedOperationException;
|
||||
File getSaveFolder() throws UnsupportedOperationException;
|
||||
|
||||
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.seibel.lod.core.wrapperInterfaces.worldGeneration;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
public abstract class AbstractExperimentalWorldGeneratorWrapper {
|
||||
public AbstractExperimentalWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { }
|
||||
public abstract void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder);
|
||||
public abstract void stop();
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
* This is used for generating chunks
|
||||
* in a variety of detail and threading levels.
|
||||
* <p>
|
||||
* Abstract instead of a interface so
|
||||
* Abstract instead of an interface, so
|
||||
* we can define its constructors.
|
||||
*
|
||||
* @author James Seibel
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.forge;
|
||||
|
||||
import com.seibel.lod.core.api.EventApi;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.lod.forge.wrappers.world.DimensionTypeWrapper;
|
||||
import com.seibel.lod.forge.wrappers.world.WorldWrapper;
|
||||
|
||||
import net.minecraftforge.client.event.InputEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.event.world.ChunkEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
/**
|
||||
* This handles all events sent to the client,
|
||||
* and is the starting point for most of the mod.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class ForgeClientProxy
|
||||
{
|
||||
private final EventApi eventApi = EventApi.INSTANCE;
|
||||
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void serverTickEvent(TickEvent.ServerTickEvent event)
|
||||
{
|
||||
eventApi.serverTickEvent();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void chunkLoadEvent(ChunkEvent.Load event)
|
||||
{
|
||||
eventApi.chunkLoadEvent(new ChunkWrapper(event.getChunk()), DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType()));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldSaveEvent(WorldEvent.Save event)
|
||||
{
|
||||
eventApi.worldSaveEvent();
|
||||
}
|
||||
|
||||
/** This is also called when a new dimension loads */
|
||||
@SubscribeEvent
|
||||
public void worldLoadEvent(WorldEvent.Load event)
|
||||
{
|
||||
eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(event.getWorld()));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldUnloadEvent(WorldEvent.Unload event)
|
||||
{
|
||||
eventApi.worldUnloadEvent();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void blockChangeEvent(BlockEvent event)
|
||||
{
|
||||
// we only care about certain block events
|
||||
if (event.getClass() == BlockEvent.BreakEvent.class ||
|
||||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
|
||||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
|
||||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
|
||||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
|
||||
{
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getWorld().getChunk(event.getPos()));
|
||||
DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType());
|
||||
|
||||
// recreate the LOD where the blocks were changed
|
||||
eventApi.blockChangeEvent(chunk, dimType);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onKeyInput(InputEvent.KeyInputEvent event)
|
||||
{
|
||||
eventApi.onKeyInput(event.getKey(), event.getAction());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.forge;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import com.electronwill.nightconfig.core.io.WritingMode;
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.enums.config.BlocksToAvoid;
|
||||
import com.seibel.lod.core.enums.config.BufferRebuildTimes;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.GenerationPriority;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.config.HorizontalQuality;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.enums.config.HorizontalScale;
|
||||
import com.seibel.lod.core.enums.config.LodTemplate;
|
||||
import com.seibel.lod.core.enums.config.VanillaOverdraw;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawOverride;
|
||||
import com.seibel.lod.core.objects.MinDefaultMax;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IWorldGenerator;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IBuffers;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IDebugging;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IThreading;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IAdvancedGraphics;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IFogQuality;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IQuality;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
|
||||
/**
|
||||
* This handles any configuration the user has access to.
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 11-16-2021
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class ForgeConfig
|
||||
{
|
||||
// CONFIG STRUCTURE
|
||||
// -> Client
|
||||
// |
|
||||
// |-> Graphics
|
||||
// | |-> QualityOption
|
||||
// | |-> FogQualityOption
|
||||
// | |-> AdvancedGraphicsOption
|
||||
// |
|
||||
// |-> World Generation
|
||||
// |
|
||||
// |-> Advanced Mod Option
|
||||
// |-> Threads
|
||||
// |-> Buffers
|
||||
// |-> Debugging
|
||||
|
||||
|
||||
|
||||
public static class Client
|
||||
{
|
||||
public final Graphics graphics;
|
||||
public final WorldGenerator worldGenerator;
|
||||
public final AdvancedModOptions advancedModOptions;
|
||||
|
||||
|
||||
//================//
|
||||
// Client Configs //
|
||||
//================//
|
||||
public Client(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.push(this.getClass().getSimpleName());
|
||||
{
|
||||
graphics = new Graphics(builder);
|
||||
worldGenerator = new WorldGenerator(builder);
|
||||
advancedModOptions = new AdvancedModOptions(builder);
|
||||
}
|
||||
builder.pop();
|
||||
}
|
||||
|
||||
|
||||
//==================//
|
||||
// Graphics Configs //
|
||||
//==================//
|
||||
public static class Graphics
|
||||
{
|
||||
public final QualityOption qualityOption;
|
||||
public final FogQualityOption fogQuality;
|
||||
public final AdvancedGraphicsOption advancedGraphicsOption;
|
||||
|
||||
Graphics(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IGraphics.DESC).push("Graphics");
|
||||
{
|
||||
qualityOption = new QualityOption(builder);
|
||||
advancedGraphicsOption = new AdvancedGraphicsOption(builder);
|
||||
fogQuality = new FogQualityOption(builder);
|
||||
}
|
||||
builder.pop();
|
||||
}
|
||||
|
||||
|
||||
public static class QualityOption
|
||||
{
|
||||
public final ForgeConfigSpec.EnumValue<HorizontalResolution> drawResolution;
|
||||
public final ForgeConfigSpec.IntValue lodChunkRenderDistance;
|
||||
public final ForgeConfigSpec.EnumValue<VerticalQuality> verticalQuality;
|
||||
public final ForgeConfigSpec.EnumValue<HorizontalScale> horizontalScale;
|
||||
public final ForgeConfigSpec.EnumValue<HorizontalQuality> horizontalQuality;
|
||||
|
||||
QualityOption(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IQuality.DESC).push(this.getClass().getSimpleName());
|
||||
|
||||
verticalQuality = builder
|
||||
.comment("\n\n"
|
||||
+ IQuality.VERTICAL_QUALITY_DESC)
|
||||
.defineEnum("Vertical Quality", IQuality.VERTICAL_QUALITY_DEFAULT);
|
||||
|
||||
horizontalScale = builder
|
||||
.comment("\n\n"
|
||||
+ IQuality.HORIZONTAL_SCALE_DESC)
|
||||
.defineEnum("Horizontal Scale", IQuality.HORIZONTAL_SCALE_DEFAULT);
|
||||
|
||||
horizontalQuality = builder
|
||||
.comment("\n\n"
|
||||
+ IQuality.HORIZONTAL_QUALITY_DESC)
|
||||
.defineEnum("Horizontal Quality", IQuality.HORIZONTAL_QUALITY_DEFAULT);
|
||||
|
||||
drawResolution = builder
|
||||
.comment("\n\n"
|
||||
+ IQuality.DRAW_RESOLUTION_DESC)
|
||||
.defineEnum("Block size", IQuality.DRAW_RESOLUTION_DEFAULT);
|
||||
|
||||
MinDefaultMax<Integer> minDefaultMax = IQuality.LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX;
|
||||
lodChunkRenderDistance = builder
|
||||
.comment("\n\n"
|
||||
+ IQuality.LOD_CHUNK_RENDER_DISTANCE_DESC)
|
||||
.defineInRange("Lod Render Distance", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class FogQualityOption
|
||||
{
|
||||
public final ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
|
||||
public final ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
|
||||
public final ForgeConfigSpec.BooleanValue disableVanillaFog;
|
||||
|
||||
FogQualityOption(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IFogQuality.DESC).push(this.getClass().getSimpleName());
|
||||
|
||||
fogDistance = builder
|
||||
.comment("\n\n"
|
||||
+ IFogQuality.FOG_DISTANCE_DESC)
|
||||
.defineEnum("Fog Distance", IFogQuality.FOG_DISTANCE_DEFAULT);
|
||||
|
||||
fogDrawOverride = builder
|
||||
.comment("\n\n"
|
||||
+ IFogQuality.FOG_DRAW_OVERRIDE_DESC)
|
||||
.defineEnum("Fog Draw Override", IFogQuality.FOG_DRAW_OVERRIDE_DEFAULT);
|
||||
|
||||
disableVanillaFog = builder
|
||||
.comment("\n\n"
|
||||
+ IFogQuality.DISABLE_VANILLA_FOG_DESC)
|
||||
.define("Experimental Disable Vanilla Fog", IFogQuality.DISABLE_VANILLA_FOG_DEFAULT);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class AdvancedGraphicsOption
|
||||
{
|
||||
public final ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
|
||||
public final ForgeConfigSpec.BooleanValue disableDirectionalCulling;
|
||||
public final ForgeConfigSpec.BooleanValue alwaysDrawAtMaxQuality;
|
||||
public final ForgeConfigSpec.EnumValue<VanillaOverdraw> vanillaOverdraw;
|
||||
public final ForgeConfigSpec.EnumValue<GpuUploadMethod> gpuUploadMethod;
|
||||
public final ForgeConfigSpec.BooleanValue useExtendedNearClipPlane;
|
||||
|
||||
AdvancedGraphicsOption(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IAdvancedGraphics.DESC).push(this.getClass().getSimpleName());
|
||||
|
||||
lodTemplate = builder
|
||||
.comment("\n\n"
|
||||
+ IAdvancedGraphics.LOD_TEMPLATE_DESC)
|
||||
.defineEnum("LOD Template", IAdvancedGraphics.LOD_TEMPLATE_DEFAULT);
|
||||
|
||||
disableDirectionalCulling = builder
|
||||
.comment("\n\n"
|
||||
+ IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DESC)
|
||||
.define("Disable Directional Culling", IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DEFAULT);
|
||||
|
||||
alwaysDrawAtMaxQuality = builder
|
||||
.comment("\n\n"
|
||||
+ IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DESC)
|
||||
.define("Always Use Max Quality", IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT);
|
||||
|
||||
vanillaOverdraw = builder
|
||||
.comment("\n\n"
|
||||
+ IAdvancedGraphics.VANILLA_OVERDRAW_DESC)
|
||||
.defineEnum("Vanilla Overdraw", IAdvancedGraphics.VANILLA_OVERDRAW_DEFAULT);
|
||||
|
||||
gpuUploadMethod = builder
|
||||
.comment("\n\n"
|
||||
+ IAdvancedGraphics.GPU_UPLOAD_METHOD_DESC)
|
||||
.defineEnum("GPU Upload Method", IAdvancedGraphics.GPU_UPLOAD_METHOD_DEFAULT);
|
||||
|
||||
// This is a temporary fix (like vanilla overdraw)
|
||||
// hopefully we can remove both once we get individual chunk rendering figured out
|
||||
useExtendedNearClipPlane = builder
|
||||
.comment("\n\n"
|
||||
+ IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DESC)
|
||||
.define("Use Extended Near Clip Plane", IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT);
|
||||
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========================//
|
||||
// WorldGenerator Configs //
|
||||
//========================//
|
||||
public static class WorldGenerator
|
||||
{
|
||||
public final ForgeConfigSpec.EnumValue<GenerationPriority> generationPriority;
|
||||
public final ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
|
||||
public final ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
|
||||
public final ForgeConfigSpec.EnumValue<BlocksToAvoid> blocksToAvoid;
|
||||
//public final ForgeConfigSpec.BooleanValue useExperimentalPreGenLoading;
|
||||
|
||||
WorldGenerator(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IWorldGenerator.DESC).push("Generation");
|
||||
|
||||
generationPriority = builder
|
||||
.comment("\n\n"
|
||||
+ IWorldGenerator.GENERATION_PRIORITY_DESC)
|
||||
.defineEnum("Generation Priority", IWorldGenerator.GENERATION_PRIORITY_DEFAULT);
|
||||
|
||||
distanceGenerationMode = builder
|
||||
.comment("\n\n"
|
||||
+ IWorldGenerator.DISTANCE_GENERATION_MODE_DESC)
|
||||
.defineEnum("Distance Generation Mode", IWorldGenerator.DISTANCE_GENERATION_MODE_DEFAULT);
|
||||
|
||||
allowUnstableFeatureGeneration = builder
|
||||
.comment("\n\n"
|
||||
+ IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DESC)
|
||||
.define("Allow Unstable Feature Generation", IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT);
|
||||
|
||||
blocksToAvoid = builder
|
||||
.comment("\n\n"
|
||||
+ IWorldGenerator.BLOCKS_TO_AVOID_DESC)
|
||||
.defineEnum("Blocks to avoid", IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT);
|
||||
|
||||
/*useExperimentalPreGenLoading = builder
|
||||
.comment("\n\n"
|
||||
+ " if a chunk has been pre-generated, then the mod would use the real chunk for the \n"
|
||||
+ "fake chunk creation. May require a deletion of the lod file to see the result. \n")
|
||||
.define("Use pre-generated chunks", false);*/
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//============================//
|
||||
// AdvancedModOptions Configs //
|
||||
//============================//
|
||||
public static class AdvancedModOptions
|
||||
{
|
||||
public final Threading threading;
|
||||
public final Debugging debugging;
|
||||
public final Buffers buffers;
|
||||
|
||||
public AdvancedModOptions(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IAdvanced.DESC).push(this.getClass().getSimpleName());
|
||||
{
|
||||
threading = new Threading(builder);
|
||||
debugging = new Debugging(builder);
|
||||
buffers = new Buffers(builder);
|
||||
}
|
||||
builder.pop();
|
||||
}
|
||||
|
||||
public static class Threading
|
||||
{
|
||||
public final ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
|
||||
public final ForgeConfigSpec.IntValue numberOfBufferBuilderThreads;
|
||||
|
||||
Threading(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IThreading.DESC).push(this.getClass().getSimpleName());
|
||||
|
||||
MinDefaultMax<Integer> minDefaultMax = IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT;
|
||||
numberOfWorldGenerationThreads = builder
|
||||
.comment("\n\n"
|
||||
+ IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DESC)
|
||||
.defineInRange("numberOfWorldGenerationThreads", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue);
|
||||
|
||||
|
||||
minDefaultMax = IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX;
|
||||
numberOfBufferBuilderThreads = builder
|
||||
.comment("\n\n"
|
||||
+ IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX)
|
||||
.defineInRange("numberOfBufferBuilderThreads", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// Debug Options //
|
||||
//===============//
|
||||
public static class Debugging
|
||||
{
|
||||
public final ForgeConfigSpec.BooleanValue drawLods;
|
||||
public final ForgeConfigSpec.EnumValue<DebugMode> debugMode;
|
||||
public final ForgeConfigSpec.BooleanValue enableDebugKeybindings;
|
||||
|
||||
Debugging(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IDebugging.DESC).push(this.getClass().getSimpleName());
|
||||
|
||||
drawLods = builder
|
||||
.comment("\n\n"
|
||||
+ IDebugging.DRAW_LODS_DESC)
|
||||
.define("Enable Rendering", IDebugging.DRAW_LODS_DEFAULT);
|
||||
|
||||
debugMode = builder
|
||||
.comment("\n\n"
|
||||
+ IDebugging.DEBUG_MODE_DESC)
|
||||
.defineEnum("Debug Mode", IDebugging.DEBUG_MODE_DEFAULT);
|
||||
|
||||
enableDebugKeybindings = builder
|
||||
.comment("\n\n"
|
||||
+ IDebugging.DEBUG_KEYBINDINGS_ENABLED_DESC)
|
||||
.define("Enable Debug Keybinding", IDebugging.DEBUG_KEYBINDINGS_ENABLED_DEFAULT);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Buffers
|
||||
{
|
||||
public final ForgeConfigSpec.EnumValue<BufferRebuildTimes> rebuildTimes;
|
||||
|
||||
Buffers(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(IBuffers.DESC).push(this.getClass().getSimpleName());
|
||||
|
||||
rebuildTimes = builder
|
||||
.comment("\n\n"
|
||||
+ IBuffers.REBUILD_TIMES_DESC)
|
||||
.defineEnum("rebuildFrequency", IBuffers.REBUILD_TIMES_DEFAULT);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** {@link Path} to the configuration file of this mod */
|
||||
private static final Path CONFIG_PATH = Paths.get("config", ModInfo.NAME + ".toml");
|
||||
|
||||
public static final ForgeConfigSpec CLIENT_SPEC;
|
||||
public static final Client CLIENT;
|
||||
|
||||
static
|
||||
{
|
||||
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
|
||||
CLIENT_SPEC = specPair.getRight();
|
||||
CLIENT = specPair.getLeft();
|
||||
CommentedFileConfig clientConfig = CommentedFileConfig.builder(CONFIG_PATH)
|
||||
.writingMode(WritingMode.REPLACE)
|
||||
.build();
|
||||
clientConfig.load();
|
||||
clientConfig.save();
|
||||
CLIENT_SPEC.setConfig(clientConfig);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLoad(final ModConfig.Loading configEvent)
|
||||
{
|
||||
LogManager.getLogger().debug(ModInfo.NAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onFileChange(final ModConfig.Reloading configEvent)
|
||||
{
|
||||
LogManager.getLogger().debug(ModInfo.NAME, "Forge config just got changed on the file system!");
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user