Compare commits

...

89 Commits

Author SHA1 Message Date
cola98765 2834a73551 fix fog for fixed render distance 2021-12-10 20:06:22 +01:00
cola98765 5a7af89f5c fix calculation of view range. if it was k*32+x where k is int and 0<=x<16. To compare with old versions add 32 2021-12-10 20:06:14 +01:00
cola98765 eff491127b changed debug key to F8 and added chat messages when using debug keys. 2021-12-10 20:06:04 +01:00
Morippi 61502c32a6 fixewd generation not working 2021-12-10 19:18:22 +01:00
Morippi b7911ec4c8 Removed most of BlockPos and ChunkPos use 2021-12-10 19:05:27 +01:00
cola98765 1115c5f6c5 moved merge works now 19min on my PC 2021-12-10 09:28:10 +01:00
Morippi 5d001bf7ea changed some parameters 2021-12-09 20:26:19 +01:00
Morippi 4e732f7916 added a parameter 2021-12-09 20:25:55 +01:00
Morippi 30d392f861 Added field name and methods name for the merge 2021-12-09 20:25:01 +01:00
cola98765 f8910b0c3b move the merge 2021-12-09 19:59:50 +01:00
Morippi da4f423d10 Added maps to cache the biome blocks couples 2021-12-09 17:30:43 +01:00
Morippi 5a4c04b5a3 added getters and creators in the new format. 2021-12-09 17:30:18 +01:00
cola98765 cd50be6531 fix FAR_FIRST and AUTO options 2021-12-09 15:20:31 +01:00
cola98765 5d4698621c buffer rebuild due to time is no longer needed 2021-12-09 14:36:24 +01:00
Morippi aba392ace4 Fixed another small bug 2021-12-09 14:31:25 +01:00
Morippi d1c0ea123a Merge remote-tracking branch 'origin/main' into main 2021-12-09 14:30:12 +01:00
Morippi 816efb2837 Fixed a small bug 2021-12-09 14:30:06 +01:00
cola98765 0221a39819 wrong "-" sign 2021-12-09 14:20:30 +01:00
Morippi cc08707f32 Fixed some part of the new format 2021-12-09 13:50:59 +01:00
Morippi 8db253f886 Merge remote-tracking branch 'origin/main' into main 2021-12-09 13:50:48 +01:00
Morippi f59bdf15d7 Created the BlockBiomeCouple 2021-12-09 13:50:37 +01:00
cola98765 ac0d439b3f Merge remote-tracking branch 'origin/main' into main 2021-12-09 13:32:41 +01:00
cola98765 4e652c7573 just switch B and R and should be fine 2021-12-09 13:31:56 +01:00
Morippi 3686bfb4e0 switched two formats 2021-12-09 12:58:52 +01:00
Morippi fe1b2c2683 Added new format classes 2021-12-09 12:57:35 +01:00
cola98765 d5ed9a22fa more consistent skylight-blocklight order 2021-12-09 11:59:37 +01:00
cola98765 e3f9c974f8 partially fix shader based light 2021-12-09 11:57:05 +01:00
cola98765 daead98102 move where in region pos is calculated 2021-12-09 09:49:57 +01:00
James Seibel 5c31927d54 Add shaders files for shader based lighting (ops) 2021-12-08 23:07:10 -06:00
James Seibel f9fa1a5260 merge GLProxy 2021-12-08 23:05:14 -06:00
James Seibel d0472ee56d Add (buggy, unfinished) shader based lighting 2021-12-08 22:59:37 -06:00
Ran e4e21d2dc8 Merge branch 'main' into 'main'
Fix for WindowOS Render null Error

See merge request jeseibel/distant-horizons-core!3
2021-12-08 14:18:05 +00:00
TomTheFurry 79e4dce569 Fix for WindowOS Render null Error 2021-12-08 14:18:04 +00:00
cola98765 408f09c0f4 added and commented out time testing code 2021-12-07 13:05:34 +01:00
cola98765 65bfedc942 Merge remote-tracking branch 'origin/main' into main 2021-12-06 10:21:33 +01:00
cola98765 fab99cd4ec fix LOWEST HorizontalQuality 2021-12-06 10:21:15 +01:00
cola98765 019f4b7c1e fix LOWEST HorizontalQuality 2021-12-06 10:16:51 +01:00
cola98765 f3b6b15bcb reverted DrawResolutionOffset 2021-12-05 20:03:17 +01:00
cola98765 7c086cdc40 HorizontalScale is now a number 2-32 2021-12-05 17:03:38 +01:00
cola98765 5617e1312c updated DYNAMIC VanillaOverdraw setting. 2021-12-05 16:43:21 +01:00
cola98765 9dd51bfdde added DrawResolutionOffset to work with DrawResolution 2021-12-05 16:37:32 +01:00
James Seibel c9dc998eef Fix a few buffer building issues 2021-12-04 22:22:00 -06:00
Morippi 068df9d5e0 Change Box to VertexOptimizer and added the DataFormat folder with empty classes 2021-12-02 23:07:08 +01:00
cola98765 44dc7c96af UNTESTED: when loading and no save was found it will try to look in better DistanceGenerationMode AND VerticalQuality 2021-12-02 17:26:05 +01:00
cola98765 89cc3513f3 added save and load methods to be later used with server 2021-12-02 11:42:49 +01:00
cola98765 7a94db77ef small movements will no longer trigger buffer regen 2021-12-02 10:24:14 +01:00
James Seibel f2fc669b37 comment out configOverride for release a1.5.4 2021-12-01 22:41:56 -06:00
James Seibel a3d4163b67 Add GpuUpload auto selection and re-arrange the config options 2021-12-01 22:32:05 -06:00
James Seibel aea4542616 Add AUTO to generationPriority (chooses best based on current world) 2021-12-01 18:53:56 -06:00
Ran a489810d68 Remove @Nullable 2021-11-30 23:05:39 +06:00
cola98765 806a1e99db couple more warnings 2021-11-30 12:05:05 +01:00
cola98765 4843027e43 javadocs error (not really problem, but prevented me form scanning the code) 2021-11-30 11:50:36 +01:00
cola98765 af4ba453ca couple warnings 2021-11-30 11:45:47 +01:00
cola98765 adc5853f0b fix for potential bug with couple things at high altitudes 2021-11-30 11:21:03 +01:00
cola98765 c42ddd29a9 fix light on servers 2021-11-30 10:27:11 +01:00
James Seibel 9b7d3c083f Add buffer timeout and improve the uploading logic slightly 2021-11-29 21:23:45 -06:00
James Seibel 87bb4ae840 re-add applyConfigOverride 2021-11-29 20:18:14 -06:00
cola98765 b34f5e7f5f moved VERTICAL_OFFSET to DataPointUtil where WORLD_HEIGHT was 2021-11-29 09:09:24 +01:00
Eric a053a79d99 Simplify the createProjectionMatrix method and add VR support. 2021-11-28 21:33:12 -07:00
James Seibel 73c041e02f Fix a potential crash when teleporting 2021-11-28 18:14:28 -06:00
cola98765 f96a5fc794 potentially fix backwards compatibility 2021-11-29 00:20:47 +01:00
James Seibel d591458cd6 comment out the applyConfigOverrides for release a1.5.3 2021-11-28 16:54:11 -06:00
James Seibel 88305d8db3 Merge branch 'main' of gitlab.com:jeseibel/distant-horizons-core 2021-11-28 16:28:38 -06:00
James Seibel 40b0517656 Hopefully prevent a issue with linux file paths in GLProxy 2021-11-28 16:08:56 -06:00
cola98765 2289826363 implemented VERTICAL_OFFSET (-64) and change WORLD_HEIGHT (1024). those numbers mean the same as 1.17 "min_y" and "height" world settings.
TODO check if it's not significantly worse in performance.
2021-11-28 14:32:08 +01:00
James Seibel e1c2b2a0a9 Add a missing comment 2021-11-27 23:28:15 -06:00
James Seibel 70f7a2422b Add Legacy OpenGL vanilla fog removal 2021-11-27 23:16:45 -06:00
James Seibel 8ad6f184dd Add Black and white logo 2021-11-27 18:04:03 -06:00
James Seibel 4a93bde7be Improve the config descriptions 2021-11-27 16:17:01 -06:00
James Seibel b9dfd93b56 rename DistantHorizons_Server_Data -> Distant_Horizons_server_data 2021-11-27 14:48:34 -06:00
James Seibel 5e78c98f17 rename "lod server data" -> Distant_Horizons_Server_Data 2021-11-27 12:03:25 -06:00
James Seibel 31035ccb1e Add setUniform(color) 2021-11-27 10:32:04 -06:00
James Seibel e840a23d01 Add FogColorMode 2021-11-27 10:10:01 -06:00
James Seibel 08b8f8778a Merge branch 'skyfog' into 'main'
Added sky fog color option

See merge request jeseibel/distant-horizons-core!2
2021-11-27 15:38:59 +00:00
coolGi2007 044f87eef2 Added sky fog color option 2021-11-27 12:28:36 +00:00
James Seibel 30cd7fd4e0 remove a debug command 2021-11-26 21:20:32 -06:00
James Seibel fbf5dfaa9d Clean up Fog, remove Fast fog, close issue #77 (near-far incorrect center) 2021-11-26 21:13:54 -06:00
James Seibel 58d4bc7f0f Add fog as a fragment shader 2021-11-26 19:24:48 -06:00
James Seibel 511a771351 Add a few extra resource links to GLProxy 2021-11-25 12:00:52 -06:00
James Seibel e806098544 Fix rendering performance 2021-11-25 11:51:35 -06:00
James Seibel 4316cfbfd7 Merge branch 'ServerPatch' into 'main'
Fixed error when going into servers

See merge request jeseibel/distant-horizons-core!1
2021-11-25 14:02:51 +00:00
coolGi2007 6be4c0303f Fixed error when going into servers 2021-11-25 07:27:14 +00:00
James Seibel 7633c7bc70 Update Readme.md 2021-11-23 02:28:13 +00:00
James Seibel 0bc96a98cf Add support for disabling rendering 2021-11-21 22:14:46 -06:00
James Seibel a0529a310b Update Readme.md 2021-11-21 18:26:20 -06:00
James Seibel ace3c03019 Update IChunkWrapper.java 2021-11-21 15:28:19 -06:00
James Seibel 16b44695ec re-add the basic shaders 2021-11-21 15:27:33 -06:00
James Seibel 151ca3902f Replace MinecraftRenderWrapper with the interface 2021-11-21 15:27:23 -06:00
James Seibel 1ba24659bc Remove all Forge files 2021-11-21 14:01:39 -06:00
109 changed files with 3154 additions and 6272 deletions
+6 -56
View File
@@ -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">![Minecraft Level Of Detail (LOD) mod - Alpha 1.4](https://i.ytimg.com/vi_webp/H2tnvEVbO1c/mqdefault.webp)</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

-234
View File
@@ -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"
}
-4
View File
@@ -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
Binary file not shown.
-5
View File
@@ -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
Vendored
-172
View File
@@ -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
View File
@@ -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
{
@@ -103,19 +101,24 @@ 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");
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, MC.getProfiler());
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 +141,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);
@@ -168,14 +182,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.worldGeneration.LodGenWorker;
import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.objects.lod.LodDimension;
@@ -103,7 +104,7 @@ 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();
//LodNodeGenWorker.restartExecutorService();
//ThreadMapUtil.clearMaps();
@@ -115,13 +116,30 @@ 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();
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,15 +147,14 @@ 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();
LodGenWorker.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();
recalculateWidths = true;
ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
@@ -167,14 +184,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 +224,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)
@@ -30,6 +30,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import com.seibel.lod.core.enums.config.LodTemplate;
import net.minecraft.util.math.BlockPos;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
@@ -41,8 +43,8 @@ import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.config.GpuUploadMethod;
import com.seibel.lod.core.enums.config.VanillaOverdraw;
import com.seibel.lod.core.enums.rendering.GLProxyContext;
import com.seibel.lod.core.objects.Box;
import com.seibel.lod.core.objects.PosToRenderContainer;
import com.seibel.lod.core.objects.VertexOptimizer;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.objects.lod.LodRegion;
import com.seibel.lod.core.objects.lod.RegionPos;
@@ -68,7 +70,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
* rendered by the LodRenderer.
*
* @author James Seibel
* @version 11-21-2021
* @version 12-8-2021
*/
public class LodBufferBuilderFactory
{
@@ -81,6 +83,8 @@ public class LodBufferBuilderFactory
/** The threads used to generate buffers. */
public static final ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(), new ThreadFactoryBuilder().setNameFormat("Buffer-Builder-%d").build());
/**
* When uploading to a buffer that is too small,
* recreate it this many times bigger than the upload payload
@@ -94,6 +98,8 @@ public class LodBufferBuilderFactory
*/
public static final int DEFAULT_MEMORY_ALLOCATION = 1024;
public static int skyLightPlayer = 15;
/**
@@ -138,16 +144,19 @@ public class LodBufferBuilderFactory
/** this is used to prevent multiple threads creating, destroying, or using the buffers at the same time */
private final ReentrantLock bufferLock = new ReentrantLock();
private volatile Box[][] boxCache;
private volatile VertexOptimizer[][] vertexOptimizerCache;
private volatile PosToRenderContainer[][] setsToRender;
private volatile RegionPos center;
private volatile int centerRegionX = 0;
private volatile int centerRegionZ = 0;
/**
* This is the ChunkPosWrapper the player was at the last time the buffers were built.
* IE the center of the buffers last time they were built
*/
private volatile AbstractChunkPosWrapper drawableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
private volatile AbstractChunkPosWrapper buildableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
private volatile int drawableCenterChunkPosX = 0;
private volatile int drawableCenterChunkPosZ = 0;
private volatile int buildableCenterChunkPosX = 0;
private volatile int buildableCenterChunkPosZ = 0;
@@ -169,7 +178,7 @@ public class LodBufferBuilderFactory
* swapped with the drawable buffers in the LodRenderer to be drawn.
*/
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
AbstractBlockPosWrapper playerBlockPos, boolean fullRegen)
int playerX, int playerY, int playerZ, boolean fullRegen)
{
// only allow one generation process to happen at a time
@@ -180,10 +189,14 @@ public class LodBufferBuilderFactory
// setupBuffers hasn't been called yet
return;
if (MC.getCurrentLightMap() == null)
// the lighting hasn't loaded yet
return;
generatingBuffers = true;
Thread thread = new Thread(() -> generateLodBuffersThread(renderer, lodDim, playerBlockPos, fullRegen));
Thread thread = new Thread(() -> generateLodBuffersThread(renderer, lodDim, playerX, playerY, playerZ, fullRegen));
mainGenThread.execute(thread);
}
@@ -192,15 +205,17 @@ public class LodBufferBuilderFactory
// more easily edited by hot swapping. Because, As far as James is aware
// you can't hot swap lambda expressions.
private void generateLodBuffersThread(LodRenderer renderer, LodDimension lodDim,
AbstractBlockPosWrapper playerBlockPos, boolean fullRegen)
int playerX, int playerY, int playerZ, boolean fullRegen)
{
bufferLock.lock();
try
{
// round the player's block position down to the nearest chunk BlockPos
AbstractChunkPosWrapper playerChunkPos = WRAPPER_FACTORY.createChunkPos(playerBlockPos);
AbstractBlockPosWrapper playerBlockPosRounded = playerChunkPos.getWorldPosition();
int playerChunkX = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerX,LodUtil.CHUNK_DETAIL_LEVEL);
int playerChunkZ = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerZ,LodUtil.CHUNK_DETAIL_LEVEL);
int playerRegionX = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerX,LodUtil.REGION_DETAIL_LEVEL);
int playerRegionZ = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerZ,LodUtil.REGION_DETAIL_LEVEL);
//long startTime = System.currentTimeMillis();
@@ -209,32 +224,28 @@ public class LodBufferBuilderFactory
startBuffers(fullRegen, lodDim);
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
if (center == null)
center = playerRegionPos;
if (setsToRender == null)
setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()];
if (setsToRender.length != lodDim.getWidth())
setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()];
if (boxCache == null)
boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()];
if (vertexOptimizerCache == null)
vertexOptimizerCache = new VertexOptimizer[lodDim.getWidth()][lodDim.getWidth()];
if (boxCache.length != lodDim.getWidth())
boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()];
if (vertexOptimizerCache.length != lodDim.getWidth())
vertexOptimizerCache = new VertexOptimizer[lodDim.getWidth()][lodDim.getWidth()];
// this will be the center of the VBOs once they have been built
buildableCenterChunkPos = playerChunkPos;
buildableCenterChunkPosX = playerChunkX;
buildableCenterChunkPosZ = playerChunkZ;
//================================//
// create the nodeToRenderThreads //
//================================//
skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerBlockPos);
skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerX, playerY, playerZ);
for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++)
{
@@ -256,7 +267,10 @@ public class LodBufferBuilderFactory
// make sure the buffers weren't
// changed while we were running this method
if (currentBuffers == null || !currentBuffers[0].building())
{
ClientApi.LOGGER.info("Buffer building quit early");
return;
}
byte minDetail = region.getMinDetailLevel();
@@ -276,7 +290,7 @@ public class LodBufferBuilderFactory
int bufferIndex;
boolean posNotInPlayerChunk;
boolean adjPosInPlayerChunk;
Box box = ThreadMapUtil.getBox();
VertexOptimizer vertexOptimizer = ThreadMapUtil.getBox();
boolean[] adjShadeDisabled = ThreadMapUtil.getAdjShadeDisabledArray();
// determine how many LODs we can stack vertically
@@ -297,8 +311,8 @@ public class LodBufferBuilderFactory
lodDim.getPosToRender(
posToRender,
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
playerX,
playerZ);
@@ -316,14 +330,14 @@ public class LodBufferBuilderFactory
posX = posToRender.getNthPosX(index);
posZ = posToRender.getNthPosZ(index);
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkX;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkZ;
//We don't want to render this fake block if
//The block is inside the render distance with, is not bigger than a chunk and is positioned in a chunk set as vanilla rendered
//
//The block is in the player chunk or in a chunk adjacent to the player
if(isThisPositionGoingToBeRendered(detailLevel, posX, posZ, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance))
if(isThisPositionGoingToBeRendered(detailLevel, posX, posZ, playerChunkX, playerChunkZ, vanillaRenderedChunks, gameChunkRenderDistance))
{
continue;
}
@@ -338,14 +352,14 @@ public class LodBufferBuilderFactory
Arrays.fill(adjShadeDisabled, false);
//We check every adj block in each direction
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
{
xAdj = posX + Box.DIRECTION_NORMAL_MAP.get(lodDirection).x;
zAdj = posZ + Box.DIRECTION_NORMAL_MAP.get(lodDirection).z;
xAdj = posX + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).x;
zAdj = posZ + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).z;
long data;
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.getX();
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.getZ();
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkX;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkZ;
adjPosInPlayerChunk = (chunkXdist == 0 && chunkZdist == 0);
//If the adj block is rendered in the same region and with same detail
@@ -354,13 +368,13 @@ public class LodBufferBuilderFactory
// We avoid cases where the adjPosition is in player chunk while the position is not
// to always have a wall underwater
if(posToRender.contains(detailLevel, xAdj, zAdj)
&& !isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance)
&& !isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkX, playerChunkZ, vanillaRenderedChunks, gameChunkRenderDistance)
&& !(posNotInPlayerChunk && adjPosInPlayerChunk))
{
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++)
{
data = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex);
adjShadeDisabled[Box.DIRECTION_INDEX.get(lodDirection)] = false;
adjShadeDisabled[VertexOptimizer.DIRECTION_INDEX.get(lodDirection)] = false;
adjData.get(lodDirection)[verticalIndex] = data;
}
}
@@ -371,17 +385,17 @@ public class LodBufferBuilderFactory
adjData.get(lodDirection)[0] = DataPointUtil.EMPTY_DATA;
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkX, playerChunkZ, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
&& !DataPointUtil.isVoid(data))
{
adjShadeDisabled[Box.DIRECTION_INDEX.get(lodDirection)] = DataPointUtil.getAlpha(data) < 255;
adjShadeDisabled[VertexOptimizer.DIRECTION_INDEX.get(lodDirection)] = DataPointUtil.getAlpha(data) < 255;
}
}
}
// We render every vertical lod present in this position
// We only stop when we find a block that is void or non existing block
// We only stop when we find a block that is void or non-existing block
long data;
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
{
@@ -402,13 +416,13 @@ public class LodBufferBuilderFactory
//We extract the data to render
data = lodDim.getData(detailLevel, posX, posZ, verticalIndex);
//If the data is not renderable (Void or non existing) we stop since there is no data left in this position
//If the data is not renderable (Void or non-existing) we stop since there is no data left in this position
if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data))
break;
//We send the call to create the vertices
CONFIG.client().graphics().advancedGraphics().getLodTemplate().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData,
detailLevel, posX, posZ, box, renderer.previousDebugMode, adjShadeDisabled);
LodTemplate.CUBIC.template.addLodToBuffer(currentBuffers[bufferIndex], playerX, playerY, playerZ, data, adjData,
detailLevel, posX, posZ, vertexOptimizer, renderer.previousDebugMode, adjShadeDisabled);
}
} // for pos to in list to render
@@ -455,12 +469,12 @@ public class LodBufferBuilderFactory
}
finally
{
// clean up any potentially open resources
if (buildableBuffers != null)
closeBuffers(fullRegen, lodDim);
try
{
// clean up any potentially open resources
if (buildableBuffers != null)
closeBuffers(fullRegen, lodDim);
// upload the new buffers
uploadBuffers(fullRegen, lodDim);
}
@@ -477,12 +491,12 @@ public class LodBufferBuilderFactory
}
}
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, AbstractChunkPosWrapper playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, int chunkPosX, int chunkPosZ, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
// skip any chunks that Minecraft is going to render
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - chunkPosX;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - chunkPosZ;
// check if the chunk is on the border
boolean isItBorderPos;
@@ -520,108 +534,120 @@ public class LodBufferBuilderFactory
*/
public void setupBuffers(LodDimension lodDimension)
{
bufferLock.lock();
int numbRegionsWide = lodDimension.getWidth();
long regionMemoryRequired;
int numberOfBuffers;
GLProxy glProxy = GLProxy.getInstance();
GLProxyContext oldContext = glProxy.getGlContext();
glProxy.setGlContext(GLProxyContext.LOD_BUILDER);
previousRegionWidth = numbRegionsWide;
numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide];
buildableBuffers = new LodBufferBuilder[numbRegionsWide][numbRegionsWide][];
buildableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][];
drawableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][];
if (glProxy.bufferStorageSupported)
try
{
buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
}
for (int x = 0; x < numbRegionsWide; x++)
{
for (int z = 0; z < numbRegionsWide; z++)
bufferLock.lock();
int numbRegionsWide = lodDimension.getWidth();
long regionMemoryRequired;
int numberOfBuffers;
GLProxy glProxy = GLProxy.getInstance();
GLProxyContext oldContext = glProxy.getGlContext();
glProxy.setGlContext(GLProxyContext.LOD_BUILDER);
previousRegionWidth = numbRegionsWide;
numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide];
buildableBuffers = new LodBufferBuilder[numbRegionsWide][numbRegionsWide][];
buildableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][];
drawableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][];
if (glProxy.bufferStorageSupported)
{
regionMemoryRequired = DEFAULT_MEMORY_ALLOCATION;
// if the memory required is greater than the max buffer
// capacity, divide the memory across multiple buffers
if (regionMemoryRequired > LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY)
buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
}
for (int x = 0; x < numbRegionsWide; x++)
{
for (int z = 0; z < numbRegionsWide; z++)
{
numberOfBuffers = (int) regionMemoryRequired / LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY + 1;
regionMemoryRequired = DEFAULT_MEMORY_ALLOCATION;
// TODO shouldn't this be determined with regionMemoryRequired?
// always allocating the max memory is a bit expensive isn't it?
regionMemoryRequired = LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY;
numberOfBuffersPerRegion[x][z] = numberOfBuffers;
buildableBuffers[x][z] = new LodBufferBuilder[numberOfBuffers];
buildableVbos[x][z] = new LodVertexBuffer[numberOfBuffers];
drawableVbos[x][z] = new LodVertexBuffer[numberOfBuffers];
if (glProxy.bufferStorageSupported)
// if the memory required is greater than the max buffer
// capacity, divide the memory across multiple buffers
if (regionMemoryRequired > LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY)
{
buildableStorageBufferIds[x][z] = new int[numberOfBuffers];
drawableStorageBufferIds[x][z] = new int[numberOfBuffers];
numberOfBuffers = (int) regionMemoryRequired / LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY + 1;
// TODO shouldn't this be determined with regionMemoryRequired?
// always allocating the max memory is a bit expensive isn't it?
regionMemoryRequired = LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY;
numberOfBuffersPerRegion[x][z] = numberOfBuffers;
buildableBuffers[x][z] = new LodBufferBuilder[numberOfBuffers];
buildableVbos[x][z] = new LodVertexBuffer[numberOfBuffers];
drawableVbos[x][z] = new LodVertexBuffer[numberOfBuffers];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds[x][z] = new int[numberOfBuffers];
drawableStorageBufferIds[x][z] = new int[numberOfBuffers];
}
}
}
else
{
// we only need one buffer for this region
numberOfBuffersPerRegion[x][z] = 1;
buildableBuffers[x][z] = new LodBufferBuilder[1];
buildableVbos[x][z] = new LodVertexBuffer[1];
drawableVbos[x][z] = new LodVertexBuffer[1];
if (glProxy.bufferStorageSupported)
else
{
buildableStorageBufferIds[x][z] = new int[1];
drawableStorageBufferIds[x][z] = new int[1];
// we only need one buffer for this region
numberOfBuffersPerRegion[x][z] = 1;
buildableBuffers[x][z] = new LodBufferBuilder[1];
buildableVbos[x][z] = new LodVertexBuffer[1];
drawableVbos[x][z] = new LodVertexBuffer[1];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds[x][z] = new int[1];
drawableStorageBufferIds[x][z] = new int[1];
}
}
}
for (int i = 0; i < numberOfBuffersPerRegion[x][z]; i++)
{
buildableBuffers[x][z][i] = new LodBufferBuilder((int) regionMemoryRequired);
buildableVbos[x][z][i] = new LodVertexBuffer();
drawableVbos[x][z][i] = new LodVertexBuffer();
// create the initial mapped buffers (system memory)
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (glProxy.bufferStorageSupported)
for (int i = 0; i < numberOfBuffersPerRegion[x][z]; i++)
{
// create the buffer storage (GPU memory)
buildableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory
buildableBuffers[x][z][i] = new LodBufferBuilder((int) regionMemoryRequired);
buildableVbos[x][z][i] = new LodVertexBuffer();
drawableVbos[x][z][i] = new LodVertexBuffer();
// create the initial mapped buffers (system memory)
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (glProxy.bufferStorageSupported)
{
// create the buffer storage (GPU memory)
buildableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
}
}
glProxy.setGlContext(oldContext);
}
catch (Exception e)
{
ClientApi.LOGGER.info("setupBuffers ran into trouble: " + e.getMessage(), e);
}
finally
{
// this shouldn't normally happen, but just in case it sill prevent deadlock
bufferLock.unlock();
}
glProxy.setGlContext(oldContext);
bufferLock.unlock();
}
@@ -634,85 +660,95 @@ public class LodBufferBuilderFactory
*/
public void destroyBuffers()
{
bufferLock.lock();
// destroy the buffer storages if they aren't already
if (buildableStorageBufferIds != null)
try
{
for (int x = 0; x < buildableStorageBufferIds.length; x++)
bufferLock.lock();
// destroy the buffer storages if they aren't already
if (buildableStorageBufferIds != null)
{
for (int z = 0; z < buildableStorageBufferIds.length; z++)
for (int x = 0; x < buildableStorageBufferIds.length; x++)
{
for (int i = 0; i < buildableStorageBufferIds[x][z].length; i++)
for (int z = 0; z < buildableStorageBufferIds.length; z++)
{
int buildableId = buildableStorageBufferIds[x][z][i];
int drawableId = drawableStorageBufferIds[x][z][i];
// make sure the buffers are deleted in a openGL context
GLProxy.getInstance().recordOpenGlCall(() ->
for (int i = 0; i < buildableStorageBufferIds[x][z].length; i++)
{
GL15.glDeleteBuffers(buildableId);
GL15.glDeleteBuffers(drawableId);
});
}
}
}
}
buildableStorageBufferIds = null;
drawableStorageBufferIds = null;
// destroy the VBOs if they aren't already
if (buildableVbos != null)
{
for (int i = 0; i < buildableVbos.length; i++)
{
for (int j = 0; j < buildableVbos.length; j++)
{
for (int k = 0; k < buildableVbos[i][j].length; k++)
{
int buildableId;
int drawableId;
// variables passed into a lambda expression
// need to be effectively final, so we have
// to use an else statement here
if (buildableVbos[i][j][k] != null)
buildableId = buildableVbos[i][j][k].id;
else
buildableId = 0;
if (drawableVbos[i][j][k] != null)
drawableId = drawableVbos[i][j][k].id;
else
drawableId = 0;
GLProxy.getInstance().recordOpenGlCall(() ->
{
if (buildableId != 0)
int buildableId = buildableStorageBufferIds[x][z][i];
int drawableId = drawableStorageBufferIds[x][z][i];
// make sure the buffers are deleted in a openGL context
GLProxy.getInstance().recordOpenGlCall(() ->
{
GL15.glDeleteBuffers(buildableId);
if (drawableId != 0)
GL15.glDeleteBuffers(drawableId);
});
});
}
}
}
}
buildableStorageBufferIds = null;
drawableStorageBufferIds = null;
// destroy the VBOs if they aren't already
if (buildableVbos != null)
{
for (int i = 0; i < buildableVbos.length; i++)
{
for (int j = 0; j < buildableVbos.length; j++)
{
for (int k = 0; k < buildableVbos[i][j].length; k++)
{
int buildableId;
int drawableId;
// variables passed into a lambda expression
// need to be effectively final, so we have
// to use an else statement here
if (buildableVbos[i][j][k] != null)
buildableId = buildableVbos[i][j][k].id;
else
buildableId = 0;
if (drawableVbos[i][j][k] != null)
drawableId = drawableVbos[i][j][k].id;
else
drawableId = 0;
GLProxy.getInstance().recordOpenGlCall(() ->
{
if (buildableId != 0)
GL15.glDeleteBuffers(buildableId);
if (drawableId != 0)
GL15.glDeleteBuffers(drawableId);
});
}
}
}
}
buildableVbos = null;
drawableVbos = null;
// these don't contain any OpenGL objects, so
// they don't require any special clean-up
buildableBuffers = null;
}
catch (Exception e)
{
ClientApi.LOGGER.info("destroyBuffers ran into trouble: " + e.getMessage(), e);
}
finally
{
// this shouldn't normally happen, but just in case it sill prevent deadlock
bufferLock.unlock();
}
buildableVbos = null;
drawableVbos = null;
// these don't contain any OpenGL objects, so
// they don't require any special clean-up
buildableBuffers = null;
bufferLock.unlock();
}
/** Calls begin on each of the buildable BufferBuilders. */
@@ -753,6 +789,7 @@ public class LodBufferBuilderFactory
private void uploadBuffers(boolean fullRegen, LodDimension lodDim)
{
GLProxy glProxy = GLProxy.getInstance();
long fence = 0;
try
{
@@ -761,15 +798,26 @@ public class LodBufferBuilderFactory
glProxy.setGlContext(GLProxyContext.LOD_BUILDER);
// determine the upload method
GpuUploadMethod uploadMethod = CONFIG.client().graphics().advancedGraphics().getGpuUploadMethod();
GpuUploadMethod uploadMethod = CONFIG.client().advanced().buffers().getGpuUploadMethod();
if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
{
// if buffer storage isn't supported
// default to SUB_DATA
CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(GpuUploadMethod.SUB_DATA);
CONFIG.client().advanced().buffers().setGpuUploadMethod(GpuUploadMethod.SUB_DATA);
uploadMethod = GpuUploadMethod.SUB_DATA;
}
// determine the upload timeout
int uploadTimeoutInMS = CONFIG.client().advanced().buffers().getGpuUploadTimeoutInMilliseconds();
// James has no idea if this does anything helpful,
// but in theory it should prevent OpenGL from drawing and
// writing to a buffer at the same time.
GL45.glMemoryBarrier(GL45.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
fence = GL45.glFenceSync(GL45.GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// actually upload the buffers
for (int x = 0; x < buildableVbos.length; x++)
{
@@ -780,16 +828,22 @@ public class LodBufferBuilderFactory
for (int i = 0; i < buildableBuffers[x][z].length; i++)
{
ByteBuffer uploadBuffer = buildableBuffers[x][z][i].getCleanedByteBuffer();
int storageBufferId = 0;
if (buildableStorageBufferIds != null)
storageBufferId = buildableStorageBufferIds[x][z][i];
vboUpload(buildableVbos[x][z][i], storageBufferId, uploadBuffer, true, uploadMethod);
vboUpload(x,z,i, uploadBuffer, true, uploadMethod);
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
// upload buffers over an extended period of time
// to hopefully prevent stuttering.
if (uploadTimeoutInMS != 0)
Thread.sleep(uploadTimeoutInMS);
GL15.glFinish();
}
}
}
}
// make sure all of the uploads finish before continuing
GL45.glClientWaitSync(fence, GL45.GL_SYNC_FLUSH_COMMANDS_BIT, 5L * 1000000000); // wait up to 5 seconds
}
catch (Exception e)
{
@@ -800,7 +854,9 @@ public class LodBufferBuilderFactory
finally
{
GL15.glFinish();
if (fence != 0)
GL45.glDeleteSync(fence);
// close the context so it can be re-used later.
// I'm guessing we can't just leave it because the executor service
// does something that invalidates the OpenGL context.
@@ -809,14 +865,24 @@ public class LodBufferBuilderFactory
}
/** Uploads the uploadBuffer so the GPU can use it. */
private void vboUpload(LodVertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer,
private void vboUpload(int xIndex, int zIndex, int iIndex, ByteBuffer uploadBuffer,
boolean allowBufferExpansion, GpuUploadMethod uploadMethod)
{
// get the vbos, buffers, ids, etc.
int storageBufferId = 0;
if (buildableStorageBufferIds != null)
storageBufferId = buildableStorageBufferIds[xIndex][zIndex][iIndex];
LodVertexBuffer vbo = buildableVbos[xIndex][zIndex][iIndex];
// this shouldn't happen, but just to be safe
if (vbo.id != -1 && GLProxy.getInstance().getGlContext() == GLProxyContext.LOD_BUILDER)
{
// this is how many points will be rendered
vbo.vertexCount = (uploadBuffer.capacity() / ((Float.BYTES * 3) + (Byte.BYTES * 4))); // TODO make this change with the LodTemplate
vbo.vertexCount = (uploadBuffer.capacity() / ((Float.BYTES * 3) + (Byte.BYTES * 4) + Byte.BYTES + Byte.BYTES)); // TODO make this change with the LodTemplate
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
try
@@ -855,7 +921,7 @@ public class LodBufferBuilderFactory
// recursively try to upload into the newly created buffer storage
// but don't recurse again if that fails
// (we don't want an infinitely expanding buffer!)
vboUpload(vbo, storageBufferId, uploadBuffer, false, uploadMethod);
vboUpload(xIndex,zIndex,iIndex, uploadBuffer, false, uploadMethod);
}
}
else
@@ -868,11 +934,6 @@ public class LodBufferBuilderFactory
// (uploading into GPU memory directly can only be done
// through the glCopyBufferSubData/glCopyNamed... methods)
GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity());
// alternative way that doesn't require GL45
// GL15.glBindBuffer(GL45.GL_COPY_WRITE_BUFFER, storageBufferId);
// GL45.glCopyBufferSubData(GL15.GL_ARRAY_BUFFER, GL45.GL_COPY_WRITE_BUFFER, 0, 0, uploadBuffer.capacity());
// GL15.glBindBuffer(GL45.GL_COPY_WRITE_BUFFER, 0);
}
}
else if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING)
@@ -888,14 +949,14 @@ public class LodBufferBuilderFactory
// map buffer range is better since it can be explicitly unsynchronized
if (GLProxy.getInstance().mapBufferRangeSupported)
vboBuffer = GL30.glMapBufferRange(GL30.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
vboBuffer = GL30.glMapBufferRange(GL30.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT | GL30.GL_MAP_INVALIDATE_BUFFER_BIT);
else
vboBuffer = GL15.glMapBuffer(GL30.GL_ARRAY_BUFFER, uploadBuffer.capacity());
if (vboBuffer == null)
{
GL15.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
GL15.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_STATIC_DRAW);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
}
else
@@ -909,7 +970,8 @@ public class LodBufferBuilderFactory
// high stutter, low GPU usage
// But simplest/most compatible
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer, GL15.GL_DYNAMIC_DRAW);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer.capacity(), GL15.GL_STATIC_DRAW);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer, GL15.GL_STATIC_DRAW);
}
else
{
@@ -919,7 +981,7 @@ public class LodBufferBuilderFactory
long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER)
{
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_STATIC_DRAW);
}
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
}
@@ -947,22 +1009,34 @@ public class LodBufferBuilderFactory
// since this is called on the main render thread
if (bufferLock.tryLock())
{
LodVertexBuffer[][][] tmpVbo = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmpVbo;
int[][][] tmpStorage = drawableStorageBufferIds;
drawableStorageBufferIds = buildableStorageBufferIds;
buildableStorageBufferIds = tmpStorage;
drawableCenterChunkPos = buildableCenterChunkPos;
// the vbos have been swapped
switchVbos = false;
bufferLock.unlock();
try
{
LodVertexBuffer[][][] tmpVbo = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmpVbo;
int[][][] tmpStorage = drawableStorageBufferIds;
drawableStorageBufferIds = buildableStorageBufferIds;
buildableStorageBufferIds = tmpStorage;
drawableCenterChunkPosX = buildableCenterChunkPosX;
drawableCenterChunkPosZ = buildableCenterChunkPosZ;
// the vbos have been swapped
switchVbos = false;
}
catch (Exception e)
{
// this shouldn't normally happen, but just in case it sill prevent deadlock
ClientApi.LOGGER.info("getVertexBuffers ran into trouble: " + e.getMessage(), e);
}
finally
{
bufferLock.unlock();
}
}
return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPos);
return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPosX, drawableCenterChunkPosZ);
}
/** A simple container to pass multiple objects back in the getVertexBuffers method. */
@@ -970,13 +1044,15 @@ public class LodBufferBuilderFactory
{
public final LodVertexBuffer[][][] vbos;
public final int[][][] storageBufferIds;
public final AbstractChunkPosWrapper drawableCenterChunkPos;
public int drawableCenterChunkPosX;
public int drawableCenterChunkPosZ;
public VertexBuffersAndOffset(LodVertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, AbstractChunkPosWrapper newDrawableCenterChunkPos)
public VertexBuffersAndOffset(LodVertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, int newDrawableCenterChunkPosX, int newDrawableCenterChunkPosZ)
{
vbos = newVbos;
storageBufferIds = newStorageBufferIds;
drawableCenterChunkPos = newDrawableCenterChunkPos;
drawableCenterChunkPosX = newDrawableCenterChunkPosX;
drawableCenterChunkPosZ = newDrawableCenterChunkPosZ;
}
}
@@ -23,7 +23,7 @@ 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.VertexOptimizer;
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
@@ -32,21 +32,24 @@ 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
* @version 12-8-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);
public abstract void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, 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)
int color, byte skyLightValue, byte blockLightValue)
{
// 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();
// TODO re-add transparency 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();
}
}
@@ -23,17 +23,15 @@ 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.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 com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
/**
* Builds LODs as rectangular prisms.
* @author James Seibel
* @version 11-8-2021
* @version 12-8-2021
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
@@ -44,10 +42,10 @@ public class CubicLodTemplate 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)
public void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
{
if (box == null)
if (vertexOptimizer == null)
return;
// equivalent to 2^detailLevel
@@ -61,29 +59,29 @@ public class CubicLodTemplate extends AbstractLodTemplate
generateBoundingBox(
box,
vertexOptimizer,
DataPointUtil.getHeight(data),
DataPointUtil.getDepth(data),
blockWidth,
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
bufferCenterBlockPos,
playerX,
playerY,
playerZ,
adjData,
color,
DataPointUtil.getLightSkyAlt(data),
DataPointUtil.getLightBlock(data),
adjShadeDisabled);
addBoundingBoxToBuffer(buffer, box);
addBoundingBoxToBuffer(buffer, vertexOptimizer);
}
private void generateBoundingBox(Box box,
private void generateBoundingBox(VertexOptimizer vertexOptimizer,
int height, int depth, int width,
double xOffset, double yOffset, double zOffset,
AbstractBlockPosWrapper bufferCenterBlockPos,
int playerX, int playerY, int playerZ,
Map<LodDirection, long[]> adjData,
int color,
int skyLight,
int blockLight,
int color, byte skyLight, byte blockLight,
boolean[] adjShadeDisabled)
{
// don't add an LOD if it is empty
@@ -98,41 +96,36 @@ public class CubicLodTemplate extends AbstractLodTemplate
// 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);
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 void addBoundingBoxToBuffer(LodBufferBuilder buffer, Box box)
private void addBoundingBoxToBuffer(LodBufferBuilder buffer, VertexOptimizer vertexOptimizer)
{
int color;
int skyLight;
int blockLight;
for (LodDirection lodDirection : Box.DIRECTIONS)
byte skyLight;
byte blockLight;
for (LodDirection lodDirection : VertexOptimizer.DIRECTIONS)
{
if(box.isCulled(lodDirection))
continue;
int verticalFaceIndex = 0;
while (box.shouldRenderFace(lodDirection, verticalFaceIndex))
while (vertexOptimizer.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);
skyLight = vertexOptimizer.getSkyLight(lodDirection, verticalFaceIndex);
blockLight = (byte) vertexOptimizer.getBlockLight();
color = vertexOptimizer.getColor(lodDirection);
addPosAndColor(buffer,
box.getX(lodDirection, vertexIndex),
box.getY(lodDirection, vertexIndex, verticalFaceIndex),
box.getZ(lodDirection, vertexIndex),
color);
vertexOptimizer.getX(lodDirection, vertexIndex),
vertexOptimizer.getY(lodDirection, vertexIndex, verticalFaceIndex) + DataPointUtil.VERTICAL_OFFSET,
vertexOptimizer.getZ(lodDirection, vertexIndex),
color, skyLight, blockLight );
}
verticalFaceIndex++;
}
@@ -24,7 +24,7 @@ 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.VertexOptimizer;
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
@@ -39,8 +39,8 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
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)
public void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
@@ -24,7 +24,7 @@ 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.VertexOptimizer;
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
@@ -37,8 +37,8 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
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)
public void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
@@ -56,7 +56,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
* @author James Seibel
* @version 10-22-2021
*/
public class LodBuilder
@SuppressWarnings("GrazieInspection") public class LodBuilder
{
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
private static final IBlockColorSingletonWrapper BLOCK_COLOR = SingletonHandler.get(IBlockColorSingletonWrapper.class);
@@ -109,6 +109,7 @@ public class LodBuilder
Thread thread = new Thread(() ->
{
//noinspection GrazieInspection
try
{
// we need a loaded client world in order to
@@ -161,6 +162,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 +170,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 +198,13 @@ public class LodBuilder
long[] data;
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 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);
lodDim.addVerticalData(detailLevel, posX, posZ, data, false);
}
posX = LevelPosUtil.convert((byte) 0, chunk.getChunkPosX() * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getChunkPosZ() * 16 + startZ, detail.detailLevel);
lodDim.mergeMultiData(detailLevel, posX, posZ, false, dataToMergeVertical, DataPointUtil.WORLD_HEIGHT / 2 + 1);
}
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 +214,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,23 +231,22 @@ 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); //DataPointUtil.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET + 1;
int count = 0;
boolean topBlock = true;
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)
@@ -264,27 +258,23 @@ public class LodBuilder
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);
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,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, zAbs, hasCeiling, hasSkyLight, topBlock);
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs);
}
lightBlock = light & 0b1111;
lightSky = (light >> 4) & 0b1111;
isDefault = ((light >> 8)) == 1;
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height, depth, color, lightSky, lightBlock, generation, isDefault);
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height - DataPointUtil.VERTICAL_OFFSET, depth - DataPointUtil.VERTICAL_OFFSET, color, lightSky, lightBlock, generation, isDefault);
topBlock = false;
yAbs = depth - 1;
count++;
@@ -297,14 +287,13 @@ 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)
{
short depth = DEFAULT_DEPTH;
for (int y = yAbs; y >= 0; y--)
{
blockPos.set(xAbs, y, zAbs);
if (!isLayerValidLodPoint(chunk, blockPos))
if (!isLayerValidLodPoint(chunk, xAbs, y, zAbs))
{
depth = (short) (y + 1);
break;
@@ -314,8 +303,9 @@ public class LodBuilder
}
/** 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;
if (config.useHeightmap)
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
@@ -323,8 +313,7 @@ public class LodBuilder
{
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,19 +333,18 @@ 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
@@ -364,8 +352,7 @@ public class LodBuilder
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);
aboveColorInt = getColorForBlock(chunk, x, y, z);
}
//if (colorInt == 0 && yAbs > 0)
@@ -382,7 +369,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 +378,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 +404,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 +415,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,24 +460,19 @@ 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();
IBlockColorWrapper blockColorWrapper;
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(x, y, z);
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;
@@ -497,7 +482,7 @@ public class LodBuilder
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 +502,15 @@ 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);
return !block.isToAvoid()
&& !(nonFullAvoidance && block.isNonFull())
&& !(noCollisionAvoidance && block.hasNoCollision());
@@ -84,7 +84,7 @@ public class LodGenWorker
// 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
// want to cause more lag than necessary or queue up
// requests that may end up being unneeded.
thread.run();
}
@@ -105,7 +105,7 @@ public class LodGenWorker
private static class LodChunkGenThread implements Runnable
{
private AbstractWorldGeneratorWrapper worldGenWrapper;
private final AbstractWorldGeneratorWrapper worldGenWrapper;
public final LodDimension lodDim;
public final DistanceGenerationMode generationMode;
@@ -126,7 +126,7 @@ public class LodGenWorker
@Override
public void run()
{
try
//try
{
// only generate LodChunks if they can
// be added to the current LodDimension
@@ -172,19 +172,19 @@ public class LodGenWorker
}// if in range
}
catch (Exception e)
{
ClientApi.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
e.printStackTrace();
}
finally
{
//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
@@ -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,14 @@
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;
}
@@ -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 byte formatLightAsByte(byte skyLight, byte blockLight)
{
return (byte) (((skyLight & SKY_LIGHT_MASK) << BYTE_SKY_LIGHT_SHIFT) | ((blockLight & BLOCK_LIGHT_MASK) << BYTE_BLOCK_LIGHT_SHIFT));
}
public int formatLightAsInt(byte skyLight, byte blockLight)
{
return ((skyLight & SKY_LIGHT_MASK) << INT_SKY_LIGHT_SHIFT) | ((blockLight & BLOCK_LIGHT_MASK) << INT_BLOCK_LIGHT_SHIFT);
}
public int convertByteToIntFormat(byte lights)
{
return 0;
}
public byte getSkyLight(byte lights)
{
return (byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public byte getBlockLight(byte lights)
{
return (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
}
}
@@ -0,0 +1,78 @@
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 String toString(short verticalData, short positionData)
{
return getLodCount(verticalData) + " " +
getLightFlag(verticalData) + " " +
getGenerationMode(verticalData) + " " +
isVoid(verticalData) + " " +
doesItExist(verticalData) + " " +'\n';
}
public static byte getLodCount(short dataPoint)
{
return (byte) ((dataPoint >>> LOD_COUNT_SHIFT) & LOD_COUNT_MASK);
}
public static boolean getLightFlag(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);
}
}
@@ -0,0 +1,81 @@
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 short WORLD_HEIGHT = MAX_WORLD_HEIGHT - MIN_WORLD_HEIGHT;
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 EMPTY_LOD_SHIFT = 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 EMPTY_LOD_MASK = 0b1;
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 - MIN_WORLD_HEIGHT) & HEIGHT_MASK) << HEIGHT_SHIFT;
verticalData |= ((depth - MIN_WORLD_HEIGHT) & 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 |= EMPTY_LOD_MASK << EMPTY_LOD_SHIFT;
return verticalData;
}
public static String toString(int verticalData, short positionData)
{
return getHeight(verticalData) + " " +
getDepth(verticalData) + " " +
getLevel(verticalData) + " " +
isTransparent(verticalData) + " " +
isBottom(verticalData) + " " +
doesItExist(verticalData) + " " + '\n';
}
public static short getHeight(int verticalData)
{
return (short) (((verticalData >>> HEIGHT_SHIFT) & HEIGHT_MASK) + MIN_WORLD_HEIGHT);
}
public static short getDepth(int verticalData)
{
return (short) (((verticalData >>> DEPTH_SHIFT) & DEPTH_MASK) + MIN_WORLD_HEIGHT);
}
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 >>> EMPTY_LOD_SHIFT) & EMPTY_LOD_MASK) == 1);
}
}
@@ -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;
}
}
@@ -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,
}
@@ -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
}
@@ -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,7 +19,7 @@
package com.seibel.lod.core.handlers;
import com.seibel.lod.core.enums.rendering.FogQuality;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
import com.seibel.lod.core.objects.math.Mat4f;
/**
@@ -35,15 +35,15 @@ import com.seibel.lod.core.objects.math.Mat4f;
* different MC versions.
*
* @author James Seibel
* @version 11-20-2021
* @version 11-26-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.
@@ -54,5 +54,5 @@ public interface IReflectionHandler
* @param newFarClipPlane the new far clip plane value.
* @return The modified matrix.
*/
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane);
Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane);
}
@@ -23,6 +23,7 @@ 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;
@@ -77,7 +78,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 = 7;
/**
* Allow saving asynchronously, but never try to save multiple regions
@@ -129,28 +130,40 @@ public class LodDimensionFileHandler
{
//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);
VerticalQuality tempVerticalQuality = VerticalQuality.HIGH;
do {
DistanceGenerationMode tempGenMode = DistanceGenerationMode.FULL;
do {
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;
} while (tempGenMode != generationMode);
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 (tempVerticalQuality == VerticalQuality.HIGH)
tempVerticalQuality = VerticalQuality.MEDIUM;
else if (tempVerticalQuality == VerticalQuality.MEDIUM)
tempVerticalQuality = VerticalQuality.LOW;
} while (tempVerticalQuality != verticalQuality);
if (!file.exists())
//there wasn't a file, don't return anything
continue;
@@ -169,7 +182,7 @@ public class LodDimensionFileHandler
fileVersion = inputStream.read();
// check if this file can be read by this file handler
if (fileVersion < LOD_SAVE_FILE_VERSION)
if (fileVersion < 6)
{
// the file we are reading is an older version,
// close the reader and delete the file.
@@ -195,17 +208,24 @@ public class LodDimensionFileHandler
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));
else if (fileVersion == 6)
{
//this is old, but readable version
byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel);
inputStream.read(data);
inputStream.close();
// add the data to our region
region.addLevelContainer(new VerticalLevelContainer(data, 6));
} else
{
// 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, LOD_SAVE_FILE_VERSION));
}
}
catch (IOException ioEx)
{
@@ -365,27 +385,69 @@ public class LodDimensionFileHandler
}
public void saveRegionFile (byte[] regionFile, RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
{
int regionX = regionPos.x;
int regionZ = regionPos.z;
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
if (fileName != null)
{
File oldFile = new File(fileName);
File newFile = new File(fileName + TMP_FILE_EXTENSION);
try (OutputStream os = new FileOutputStream(newFile))
{
os.write(regionFile);
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
os.close();
}
catch (IOException ioEx)
{
ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
ioEx.printStackTrace();
}
}
}
public byte[] getRegionFile (RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
{
int regionX = regionPos.x;
int regionZ = regionPos.z;
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
if (fileName != null)
{
File file = new File(fileName);
try (InputStream is = new FileInputStream(file))
{
byte[] data = ThreadMapUtil.getSaveContainer(detailLevel);
is.read(data);
is.close();
return Arrays.copyOf(data, (int) file.length());
}
catch (IOException ioEx)
{
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
ioEx.printStackTrace();
}
}
return new byte[0];
}
//================//
// helper methods //
//================//
public byte[] getHashFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
public int getHashFromFile(RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, 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];
if (fileName == null)
return 0;
File file = new File(fileName);
return file.hashCode();
}
@@ -25,7 +25,7 @@ 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.enums.rendering.FogDrawMode;
import com.seibel.lod.core.objects.math.Mat4f;
/**
@@ -35,7 +35,7 @@ import com.seibel.lod.core.objects.math.Mat4f;
* presence/absence of other mods.
*
* @author James Seibel
* @version 11-20-2021
* @version 11-26-2021
*/
public class ReflectionHandler implements IReflectionHandler
{
@@ -100,14 +100,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 +129,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;
}
}
@@ -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)
{
@@ -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,11 @@ 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;
/** creates an empty box */
@SuppressWarnings("serial")
public Box()
public VertexOptimizer()
{
boxOffset = new int[3];
boxWidth = new int[3];
@@ -236,8 +233,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 +305,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
@@ -364,9 +334,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 +341,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;
}
@@ -521,17 +488,6 @@ 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.
*/
@@ -112,4 +112,6 @@ public interface LevelContainer
* @return data as a String
*/
int getMaxNumberOfLods();
void mergeMultiData(int posX, int posZ, long[] dataToMergeVertical, int inputVerticalData);
}
@@ -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);
@@ -551,7 +551,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 +636,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 +679,13 @@ 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() == GenerationPriority.AUTO && 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);
}
/**
@@ -905,4 +909,39 @@ public class LodDimension
{
isRegionDirty[i][j] = val;
}
public void mergeMultiData(byte detailLevel, int posX, int posZ, boolean dontSave, long[] dataToMergeVertical, int inputVerticalData)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
// don't continue if the region can't be saved
LodRegion region = getRegion(regionPosX, regionPosZ);
if (region == null)
return;
region.mergeMultiData(detailLevel, posX, posZ, dataToMergeVertical, inputVerticalData);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
try
{
// mark the region as dirty, so it will be saved to disk
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
regenRegionBuffer[xIndex][zIndex] = true;
regenDimensionBuffers = true;
}
catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// If this happens, the method was probably
// called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
}
}
}
@@ -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);
}
@@ -382,7 +385,7 @@ public class LodRegion
{
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
{
if (!requireCorrectDetailLevel)
if (requireCorrectDetailLevel)
childrenCount++;
else
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
@@ -391,7 +394,7 @@ public class LodRegion
}
if (!requireCorrectDetailLevel)
if (requireCorrectDetailLevel)
{
// If all the four children exist go deeper
if (childrenCount == 4)
@@ -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);
}
@@ -608,4 +610,16 @@ public class LodRegion
{
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
}
public void mergeMultiData(byte detailLevel, int posX, int posZ, long[] dataToMergeVertical, int inputVerticalData)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
// The dataContainer could have null entries if the
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
this.dataContainer[detailLevel].mergeMultiData(posX, posZ, dataToMergeVertical, inputVerticalData);
}
}
@@ -19,6 +19,7 @@
package com.seibel.lod.core.objects.lod;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
@@ -35,10 +36,22 @@ public class VerticalLevelContainer implements LevelContainer
public final byte detailLevel;
public final int size;
public final int chunkCount = 0;
public final int chunkSize = 0;
public final int maxVerticalData;
public final long[] dataContainer;
//length should be chunkCount
public final byte[] verticalSize = null;
//length should be chunkCount
public final short[] positionData = null;
public final int[][] verticalData = null;
public final int[][] colorData = null;
public final byte[][] lightData = null;
public VerticalLevelContainer(byte detailLevel)
{
this.detailLevel = detailLevel;
@@ -56,19 +69,13 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public void clear(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
{
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = DataPointUtil.EMPTY_DATA;
}
}
@Override
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data;
return true;
}
@@ -76,8 +83,6 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public boolean addVerticalData(long[] data, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data[verticalIndex];
return true;
@@ -92,15 +97,13 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public long getData(int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex];
}
@Override
public long getSingleData(int posX, int posZ)
{
return getData(posX, posZ, 0);
return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData];
}
@Override
@@ -117,30 +120,77 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public boolean doesItExist(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return DataPointUtil.doesItExist(getSingleData(posX, posZ));
}
public VerticalLevelContainer(byte[] inputData)
public VerticalLevelContainer(byte[] inputData, int version)
{
int tempMaxVerticalData;
int tempIndex;
int index = 0;
long newData;
detailLevel = inputData[index];
index++;
maxVerticalData = inputData[index] & 0b01111111;
tempMaxVerticalData = inputData[index] & 0b01111111;
index++;
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int x = size * size * maxVerticalData;
this.dataContainer = new long[x];
for (int i = 0; i < x; i++)
int x = size * size * tempMaxVerticalData;
long[] tempDataContainer = new long[x];
if (version == 6)
{
newData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
dataContainer[i] = newData;
for (int i = 0; i < x; i++)
{
newData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
newData = DataPointUtil.createDataPoint(
DataPointUtil.getAlpha(newData),
DataPointUtil.getRed(newData),
DataPointUtil.getGreen(newData),
DataPointUtil.getBlue(newData),
DataPointUtil.getHeight(newData) - DataPointUtil.VERTICAL_OFFSET,
DataPointUtil.getDepth(newData) - DataPointUtil.VERTICAL_OFFSET,
DataPointUtil.getLightSky(newData),
DataPointUtil.getLightBlock(newData),
DataPointUtil.getGenerationMode(newData),
DataPointUtil.getFlag(newData)
);
tempDataContainer[i] = newData;
}
}
else //if (version == 7)
{
for (int i = 0; i < x; i++)
{
newData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
tempDataContainer[i] = newData;
}
}
if (tempMaxVerticalData > DetailDistanceUtil.getMaxVerticalData(detailLevel))
{
int tempMaxVerticalData2 = DetailDistanceUtil.getMaxVerticalData(detailLevel);
long[] dataToMerge = new long[tempMaxVerticalData];
long[] tempDataContainer2 = new long[size * size * tempMaxVerticalData2];
for (int i = 0; i < size * size; i++)
{
System.arraycopy(tempDataContainer, i * tempMaxVerticalData, dataToMerge, 0, tempMaxVerticalData);
dataToMerge = DataPointUtil.mergeMultiData(dataToMerge, tempMaxVerticalData, tempMaxVerticalData2);
System.arraycopy(dataToMerge, 0, tempDataContainer2, i * tempMaxVerticalData2, tempMaxVerticalData2);
}
maxVerticalData = tempMaxVerticalData2;
this.dataContainer = tempDataContainer2;
}
else
{
maxVerticalData = tempMaxVerticalData;
this.dataContainer = tempDataContainer;
}
}
@@ -159,9 +209,6 @@ public class VerticalLevelContainer implements LevelContainer
int lowerMaxVertical = dataToMerge.length / 4;
int childPosX;
int childPosZ;
long[] data;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
@@ -172,9 +219,7 @@ public class VerticalLevelContainer implements LevelContainer
dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
}
}
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData());
addVerticalData(data, posX, posZ);
mergeMultiData(posX, posZ, dataToMerge, lowerMaxVertical);
}
@Override
@@ -209,6 +254,325 @@ public class VerticalLevelContainer implements LevelContainer
return tempData;
}
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;
}
}
/**
*
* @param positionDataToMerge
* @effect
*/
public void computePositionData(short[] positionDataToMerge)
{
}
/**
*
* @param verticalDataToMerge
* @param maxVerticalData max vertical size of the merged data
* @effect save in
*/
public void computeHeightAndDepthData(int[] verticalDataToMerge, int maxVerticalData)
{
}
/**
*
* @param colorDataToMerge
* @param lightDataToMerge
* @effect
*/
public void computeColorLightVerticalData(int[] colorDataToMerge, byte[] lightDataToMerge, int[] verticalDataToMerge)
{
}
/**
* This method merge column of multiple data together
* @param dataToMerge one or more columns of data
* @param inputVerticalData vertical size of an input data
*/
public void mergeMultiData(int posX, int posZ, long[] dataToMerge, int inputVerticalData)
{
int size = dataToMerge.length / inputVerticalData;
final int arrayPos = (posX * this.size + posZ) * maxVerticalData;
// We initialize the arrays that are going to be used
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((DataPointUtil.WORLD_HEIGHT / 2 + 1) * 2);
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 (DataPointUtil.doesItExist(singleData))
{
genMode = Math.min(genMode, DataPointUtil.getGenerationMode(singleData));
allEmpty = false;
if (!DataPointUtil.isVoid(singleData))
{
allVoid = false;
depth = DataPointUtil.getDepth(singleData);
height = DataPointUtil.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;
if (allVoid)
{
dataContainer[arrayPos] = DataPointUtil.createVoidDataPoint(genMode);
return;
}
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > maxVerticalData)
{
ii = DataPointUtil.WORLD_HEIGHT - DataPointUtil.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--)
{
if (j >= heightAndDepth.length / 2)
break;
height = heightAndDepth[j * 2];
depth = heightAndDepth[j * 2 + 1];
if ((depth == 0 && height == 0))
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 (DataPointUtil.doesItExist(singleData) && !DataPointUtil.isVoid(singleData))
{
if ((depth <= DataPointUtil.getDepth(singleData) && DataPointUtil.getDepth(singleData) <= height)
|| (depth <= DataPointUtil.getHeight(singleData) && DataPointUtil.getHeight(singleData) <= height))
{
if (DataPointUtil.getHeight(singleData) > DataPointUtil.getHeight(data))
data = singleData;
}
}
else
break;
}
if (!DataPointUtil.doesItExist(data))
{
singleData = dataToMerge[index * inputVerticalData];
data = DataPointUtil.createVoidDataPoint(DataPointUtil.getGenerationMode(singleData));
}
if (DataPointUtil.doesItExist(data))
{
allEmpty = false;
if (!DataPointUtil.isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha += DataPointUtil.getAlpha(data);
tempRed += DataPointUtil.getRed(data);
tempGreen += DataPointUtil.getGreen(data);
tempBlue += DataPointUtil.getBlue(data);
tempLightBlock += DataPointUtil.getLightBlock(data);
tempLightSky += DataPointUtil.getLightSky(data);
if (!DataPointUtil.getFlag(data)) allDefault = false;
}
tempGenMode = (byte) Math.min(tempGenMode, DataPointUtil.getGenerationMode(data));
}
else
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
}
if (allEmpty)
//no child has been initialized
dataContainer[arrayPos + j] = DataPointUtil.EMPTY_DATA;
else if (allVoid)
//all the children are void
dataContainer[arrayPos + j] = DataPointUtil.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;
dataContainer[arrayPos + j] = DataPointUtil.createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
}
}
}
@Override
@SuppressWarnings("unused")
public String toString()
@@ -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-8-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;
@@ -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();
}
}
@@ -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,9 +449,9 @@ public class LodBufferBuilder
public ByteBuffer getCleanedByteBuffer()
{
LodBufferBuilder.DrawState bufferbuilder$drawstate = this.vertexCounts.get(this.lastRenderedCountIndex++);
((Buffer) this.buffer).position(this.totalUploadedBytes);
this.buffer.position(this.totalUploadedBytes);
this.totalUploadedBytes += bufferbuilder$drawstate.vertexCount() * bufferbuilder$drawstate.format().getVertexSize();
((Buffer) this.buffer).limit(this.totalUploadedBytes);
this.buffer.limit(this.totalUploadedBytes);
if (this.lastRenderedCountIndex == this.vertexCounts.size() && this.vertices == 0)
{
this.clear();
@@ -449,7 +459,7 @@ public class LodBufferBuilder
ByteBuffer bytebuffer = this.buffer.slice();
bytebuffer.order(this.buffer.order()); // FORGE: Fix incorrect byte order
((Buffer) this.buffer).clear();
this.buffer.clear();
return bytebuffer; // the original method also returned bufferbuilder$drawstate
}
@@ -534,7 +544,7 @@ public class LodBufferBuilder
public void putBulkData(ByteBuffer buffer)
{
ensureCapacity(buffer.limit() + this.format.getVertexSize());
((Buffer) this.buffer).position(this.vertices * this.format.getVertexSize());
this.buffer.position(this.vertices * this.format.getVertexSize());
this.buffer.put(buffer);
this.vertices += buffer.limit() / this.format.getVertexSize();
this.nextElementByte += buffer.limit();
@@ -98,7 +98,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.vertexSize == vertexformat.vertexSize && 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;
@@ -17,19 +17,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers;
package com.seibel.lod.core.objects.rending;
import net.minecraft.world.gen.Heightmap;
import com.seibel.lod.core.enums.rendering.FogDistance;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
/**
* Stores any variables or code that
* may be shared between wrapper objects.
* This object is just a replacement for an array
* to make things easier to understand in the LodRenderer.
*
* @author James Seibel
* @version 11-20-2021
* @version 11-26-2021
*/
public class WrapperUtil
public class LodFogConfig
{
/** If we ever need to use a heightmap for any reason, use this one. */
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
public FogDrawMode fogDrawMode;
public FogDistance fogDistance;
public float nearFogStart = 0;
public float nearFogEnd = 0;
public float farFogStart = 0;
public float farFogEnd = 0;
}
@@ -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;
}
}
}
@@ -19,12 +19,14 @@
package com.seibel.lod.core.render;
import java.io.FileNotFoundException;
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.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities;
@@ -32,10 +34,12 @@ import org.lwjgl.opengl.GLCapabilities;
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.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,20 +47,24 @@ 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-8-2021
*/
public class GLProxy
{
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.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 GLProxy instance = null;
@@ -82,6 +90,8 @@ public class GLProxy
public LodShaderProgram lodShaderProgram;
/** This is the VAO that is used when rendering */
public final int vertexArrayObjectId;
/** This is the 2D texture that holds MC's lightmap */
public final int lightMapTextureId;
/** Requires OpenGL 4.5, and offers the best buffer uploading */
@@ -92,17 +102,19 @@ public class GLProxy
/**
* @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.");
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.");
// 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 //
@@ -159,19 +171,54 @@ public class GLProxy
// get specific capabilities
// TODO re-add buffer storage support
bufferStorageSupported = lodBuilderGlCapabilities.glBufferStorage != 0;
mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0;
// 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.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
}
// if using AUTO gpuUpload
// determine a good default for the GPU
if (CONFIG.client().advanced().buffers().getGpuUploadMethod() == GpuUploadMethod.AUTO)
{
GpuUploadMethod uploadMethod;
String vendor = GL15.glGetString(GL15.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION"
if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE"))
{
// NVIDIA card
if (bufferStorageSupported)
{
uploadMethod = GpuUploadMethod.BUFFER_STORAGE;
}
else
{
uploadMethod = GpuUploadMethod.SUB_DATA;
}
}
else
{
// AMD or Intel card
if (mapBufferRangeSupported)
{
uploadMethod = GpuUploadMethod.BUFFER_MAPPING;
}
else
{
uploadMethod = GpuUploadMethod.DATA;
}
}
CONFIG.client().advanced().buffers().setGpuUploadMethod(uploadMethod);
ClientApi.LOGGER.info("GPU Vendor [" + vendor + "], Upload method set to [" + uploadMethod + "].");
}
@@ -179,7 +226,6 @@ public class GLProxy
// shader setup //
//==============//
//setGlContext(GLProxyContext.LOD_RENDER);
setGlContext(GLProxyContext.MINECRAFT);
createShaderProgram();
@@ -188,6 +234,7 @@ public class GLProxy
// this must be created on minecraft's render context to work correctly
vertexArrayObjectId = GL30.glGenVertexArrays();
lightMapTextureId = GL30.glGenTextures();
@@ -201,10 +248,14 @@ public class GLProxy
// 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 */
/**
* Creates all required shaders
* @throws RuntimeException
* @throws FileNotFoundException
*/
public void createShaderProgram()
{
LodShader vertexShader = null;
@@ -213,13 +264,13 @@ public class GLProxy
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);
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/standard.vert", false);
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/flat_shaded.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);
// vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "C:/Users/James Seibel/Desktop/shaders/standard.vert", true);
// fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/flat_shaded.frag", true);
// create the shaders
@@ -245,6 +296,35 @@ public class GLProxy
{
ClientApi.LOGGER.error("Unable to compile shaders. Error: " + e.getMessage());
}
// get the shaders from the resource folder
// Use File.separator ONLY if the file is external (not in the resourse), otherwise use "/"
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/standard.vert", false);
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/flat_shaded.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/standard.vert", true);
// fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/flat_shaded.frag", true);
// create the shaders
lodShaderProgram = new LodShaderProgram();
// Attach the compiled shaders to the program, throws RuntimeException on link error
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);
}
@@ -364,6 +444,29 @@ 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 void 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);
}
}
}
@@ -19,20 +19,23 @@
package com.seibel.lod.core.render;
import java.awt.Color;
import java.util.HashSet;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.NVFogDistance;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.VertexBuffersAndOffset;
import com.seibel.lod.core.enums.config.GpuUploadMethod;
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.FogQuality;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
import com.seibel.lod.core.handlers.IReflectionHandler;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.objects.lod.RegionPos;
@@ -40,42 +43,34 @@ 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.objects.opengl.LodVertexBuffer;
import com.seibel.lod.core.objects.rending.NearFarFogSettings;
import com.seibel.lod.core.objects.rending.LodFogConfig;
import com.seibel.lod.core.render.shader.LodShaderProgram;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
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.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.minecraft.IProfilerWrapper;
import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
/**
* This is where all the magic happens. <br>
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 11-8-2021
* @version 12-8-2021
*/
public class LodRenderer
{
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 IReflectionHandler REFLECTION_HANDLER = SingletonHandler.get(IReflectionHandler.class);
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
// /**
// * this is the light used when rendering the LODs,
// * it should be something different from what is used by Minecraft
// */
// private static final int LOD_GL_LIGHT_NUMBER = GL15.GL_LIGHT2;
/**
* If true the LODs colors will be replaced with
* a checkerboard, this can be used for debugging.
@@ -98,7 +93,8 @@ public class LodRenderer
@SuppressWarnings("unused")
private int[][][] storageBufferIds;
private AbstractChunkPosWrapper vbosCenter = FACTORY.createChunkPos();
private int vbosCenterX = 0;
private int vbosCenterZ = 0;
/** This is used to determine if the LODs should be regenerated */
@@ -169,16 +165,19 @@ public class LodRenderer
if (MC_RENDER.playerHasBlindnessEffect())
{
// if the player is blind don't render LODs,
// if the player is blind, don't render LODs,
// and don't change minecraft's fog
// which blindness relies on.
return;
}
if (CONFIG.client().graphics().fogQuality().getDisableVanillaFog())
GLProxy.getInstance().disableLegacyFog();
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// TODO move the buffer regeneration logic into its own class (probably called in the client api instead)
// starting here...
determineIfLodsShouldRegenerate(lodDim, partialTicks);
@@ -194,7 +193,7 @@ public class LodRenderer
if ((partialRegen || fullRegen) && !lodBufferBuilderFactory.generatingBuffers && !lodBufferBuilderFactory.newBuffersAvailable())
{
// generate the LODs on a separate thread to prevent stuttering or freezing
lodBufferBuilderFactory.generateLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos(), true);
lodBufferBuilderFactory.generateLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getY(), MC.getPlayerBlockPos().getZ(), true);
// the regen process has been started,
// it will be done when lodBufferBuilder.newBuffersAvailable()
@@ -203,7 +202,7 @@ public class LodRenderer
partialRegen = false;
}
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// TODO move the buffer regeneration logic into its own class (probably called in the client api instead)
// ...ending here
if (lodBufferBuilderFactory.newBuffersAvailable())
@@ -243,7 +242,7 @@ public class LodRenderer
int currentProgram = GL20.glGetInteger(GL20.GL_CURRENT_PROGRAM);
Mat4f modelViewMatrix = offsetTheModelViewMatrix(mcModelViewMatrix, partialTicks);
Mat4f modelViewMatrix = translateModelViewMatrix(mcModelViewMatrix, partialTicks);
vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
// required for setupFog and setupProjectionMatrix
if (MC.getWrappedClientWorld().getDimensionType().hasCeiling())
@@ -252,54 +251,23 @@ public class LodRenderer
farPlaneBlockDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * LodUtil.CHUNK_WIDTH;
Mat4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks);
Mat4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance);
// commented out until we can add shaders to handle lighting
//setupLighting(lodDim, partialTicks);
// // determine the current fog settings, so they can be
// // reset after drawing the LODs
// float defaultFogStartDist = GL15.glGetFloat(GL15.GL_FOG_START);
// float defaultFogEndDist = GL15.glGetFloat(GL15.GL_FOG_END);
// int defaultFogMode = GL15.glGetInteger(GL15.GL_FOG_MODE);
// int defaultFogDistance = glProxy.fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1;
//ShaderInstance mcShader = RenderSystem.getShader();
// NearFarFogSettings fogSettings = determineFogSettings();
LodFogConfig fogSettings = determineFogConfig();
//===========//
// rendering //
//===========//
profiler.popPush("LOD draw");
if (vbos != null)
{
Vec3f cameraDir = MC_RENDER.getLookAtVector();
// TODO re-enable once rendering is totally working
boolean cullingDisabled = true; //LodConfig.client().graphics.advancedGraphicsOption.disableDirectionalCulling.get();
// boolean renderBufferStorage = config.client().graphics().advancedGraphics().getGpuUploadMethod() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported;
// used to determine what type of fog to render
// int halfWidth = vbos.length / 2;
// int quarterWidth = vbos.length / 4;
// where the center of the built buffers is (needed when culling regions)
RegionPos vboCenterRegionPos = new RegionPos(vbosCenter);
//==============//
// shader setup //
//==============//
// can be used when testing shaders
//glProxy.createShaderProgram();
// glProxy.createShaderProgram();
LodShaderProgram shaderProgram = glProxy.lodShaderProgram;
@@ -311,47 +279,156 @@ public class LodRenderer
shaderProgram.enableVertexAttribute(posAttrib);
int colAttrib = shaderProgram.getAttributeLocation("color");
shaderProgram.enableVertexAttribute(colAttrib);
int blockSkyLightAttrib = shaderProgram.getAttributeLocation("blockSkyLight");
// TODO the block sky light is being passed in correctly but the data
// we were given appears to be incorrect, so we won't use it for now
//shaderProgram.enableVertexAttribute(blockSkyLightAttrib);
int blockLightAttrib = shaderProgram.getAttributeLocation("blockLight");
shaderProgram.enableVertexAttribute(blockLightAttrib);
// upload the required uniforms
// global uniforms
int mvmUniform = shaderProgram.getUniformLocation("modelViewMatrix");
shaderProgram.setUniform(mvmUniform, modelViewMatrix);
int projUniform = shaderProgram.getUniformLocation("projectionMatrix");
shaderProgram.setUniform(projUniform, projectionMatrix);
int cameraUniform = shaderProgram.getUniformLocation("cameraPos");
shaderProgram.setUniform(cameraUniform, getTranslatedCameraPos());
int fogColorUniform = shaderProgram.getUniformLocation("fogColor");
shaderProgram.setUniform(fogColorUniform, getFogColor());
int skyLightUniform = shaderProgram.getUniformLocation("worldSkyLight");
shaderProgram.setUniform(skyLightUniform, (int) (MC.getSkyDarken(partialTicks) * 15));
int lightMapUniform = shaderProgram.getUniformLocation("lightMap");
GL20.glBindTexture(GL20.GL_TEXTURE_2D, glProxy.lightMapTextureId);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_BORDER);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_BORDER);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_NEAREST);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_NEAREST);
// get the latest lightmap from MC
try
{
int lightMapHeight = MC_RENDER.getLightmapTextureHeight();
int lightMapWidth = MC_RENDER.getLightmapTextureWidth();
int[] pixels = MC_RENDER.getLightmapPixels();
// 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
GL20.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_RGBA, lightMapWidth,
lightMapHeight, 0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixels);
// TODO is this needed/correct?
shaderProgram.setUniform(lightMapUniform, 0);
}
catch (Exception e)
{
ClientApi.LOGGER.info(e.getMessage(), e);
}
// region dependent uniforms
int fogEnabledUniform = shaderProgram.getUniformLocation("fogEnabled");
int nearFogEnabledUniform = shaderProgram.getUniformLocation("nearFogEnabled");
int farFogEnabledUniform = shaderProgram.getUniformLocation("farFogEnabled");
// near
int nearFogStartUniform = shaderProgram.getUniformLocation("nearFogStart");
int nearFogEndUniform = shaderProgram.getUniformLocation("nearFogEnd");
// far
int farFogStartUniform = shaderProgram.getUniformLocation("farFogStart");
int farFogEndUniform = shaderProgram.getUniformLocation("farFogEnd");
//===========//
// rendering //
//===========//
profiler.popPush("LOD draw");
boolean cullingDisabled = CONFIG.client().graphics().advancedGraphics().getDisableDirectionalCulling();
boolean renderBufferStorage = CONFIG.client().advanced().buffers().getGpuUploadMethod() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported;
// where the center of the buffers is (needed when culling regions)
int vboCenterRegionPosX = vbosCenterX;
int vboCenterRegionPosZ = vbosCenterZ;
int vboPosX;
int vboPosZ;
// render each of the buffers
for (int x = 0; x < vbos.length; x++)
{
for (int z = 0; z < vbos.length; z++)
{
RegionPos vboPos = new RegionPos(
x + vboCenterRegionPos.x - (lodDim.getWidth() / 2),
z + vboCenterRegionPos.z - (lodDim.getWidth() / 2));
vboPosX = x + vboCenterRegionPosX - (lodDim.getWidth() / 2);
vboPosZ = z + vboCenterRegionPosZ - (lodDim.getWidth() / 2);
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(), cameraDir, vboPos.blockPos()))
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(),
MC_RENDER.getLookAtVector(),
LodUtil.convertLevelPos(LodUtil.REGION_DETAIL_LEVEL, vboPosX, LodUtil.BLOCK_DETAIL_LEVEL),
LodUtil.convertLevelPos(LodUtil.REGION_DETAIL_LEVEL, vboPosZ, LodUtil.BLOCK_DETAIL_LEVEL)))
{
// TODO add fog to the fragment shader
// if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth)
// && (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth))
// setupFog(fogSettings.near.distance, fogSettings.near.quality);
// else
// setupFog(fogSettings.far.distance, fogSettings.far.quality);
// fog may be different from region to region
applyFog(shaderProgram,
fogSettings, fogEnabledUniform, nearFogEnabledUniform, farFogEnabledUniform,
nearFogStartUniform, nearFogEndUniform, farFogStartUniform, farFogEndUniform);
// actual rendering
int bufferId = 0;
for (int i = 0; i < vbos[x][z].length; i++)
{
bufferId = (storageBufferIds != null && renderBufferStorage) ? storageBufferIds[x][z][i] : vbos[x][z][i].id;
drawArrays(bufferId, vbos[x][z][i].vertexCount, posAttrib, colAttrib, blockLightAttrib, blockSkyLightAttrib);
}
// if (storageBufferIds != null && renderBufferStorage)
// for (int i = 0; i < storageBufferIds[x][z].length; i++)
// drawArrays(storageBufferIds[x][z][i], vbos[x][z][i].vertexCount, posAttrib, colAttrib);
// else
for (int i = 0; i < vbos[x][z].length; i++)
drawArrays(vbos[x][z][i].id, vbos[x][z][i].vertexCount, posAttrib, colAttrib);
}
}
}
GL20.glDisableVertexAttribArray(posAttrib);
GL20.glDisableVertexAttribArray(colAttrib);
GL20.glBindTexture(GL20.GL_TEXTURE_2D, 0);
//================//
// render cleanup //
//================//
// if this cleanup isn't done MC will crash
// when trying to render its own terrain
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
GL20.glDisableVertexAttribArray(posAttrib);
GL20.glDisableVertexAttribArray(colAttrib);
// GL20.glDisableVertexAttribArray(blockSkyLightAttrib);
GL20.glDisableVertexAttribArray(blockLightAttrib);
}
@@ -368,26 +445,35 @@ public class LodRenderer
GL15.glDisable(GL15.GL_BLEND); // TODO: what should this be reset to?
GL20.glUseProgram(currentProgram);
//RenderSystem.setShader(() -> mcShader);
// clear the depth buffer so everything drawn is drawn
// clear the depth buffer so everything is drawn
// over the LODs
GL15.glClear(GL15.GL_DEPTH_BUFFER_BIT);
// end of internal LOD profiling
profiler.pop();
}
// Temporary variables James was using while working with the shader lightmap
int[] testArray = null;
int testInt = 0;
/** This is where the actual drawing happens. */
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib)
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib, int blockLightAttrib, int blockSkyLightAttrib)
{
if (glBufferId == 0)
return;
// can be used to check for OpenGL errors
// int error = GL15.glGetError();
// ClientProxy.LOGGER.info(Integer.toHexString(error));
// ClientApi.LOGGER.info(Integer.toHexString(error));
// bind the buffer we are going to draw
@@ -395,136 +481,94 @@ public class LodRenderer
GL30.glBindVertexArray(GLProxy.getInstance().vertexArrayObjectId);
// let OpenGL know how our buffer is set up
int vertexByteCount = (Float.BYTES * 3) + (Byte.BYTES * 4);
int vertexByteCount = (Float.BYTES * 3) + (Byte.BYTES * 4) + Byte.BYTES + Byte.BYTES; // TODO move this into the template
GL20.glEnableVertexAttribArray(posAttrib);
GL20.glVertexAttribPointer(posAttrib, 3, GL15.GL_FLOAT, false, vertexByteCount, 0);
GL20.glEnableVertexAttribArray(colAttrib);
GL20.glVertexAttribPointer(colAttrib, 4, GL15.GL_UNSIGNED_BYTE, true, vertexByteCount, Float.BYTES * 3);
GL20.glEnableVertexAttribArray(blockLightAttrib);
GL20.glVertexAttribPointer(blockLightAttrib, 1, GL15.GL_UNSIGNED_BYTE, false, vertexByteCount, Float.BYTES * (3 + 1));
// GL20.glEnableVertexAttribArray(blockSkyLightAttrib);
// GL20.glVertexAttribPointer(blockSkyLightAttrib, 1, GL15.GL_UNSIGNED_BYTE, false, vertexByteCount, Float.BYTES * (3 + 1 + 1));
// draw the LODs
GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, vertexCount);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
GL20.glDisableVertexAttribArray(posAttrib);
GL20.glDisableVertexAttribArray(colAttrib);
}
//=================//
// Setup Functions //
//=================//
@SuppressWarnings("unused")
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
/** Create all buffers that will be used. */
public void setupBuffers(LodDimension lodDim)
{
if (fogQuality == FogQuality.OFF)
{
GL15.glDisable(GL15.GL_FOG);
return;
}
lodBufferBuilderFactory.setupBuffers(lodDim);
}
/** Return what fog settings should be used when rendering. */
private LodFogConfig determineFogConfig()
{
LodFogConfig fogConfig = new LodFogConfig();
if (fogDistance == FogDistance.NEAR_AND_FAR)
{
throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance.");
}
// determine the fog distance mode to use
int glFogDistanceMode;
if (fogQuality == FogQuality.FANCY)
{
// fancy fog (fragment distance based fog)
glFogDistanceMode = NVFogDistance.GL_EYE_RADIAL_NV;
}
fogConfig.fogDrawMode = CONFIG.client().graphics().fogQuality().getFogDrawMode();
if (fogConfig.fogDrawMode == FogDrawMode.USE_OPTIFINE_SETTING)
fogConfig.fogDrawMode = REFLECTION_HANDLER.getFogDrawMode();
// how different distances are drawn depends on the quality set
fogConfig.fogDistance = CONFIG.client().graphics().fogQuality().getFogDistance();
// far fog //
if (CONFIG.client().graphics().fogQuality().getFogDistance() == FogDistance.NEAR_AND_FAR)
fogConfig.farFogStart = farPlaneBlockDistance * 0.9f;
else
{
// fast fog (frustum distance based fog)
glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV;
}
// for more realistic fog when using FAR
fogConfig.farFogStart = Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f);
// the multipliers are percentages
// of the regular view distance.
if (fogDistance == FogDistance.FAR)
{
// the reason that I wrote fogEnd then fogStart backwards
// is because we are using fog backwards to how
// it is normally used, with it hiding near objects
// instead of far objects.
if (fogQuality == FogQuality.FANCY)
{
// for more realistic fog when using FAR
if (CONFIG.client().graphics().fogQuality().getFogDistance() == FogDistance.NEAR_AND_FAR)
GL15.glFogf(GL15.GL_FOG_START, farPlaneBlockDistance * 1.6f * 0.9f);
else
GL15.glFogf(GL15.GL_FOG_START, Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f));
GL15.glFogf(GL15.GL_FOG_END, farPlaneBlockDistance * 1.6f);
}
else if (fogQuality == FogQuality.FAST)
{
// for the far fog of the normal chunks
// to start right where the LODs' end use:
// end = 0.8f, start = 1.5f
GL15.glFogf(GL15.GL_FOG_START, farPlaneBlockDistance * 0.75f);
GL15.glFogf(GL15.GL_FOG_END, farPlaneBlockDistance * 1.0f);
}
}
else if (fogDistance == FogDistance.NEAR)
{
if (fogQuality == FogQuality.FANCY)
{
GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.41f);
GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.6f);
}
else if (fogQuality == FogQuality.FAST)
{
GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.0f);
GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.5f);
}
}
fogConfig.farFogEnd = farPlaneBlockDistance;
GL15.glEnable(GL15.GL_FOG);
GL15.glFogi(GL15.GL_FOG_MODE, GL15.GL_LINEAR);
// 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.
fogConfig.nearFogEnd = vanillaBlockRenderedDistance * 1.41f;
fogConfig.nearFogStart = vanillaBlockRenderedDistance * 1.6f;
return fogConfig;
}
/**
* Revert any changes that were made to the fog
* and sets up the fog for Minecraft.
*/
@SuppressWarnings("unused")
private void cleanupFog(NearFarFogSettings fogSettings,
float defaultFogStartDist, float defaultFogEndDist,
int defaultFogMode, int defaultFogDistance)
private Color getFogColor()
{
GL15.glFogf(GL15.GL_FOG_START, defaultFogStartDist);
GL15.glFogf(GL15.GL_FOG_END, defaultFogEndDist);
GL15.glFogi(GL15.GL_FOG_MODE, defaultFogMode);
Color fogColor;
// disable fog if Minecraft wasn't rendering fog
// or we want it disabled
if (!fogSettings.vanillaIsRenderingFog
|| CONFIG.client().graphics().fogQuality().getDisableVanillaFog())
{
// Make fog render a infinite distance away.
// This doesn't technically disable Minecraft's fog
// so performance will probably be the same regardless, unlike
// Optifine's no fog setting.
// we can't disable minecraft's fog outright because by default
// minecraft will re-enable the fog after our code
GL15.glFogf(GL15.GL_FOG_START, 0.0F);
GL15.glFogf(GL15.GL_FOG_END, Float.MAX_VALUE);
GL15.glFogf(GL15.GL_FOG_DENSITY, Float.MAX_VALUE);
}
if (CONFIG.client().graphics().fogQuality().getFogColorMode() == FogColorMode.USE_SKY_COLOR)
fogColor = MC_RENDER.getSkyColor();
else
fogColor = MC_RENDER.getFogColor();
return fogColor;
}
/**
* Translate the camera relative to the LodDimension's center,
* this is done since all LOD buffers are created in world space
@@ -532,7 +576,7 @@ public class LodRenderer
* (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
* accuracy vs the model view matrix, which only uses floats)
*/
private Mat4f offsetTheModelViewMatrix(Mat4f mcModelViewMatrix, float partialTicks)
private Mat4f translateModelViewMatrix(Mat4f mcModelViewMatrix, float partialTicks)
{
// get all relevant camera info
Vec3d projectedView = MC_RENDER.getCameraExactPosition();
@@ -540,106 +584,77 @@ public class LodRenderer
// translate the camera relative to the regions' center
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
// accuracy vs the model view matrix, which only uses floats)
AbstractBlockPosWrapper bufferPos = vbosCenter.getWorldPosition();
double xDiff = projectedView.x - bufferPos.getX();
double zDiff = projectedView.z - bufferPos.getZ();
int bufferPosX = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterX, LodUtil.CHUNK_DETAIL_LEVEL);
int bufferPosZ = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterZ, LodUtil.CHUNK_DETAIL_LEVEL);
double xDiff = projectedView.x - bufferPosX;
double zDiff = projectedView.z - bufferPosZ;
mcModelViewMatrix.multiplyTranslationMatrix(-xDiff, -projectedView.y, -zDiff);
return mcModelViewMatrix;
}
/**
* Similar to translateModelViewMatrix (above),
* but for the camera position
*/
private Vec3f getTranslatedCameraPos()
{
int worldCenterX = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterX, LodUtil.CHUNK_DETAIL_LEVEL);
int worldCenterZ = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterZ, LodUtil.CHUNK_DETAIL_LEVEL);
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
return new Vec3f((float)cameraPos.x - worldCenterX, (float)cameraPos.y, (float)cameraPos.z - worldCenterZ);
}
/**
* create and return a new projection matrix based on MC's projection matrix
* @param currentProjectionMatrix this is Minecraft's current projection matrix
* @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance
* @param partialTicks how many ticks into the frame we are
*/
private Mat4f createProjectionMatrix(Mat4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks)
private Mat4f createProjectionMatrix(Mat4f currentProjectionMatrix, float vanillaBlockRenderedDistance)
{
// create the new projection matrix
Mat4f lodProj = Mat4f.perspective(
MC_RENDER.getFov(partialTicks),
(float) this.MC_RENDER.getScreenWidth() / (float) this.MC_RENDER.getScreenHeight(),
//Create a copy of the current matrix, so the current matrix isn't modified.
Mat4f lodProj = currentProjectionMatrix.copy();
//Set new far and near clip plane values.
lodProj.setClipPlanes(
CONFIG.client().graphics().advancedGraphics().getUseExtendedNearClipPlane() ? vanillaBlockRenderedDistance / 5 : 1,
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2);
// get Minecraft's un-edited projection matrix
// (this is before it is zoomed, distorted, etc.)
Mat4f defaultMcProj = MC_RENDER.getDefaultProjectionMatrix(partialTicks);
// true here means use "use fov setting" (probably)
// this logic strips away the defaultMcProj matrix, so we
// can get the distortionMatrix, which represents all
// transformations, zooming, distortions, etc. done
// to Minecraft's Projection matrix
Mat4f defaultMcProjInv = defaultMcProj.copy();
defaultMcProjInv.invert();
Mat4f distortionMatrix = defaultMcProjInv.copy();
distortionMatrix.multiply(currentProjectionMatrix);
// edit the lod projection to match Minecraft's
// (so the LODs line up with the real world)
lodProj.multiply(distortionMatrix);
return lodProj;
}
///** setup the lighting to be used for the LODs */
/*private void setupLighting(LodDimension lodDimension, float partialTicks)
private void applyFog(LodShaderProgram shaderProgram,
LodFogConfig fogSettings, int fogEnabledUniform, int nearFogEnabledUniform, int farFogEnabledUniform,
int nearFogStartUniform, int nearFogEndUniform, int farFogStartUniform, int farFogEndUniform)
{
// Determine if the player has night vision
boolean playerHasNightVision = false;
if (this.mc.getPlayer() != null)
if (fogSettings.fogDrawMode != FogDrawMode.FOG_DISABLED)
{
Iterator<EffectInstance> iterator = this.mc.getPlayer().getActiveEffects().iterator();
while (iterator.hasNext())
{
EffectInstance instance = iterator.next();
if (instance.getEffect() == Effects.NIGHT_VISION)
{
playerHasNightVision = true;
break;
}
}
shaderProgram.setUniform(fogEnabledUniform, true);
shaderProgram.setUniform(nearFogEnabledUniform, fogSettings.fogDistance != FogDistance.FAR);
shaderProgram.setUniform(farFogEnabledUniform, fogSettings.fogDistance != FogDistance.NEAR);
// near
shaderProgram.setUniform(nearFogStartUniform, fogSettings.nearFogStart);
shaderProgram.setUniform(nearFogEndUniform, fogSettings.nearFogEnd);
// far
shaderProgram.setUniform(farFogStartUniform, fogSettings.farFogStart);
shaderProgram.setUniform(farFogEndUniform, fogSettings.farFogEnd);
}
else
{
shaderProgram.setUniform(fogEnabledUniform, false);
}
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f;
sunBrightness = playerHasNightVision ? 1.0f : sunBrightness;
float gamma = (float) mc.getOptions().gamma - 0.0f;
float dayEffect = (sunBrightness - 0.2f) * 1.25f;
float lightStrength = (gamma * 0.34f - 0.01f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.2980392157f + 0.1647058824f
float blueLightStrength = (gamma * 0.44f + 0.12f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.4235294118f + 0.2784313725f
float[] lightAmbient = {lightStrength, lightStrength, blueLightStrength, 1.0f};
// can be used for debugging
// if (partialTicks < 0.005)
// ClientProxy.LOGGER.debug(lightStrength);
ByteBuffer temp = ByteBuffer.allocateDirect(16);
temp.order(ByteOrder.nativeOrder());
GL15.glLightfv(LOD_GL_LIGHT_NUMBER, GL15.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
GL15.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
RenderSystem.enableLighting();
}*/
/** Create all buffers that will be used. */
public void setupBuffers(LodDimension lodDim)
{
lodBufferBuilderFactory.setupBuffers(lodDim);
}
//======================//
// Other Misc Functions //
//======================//
/**
* If this is called then the next time "drawLODs" is called
* the LODs will be regenerated; the same as if the player moved.
@@ -649,7 +664,6 @@ public class LodRenderer
fullRegen = true;
}
/**
* Replace the current Vertex Buffers with the newly
* created buffers from the lodBufferBuilder. <br><br>
@@ -664,7 +678,8 @@ public class LodRenderer
VertexBuffersAndOffset result = lodBufferBuilderFactory.getVertexBuffers();
vbos = result.vbos;
storageBufferIds = result.storageBufferIds;
vbosCenter = result.drawableCenterChunkPos;
vbosCenterX = result.drawableCenterChunkPosX;
vbosCenterZ = result.drawableCenterChunkPosZ;
}
/** Calls the BufferBuilder's destroyBuffers method. */
@@ -672,98 +687,7 @@ public class LodRenderer
{
lodBufferBuilderFactory.destroyBuffers();
}
/** Return what fog settings should be used when rendering. */
@SuppressWarnings("unused")
private NearFarFogSettings determineFogSettings()
{
NearFarFogSettings fogSettings = new NearFarFogSettings();
FogQuality quality = REFLECTION_HANDLER.getFogQuality();
FogDrawOverride override = CONFIG.client().graphics().fogQuality().getFogDrawOverride();
fogSettings.vanillaIsRenderingFog = quality != FogQuality.OFF;
// use any fog overrides the user may have set
switch (override)
{
case FANCY:
quality = FogQuality.FANCY;
break;
case NO_FOG:
quality = FogQuality.OFF;
break;
case FAST:
quality = FogQuality.FAST;
break;
case OPTIFINE_SETTING:
// don't override anything
break;
}
// how different distances are drawn depends on the quality set
switch (quality)
{
case FANCY:
fogSettings.near.quality = FogQuality.FANCY;
fogSettings.far.quality = FogQuality.FANCY;
switch (CONFIG.client().graphics().fogQuality().getFogDistance())
{
case NEAR_AND_FAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.FAR;
break;
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
break;
}
break;
case FAST:
fogSettings.near.quality = FogQuality.FAST;
fogSettings.far.quality = FogQuality.FAST;
// fast fog setting should only have one type of
// fog, since the LODs are separated into a near
// and far portion; and fast fog is rendered from the
// frustrum's perspective instead of the camera
switch (CONFIG.client().graphics().fogQuality().getFogDistance())
{
case NEAR_AND_FAR:
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
break;
}
break;
case OFF:
fogSettings.near.quality = FogQuality.OFF;
fogSettings.far.quality = FogQuality.OFF;
break;
}
return fogSettings;
}
/** Determines if the LODs should have a fullRegen or partialRegen */
@@ -804,8 +728,8 @@ public class LodRenderer
if (newTime - prevPlayerPosTime > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveTimeout)
{
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|| MC.getPlayerChunkPos().getX() != LevelPosUtil.getPosX(previousPos)
|| MC.getPlayerChunkPos().getZ() != LevelPosUtil.getPosZ(previousPos))
|| Math.abs(MC.getPlayerChunkPos().getX() - LevelPosUtil.getPosX(previousPos)) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance
|| Math.abs(MC.getPlayerChunkPos().getZ() - LevelPosUtil.getPosZ(previousPos)) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance)
{
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
fullRegen = true;
@@ -815,7 +739,7 @@ public class LodRenderer
}
/*
// determine how far the lighting has to
// change in order to rebuild the buffers
@@ -847,12 +771,6 @@ public class LodRenderer
fullRegen = true;
prevBrightness = MC_RENDER.getGamma();
prevSkyBrightness = skyBrightness;
}
/*if (lightMap != lastLightMap)
{
fullRegen = true;
lastLightMap = lightMap;
}*/
//================//
@@ -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) ||
@@ -21,14 +21,13 @@ package com.seibel.lod.core.render.shader;
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 com.seibel.lod.core.api.ClientApi;
/**
* This object holds a OpenGL reference to a shader
* and allows for reading in and compiling a shader file.
@@ -59,14 +58,23 @@ public class LodShader
* @param absoluteFilePath If false the file path is relative to the resource jar folder.
* @throws Exception if the shader fails to compile
*/
public static LodShader loadShader(int type, String path, boolean absoluteFilePath) throws Exception
public static LodShader loadShader(int type, 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 = LodShader.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,7 +84,7 @@ 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();
@@ -90,7 +98,7 @@ public class LodShader
* @param source Source of the shader
* @throws Exception if the shader fails to compile
*/
public static LodShader createShader(int type, CharSequence source) throws Exception
public static LodShader createShader(int type, CharSequence source)
{
LodShader shader = new LodShader(type);
GL20.glShaderSource(shader.id, source);
@@ -100,17 +108,17 @@ public class LodShader
}
/**
* Compiles the shader and checks it's status afterwards.
* Compiles the shader and checks its status afterwards.
* @throws Exception if the shader fails to compile
*/
public void compile() throws Exception
public void compile()
{
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));
throw new RuntimeException("Shader compiler error. Details: "+GL20.glGetShaderInfoLog(id));
}
}
@@ -19,12 +19,15 @@
package com.seibel.lod.core.render.shader;
import java.awt.Color;
import java.nio.FloatBuffer;
import org.lwjgl.opengl.GL20;
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;
/**
@@ -34,7 +37,7 @@ import com.seibel.lod.core.objects.math.Mat4f;
* can (and needs to be) done with a shader program.
*
* @author James Seibel
* @version 11-8-2021
* @version 11-26-2021
*/
public class LodShaderProgram
{
@@ -70,7 +73,7 @@ public class LodShaderProgram
* Links the shader program to the current OpenGL context.
* @throws Exception Exception if the program failed to link
*/
public void link() throws Exception
public void link()
{
GL20.glLinkProgram(this.id);
checkLinkStatus();
@@ -80,11 +83,11 @@ public class LodShaderProgram
* Checks if the program was linked successfully.
* @throws Exception if the program failed to link
*/
public void checkLinkStatus() throws Exception
public void checkLinkStatus()
{
int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS);
if (status != GL20.GL_TRUE)
throw new Exception(GL20.glGetProgramInfoLog(this.id));
throw new RuntimeException("Shader Link Error. Details: "+GL20.glGetProgramInfoLog(this.id));
}
@@ -140,7 +143,7 @@ public class LodShaderProgram
}
/**
* Gets the location of an uniform variable with specified name.
* Gets the location of a uniform variable with specified name.
* Calls GL20.glGetUniformLocation(id, name)
*
* @param name Uniform name
@@ -152,23 +155,33 @@ public class LodShaderProgram
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, boolean value)
{
GL20.glUniform1i(location, value ? 1 : 0);
}
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, float value)
{
GL20.glUniform1f(location, value);
}
public void setUniform(int location, Vec3f value)
{
GL20.glUniform3f(location, value.x, value.y, value.z);
}
public void setUniform(int location, Vec3d value)
{
GL20.glUniform3f(location, (float) value.x, (float) value.y, (float) value.z);
}
public void setUniform(int location, Mat4f value)
{
try (MemoryStack stack = MemoryStack.stackPush())
@@ -179,6 +192,12 @@ public class LodShaderProgram
}
}
/** Converts the color's RGBA values into values between 0 and 1. */
public void setUniform(int location, Color value)
{
GL20.glUniform4f(location, value.getRed() / 256.0f, value.getGreen() / 256.0f, value.getBlue() / 256.0f, value.getAlpha() / 256.0f);
}
}
@@ -58,7 +58,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;
@@ -262,12 +263,13 @@ public class DataPointUtil
* @param maxVerticalData max vertical size of the merged data
* @return one column of correctly parsed data
*/
@Deprecated
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((worldHeight / 2 + 1) * 2);
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((WORLD_HEIGHT / 2 + 1) * 2);
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
@@ -412,7 +414,7 @@ public class DataPointUtil
int j = 0;
while (count > maxVerticalData)
{
ii = worldHeight;
ii = WORLD_HEIGHT - VERTICAL_OFFSET;
for (i = 0; i < count - 1; i++)
{
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii)
@@ -23,7 +23,7 @@ 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 +33,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 +79,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 +96,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;
@@ -139,12 +139,12 @@ public class DetailDistanceUtil
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,7 @@ 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.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,21 +35,21 @@ 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-8-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);
@@ -140,7 +140,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;
@@ -403,10 +403,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,7 @@ 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;
/**
* Holds data used by specific threads so
@@ -55,7 +55,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 +65,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 +82,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());
@@ -128,7 +128,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);
}
@@ -34,15 +34,15 @@ import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGenera
*/
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();
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);
}
@@ -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
@@ -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();
}
@@ -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,9 @@ 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;
import com.seibel.lod.forge.wrappers.block.BlockShapeWrapper;
import net.minecraft.block.Block;
/**
* @author James Seibel
@@ -30,23 +32,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);
}
@@ -31,8 +31,9 @@ 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;
@@ -42,192 +43,197 @@ 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-1-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"
+ "\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);
+ " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \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.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 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 +243,77 @@ 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"
+ ""
+ " How detailed should fake chunks be generated outside the vanilla render distance? \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"
+ ""
+ " Multithreaded - 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"
+ ""
+ " Multithreaded - 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"
+ ""
+ " Multithreaded - 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"
+ ""
+ " Multithreaded - 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);
+ " SingleThreaded - 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 +327,29 @@ 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);
}
@@ -336,16 +358,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 +376,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 +422,78 @@ 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 rending 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_TIMEOUT_IN_MILLISECONDS_DEFAULT = new MinDefaultMax<Integer>(0, 0, 5000);
String GPU_UPLOAD_TIMEOUT_IN_MILLISECONDS_DESC = ""
+ " How long should we wait before uploading a buffer to the GPU? \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. \n"
+ "\n"
+ " NOTE:\n"
+ " This should be a last resort option."
+ " Only change this from [0], after you have tried all of the \n"
+ " \"GPU Upload methods\" and determined even the best stutters with yoru hardware.";
int getGpuUploadTimeoutInMilliseconds();
void setGpuUploadTimeoutInMilliseconds(int newTimeoutInMilliseconds);
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);
}
}
}
@@ -19,6 +19,7 @@
package com.seibel.lod.core.wrapperInterfaces.minecraft;
import java.awt.Color;
import java.util.HashSet;
import com.seibel.lod.core.objects.math.Mat4f;
@@ -32,33 +33,47 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
* rendering in Minecraft.
*
* @author James Seibel
* @version 11-18-2021
* @version 12-8-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.
*/
public HashSet<AbstractChunkPosWrapper> getRenderedChunks();
HashSet<AbstractChunkPosWrapper> getRenderedChunks();
/** @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();
}
@@ -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);
}
@@ -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();
}
@@ -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,28 @@ 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();
public int getSeaLevel();
int getSeaLevel();
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
public File getSaveFolder() throws UnsupportedOperationException;
File getSaveFolder() throws UnsupportedOperationException;
}
@@ -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!");
}
}
@@ -1,80 +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.ModInfo;
import com.seibel.lod.forge.wrappers.ForgeDependencySetup;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
/**
* Initialize and setup the Mod. <br>
* If you are looking for the real start of the mod
* check out the ClientProxy.
*
* @author James Seibel
* @version 11-16-2021
*/
@Mod(ModInfo.ID)
public class ForgeMain
{
public static ForgeClientProxy forgeClientProxy;
private void init(final FMLCommonSetupEvent event)
{
// make sure the dependencies are set up before the mod needs them
ForgeDependencySetup.createInitialBindings();
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ForgeConfig.CLIENT_SPEC);
}
public ForgeMain()
{
// Register the methods
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientStart);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
}
private void onClientStart(final FMLClientSetupEvent event)
{
forgeClientProxy = new ForgeClientProxy();
MinecraftForge.EVENT_BUS.register(forgeClientProxy);
}
@SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event)
{
// this is called when the server starts
}
}
@@ -1,80 +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.mixins;
import org.lwjgl.opengl.GL15;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.forge.wrappers.McObjectConverter;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.WorldRenderer;
/**
* This class is used to mix in my rendering code
* before Minecraft starts rendering blocks.
* If this wasn't done, and we used Forge's
* render last event, the LODs would render on top
* of the normal terrain.
*
* @author James Seibel
* @version 9-19-2021
*/
@Mixin(WorldRenderer.class)
public class MixinWorldRenderer
{
private static float previousPartialTicks = 0;
@Inject(at = @At("RETURN"), method = "renderSky(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V")
private void renderSky(MatrixStack matrixStackIn, float partialTicks, CallbackInfo callback)
{
// get the partial ticks since renderBlockLayer doesn't
// have access to them
previousPartialTicks = partialTicks;
}
@Inject(at = @At("HEAD"), method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/matrix/MatrixStack;DDD)V")
private void renderChunkLayer(RenderType renderType, MatrixStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
{
// only render if LODs are enabled and
// only render before solid blocks
if (renderType.equals(RenderType.solid()))
{
// get MC's current projection matrix
float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
mcProjectionMatrix.transpose();
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks);
}
}
}
@@ -1,37 +0,0 @@
package com.seibel.lod.forge.wrappers;
import com.seibel.lod.core.handlers.IReflectionHandler;
import com.seibel.lod.core.handlers.ReflectionHandler;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
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.forge.wrappers.block.BlockColorSingletonWrapper;
import com.seibel.lod.forge.wrappers.config.LodConfigWrapperSingleton;
import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.lod.forge.wrappers.minecraft.MinecraftWrapper;
/**
* Binds all necessary dependencies so we
* can access them in Core. <br>
* This needs to be called before any Core classes
* are loaded.
*
* @author James Seibel
* @version 11-20-2021
*/
public class ForgeDependencySetup
{
public static void createInitialBindings()
{
SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE);
SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions()));
}
}
@@ -1,56 +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.wrappers;
import java.nio.FloatBuffer;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.objects.math.Mat4f;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.Matrix4f;
/**
* This class converts to and from Minecraft objects (Ex: Matrix4f)
* and objects we created (Ex: Mat4f).
*
* @author James Seibel
* @version 11-20-2021
*/
public class McObjectConverter
{
/** 4x4 float matrix converter */
public static Mat4f Convert(Matrix4f mcMatrix)
{
FloatBuffer buffer = FloatBuffer.allocate(16);
mcMatrix.store(buffer);
Mat4f matrix = new Mat4f(buffer);
matrix.transpose();
return matrix;
}
public static Direction Convert(LodDirection lodDirection)
{
return Direction.byName(lodDirection.name());
}
}
@@ -1,91 +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.wrappers;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.objects.lod.LodDimension;
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.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.forge.wrappers.worldGeneration.WorldGeneratorWrapper;
/**
* This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 11-20-2021
*/
public class WrapperFactory implements IWrapperFactory
{
public static final WrapperFactory INSTANCE = new WrapperFactory();
@Override
public AbstractBlockPosWrapper createBlockPos()
{
return new BlockPosWrapper();
}
@Override
public AbstractBlockPosWrapper createBlockPos(int x, int y, int z)
{
return new BlockPosWrapper(x,y,z);
}
@Override
public AbstractChunkPosWrapper createChunkPos()
{
return new ChunkPosWrapper();
}
@Override
public AbstractChunkPosWrapper createChunkPos(int x, int z)
{
return new ChunkPosWrapper(x, z);
}
@Override
public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos)
{
return new ChunkPosWrapper(newChunkPos);
}
@Override
public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos)
{
return new ChunkPosWrapper(blockPos);
}
@Override
public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper);
}
}
@@ -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.forge.wrappers.block;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import net.minecraft.block.Blocks;
/**
* Contains methods that would have been static in BlockColorWrapper.
* Since interfaces can't create/implement static methods we have
* to split the object up in two.
*
* @author James Seibel
* @version 11-17-2021
*/
public class BlockColorSingletonWrapper implements IBlockColorSingletonWrapper
{
public static final BlockColorSingletonWrapper INSTANCE = new BlockColorSingletonWrapper();
@Override
public IBlockColorWrapper getWaterColor()
{
return BlockColorWrapper.getBlockColorWrapper(Blocks.WATER);
}
}
@@ -1,332 +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.wrappers.block;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.forge.wrappers.minecraft.MinecraftWrapper;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.AbstractTopPlantBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.BushBlock;
import net.minecraft.block.FlowerBlock;
import net.minecraft.block.GrassBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.TallGrassBlock;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.client.model.data.ModelDataMap;
/**
* @author ??
* @version 11-17-2021
*/
public class BlockColorWrapper implements IBlockColorWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static final BlockPos blockPos = new BlockPos(0, 0, 0);
public static final Random random = new Random(0);
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
private final Block block;
private int color;
private boolean isColored;
private boolean toTint;
private boolean foliageTint;
private boolean grassTint;
private boolean waterTint;
/**Constructor only require for the block instance we are wrapping**/
public BlockColorWrapper(Block block)
{
this.block = block;
this.color = 0;
this.isColored = true;
this.toTint = false;
this.foliageTint = false;
this.grassTint = false;
this.waterTint = false;
setupColorAndTint();
/*StringBuilder s = new StringBuilder();
s.append(block + "\n"
+ Integer.toHexString(
Minecraft.getInstance().getBlockColors().createDefault().getColor(
block.defaultBlockState(),
(World) MinecraftWrapper.INSTANCE.getWrappedServerLevel().getLevel(),
blockPosWrapper.getBlockPos())) + "\n"
);
for(Property x : Minecraft.getInstance().getBlockColors().getColoringProperties(block))
s.append(x.getName() + " " + x.getPossibleValues() + '\n');
System.out.println(s);*/
//System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint);
}
/**
* this return a wrapper of the block in input
* @param block object of the block to wrap
*/
public static IBlockColorWrapper getBlockColorWrapper(Block block)
{
//first we check if the block has already been wrapped
if (blockColorWrapperMap.containsKey(block) && blockColorWrapperMap.get(block) != null)
return blockColorWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockColorWrapper blockWrapper = new BlockColorWrapper(block);
blockColorWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint()
{
BlockState blockState = block.defaultBlockState();
BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
boolean isTinted = false;
int listSize = 0;
// first step is to check if this block has a tinted face
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
listSize = Math.max(listSize, quads.size());
for (BakedQuad bakedQuad : quads)
{
isTinted |= bakedQuad.isTinted();
}
}
//if it contains a tinted face then we store this block in the toTint set
if (isTinted)
this.toTint = true;
//now we get the first non empty face
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
if (!quads.isEmpty())
break;
}
//the quads list is not empty we extract the first one
if (!quads.isEmpty())
{
isColored = true;
texture = quads.get(0).getSprite();
}
else
{
isColored = true;
texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientWorld(), blockPosWrapper.getBlockPos());
}
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int tempColor;
int colorMultiplier;
// generate the block's color
for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getWidth(); u++)
{
for (int v = 0; v < texture.getHeight(); v++)
{
tempColor = texture.getPixelRGBA(frameIndex, u, v);
if (ColorUtil.getAlpha(texture.getPixelRGBA(frameIndex, u, v)) == 0)
continue;
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(tempColor) > (ColorUtil.getBlue(tempColor) + 30)) || !(ColorUtil.getGreen(tempColor) > (ColorUtil.getRed(tempColor) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(tempColor) * colorMultiplier;
red += ColorUtil.getBlue(tempColor) * colorMultiplier;
green += ColorUtil.getGreen(tempColor) * colorMultiplier;
blue += ColorUtil.getRed(tempColor) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = 0;
else
{
// determine the average color
alpha /= count;
red /= count;
green /= count;
blue /= count;
tempColor = ColorUtil.rgbToInt(alpha, red, green, blue);
}
// determine if this block should use the biome color tint
if ((grassInstance() || leavesInstance() || waterIstance()) && (float) numberOfGreyPixel / count > 0.75f)
this.toTint = true;
// we check which kind of tint we need to apply
this.grassTint = grassInstance() && toTint;
this.foliageTint = leavesInstance() && toTint;
this.waterTint = waterIstance() && toTint;
color = tempColor;
}
/** determine if the given block should use the biome's grass color */
private boolean grassInstance()
{
return block instanceof GrassBlock
|| block instanceof BushBlock
|| block instanceof IGrowable
|| block instanceof AbstractPlantBlock
|| block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean leavesInstance()
{
return block instanceof LeavesBlock
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's foliage color */
private boolean waterIstance()
{
return block == Blocks.WATER;
}
//--------------//
//Colors getters//
//--------------//
@Override
public boolean hasColor()
{
return isColored;
}
@Override
public int getColor()
{
return color;
}
//------------//
//Tint getters//
//------------//
@Override
public boolean hasTint()
{
return toTint;
}
@Override
public boolean hasGrassTint()
{
return grassTint;
}
@Override
public boolean hasFolliageTint()
{
return foliageTint;
}
@Override
public boolean hasWaterTint()
{
return waterTint;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockColorWrapper))
return false;
BlockColorWrapper that = (BlockColorWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -1,102 +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.wrappers.block;
import java.util.Objects;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import net.minecraft.util.math.BlockPos;
/**
* @author James Seibel
* @version 11-20-2021
*/
public class BlockPosWrapper extends AbstractBlockPosWrapper
{
private final BlockPos.Mutable blockPos;
public BlockPosWrapper()
{
this.blockPos = new BlockPos.Mutable(0, 0, 0);
}
public BlockPosWrapper(int x, int y, int z)
{
this.blockPos = new BlockPos.Mutable(x, y, z);
}
@Override
public void set(int x, int y, int z)
{
blockPos.set(x, y, z);
}
@Override
public int getX()
{
return blockPos.getX();
}
@Override
public int getY()
{
return blockPos.getY();
}
@Override
public int getZ()
{
return blockPos.getZ();
}
@Override
public int get(LodDirection.Axis axis)
{
return axis.choose(getX(), getY(), getZ());
}
public BlockPos.Mutable getBlockPos()
{
return blockPos;
}
@Override
public boolean equals(Object o)
{
return blockPos.equals(o);
}
@Override
public int hashCode()
{
return Objects.hash(blockPos);
}
@Override
public BlockPosWrapper offset(int x, int y, int z)
{
blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z);
return this;
}
}
@@ -1,179 +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.wrappers.block;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.SixWayBlock;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
/**
* @author ??
* @version 11-18-2021
*/
public class BlockShapeWrapper implements IBlockShapeWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockShapeWrapper> blockShapeWrapperMap = new ConcurrentHashMap<>();
public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper();
private final Block block;
private boolean toAvoid;
private boolean nonFull;
private boolean noCollision;
/**Constructor only require for the block instance we are wrapping**/
public BlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper)
{
this.block = block;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = ofBlockToAvoid();
setupShapes((ChunkWrapper) chunkWrapper, (BlockPosWrapper) blockPosWrapper);
//System.out.println(block + " non full " + nonFull + " no collision " + noCollision + " to avoid " + toAvoid);
}
private BlockShapeWrapper()
{
this.block = Blocks.WATER;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = false;
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
*/
static public BlockShapeWrapper getBlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper)
{
//first we check if the block has already been wrapped
if (blockShapeWrapperMap.containsKey(block) && blockShapeWrapperMap.get(block) != null)
return blockShapeWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockShapeWrapper blockWrapper = new BlockShapeWrapper(block, chunkWrapper, blockPosWrapper);
blockShapeWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
private void setupShapes(ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper)
{
IBlockReader chunk = chunkWrapper.getChunk();
BlockPos blockPos = blockPosWrapper.getBlockPos();
boolean noCollisionSetted = false;
boolean nonFullSetted = false;
if (!block.defaultBlockState().getFluidState().isEmpty() || block instanceof SixWayBlock)
{
noCollisionSetted = true;
nonFullSetted = true;
noCollision = false;
nonFull = false;
}
if (!nonFullSetted)
{
VoxelShape voxelShape = block.defaultBlockState().getShape(chunk, blockPos);
if (!voxelShape.isEmpty())
{
AxisAlignedBB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
nonFull = xWidth < 1 && zWidth < 1 && yWidth < 1;
}
else
{
nonFull = false;
}
}
if (!noCollisionSetted)
{
VoxelShape collisionShape = block.defaultBlockState().getCollisionShape(chunk, blockPos);
noCollision = collisionShape.isEmpty();
}
}
@Override
public boolean ofBlockToAvoid()
{
return block.equals(Blocks.AIR)
|| block.equals(Blocks.CAVE_AIR)
|| block.equals(Blocks.BARRIER)
|| block.equals(Blocks.VOID_AIR);
}
//-----------------//
//Avoidance getters//
//-----------------//
@Override
public boolean isNonFull()
{
return nonFull;
}
@Override
public boolean hasNoCollision()
{
return noCollision;
}
@Override
public boolean isToAvoid()
{
return toAvoid;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockShapeWrapper))
return false;
BlockShapeWrapper that = (BlockShapeWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -1,135 +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.wrappers.chunk;
import java.util.Objects;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
/**
* @author James Seibel
* @version 11-18-2021
*/
public class ChunkPosWrapper extends AbstractChunkPosWrapper
{
private final ChunkPos chunkPos;
public ChunkPosWrapper(ChunkPos newChunkPos)
{
this.chunkPos = newChunkPos;
}
public ChunkPosWrapper(BlockPos blockPos)
{
this.chunkPos = new ChunkPos(blockPos);
}
public ChunkPosWrapper(AbstractChunkPosWrapper newChunkPos)
{
this.chunkPos = ((ChunkPosWrapper) newChunkPos).chunkPos;
}
public ChunkPosWrapper(AbstractBlockPosWrapper blockPos)
{
this.chunkPos = new ChunkPos(((BlockPosWrapper) blockPos).getBlockPos());
}
public ChunkPosWrapper(int chunkX, int chunkZ)
{
this.chunkPos = new ChunkPos(chunkX, chunkZ);
}
public ChunkPosWrapper()
{
this.chunkPos = new ChunkPos(0, 0);
}
@Override
public int getX()
{
return chunkPos.x;
}
@Override
public int getZ()
{
return chunkPos.z;
}
@Override
public int getMinBlockX()
{
return chunkPos.getMinBlockX();
}
@Override
public int getMinBlockZ()
{
return chunkPos.getMinBlockZ();
}
@Override
public int getRegionX()
{
return chunkPos.getRegionX();
}
@Override
public int getRegionZ()
{
return chunkPos.getRegionZ();
}
public ChunkPos getChunkPos()
{
return chunkPos;
}
@Override
public boolean equals(Object o)
{
return chunkPos.equals(o);
}
@Override
public int hashCode()
{
return Objects.hash(chunkPos);
}
@Override
public BlockPosWrapper getWorldPosition()
{
BlockPos blockPos = chunkPos.getWorldPosition();
return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
}
@@ -1,125 +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.wrappers.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.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.forge.wrappers.WrapperUtil;
import com.seibel.lod.forge.wrappers.block.BlockColorWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.block.BlockShapeWrapper;
import com.seibel.lod.forge.wrappers.world.BiomeWrapper;
import net.minecraft.block.BlockState;
import net.minecraft.block.ILiquidContainer;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.world.chunk.IChunk;
/**
* @author ??
* @version 11-17-2021
*/
public class ChunkWrapper implements IChunkWrapper
{
private final IChunk chunk;
private final ChunkPosWrapper chunkPos;
@Override
public int getHeight()
{
return chunk.getMaxBuildHeight();
}
@Override
public boolean isPositionInWater(AbstractBlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos());
//This type of block is always in water
return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
@Override
public int getHeightMapValue(int xRel, int zRel)
{
return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
}
@Override
public BiomeWrapper getBiome(int xRel, int yAbs, int zRel)
{
return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2));
}
@Override
public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos)
{
return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock());
}
@Override
public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos)
{
return BlockShapeWrapper.getBlockShapeWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock(), this, blockPos);
}
public ChunkWrapper(IChunk chunk)
{
this.chunk = chunk;
this.chunkPos = new ChunkPosWrapper(chunk.getPos());
}
public IChunk getChunk()
{
return chunk;
}
@Override
public ChunkPosWrapper getPos()
{
return chunkPos;
}
@Override
public boolean isLightCorrect()
{
return chunk.isLightCorrect();
}
@Override
public boolean isWaterLogged(AbstractBlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(((BlockPosWrapper)blockPos).getBlockPos());
//This type of block is always in water
return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
@Override
public int getEmittedBrightness(AbstractBlockPosWrapper blockPos)
{
return chunk.getLightEmission(((BlockPosWrapper)blockPos).getBlockPos());
}
}
@@ -1,491 +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.wrappers.config;
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.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.forge.ForgeConfig;
/**
* @author James Seibel
* @version 11-16-2021
*/
public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
{
public static final LodConfigWrapperSingleton INSTANCE = new LodConfigWrapperSingleton();
private static final Client client = new Client();
@Override
public IClient client()
{
return client;
}
public static class Client implements IClient
{
public final IGraphics graphics;
public final IWorldGenerator worldGenerator;
public final IAdvanced advanced;
@Override
public IGraphics graphics()
{
return graphics;
}
@Override
public IWorldGenerator worldGenerator()
{
return worldGenerator;
}
@Override
public IAdvanced advanced()
{
return advanced;
}
//================//
// Client Configs //
//================//
public Client()
{
graphics = new Graphics();
worldGenerator = new WorldGenerator();
advanced = new Advanced();
}
//==================//
// Graphics Configs //
//==================//
public static class Graphics implements IGraphics
{
public final IQuality quality;
public final IFogQuality fogQuality;
public final IAdvancedGraphics advancedGraphics;
@Override
public IQuality quality()
{
return quality;
}
@Override
public IFogQuality fogQuality()
{
return fogQuality;
}
@Override
public IAdvancedGraphics advancedGraphics()
{
return advancedGraphics;
}
Graphics()
{
quality = new Quality();
advancedGraphics = new AdvancedGraphics();
fogQuality = new FogQuality();
}
public static class Quality implements IQuality
{
@Override
public HorizontalResolution getDrawResolution()
{
return ForgeConfig.CLIENT.graphics.qualityOption.drawResolution.get();
}
@Override
public void setDrawResolution(HorizontalResolution newHorizontalResolution)
{
ForgeConfig.CLIENT.graphics.qualityOption.drawResolution.set(newHorizontalResolution);
}
@Override
public int getLodChunkRenderDistance()
{
return ForgeConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get();
}
@Override
public void setLodChunkRenderDistance(int newLodChunkRenderDistance)
{
ForgeConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(newLodChunkRenderDistance);
}
@Override
public VerticalQuality getVerticalQuality()
{
return ForgeConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
}
@Override
public void setVerticalQuality(VerticalQuality newVerticalQuality)
{
ForgeConfig.CLIENT.graphics.qualityOption.verticalQuality.set(newVerticalQuality);
}
@Override
public HorizontalScale getHorizontalScale()
{
return ForgeConfig.CLIENT.graphics.qualityOption.horizontalScale.get();
}
@Override
public void setHorizontalScale(HorizontalScale newHorizontalScale)
{
ForgeConfig.CLIENT.graphics.qualityOption.horizontalScale.set(newHorizontalScale);
}
@Override
public HorizontalQuality getHorizontalQuality()
{
return ForgeConfig.CLIENT.graphics.qualityOption.horizontalQuality.get();
}
@Override
public void setHorizontalQuality(HorizontalQuality newHorizontalQuality)
{
ForgeConfig.CLIENT.graphics.qualityOption.horizontalQuality.set(newHorizontalQuality);
}
}
public static class FogQuality implements IFogQuality
{
@Override
public FogDistance getFogDistance()
{
return ForgeConfig.CLIENT.graphics.fogQuality.fogDistance.get();
}
@Override
public void setFogDistance(FogDistance newFogDistance)
{
ForgeConfig.CLIENT.graphics.fogQuality.fogDistance.set(newFogDistance);
}
@Override
public FogDrawOverride getFogDrawOverride()
{
return ForgeConfig.CLIENT.graphics.fogQuality.fogDrawOverride.get();
}
@Override
public void setFogDrawOverride(FogDrawOverride newFogDrawOverride)
{
ForgeConfig.CLIENT.graphics.fogQuality.fogDrawOverride.set(newFogDrawOverride);
}
@Override
public boolean getDisableVanillaFog()
{
return ForgeConfig.CLIENT.graphics.fogQuality.disableVanillaFog.get();
}
@Override
public void setDisableVanillaFog(boolean newDisableVanillaFog)
{
ForgeConfig.CLIENT.graphics.fogQuality.disableVanillaFog.set(newDisableVanillaFog);
}
}
public static class AdvancedGraphics implements IAdvancedGraphics
{
@Override
public LodTemplate getLodTemplate()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.get();
}
@Override
public void setLodTemplate(LodTemplate newLodTemplate)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.set(newLodTemplate);
}
@Override
public boolean getDisableDirectionalCulling()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
}
@Override
public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.set(newDisableDirectionalCulling);
}
@Override
public boolean getAlwaysDrawAtMaxQuality()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.get();
}
@Override
public void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.set(newAlwaysDrawAtMaxQuality);
}
@Override
public VanillaOverdraw getVanillaOverdraw()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get();
}
@Override
public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(newVanillaOverdraw);
}
@Override
public GpuUploadMethod getGpuUploadMethod()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get();
}
@Override
public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(newDisableVanillaFog);
}
@Override
public boolean getUseExtendedNearClipPlane()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get();
}
@Override
public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.set(newUseExtendedNearClipPlane);
}
}
}
//========================//
// WorldGenerator Configs //
//========================//
public static class WorldGenerator implements IWorldGenerator
{
@Override
public GenerationPriority getGenerationPriority()
{
return ForgeConfig.CLIENT.worldGenerator.generationPriority.get();
}
@Override
public void setGenerationPriority(GenerationPriority newGenerationPriority)
{
ForgeConfig.CLIENT.worldGenerator.generationPriority.set(newGenerationPriority);
}
@Override
public DistanceGenerationMode getDistanceGenerationMode()
{
return ForgeConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
}
@Override
public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode)
{
ForgeConfig.CLIENT.worldGenerator.distanceGenerationMode.set(newDistanceGenerationMode);
}
@Override
public boolean getAllowUnstableFeatureGeneration()
{
return ForgeConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.get();
}
@Override
public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration)
{
ForgeConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(newAllowUnstableFeatureGeneration);
}
@Override
public BlocksToAvoid getBlocksToAvoid()
{
return ForgeConfig.CLIENT.worldGenerator.blocksToAvoid.get();
}
@Override
public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid)
{
ForgeConfig.CLIENT.worldGenerator.blocksToAvoid.set(newBlockToAvoid);
}
}
//============================//
// AdvancedModOptions Configs //
//============================//
public static class Advanced implements IAdvanced
{
public final IThreading threading;
public final IDebugging debugging;
public final IBuffers buffers;
@Override
public IThreading threading()
{
return threading;
}
@Override
public IDebugging debugging()
{
return debugging;
}
@Override
public IBuffers buffers()
{
return buffers;
}
public Advanced()
{
threading = new Threading();
debugging = new Debugging();
buffers = new Buffers();
}
public static class Threading implements IThreading
{
@Override
public int getNumberOfWorldGenerationThreads()
{
return ForgeConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get();
}
@Override
public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads)
{
ForgeConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.set(newNumberOfWorldGenerationThreads);
}
@Override
public int getNumberOfBufferBuilderThreads()
{
return ForgeConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.get();
}
@Override
public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads)
{
ForgeConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.set(newNumberOfWorldBuilderThreads);
}
}
//===============//
// Debug Options //
//===============//
public static class Debugging implements IDebugging
{
@Override
public boolean getDrawLods()
{
return ForgeConfig.CLIENT.advancedModOptions.debugging.drawLods.get();
}
@Override
public void setDrawLods(boolean newDrawLods)
{
ForgeConfig.CLIENT.advancedModOptions.debugging.drawLods.set(newDrawLods);
}
@Override
public DebugMode getDebugMode()
{
return ForgeConfig.CLIENT.advancedModOptions.debugging.debugMode.get();
}
@Override
public void setDebugMode(DebugMode newDebugMode)
{
ForgeConfig.CLIENT.advancedModOptions.debugging.debugMode.set(newDebugMode);
}
@Override
public boolean getDebugKeybindingsEnabled()
{
return ForgeConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.get();
}
@Override
public void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings)
{
ForgeConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.set(newEnableDebugKeybindings);
}
}
public static class Buffers implements IBuffers
{
@Override
public BufferRebuildTimes getRebuildTimes()
{
return ForgeConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get();
}
@Override
public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes)
{
ForgeConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.set(newBufferRebuildTimes);
}
}
}
}
}
@@ -1,139 +0,0 @@
package com.seibel.lod.forge.wrappers.minecraft;
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.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.forge.wrappers.McObjectConverter;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk;
import net.minecraft.potion.Effects;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
/**
* A singleton that contains everything
* related to rendering in Minecraft.
*
* @author James Seibel
* @version 11-18-2021
*/
public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
private final GameRenderer gameRenderer = Minecraft.getInstance().gameRenderer;
private final static Minecraft mc = Minecraft.getInstance();
@Override
public Vec3f getLookAtVector()
{
ActiveRenderInfo camera = gameRenderer.getMainCamera();
Vector3f cameraDir = camera.getLookVector();
return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z());
}
@Override
public AbstractBlockPosWrapper getCameraBlockPosition()
{
ActiveRenderInfo camera = gameRenderer.getMainCamera();
BlockPos blockPos = camera.getBlockPosition();
return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
@Override
public boolean playerHasBlindnessEffect()
{
return mc.player.getActiveEffectsMap().get(Effects.BLINDNESS) != null;
}
@Override
public Vec3d getCameraExactPosition()
{
ActiveRenderInfo camera = gameRenderer.getMainCamera();
Vector3d projectedView = camera.getPosition();
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
}
@Override
public Mat4f getDefaultProjectionMatrix(float partialTicks)
{
return McObjectConverter.Convert(gameRenderer.getProjectionMatrix(gameRenderer.getMainCamera(), partialTicks, true));
}
@Override
public double getGamma()
{
return mc.options.gamma;
}
@Override
public double getFov(float partialTicks)
{
return gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true);
}
/** Measured in chunks */
@Override
public int getRenderDistance()
{
return mc.options.renderDistance;
}
@Override
public int getScreenWidth()
{
return mc.getWindow().getWidth();
}
@Override
public int getScreenHeight()
{
return mc.getWindow().getHeight();
}
/**
* This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br>
* <p>
* Note: This isn't perfect. It will return some chunks that are outside
* the clipping plane. (For example, if you are high above the ground some chunks
* will be incorrectly added, even though they are outside render range).
*/
@Override
public HashSet<AbstractChunkPosWrapper> getRenderedChunks()
{
HashSet<AbstractChunkPosWrapper> loadedPos = new HashSet<>();
// Wow, those are some long names!
// go through every RenderInfo to get the compiled chunks
WorldRenderer renderer = mc.levelRenderer;
for (WorldRenderer.LocalRenderInformationContainer worldRenderer$LocalRenderInformationContainer : renderer.renderChunks)
{
CompiledChunk compiledChunk = worldRenderer$LocalRenderInformationContainer.chunk.getCompiledChunk();
if (!compiledChunk.hasNoRenderableLayers())
{
// add the ChunkPos for every rendered chunk
BlockPos bpos = worldRenderer$LocalRenderInformationContainer.chunk.getOrigin();
loadedPos.add(new ChunkPosWrapper(bpos));
}
}
return loadedPos;
}
}
@@ -1,394 +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.wrappers.minecraft;
import java.awt.Color;
import java.io.File;
import java.util.ArrayList;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.forge.wrappers.McObjectConverter;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.forge.wrappers.misc.LightMapWrapper;
import com.seibel.lod.forge.wrappers.world.DimensionTypeWrapper;
import com.seibel.lod.forge.wrappers.world.WorldWrapper;
import net.minecraft.client.GameSettings;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.network.play.ClientPlayNetHandler;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.model.ModelManager;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.crash.CrashReport;
import net.minecraft.entity.Entity;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
/**
* A singleton that wraps the Minecraft class
* to allow for easier movement between Minecraft versions.
*
* @author James Seibel
* @version 9-16-2021
*/
public class MinecraftWrapper implements IMinecraftWrapper
{
public static final MinecraftWrapper INSTANCE = new MinecraftWrapper();
private final Minecraft mc = Minecraft.getInstance();
/**
* The lightmap for the current:
* Time, dimension, brightness setting, etc.
*/
private NativeImage lightMap = null;
private ProfilerWrapper profilerWrapper;
private MinecraftWrapper()
{
}
//================//
// helper methods //
//================//
/**
* This should be called at the beginning of every frame to
* clear any Minecraft data that becomes out of date after a frame. <br> <br>
* <p>
* LightMaps and other time sensitive objects fall in this category. <br> <br>
* <p>
* This doesn't affect OpenGL objects in any way.
*/
@Override
public void clearFrameObjectCache()
{
lightMap = null;
}
//=================//
// method wrappers //
//=================//
@Override
public float getShade(LodDirection lodDirection)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return mc.level.getShade(mcDir, true);
}
@Override
public boolean hasSinglePlayerServer()
{
return mc.hasSingleplayerServer();
}
@Override
public String getCurrentServerName()
{
return mc.getCurrentServer().name;
}
@Override
public String getCurrentServerIp()
{
return mc.getCurrentServer().ip;
}
@Override
public String getCurrentServerVersion()
{
return mc.getCurrentServer().version.getString();
}
/** Returns the dimension the player is currently in */
@Override
public IDimensionTypeWrapper getCurrentDimension()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(mc.player.level.dimensionType());
}
@Override
public String getCurrentDimensionId()
{
return LodUtil.getDimensionIDFromWorld(WorldWrapper.getWorldWrapper(mc.level));
}
/** This texture changes every frame */
@Override
public ILightMapWrapper getCurrentLightMap()
{
// get the current lightMap if the cache is empty
if (lightMap == null)
{
LightTexture tex = mc.gameRenderer.lightTexture();
lightMap = tex.lightPixels;
}
return new LightMapWrapper(lightMap);
}
/**
* 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
*/
@Override
public int getColorIntFromLightMap(int u, int v)
{
if (lightMap == null)
{
// make sure the lightMap is up-to-date
getCurrentLightMap();
}
return lightMap.getPixelRGBA(u, v);
}
/**
* 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
*/
@Override
public Color getColorFromLightMap(int u, int v)
{
return LodUtil.intToColor(lightMap.getPixelRGBA(u, v));
}
//=============//
// Simple gets //
//=============//
public ClientPlayerEntity getPlayer()
{
return mc.player;
}
@Override
public boolean playerExists()
{
return mc.player != null;
}
@Override
public BlockPosWrapper getPlayerBlockPos()
{
BlockPos playerPos = getPlayer().blockPosition();
return new BlockPosWrapper(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
@Override
public ChunkPosWrapper getPlayerChunkPos()
{
return new ChunkPosWrapper(getPlayer().xChunk, getPlayer().zChunk);
}
public GameSettings getOptions()
{
return mc.options;
}
public ModelManager getModelManager()
{
return mc.getModelManager();
}
public ClientWorld getClientWorld()
{
return mc.level;
}
/**
* Attempts to get the ServerWorld for the dimension
* the user is currently in.
* @returns null if no ServerWorld is available
*/
@Override
public IWorldWrapper getWrappedServerWorld()
{
if (mc.level == null)
return null;
DimensionType dimension = mc.level.dimensionType();
IntegratedServer server = mc.getSingleplayerServer();
if (server == null)
return null;
ServerWorld serverWorld = null;
Iterable<ServerWorld> worlds = server.getAllLevels();
for (ServerWorld world : worlds)
{
if (world.dimensionType() == dimension)
{
serverWorld = world;
break;
}
}
return WorldWrapper.getWorldWrapper(serverWorld);
}
@Override
public IWorldWrapper getWrappedClientWorld()
{
return WorldWrapper.getWorldWrapper(mc.level);
}
@Override
public File getGameDirectory()
{
return mc.gameDirectory;
}
@Override
public IProfilerWrapper getProfiler()
{
if (profilerWrapper == null)
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
else if (mc.getProfiler() != profilerWrapper.profiler)
profilerWrapper.profiler = mc.getProfiler();
return profilerWrapper;
}
public ClientPlayNetHandler getConnection()
{
return mc.getConnection();
}
public GameRenderer getGameRenderer()
{
return mc.gameRenderer;
}
public Entity getCameraEntity()
{
return mc.cameraEntity;
}
public MainWindow getWindow()
{
return mc.getWindow();
}
@Override
public float getSkyDarken(float partialTicks)
{
return mc.level.getSkyDarken(partialTicks);
}
public IntegratedServer getSinglePlayerServer()
{
return mc.getSingleplayerServer();
}
@Override
public boolean connectedToServer()
{
return mc.getCurrentServer() != null;
}
public ServerData getCurrentServer()
{
return mc.getCurrentServer();
}
public WorldRenderer getLevelRenderer()
{
return mc.levelRenderer;
}
/** Returns all worlds available to the server */
@Override
public ArrayList<IWorldWrapper> getAllServerWorlds()
{
ArrayList<IWorldWrapper> worlds = new ArrayList<IWorldWrapper>();
Iterable<ServerWorld> serverWorlds = mc.getSingleplayerServer().getAllLevels();
for (ServerWorld world : serverWorlds)
{
worlds.add(WorldWrapper.getWorldWrapper(world));
}
return worlds;
}
@Override
public void sendChatMessage(String string)
{
getPlayer().sendMessage(new StringTextComponent(string), getPlayer().getUUID());
}
/**
* Crashes Minecraft, displaying the given errorMessage <br> <br>
* In the following format: <br>
*
* The game crashed whilst <strong>errorMessage</strong> <br>
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
* Exit Code: -1 <br>
*/
@Override
public void crashMinecraft(String errorMessage, Throwable exception)
{
ClientApi.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report);
}
}
@@ -1,43 +0,0 @@
package com.seibel.lod.forge.wrappers.minecraft;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import net.minecraft.profiler.IProfiler;
/**
*
*
* @author James Seibel
* @version 11-20-2021
*/
public class ProfilerWrapper implements IProfilerWrapper
{
public IProfiler profiler;
public ProfilerWrapper(IProfiler newProfiler)
{
profiler = newProfiler;
}
/** starts a new section inside the currently running section */
@Override
public void push(String newSection)
{
profiler.push(newSection);
}
/** ends the currently running section and starts a new one */
@Override
public void popPush(String newSection)
{
profiler.popPush(newSection);
}
/** ends the currently running section */
@Override
public void pop()
{
profiler.pop();
}
}
@@ -1,31 +0,0 @@
package com.seibel.lod.forge.wrappers.misc;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import net.minecraft.client.renderer.texture.NativeImage;
/**
*
* @author Leonardo Amato
* @version 11-13-2021
*/
public class LightMapWrapper implements ILightMapWrapper
{
private NativeImage lightMap = null;
public LightMapWrapper(NativeImage newlightMap)
{
lightMap = newlightMap;
}
public void setLightMap(NativeImage newlightMap)
{
lightMap = newlightMap;
}
@Override
public int getLightValue(int skyLight, int blockLight)
{
return lightMap.getPixelRGBA(skyLight, blockLight);
}
}
@@ -1,60 +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.wrappers.world;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeColorWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import net.minecraft.world.biome.BiomeColors;
/**
* @author Cola?
* @version 11-15-2021
*/
public class BiomeColorWrapperSingleton implements IBiomeColorWrapperSingleton
{
private static final BiomeColorWrapperSingleton instance = new BiomeColorWrapperSingleton();
@Override
public IBiomeColorWrapperSingleton getInstance()
{
return instance;
}
@Override
public int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageGrassColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageWaterColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageFoliageColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
}
@@ -1,157 +0,0 @@
package com.seibel.lod.forge.wrappers.world;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.forge.wrappers.block.BlockColorSingletonWrapper;
import com.seibel.lod.forge.wrappers.block.BlockColorWrapper;
import net.minecraft.block.Blocks;
import net.minecraft.world.biome.Biome;
/**
* @author James Seibel
* @version 11-15-2021
*/
public class BiomeWrapper implements IBiomeWrapper
{
public static final ConcurrentMap<Biome, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
private final Biome biome;
public BiomeWrapper(Biome biome)
{
this.biome = biome;
}
static public BiomeWrapper getBiomeWrapper(Biome biome)
{
//first we check if the biome has already been wrapped
if(biomeWrapperMap.containsKey(biome) && biomeWrapperMap.get(biome) != null)
return biomeWrapperMap.get(biome);
//if it hasn't been created yet, we create it and save it in the map
BiomeWrapper biomeWrapper = new BiomeWrapper(biome);
biomeWrapperMap.put(biome, biomeWrapper);
//we return the newly created wrapper
return biomeWrapper;
}
/** Returns a color int for the given biome. */
@Override
public int getColorForBiome(int x, int z)
{
int colorInt;
int tintValue = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.NETHERRACK).getColor();
break;
case THEEND:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.END_STONE).getColor();
break;
case BEACH:
case DESERT:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SAND).getColor();
break;
case EXTREME_HILLS:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.STONE).getColor();
break;
case MUSHROOM:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.MYCELIUM).getColor();
break;
case ICY:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SNOW).getColor();
break;
case MESA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.RED_SAND).getColor();
break;
case OCEAN:
case RIVER:
colorInt = BlockColorSingletonWrapper.INSTANCE.getWaterColor().getColor();
tintValue = biome.getWaterColor();
break;
case PLAINS:
case SAVANNA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.GRASS_BLOCK).getColor();
tintValue = biome.getGrassColor(x, z);
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case TAIGA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SPRUCE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case JUNGLE:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.JUNGLE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case NONE:
default:
case SWAMP:
case FOREST:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.OAK_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
}
return colorInt;
}
@Override
public int getGrassTint(int x, int z)
{
return biome.getGrassColor(x, z);
}
@Override
public int getFolliageTint()
{
return biome.getFoliageColor();
}
@Override
public int getWaterTint()
{
return biome.getWaterColor();
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BiomeWrapper))
return false;
BiomeWrapper that = (BiomeWrapper) o;
return Objects.equals(biome, that.biome);
}
@Override public int hashCode()
{
return Objects.hash(biome);
}
}
@@ -1,56 +0,0 @@
package com.seibel.lod.forge.wrappers.world;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import net.minecraft.world.DimensionType;
/**
* @author ??
* @version 11-15-2021
*/
public class DimensionTypeWrapper implements IDimensionTypeWrapper
{
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>();
private final DimensionType dimensionType;
public DimensionTypeWrapper(DimensionType dimensionType)
{
this.dimensionType = dimensionType;
}
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
{
//first we check if the biome has already been wrapped
if(dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
return dimensionTypeWrapperMap.get(dimensionType);
//if it hasn't been created yet, we create it and save it in the map
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper;
}
@Override
public String getDimensionName()
{
return dimensionType.effectsLocation().getPath();
}
@Override
public boolean hasCeiling()
{
return dimensionType.hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return dimensionType.hasSkyLight();
}
}
@@ -1,151 +0,0 @@
package com.seibel.lod.forge.wrappers.world;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.enums.WorldType;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.world.IWorld;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
/**
* @author James Seibel
* @author ??
* @version 11-20-2021
*/
public class WorldWrapper implements IWorldWrapper
{
private static final ConcurrentMap<IWorld, WorldWrapper> worldWrapperMap = new ConcurrentHashMap<>();
private final IWorld world;
public final WorldType worldType;
public WorldWrapper(IWorld newWorld)
{
world = newWorld;
if (world.getClass() == ServerWorld.class)
worldType = WorldType.ServerWorld;
else if (world.getClass() == ClientWorld.class)
worldType = WorldType.ClientWorld;
else
worldType = WorldType.Unknown;
}
public static WorldWrapper getWorldWrapper(IWorld world)
{
//first we check if the biome has already been wrapped
if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null)
return worldWrapperMap.get(world);
//if it hasn't been created yet, we create it and save it in the map
WorldWrapper worldWrapper = new WorldWrapper(world);
worldWrapperMap.put(world, worldWrapper);
//we return the newly created wrapper
return worldWrapper;
}
public static void clearMap()
{
worldWrapperMap.clear();
}
@Override
public WorldType getWorldType()
{
return worldType;
}
@Override
public DimensionTypeWrapper getDimensionType()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType());
}
@Override
public int getBlockLight(AbstractBlockPosWrapper blockPos)
{
return world.getLightEngine().blockEngine.getLightValue(((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getSkyLight(AbstractBlockPosWrapper blockPos)
{
return world.getLightEngine().skyEngine.getLightValue(((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public BiomeWrapper getBiome(AbstractBlockPosWrapper blockPos)
{
return BiomeWrapper.getBiomeWrapper(world.getBiome(((BlockPosWrapper) blockPos).getBlockPos()));
}
public IWorld getWorld()
{
return world;
}
@Override
public boolean hasCeiling()
{
return world.dimensionType().hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return world.dimensionType().hasSkyLight();
}
@Override
public boolean isEmpty()
{
return world == null;
}
@Override
public int getHeight()
{
return world.getHeight();
}
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
@Override
public File getSaveFolder() throws UnsupportedOperationException
{
if (worldType != WorldType.ServerWorld)
throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds.");
ServerChunkProvider chunkSource = ((ServerWorld) world).getChunkSource();
return chunkSource.dataStorage.dataFolder;
}
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
public ServerWorld getServerWorld() throws UnsupportedOperationException
{
if (worldType != WorldType.ServerWorld)
throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds.");
return (ServerWorld) world;
}
@Override
public int getSeaLevel()
{
// TODO this is depreciated, what should we use instead?
return world.getSeaLevel();
}
}
@@ -1,332 +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.wrappers.worldGeneration;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.forge.wrappers.WrapperUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.particles.IParticleData;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.SectionPos;
import net.minecraft.util.registry.DynamicRegistries;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.DimensionType;
import net.minecraft.world.EmptyTickList;
import net.minecraft.world.ISeedReader;
import net.minecraft.world.ITickList;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeManager;
import net.minecraft.world.border.WorldBorder;
import net.minecraft.world.chunk.AbstractChunkProvider;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraft.world.gen.feature.structure.StructureStart;
import net.minecraft.world.lighting.WorldLightManager;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.IWorldInfo;
/**
* This is a fake ServerWorld used when generating features.
* It allows us to keep each LodChunk generation independent
* of the actual ServerWorld, allowing
* multithread generation.
*
* @author James Seibel
* @version 7-26-2021
*/
public class LodServerWorld implements ISeedReader
{
public HashMap<Heightmap.Type, Heightmap> heightmaps = new HashMap<>();
public final IChunk chunk;
public final ServerWorld serverWorld;
public LodServerWorld(ServerWorld newServerWorld, IChunk newChunk)
{
chunk = newChunk;
serverWorld = newServerWorld;
}
@Override
public int getHeight(Heightmap.Type heightmapType, int x, int z)
{
// make sure the block position is set relative to the chunk
x = x % LodUtil.CHUNK_WIDTH;
x = (x < 0) ? x + 16 : x;
z = z % LodUtil.CHUNK_WIDTH;
z = (z < 0) ? z + 16 : z;
return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z);
}
@Override
public Biome getBiome(BlockPos pos)
{
return chunk.getBiomes().getNoiseBiome(pos.getX() >> 2, pos.getY() >> 2, pos.getZ() >> 2);
}
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft)
{
return chunk.setBlockState(pos, state, false) == state;
}
@Override
public BlockState getBlockState(BlockPos pos)
{
return chunk.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos pos)
{
return chunk.getFluidState(pos);
}
@Override
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> state)
{
return state.test(chunk.getBlockState(pos));
}
@Override
public ITickList<Block> getBlockTicks()
{
return EmptyTickList.empty();
}
@Override
public IChunk getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull)
{
return chunk;
}
@Override
public Stream<? extends StructureStart<?>> startsForFeature(SectionPos p_241827_1_, Structure<?> p_241827_2_)
{
return serverWorld.startsForFeature(p_241827_1_, p_241827_2_);
}
@Override
public ITickList<Fluid> getLiquidTicks()
{
return EmptyTickList.empty();
}
@Override
public WorldLightManager getLightEngine()
{
return new WorldLightManager(null, false, false);
}
@Override
public long getSeed()
{
return serverWorld.getSeed();
}
@Override
public DynamicRegistries registryAccess()
{
return serverWorld.registryAccess();
}
/**
* All methods below shouldn't be needed
* and thus have been left unimplemented.
*/
@Override
public Random getRandom()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void playSound(PlayerEntity player, BlockPos pos, SoundEvent soundIn, SoundCategory category, float volume,
float pitch)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void addParticle(IParticleData particleData, double x, double y, double z, double xSpeed, double ySpeed,
double zSpeed)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public BiomeManager getBiomeManager()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSeaLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public float getShade(Direction p_230487_1_, boolean p_230487_2_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public WorldBorder getWorldBorder()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean removeBlock(BlockPos pos, boolean isMoving)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public ServerWorld getLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public AbstractChunkProvider getChunkSource()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public IWorldInfo getLevelData()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void levelEvent(PlayerEntity arg0, int arg1, BlockPos arg2, int arg3)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<Entity> getEntities(Entity arg0, AxisAlignedBB arg1, Predicate<? super Entity> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public <T extends Entity> List<T> getEntitiesOfClass(Class<? extends T> arg0, AxisAlignedBB arg1,
Predicate<? super T> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<? extends PlayerEntity> players()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSkyDarken()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean isClientSide()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DimensionType dimensionType()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public TileEntity getBlockEntity(BlockPos p_175625_1_)
{
throw new UnsupportedOperationException("Not Implemented");
}
}
@@ -1,389 +0,0 @@
package com.seibel.lod.forge.wrappers.worldGeneration;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig;
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.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;
import com.seibel.lod.forge.wrappers.WrapperUtil;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.forge.wrappers.world.WorldWrapper;
import net.minecraft.util.palette.UpgradeData;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.ChunkPrimer;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.IceAndSnowFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.template.TemplateManager;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.server.ServerWorldLightManager;
/**
* @author James Seibel
* @version 11-13-2021
*/
public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper
{
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
/**
* If a configured feature fails for whatever reason,
* add it to this list. This will hopefully remove any
* features that could cause issues down the line.
*/
private static final ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> FEATURES_TO_AVOID = new ConcurrentHashMap<>();
public final ServerWorld serverWorld;
public final LodDimension lodDim;
public final LodBuilder lodBuilder;
public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
super(newLodBuilder, newLodDimension, worldWrapper);
lodBuilder = newLodBuilder;
lodDim = newLodDimension;
serverWorld = ((WorldWrapper) worldWrapper).getServerWorld();
}
/** takes about 2-5 ms */
@Override
public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode)
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
// generate fake height data for this LOD
int seaLevel = serverWorld.getSeaLevel();
boolean simulateHeight = generationMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
boolean inTheEnd = false;
// add fake heightmap data so our LODs aren't at height 0
Heightmap heightmap = new Heightmap(chunk, WrapperUtil.DEFAULT_HEIGHTMAP);
for (int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++)
{
if (simulateHeight)
{
// these heights are of course aren't super accurate,
// they are just to simulate height data where there isn't any
switch (chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory())
{
case NETHER:
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
break;
case EXTREME_HILLS:
heightmap.setHeight(x, z, seaLevel + 30);
break;
case MESA:
case JUNGLE:
heightmap.setHeight(x, z, seaLevel + 20);
break;
case BEACH:
heightmap.setHeight(x, z, seaLevel + 5);
break;
case NONE:
heightmap.setHeight(x, z, 0);
break;
case OCEAN:
case RIVER:
heightmap.setHeight(x, z, seaLevel);
break;
case THEEND:
inTheEnd = true;
break;
// DESERT
// FOREST
// ICY
// MUSHROOM
// SAVANNA
// SWAMP
// TAIGA
// PLAINS
default:
heightmap.setHeight(x, z, seaLevel + 10);
break;
}// heightmap switch
}
else
{
// we aren't simulating height
// always use sea level
heightmap.setHeight(x, z, seaLevel);
}
}// z
}// x
chunk.setHeightmap(WrapperUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData());
if (!inTheEnd)
{
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
else
{
// if we are in the end, don't generate any chunks.
// Since we don't know where the islands are, everything
// generates the same, and it looks awful.
//TODO it appears that 'if' can be collapsed, but comment says that it should not be a case
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
// long startTime = System.currentTimeMillis();
// long endTime = System.currentTimeMillis();
// System.out.println(endTime - startTime);
}
/** takes about 10 - 20 ms */
@Override
public void generateSurface(AbstractChunkPosWrapper pos)
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine();
TemplateManager templateManager = serverWorld.getStructureManager();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// this feature has been proven to be thread safe,
// so we will add it
IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC);
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.SURFACE));
/*TODO if we want to use Biome utils and terrain utils for overworld
* lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/
}
/**
* takes about 15 - 20 ms
* <p>
* Causes concurrentModification Exceptions,
* which could cause instability or world generation bugs
*/
@Override
public void generateFeatures(AbstractChunkPosWrapper pos)
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine();
TemplateManager templateManager = serverWorld.getStructureManager();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// get all the biomes in the chunk
HashSet<Biome> biomes = new HashSet<>();
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2);
// Issue #35
// For some reason Jungle biomes cause incredible lag
// the features here must be interacting with each other
// in unpredictable ways (specifically tree feature generation).
// When generating Features my CPU usage generally hovers around 30 - 40%
// when generating Jungles it spikes to 100%.
if (biome.getBiomeCategory() != Biome.Category.JUNGLE)
{
// should probably use the heightmap here instead of seaLevel,
// but this seems to get the job done well enough
biomes.add(biome);
}
}
}
boolean allowUnstableFeatures = CONFIG.client().worldGenerator().getAllowUnstableFeatureGeneration();
// generate all the features related to this chunk.
// this may or may not be thread safe
for (Biome biome : biomes)
{
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.generationSettings.features();
for (List<Supplier<ConfiguredFeature<?, ?>>> suppliers : featuresForState)
{
for (Supplier<ConfiguredFeature<?, ?>> featureSupplier : suppliers)
{
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
if (!allowUnstableFeatures &&
FEATURES_TO_AVOID.containsKey(configuredFeature.hashCode()))
continue;
try
{
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
}
catch (ConcurrentModificationException | UnsupportedOperationException e)
{
// This will happen. I'm not sure what to do about it
// except pray that it doesn't affect the normal world generation
// in any harmful way.
// Update: this can cause crashes and high CPU usage.
// Issue #35
// I tried cloning the config for each feature, but that
// path was blocked since I can't clone lambda methods.
// I tried using a deep cloning library and discovered
// the problem there.
// ( https://github.com/kostaskougios/cloning
// and
// https://github.com/EsotericSoftware/kryo )
if (!allowUnstableFeatures)
FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
// This will happen when the LodServerWorld
// isn't able to return something that a feature
// generator needs
catch (Exception e)
{
// I'm not sure what happened, print to the log
e.printStackTrace();
if (!allowUnstableFeatures)
FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
}
}
}
// generate a Lod like normal
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.FEATURES));
}
/**
* Generates using MC's ServerWorld.
* <p>
* on pre generated chunks 0 - 1 ms <br>
* on un generated chunks 0 - 50 ms <br>
* with the median seeming to hover around 15 - 30 ms <br>
* and outliers in the 100 - 200 ms range <br>
* <p>
* Note this should not be multithreaded and does cause server/simulation lag
* (Higher lag for generating than loading)
*/
@Override
public void generateFull(AbstractChunkPosWrapper pos)
{
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.FULL));
}
/*
* performance/generation tests related to
* serverWorld.getChunk(x, z, ChunkStatus. *** )
true/false is whether they generated blocks or not
the time is how long it took to generate
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
ChunkStatus.BIOMES 1 - 10 ms false (no height)
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
ChunkStatus.FEATURES 7 - 25 ms true
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
ChunkStatus.LIGHT 20 - 40 ms true
ChunkStatus.FULL 30 - 50 ms true
ChunkStatus.SPAWN 50 - 80 ms true
At this point I would suggest using FEATURES, as it generates snow and trees
(and any other object that are needed to make biomes distinct)
Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
*/
}
@@ -1,72 +0,0 @@
# Note: to update code in eclipse run the "eclipse" command in graldew
# used when creating the projection matrix
public net.minecraft.client.renderer.GameRenderer func_215311_a(Lnet/minecraft/client/renderer/ActiveRenderInfo;FZ)D # getFOVModifier
public net.minecraft.client.renderer.GameRenderer field_78529_t # rendererUpdateCount
public net.minecraft.client.renderer.GameRenderer func_228380_a_(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V # hurtCameraEffect
public net.minecraft.client.renderer.GameRenderer func_228383_b_(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V # applyBobbing
# used when accessing built byteBuffers
public net.minecraft.client.renderer.BufferBuilder field_179001_a # byteBuffer
# used when determining where to save files too
public net.minecraft.world.storage.DimensionSavedDataManager field_215759_d # folder
# used when generating LodChunks
public net.minecraft.block.AbstractBlock$AbstractBlockState field_235704_h_ # materialColor
# used when determining which chunks Vanilla Minecraft is going to render
public net.minecraft.client.renderer.WorldRenderer$LocalRenderInformationContainer
public net.minecraft.client.renderer.WorldRenderer field_72755_R # renderInfos
public net.minecraft.client.renderer.WorldRenderer$LocalRenderInformationContainer field_178036_a # renderChunk
# used in world generation
public net.minecraft.world.server.ServerWorld field_241106_P_ # structuremanager
public net.minecraft.world.gen.Heightmap func_202267_b(II)I # getDataArrayIndex
public net.minecraft.world.gen.Heightmap func_202272_a(III)V # set
public net.minecraft.world.chunk.Chunk field_76634_f # heightMap
public net.minecraft.world.chunk.Chunk field_76652_q # sections
public net.minecraft.world.chunk.ChunkPrimer field_201661_i # sections
public net.minecraft.world.server.ChunkManager field_219269_w # templateManager
public net.minecraft.world.server.ChunkManager field_219256_j # lightManager
public net.minecraft.world.gen.feature.template.TemplateManager field_186240_a # templates
public net.minecraft.world.biome.Biome field_242424_k # biomeGenerationSettings
public net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider field_227406_b_ # weightedStates
public net.minecraft.world.gen.placement.ConfiguredPlacement field_215096_a # decorator
public net.minecraft.world.gen.placement.ConfiguredPlacement field_215097_b # config
public net.minecraft.util.WeightedList field_220658_a # weightedEntries
public net.minecraft.world.gen.feature.FeatureSpread field_242250_b # base
public net.minecraft.world.gen.feature.FeatureSpread field_242251_c # spread
public net.minecraft.world.gen.feature.ConfiguredFeature func_242765_a(Lnet/minecraft/world/ISeedReader;Lnet/minecraft/world/gen/ChunkGenerator;Ljava/util/Random;Lnet/minecraft/util/math/BlockPos;)Z # place
public net.minecraft.world.server.ServerChunkProvider field_217244_j # dataStorage
public net.minecraft.world.lighting.WorldLightManager field_215576_a # blockEngine
public net.minecraft.world.lighting.WorldLightManager field_215577_b # skyEngine
public net.minecraft.world.gen.feature.Feature field_236290_a_ # configuredCodec
# used for uploading vertex buffers off the render thread
public net.minecraft.client.renderer.vertex.VertexBuffer field_177365_a # id
public net.minecraft.client.renderer.vertex.VertexBuffer field_177363_b # format
public net.minecraft.client.renderer.vertex.VertexBuffer field_177364_c # vertexCount
# used for accessing the lightmap
public net.minecraft.client.renderer.LightTexture field_205111_b # lightPixels
#=====================#
# Examples from Forge #
#=====================#
# Makes public the IScreenFactory class in ScreenManager
public net.minecraft.client.gui.ScreenManager$IScreenFactory
# Makes protected and removes the final modifier from 'random' in MinecraftServer
protected-f net.minecraft.server.MinecraftServer field_147146_q #random
# Makes public the 'createNamedService' method in Util,
# accepting a String and returns an ExecutorService
public net.minecraft.util.Util func_240979_a_(Ljava/lang/String;)Ljava/util/concurrent/ExecutorService; #createNamedService
# Makes public the 'func_239776_a_' method in UUIDCodec,
# accepting two longs and returning an int[]
public net.minecraft.util.UUIDCodec func_239776_a_(JJ)[I #func_239776_a_

Some files were not shown because too many files have changed in this diff Show More