Compare commits

...

59 Commits

Author SHA1 Message Date
cola98765 1860729256 made this wokin with latest 1.16.5 2021-12-07 13:51:54 +01:00
cola98765 d0b0fae54e fix to that merge 2021-12-07 13:32:06 +01:00
Morippi 7786e1fea6 Change Box to VertexOptimizer and added the DataFormat folder with empty classes 2021-12-07 13:28:38 +01:00
cola98765 e5ad718ed0 fix LOWEST HorizontalQuality 2021-12-07 13:12:35 +01:00
cola98765 bda34d0a6d reverted DrawResolutionOffset 2021-12-07 13:12:22 +01:00
cola98765 2584afbd05 HorizontalScale is now a number 2-32 2021-12-07 13:12:14 +01:00
cola98765 72e4b520db updated DYNAMIC VanillaOverdraw setting. 2021-12-07 13:12:09 +01:00
cola98765 3220ad0f7a added DrawResolutionOffset to work with DrawResolution 2021-12-07 13:12:05 +01:00
James Seibel aba99f8210 Fix a few buffer building issues 2021-12-07 13:11:54 +01:00
Morippi 430579be28 Changed how data from the level container are passed as output. Removed the Thread system 2021-12-07 11:56:13 +01:00
cola98765 30cc294a04 reworked it back to primitive arrays 2021-12-04 14:03:01 +01:00
cola98765 aa79d5b5d4 small fix 2021-12-03 13:37:36 +01:00
cola98765 509ae5aba0 added new DataPoint. predicting is broken, and there are still couple errors in logs 2021-12-03 12:39:01 +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
106 changed files with 2318 additions and 6405 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;
@@ -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)
@@ -143,10 +146,18 @@ public class ClientApi
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 +179,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);
@@ -30,6 +30,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import com.seibel.lod.core.objects.VertexOptimizer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
@@ -41,8 +42,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 +69,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
* rendered by the LodRenderer.
*
* @author James Seibel
* @version 11-21-2021
* @version 11-29-2021
*/
public class LodBufferBuilderFactory
{
@@ -81,6 +82,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 +97,8 @@ public class LodBufferBuilderFactory
*/
public static final int DEFAULT_MEMORY_ALLOCATION = 1024;
public static int skyLightPlayer = 15;
/**
@@ -138,7 +143,7 @@ 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;
@@ -180,6 +185,10 @@ public class LodBufferBuilderFactory
// setupBuffers hasn't been called yet
return;
if (MC.getCurrentLightMap() == null)
// the lighting hasn't loaded yet
return;
generatingBuffers = true;
@@ -220,11 +229,11 @@ public class LodBufferBuilderFactory
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;
@@ -256,7 +265,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,14 +288,16 @@ 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
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte) 0);
//we get or create the map that will contain the adj data
Map<LodDirection, long[]> adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData);
Map<LodDirection, int[]> adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData);
Map<LodDirection, byte[]> adjFlags = ThreadMapUtil.getAdjFlagsArray(maxVerticalData);
//previous setToRender cache
if (setsToRender[xR][zR] == null)
@@ -338,12 +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;
long data;
xAdj = posX + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).x;
zAdj = posZ + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).z;
int color;
int data;
byte flags;
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.getX();
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.getZ();
adjPosInPlayerChunk = (chunkXdist == 0 && chunkZdist == 0);
@@ -360,64 +376,81 @@ public class LodBufferBuilderFactory
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;
flags = lodDim.getFlags(detailLevel, xAdj, zAdj, verticalIndex);
adjShadeDisabled[VertexOptimizer.DIRECTION_INDEX.get(lodDirection)] = false;
adjData.get(lodDirection)[verticalIndex] = data;
adjFlags.get(lodDirection)[verticalIndex] = flags;
}
}
else
{
//Otherwise, we check if this position is
data = lodDim.getSingleData(detailLevel, xAdj, zAdj);
data = lodDim.getData(detailLevel, xAdj, zAdj, 0);
flags = lodDim.getFlags(detailLevel, xAdj, zAdj, 0);
adjData.get(lodDirection)[0] = DataPointUtil.EMPTY_DATA;
adjData.get(lodDirection)[0] = 0;
adjFlags.get(lodDirection)[0] = 0;
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
&& !DataPointUtil.isVoid(data))
&& DataPointUtil.doesItExist(flags) && !DataPointUtil.isVoid(flags))
{
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
long data;
// We only stop when we find a block that is void or non-existing block
int color;
int data;
byte flags;
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
{
//we get the above block as adj UP
if (verticalIndex > 0)
{
adjData.get(LodDirection.UP)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex - 1);
adjFlags.get(LodDirection.UP)[0] = lodDim.getFlags(detailLevel, posX, posZ, verticalIndex - 1);
}
else
adjData.get(LodDirection.UP)[0] = DataPointUtil.EMPTY_DATA;
{
adjData.get(LodDirection.UP)[0] = 0;
adjFlags.get(LodDirection.UP)[0] = 0;
}
//we get the below block as adj DOWN
if (verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ) - 1)
{
adjData.get(LodDirection.DOWN)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex + 1);
adjFlags.get(LodDirection.DOWN)[0] = lodDim.getFlags(detailLevel, posX, posZ, verticalIndex + 1);
}
else
adjData.get(LodDirection.DOWN)[0] = DataPointUtil.EMPTY_DATA;
{
adjData.get(LodDirection.DOWN)[0] = 0;
adjFlags.get(LodDirection.DOWN)[0] = 0;
}
//We extract the data to render
color = lodDim.getColor(detailLevel, posX, posZ, verticalIndex);
data = lodDim.getData(detailLevel, posX, posZ, verticalIndex);
flags = lodDim.getFlags(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 (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data))
//If the data is not renderable (Void or non-existing) we stop since there is no data left in this position
if (!DataPointUtil.doesItExist(flags) || DataPointUtil.isVoid(flags))
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);
CONFIG.client().graphics().advancedGraphics().getLodTemplate().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, color, data, flags, adjData, adjFlags,
detailLevel, posX, posZ, vertexOptimizer, renderer.previousDebugMode, adjShadeDisabled);
}
} // for pos to in list to render
// the thread executed successfully
return true;
};
nodeToRenderThreads.add(dataToRenderThread);
}
} // region z
} // region z
@@ -455,12 +488,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);
}
@@ -520,108 +553,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 +679,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 +808,7 @@ public class LodBufferBuilderFactory
private void uploadBuffers(boolean fullRegen, LodDimension lodDim)
{
GLProxy glProxy = GLProxy.getInstance();
long fence = 0;
try
{
@@ -761,15 +817,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 +847,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 +873,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,9 +884,19 @@ 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)
{
@@ -855,7 +940,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 +953,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 +968,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 +989,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 +1000,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,19 +1028,30 @@ 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;
drawableCenterChunkPos = buildableCenterChunkPos;
// 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);
@@ -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;
@@ -37,8 +37,8 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
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, AbstractBlockPosWrapper bufferCenterBlockPos, int color, int data, byte flags, Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
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,
@@ -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.util.DataPointUtil;
@@ -44,43 +44,43 @@ 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, AbstractBlockPosWrapper bufferCenterBlockPos,
int color, int data, byte flags,
Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
{
if (box == null)
if (vertexOptimizer == null)
return;
// equivalent to 2^detailLevel
int blockWidth = 1 << detailLevel;
int color;
if (debugging != DebugMode.OFF)
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
else
color = DataPointUtil.getColor(data);
generateBoundingBox(
box,
vertexOptimizer,
DataPointUtil.getHeight(data),
DataPointUtil.getDepth(data),
blockWidth,
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
bufferCenterBlockPos,
adjData,
adjFlags,
color,
DataPointUtil.getLightSkyAlt(data),
DataPointUtil.getLightSkyAlt(data, flags),
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,
Map<LodDirection, long[]> adjData,
Map<LodDirection, int[]> adjData,
Map<LodDirection, byte[]> adjFlags,
int color,
int skyLight,
int blockLight,
@@ -100,38 +100,38 @@ public class CubicLodTemplate extends AbstractLodTemplate
// 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);
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.setUpCulling(32, bufferCenterBlockPos);
vertexOptimizer.setAdjData(adjData, adjFlags);
}
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)
for (LodDirection lodDirection : VertexOptimizer.DIRECTIONS)
{
if(box.isCulled(lodDirection))
if(vertexOptimizer.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 = vertexOptimizer.getColor(lodDirection);
skyLight = vertexOptimizer.getSkyLight(lodDirection, verticalFaceIndex);
blockLight = vertexOptimizer.getBlockLight();
color = ColorUtil.applyLightValue(color, skyLight, blockLight);
addPosAndColor(buffer,
box.getX(lodDirection, vertexIndex),
box.getY(lodDirection, vertexIndex, verticalFaceIndex),
box.getZ(lodDirection, vertexIndex),
vertexOptimizer.getX(lodDirection, vertexIndex),
vertexOptimizer.getY(lodDirection, vertexIndex, verticalFaceIndex) + DataPointUtil.VERTICAL_OFFSET,
vertexOptimizer.getZ(lodDirection, vertexIndex),
color);
}
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,9 @@ 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, AbstractBlockPosWrapper bufferCenterBlockPos, int color, int data, byte flags,
Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
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,9 @@ 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, AbstractBlockPosWrapper bufferCenterBlockPos, int color, int data, byte flags,
Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
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
@@ -194,105 +195,107 @@ public class LodBuilder
startX = detail.startX[i];
startZ = detail.startZ[i];
long[] data;
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
// creates a vertical DataPoint
// equivalent to 2^detailLevel
int size = 1 << detail.detailLevel;
int[] dataToMergeColor = ThreadMapUtil.getBuilderVerticalArrayColor(detail.detailLevel);
int[] dataToMergeData = ThreadMapUtil.getBuilderVerticalArrayData(detail.detailLevel);
byte[] dataToMergeFlags = ThreadMapUtil.getBuilderVerticalArrayFlags(detail.detailLevel);
int verticalData = DataPointUtil.WORLD_HEIGHT / 2 + 1;
AbstractChunkPosWrapper chunkPos = chunk.getPos();
int height;
int depth;
int color;
int light;
int lightSky;
int lightBlock;
byte generation = config.distanceGenerationMode.complexity;
int xRel;
int zRel;
int xAbs;
int yAbs;
int zAbs;
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;
//Calculate the height of the lod
yAbs = DataPointUtil.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET + 1;
int count = 0;
boolean topBlock = true;
while (yAbs > 0)
{
height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
// If the lod is at the default height, it must be void data
if (height == DEFAULT_HEIGHT)
{
if (topBlock)
dataToMergeFlags[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
break;
}
yAbs = height - 1;
// We search light on above air block
depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
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);
}
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);
}
lightBlock = light & 0b1111;
lightSky = (light >> 4) & 0b1111;
isDefault = ((light >> 8)) == 1;
DataPointUtil.createDataPoint(height - DataPointUtil.VERTICAL_OFFSET, depth - DataPointUtil.VERTICAL_OFFSET, color, lightSky, lightBlock, generation, isDefault);
dataToMergeColor[index * verticalData + count] = ThreadMapUtil.dataPointColor;
dataToMergeData[index * verticalData + count] = ThreadMapUtil.dataPointData;
dataToMergeFlags[index * verticalData + count] = ThreadMapUtil.dataPointFlags;
topBlock = false;
yAbs = depth - 1;
count++;
}
}
DataPointUtil.mergeMultiData(dataToMergeColor, dataToMergeData, dataToMergeFlags, DataPointUtil.WORLD_HEIGHT / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
int[] mergedColor = ThreadMapUtil.getRawVerticalDataArrayColor();
int[] mergedData = ThreadMapUtil.getRawVerticalDataArrayData();
byte[] mergedFlags = ThreadMapUtil.getRawVerticalDataArrayFlags();
//lodDim.clear(detailLevel, posX, posZ);
if (data != null && data.length != 0)
if (mergedFlags.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);
lodDim.addVerticalData(detailLevel, posX, posZ, mergedColor, mergedData, mergedFlags, false);
}
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ());
}
/** creates a vertical DataPoint */
private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunkWrapper chunk, LodBuilderConfig config, int startX, int startZ)
{
// equivalent to 2^detailLevel
int size = 1 << detail.detailLevel;
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
int verticalData = DataPointUtil.worldHeight / 2 + 1;
AbstractChunkPosWrapper chunkPos = chunk.getPos();
int height;
int depth;
int color;
int light;
int lightSky;
int lightBlock;
int generation = config.distanceGenerationMode.complexity;
int xRel;
int zRel;
int xAbs;
int yAbs;
int zAbs;
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;
//Calculate the height of the lod
yAbs = DataPointUtil.worldHeight + 1;
int count = 0;
boolean topBlock = true;
while (yAbs > 0)
{
height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
// If the lod is at the default height, it must be void data
if (height == DEFAULT_HEIGHT)
{
if (topBlock)
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
break;
}
yAbs = height - 1;
// We search light on above air block
depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
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);
}
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);
}
lightBlock = light & 0b1111;
lightSky = (light >> 4) & 0b1111;
isDefault = ((light >> 8)) == 1;
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height, depth, color, lightSky, lightBlock, generation, isDefault);
topBlock = false;
yAbs = depth - 1;
count++;
}
}
return dataToMerge;
}
/**
* Find the lowest valid point from the bottom.
* Used when creating a vertical LOD.
@@ -400,7 +403,7 @@ public class LodBuilder
if (world != null && !world.isEmpty())
if (world != null)
{
// server world sky light (always accurate)
blockLight = world.getBlockLight(blockPos);
@@ -428,39 +431,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(blockPos);
// 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(blockPos);
//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 (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
{
skyLight = 12;
isDefault = 1;
}
else
skyLight = 0;
}
else
skyLight = 0;
}
}
if (hasSkyLight)
skyLight = 0;
}
}
@@ -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;
@@ -0,0 +1,5 @@
package com.seibel.lod.core.dataFormat;
public class BlockDataFormat
{
}
@@ -0,0 +1,5 @@
package com.seibel.lod.core.dataFormat;
public class ColorFormat
{
}
@@ -0,0 +1,5 @@
package com.seibel.lod.core.dataFormat;
public class DepthHeightFormat
{
}
@@ -0,0 +1,5 @@
package com.seibel.lod.core.dataFormat;
public class LightFormat
{
}
@@ -0,0 +1,5 @@
package com.seibel.lod.core.dataFormat;
public class PositionDataFormat
{
}
@@ -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);
}
@@ -21,8 +21,8 @@ package com.seibel.lod.core.handlers;
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 +77,7 @@ public class LodDimensionFileHandler
* file handler, older versions (smaller numbers) will be deleted and overwritten,
* newer versions (larger numbers) will be ignored and won't be read.
*/
public static final int LOD_SAVE_FILE_VERSION = 6;
public static final int LOD_SAVE_FILE_VERSION = 8;
/**
* Allow saving asynchronously, but never try to save multiple regions
@@ -129,28 +129,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 +181,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 +207,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 < LOD_SAVE_FILE_VERSION)
{
//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, fileVersion));
} 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 +384,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;
}
}
@@ -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);
@@ -207,7 +206,7 @@ public class Box
/** creates an empty box */
@SuppressWarnings("serial")
public Box()
public VertexOptimizer()
{
boxOffset = new int[3];
boxWidth = new int[3];
@@ -339,25 +338,27 @@ public class Box
* 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
*/
public void setAdjData(Map<LodDirection, long[]> adjData)
public void setAdjData(Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags)
{
int height;
int depth;
int minY = getMinY();
int maxY = getMaxY();
long singleAdjDataPoint;
int singleAdjData;
byte singleAdjFlags;
/* TODO implement attached vertical face culling
//Up direction case
if(DataPointUtil.doesItExist(adjData.get(Direction.UP)))
{
height = DataPointUtil.getHeight(singleAdjDataPoint);
depth = DataPointUtil.getDepth(singleAdjDataPoint);
height = DataPointUtil.getHeight(singleAdjData);
depth = DataPointUtil.getDepth(singleAdjData);
}*/
//Down direction case
singleAdjDataPoint = adjData.get(LodDirection.DOWN)[0];
if(DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
singleAdjData = adjData.get(LodDirection.DOWN)[0];
singleAdjFlags = adjFlags.get(LodDirection.DOWN)[0];
if(DataPointUtil.doesItExist(singleAdjFlags))
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
else
skyLights.get(LodDirection.DOWN)[0] = skyLights.get(LodDirection.UP)[0];
//other sided
@@ -367,14 +368,15 @@ public class Box
if (isCulled(lodDirection))
continue;
long[] dataPoint = adjData.get(lodDirection);
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
int[] data = adjData.get(lodDirection);
byte[] flags = adjFlags.get(lodDirection);
if (DataPointUtil.isVoid(flags[0]))
{
adjHeight.get(lodDirection)[0] = maxY;
adjDepth.get(lodDirection)[0] = minY;
adjHeight.get(lodDirection)[1] = VOID_FACE;
adjDepth.get(lodDirection)[1] = VOID_FACE;
skyLights.get(lodDirection)[0] = 15; //in void set full sky light
skyLights.get(lodDirection)[0] = 15; //in void set full skylight
continue;
}
@@ -384,15 +386,16 @@ public class Box
boolean toFinish = false;
int toFinishIndex = 0;
boolean allAbove = true;
for (i = 0; i < dataPoint.length; i++)
for (i = 0; i < flags.length; i++)
{
singleAdjDataPoint = dataPoint[i];
singleAdjData = data[i];
singleAdjFlags = flags[i];
if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
if (DataPointUtil.isVoid(singleAdjFlags) || !DataPointUtil.doesItExist(singleAdjFlags))
break;
height = DataPointUtil.getHeight(singleAdjDataPoint);
depth = DataPointUtil.getDepth(singleAdjDataPoint);
height = DataPointUtil.getHeight(singleAdjData);
depth = DataPointUtil.getDepth(singleAdjData);
if (depth <= maxY)
{
@@ -405,12 +408,12 @@ public class Box
{
adjHeight.get(lodDirection)[0] = getMaxY();
adjDepth.get(lodDirection)[0] = getMinY();
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags); //skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(lodDirection)[faceToDraw] = getMinY();
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
}
faceToDraw++;
toFinish = false;
@@ -436,12 +439,12 @@ public class Box
{
adjHeight.get(lodDirection)[0] = getMaxY();
adjDepth.get(lodDirection)[0] = height;
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags); //skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(lodDirection)[faceToDraw] = height;
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
}
toFinish = false;
faceToDraw++;
@@ -453,7 +456,7 @@ public class Box
// the adj data intersects the higher part of the current data
// we start the creation of a new face
adjHeight.get(lodDirection)[faceToDraw] = depth;
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjData);
firstFace = false;
toFinish = true;
toFinishIndex = i + 1;
@@ -469,7 +472,7 @@ public class Box
}
adjDepth.get(lodDirection)[faceToDraw] = height;
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
faceToDraw++;
adjHeight.get(lodDirection)[faceToDraw] = depth;
firstFace = false;
@@ -489,11 +492,12 @@ public class Box
else if (toFinish)
{
adjDepth.get(lodDirection)[faceToDraw] = minY;
if(toFinishIndex < dataPoint.length)
if(toFinishIndex < flags.length)
{
singleAdjDataPoint = dataPoint[toFinishIndex];
if (DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
singleAdjData = data[toFinishIndex];
singleAdjFlags = flags[toFinishIndex];
if (DataPointUtil.doesItExist(singleAdjFlags))
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
else
skyLights.get(lodDirection)[faceToDraw] = skyLights.get(LodDirection.UP)[0];
}
@@ -32,7 +32,7 @@ public interface LevelContainer
* @param index z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addData(long data, int posX, int posZ, int index);
boolean addData(int color, int data, byte flags, int posX, int posZ, int index);
/**
* With this you can add data to the level container
@@ -41,7 +41,7 @@ public interface LevelContainer
* @param posZ z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addVerticalData(long[] data, int posX, int posZ);
boolean addVerticalData(int[] color, int[] data, byte[] flags, int posX, int posZ);
/**
* With this you can add data to the level container
@@ -50,25 +50,18 @@ public interface LevelContainer
* @param posZ z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addSingleData(long data, int posX, int posZ);
/**
* With this you can get data from the level container
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return the data in long array format
*/
long getData(int posX, int posZ, int index);
/**
* With this you can get data from the level container
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return the data in long array format
*/
long getSingleData(int posX, int posZ);
boolean addSingleData(int color, int data, byte flags, int posX, int posZ);
int getColor(int posX, int posZ, int verticalIndex);
int getData(int posX, int posZ, int index);
byte getFlags(int posX, int posZ, int index);
byte getSingleFlags(int posX, int posZ);
/**
* data is returned to ThreadMapUtil variables
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return true only if the data exist
@@ -30,12 +30,7 @@ import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.handlers.LodDimensionFileHandler;
import com.seibel.lod.core.objects.PosToGenerateContainer;
import com.seibel.lod.core.objects.PosToRenderContainer;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodThreadFactory;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.util.*;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
@@ -128,7 +123,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);
@@ -445,7 +440,7 @@ public class LodDimension
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave)
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, int color, int data, byte flags, boolean dontSave)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
@@ -455,7 +450,7 @@ public class LodDimension
if (region == null)
return false;
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, data);
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, color, data, flags);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
@@ -487,7 +482,7 @@ public class LodDimension
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data, boolean dontSave)
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, int[] color, int[] data, byte[] flags, boolean dontSave)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
@@ -497,7 +492,7 @@ public class LodDimension
if (region == null)
return false;
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data);
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, color, data, flags);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
@@ -566,7 +561,9 @@ public class LodDimension
byte detailLevel;
int posX;
int posZ;
long data;
int color;
int data;
byte flags;
int numbChunksWide = (width) * 32;
int circleLimit = Integer.MAX_VALUE;
@@ -614,11 +611,11 @@ public class LodDimension
posX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, detailLevel);
posZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, zChunkToCheck, detailLevel);
data = getSingleData(detailLevel, posX, posZ);
flags = getSingleFlags(detailLevel, posX, posZ);
//we will generate the position only if the current generation complexity is lower than the target one.
//an un-generated area will always have 0 generation
if (DataPointUtil.getGenerationMode(data) < complexity)
if (DataPointUtil.getGenerationMode(flags) < complexity)
{
posToGenerate.addPosToGenerate(detailLevel, posX, posZ);
if (maxDataToGenerate >= 0)
@@ -680,8 +677,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);
}
/**
@@ -700,43 +702,52 @@ public class LodDimension
return region.getMaxVerticalData(detailLevel);
}
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
public int getColor(byte detailLevel, int posX, int posZ, int verticalIndex)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return DataPointUtil.EMPTY_DATA;
return region.getData(detailLevel, posX, posZ, verticalIndex);
return 0;
else
return region.getColor(detailLevel, posX, posZ, verticalIndex);
}
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public long getSingleData(byte detailLevel, int posX, int posZ)
public int getData(byte detailLevel, int posX, int posZ, int verticalIndex)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return DataPointUtil.EMPTY_DATA;
return 0;
else
return region.getData(detailLevel, posX, posZ, verticalIndex);
}
public byte getFlags(byte detailLevel, int posX, int posZ, int verticalIndex)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
return region.getSingleData(detailLevel, posX, posZ);
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return 0;
else
return region.getFlags(detailLevel, posX, posZ, verticalIndex);
}
public byte getSingleFlags(byte detailLevel, int posX, int posZ)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return (byte) 0;
else
return region.getSingleFlags(detailLevel, posX, posZ);
}
/** Clears the given region */
@@ -154,7 +154,7 @@ public class LodRegion
* TODO this will always return true unless it has
* @return true if the data was added successfully
*/
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data)
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, int color, int data, byte flags)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
@@ -166,7 +166,7 @@ public class LodRegion
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
}
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
this.dataContainer[detailLevel].addData(color, data, flags, posX, posZ, verticalIndex);
return true;
}
@@ -177,7 +177,7 @@ public class LodRegion
* TODO this will always return true unless it has
* @return true if the data was added successfully
*/
public boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data)
public boolean addVerticalData(byte detailLevel, int posX, int posZ, int[] color, int[] data, byte[] flags)
{
//position is already relative
//posX = LevelPosUtil.getRegionModule(detailLevel, posX);
@@ -188,29 +188,35 @@ public class LodRegion
if (this.dataContainer[detailLevel] == null)
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
return this.dataContainer[detailLevel].addVerticalData(data, posX, posZ);
return this.dataContainer[detailLevel].addVerticalData(color, data, flags, posX, posZ);
}
/**
* Get the dataPoint at the given relative position.
* @return the data at the relative pos and detail level,
* 0 if the data doesn't exist.
*/
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
public int getColor(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getColor(posX, posZ, verticalIndex);
}
public int getData(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
}
public byte getFlags(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getFlags(posX, posZ, verticalIndex);
}
/**
* Get the dataPoint at the given relative position.
* Get the flags at the given relative position.
* @return the data at the relative pos and detail level,
* 0 if the data doesn't exist.
*/
public long getSingleData(byte detailLevel, int posX, int posZ)
public byte getSingleFlags(byte detailLevel, int posX, int posZ)
{
return dataContainer[detailLevel].getSingleData(posX, posZ);
return dataContainer[detailLevel].getSingleFlags(posX, posZ);
}
/**
* Clears the datapoint at the given relative position
*/
@@ -358,6 +364,7 @@ public class LodRegion
posZ + regionPosZ * size);
}
else
{
//if (desiredLevel > detailLevel)
//{
// we have gone beyond the target Detail level
@@ -408,6 +415,7 @@ public class LodRegion
}
}
}
}
}
@@ -479,7 +487,7 @@ public class LodRegion
if (dataContainer[detailLevel].doesItExist(posX, posZ))
// We take the bottom information always
// TODO what does that mean? bottom of what?
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleData(posX, posZ));
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleFlags(posX, posZ));
else
return DistanceGenerationMode.NONE.complexity;
}
@@ -37,14 +37,19 @@ public class VerticalLevelContainer implements LevelContainer
public final int size;
public final int maxVerticalData;
public final long[] dataContainer;
public final int[] dataContainerColor;
public final int[] dataContainerData;
public final byte[] dataContainerFlags;
public VerticalLevelContainer(byte detailLevel)
{
this.detailLevel = detailLevel;
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel);
dataContainer = new long[size * size * DetailDistanceUtil.getMaxVerticalData(detailLevel)];
final int i = size * size * maxVerticalData;
dataContainerColor = new int[i];
dataContainerData = new int[i];
dataContainerFlags = new byte[i];
}
@Override
@@ -60,47 +65,77 @@ public class VerticalLevelContainer implements LevelContainer
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
{
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = DataPointUtil.EMPTY_DATA;
final int i = (posX * size + posZ) * maxVerticalData + verticalIndex;
dataContainerColor[i] = 0;
dataContainerData[i] = 0;
dataContainerFlags[i] = 0;
}
}
@Override
public boolean addData(long data, int posX, int posZ, int verticalIndex)
public boolean addData(int color, int data, byte flags, int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data;
final int i = (posX * size + posZ) * maxVerticalData + verticalIndex;
dataContainerColor[i] = color;
dataContainerData[i] = data;
dataContainerFlags[i] = flags;
return true;
}
@Override
public boolean addVerticalData(long[] data, int posX, int posZ)
public boolean addVerticalData(int[] color, int[] data, byte[] flags, 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];
{
final int i = (posX * size + posZ) * maxVerticalData + verticalIndex;
dataContainerColor[i] = color[verticalIndex];
dataContainerData[i] = data[verticalIndex];
dataContainerFlags[i] = flags[verticalIndex];
}
return true;
}
@Override
public boolean addSingleData(long data, int posX, int posZ)
public boolean addSingleData(int color, int data, byte flags, int posX, int posZ)
{
return addData(data, posX, posZ, 0);
return addData(color, data, flags, posX, posZ, 0);
}
@Override
public long getData(int posX, int posZ, int verticalIndex)
public int getColor(int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex];
return dataContainerColor[(posX * size + posZ) * maxVerticalData + verticalIndex];
}
@Override
public int getData(int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainerData[(posX * size + posZ) * maxVerticalData + verticalIndex];
}
@Override
public long getSingleData(int posX, int posZ)
public byte getFlags(int posX, int posZ, int verticalIndex)
{
return getData(posX, posZ, 0);
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainerFlags[(posX * size + posZ) * maxVerticalData + verticalIndex];
}
@Override
public byte getSingleFlags(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainerFlags[(posX * size + posZ) * maxVerticalData];
}
@Override
@@ -119,28 +154,139 @@ public class VerticalLevelContainer implements LevelContainer
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return DataPointUtil.doesItExist(getSingleData(posX, posZ));
return DataPointUtil.doesItExist(dataContainerFlags[(posX * size + posZ) * maxVerticalData]);
}
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;
int[] tempDataContainerColor = new int[x];
int[] tempDataContainerData = new int[x];
byte[] tempDataContainerFlags = new byte[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;
long oldData;
for (int i = 0; i < x; i++)
{
oldData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
oldData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
/*
|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 |
*/
DataPointUtil.createDataPoint(
(int)((oldData >> 60) << 4) + 15,
(int)(oldData >> 52) & 0xFF,
(int)(oldData >> 44) & 0xFF,
(int)(oldData >> 36) & 0xFF,
(int)(oldData >> 26) & 0x3FF - DataPointUtil.VERTICAL_OFFSET,
(int)(oldData >> 16) & 0x3FF - DataPointUtil.VERTICAL_OFFSET,
(int)(oldData >> 8) & 0xF,
(int)(oldData >> 12) & 0xF,
(int)(oldData >> 5) & 0x1,
((oldData >> 5) & 0x1) == 1
);
tempDataContainerColor[i] = ThreadMapUtil.dataPointColor;
tempDataContainerData[i] = ThreadMapUtil.dataPointData;
tempDataContainerFlags[i] = ThreadMapUtil.dataPointFlags;
}
}
else if (version == 7)
{
long oldData;
for (int i = 0; i < x; i++)
{
oldData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
oldData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
DataPointUtil.createDataPoint(
(int)((oldData >> 60) << 4) + 15,
(int)(oldData >> 52) & 0xFF,
(int)(oldData >> 44) & 0xFF,
(int)(oldData >> 36) & 0xFF,
(int)(oldData >> 26) & 0x3FF - DataPointUtil.VERTICAL_OFFSET - 64,
(int)(oldData >> 16) & 0x3FF - DataPointUtil.VERTICAL_OFFSET - 64,
(int)(oldData >> 8) & 0xF,
(int)(oldData >> 12) & 0xF,
(int)(oldData >> 5) & 0x1,
((oldData >> 5) & 0x1) == 1
);
tempDataContainerColor[i] = ThreadMapUtil.dataPointColor;
tempDataContainerData[i] = ThreadMapUtil.dataPointData;
tempDataContainerFlags[i] = ThreadMapUtil.dataPointFlags;
}
}
else //if (version == 8)
{
int color;
int data;
for (int i = 0; i < x; i++)
{
byte flags = inputData[index];
index++;
data = 0;
color = 0;
for (tempIndex = 0; tempIndex < 4; tempIndex++)
{
data += (((int) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
color += (((int) inputData[index + tempIndex + 4]) & 0xff) << (8 * tempIndex);
}
index += 8;
tempDataContainerColor[i] = color;
tempDataContainerData[i] = data;
tempDataContainerFlags[i] = flags;
}
}
if (tempMaxVerticalData > DetailDistanceUtil.getMaxVerticalData(detailLevel))
{
int tempMaxVerticalData2 = DetailDistanceUtil.getMaxVerticalData(detailLevel);
int[] dataToMergeColor = new int[tempMaxVerticalData];
int[] dataToMergeData = new int[tempMaxVerticalData];
byte[] dataToMergeFlags = new byte[tempMaxVerticalData];
int[] tempDataContainer2Color = new int[size * size * tempMaxVerticalData2];
int[] tempDataContainer2Data = new int[size * size * tempMaxVerticalData2];
byte[] tempDataContainer2Flags = new byte[size * size * tempMaxVerticalData2];
for (int i = 0; i < size * size; i++)
{
System.arraycopy(tempDataContainerColor, i * tempMaxVerticalData, dataToMergeColor, 0, tempMaxVerticalData);
System.arraycopy(tempDataContainerData, i * tempMaxVerticalData, dataToMergeData, 0, tempMaxVerticalData);
System.arraycopy(tempDataContainerFlags, i * tempMaxVerticalData, dataToMergeFlags, 0, tempMaxVerticalData);
DataPointUtil.mergeMultiData(dataToMergeColor, dataToMergeData, dataToMergeFlags, tempMaxVerticalData, tempMaxVerticalData2);
System.arraycopy(ThreadMapUtil.getRawVerticalDataArrayColor(), 0, tempDataContainer2Color, i * tempMaxVerticalData2, tempMaxVerticalData2);
System.arraycopy(ThreadMapUtil.getRawVerticalDataArrayData(), 0, tempDataContainer2Data, i * tempMaxVerticalData2, tempMaxVerticalData2);
System.arraycopy(ThreadMapUtil.getRawVerticalDataArrayFlags(), 0, tempDataContainer2Flags, i * tempMaxVerticalData2, tempMaxVerticalData2);
}
maxVerticalData = tempMaxVerticalData2;
this.dataContainerColor = tempDataContainer2Color;
this.dataContainerData = tempDataContainer2Data;
this.dataContainerFlags = tempDataContainer2Flags;
}
else
{
maxVerticalData = tempMaxVerticalData;
this.dataContainerColor = tempDataContainerColor;
this.dataContainerData = tempDataContainerData;
this.dataContainerFlags = tempDataContainerFlags;
}
}
@@ -154,12 +300,13 @@ public class VerticalLevelContainer implements LevelContainer
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
{
//We reset the array
long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray(detailLevel);
int[] dataToMergeColor = ThreadMapUtil.getVerticalUpdateArrayColor(detailLevel);
int[] dataToMergeData = ThreadMapUtil.getVerticalUpdateArrayData(detailLevel);
byte[] dataToMergeFlags = ThreadMapUtil.getVerticalUpdateArrayFlags(detailLevel);
int lowerMaxVertical = dataToMerge.length / 4;
int lowerMaxVertical = dataToMergeFlags.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++)
@@ -169,12 +316,17 @@ public class VerticalLevelContainer implements LevelContainer
childPosX = 2 * posX + x;
childPosZ = 2 * posZ + z;
for (int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++)
dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
{
final int i = (z * 2 + x) * lowerMaxVertical + verticalIndex;
dataToMergeColor[i] = lowerLevelContainer.getColor(childPosX, childPosZ, verticalIndex);
dataToMergeData[i] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
dataToMergeFlags[i] = lowerLevelContainer.getFlags(childPosX, childPosZ, verticalIndex);
}
}
}
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData());
addVerticalData(data, posX, posZ);
DataPointUtil.mergeMultiData(dataToMergeColor, dataToMergeData, dataToMergeFlags, lowerMaxVertical, getMaxVerticalData());
addVerticalData(ThreadMapUtil.getRawVerticalDataArrayColor(), ThreadMapUtil.getRawVerticalDataArrayData(), ThreadMapUtil.getRawVerticalDataArrayFlags(), posX, posZ);
}
@Override
@@ -183,7 +335,8 @@ public class VerticalLevelContainer implements LevelContainer
int index = 0;
int x = size * size;
int tempIndex;
long current;
int currentColor;
int currentData;
boolean allGenerated = true;
byte[] tempData = ThreadMapUtil.getSaveContainer(detailLevel);
@@ -196,12 +349,18 @@ public class VerticalLevelContainer implements LevelContainer
{
for (j = 0; j < maxVerticalData; j++)
{
current = dataContainer[i * maxVerticalData + j];
for (tempIndex = 0; tempIndex < 8; tempIndex++)
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
currentColor = dataContainerColor[i * maxVerticalData + j];
currentData = dataContainerData[i * maxVerticalData + j];
tempData[index] = dataContainerFlags[i * maxVerticalData + j];
index++;
for (tempIndex = 0; tempIndex < 4; tempIndex++)
{
tempData[index + tempIndex] = (byte) (currentData >>> (8 * tempIndex));
tempData[index + tempIndex + 4] = (byte) (currentColor >>> (8 * tempIndex));
}
index += 8;
}
if(!DataPointUtil.doesItExist(dataContainer[i]))
if(!DataPointUtil.doesItExist(dataContainerFlags[i]))
allGenerated = false;
}
if (allGenerated)
@@ -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;
}
}
@@ -25,8 +25,6 @@ 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;
@@ -53,7 +51,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 +94,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 +269,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 +323,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();
@@ -439,9 +435,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 +445,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 +530,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.File;
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-1-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;
@@ -101,7 +109,7 @@ public class GLProxy
// where the GL context is
if (GLFW.glfwGetCurrentContext() == 0L)
throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!");
//============================//
@@ -159,11 +167,9 @@ 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)
{
@@ -172,6 +178,43 @@ public class GLProxy
}
// 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 +222,6 @@ public class GLProxy
// shader setup //
//==============//
//setGlContext(GLProxyContext.LOD_RENDER);
setGlContext(GLProxyContext.MINECRAFT);
createShaderProgram();
@@ -213,13 +255,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" + File.separator + "standard.vert", false);
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders" + File.separator + "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
@@ -364,6 +406,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,21 @@
package com.seibel.lod.core.render;
import java.awt.Color;
import java.util.HashSet;
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.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,7 +41,7 @@ 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;
@@ -50,32 +51,26 @@ 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 11-27-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.
@@ -169,16 +164,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);
@@ -203,7 +201,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 +241,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 +250,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 +278,89 @@ public class LodRenderer
shaderProgram.enableVertexAttribute(posAttrib);
int colAttrib = shaderProgram.getAttributeLocation("color");
shaderProgram.enableVertexAttribute(colAttrib);
// 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());
// 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)
RegionPos vboCenterRegionPos = new RegionPos(vbosCenter);
RegionPos vboPos = new RegionPos();
// 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));
vboPos.x = x + vboCenterRegionPos.x - (lodDim.getWidth() / 2);
vboPos.z = z + vboCenterRegionPos.z - (lodDim.getWidth() / 2);
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(), cameraDir, vboPos.blockPos()))
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(), MC_RENDER.getLookAtVector(), vboPos.blockPos()))
{
// 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);
}
// 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);
//================//
// render cleanup //
//================//
// if this cleanup isn't done MC may crash
// when trying to render its own terrain
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
GL20.glDisableVertexAttribArray(posAttrib);
GL20.glDisableVertexAttribArray(colAttrib);
}
@@ -368,16 +377,23 @@ 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();
}
/** This is where the actual drawing happens. */
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib)
@@ -401,130 +417,84 @@ public class LodRenderer
GL20.glEnableVertexAttribArray(colAttrib);
GL20.glVertexAttribPointer(colAttrib, 4, GL15.GL_UNSIGNED_BYTE, true, vertexByteCount, Float.BYTES * 3);
// 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 * 1.6f * 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 * 1.6f);
// 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 * 1.6f;
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 +502,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();
@@ -547,99 +517,68 @@ public class LodRenderer
return mcModelViewMatrix;
}
/**
* Similar to translateModelViewMatrix (above),
* but for the camera position
*/
private Vec3f getTranslatedCameraPos()
{
AbstractBlockPosWrapper worldCenter = vbosCenter.getWorldPosition();
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
return new Vec3f((float)cameraPos.x - worldCenter.getX(), (float)cameraPos.y, (float)cameraPos.z - worldCenter.getZ());
}
/**
* 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 +588,6 @@ public class LodRenderer
fullRegen = true;
}
/**
* Replace the current Vertex Buffers with the newly
* created buffers from the lodBufferBuilder. <br><br>
@@ -672,98 +610,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 +651,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;
@@ -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);
/**
@@ -92,16 +93,15 @@ public class RenderUtil
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) ||
@@ -100,7 +100,7 @@ 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
@@ -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
{
@@ -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);
}
}
@@ -57,8 +57,9 @@ public class DataPointUtil
//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 int worldHeight = 256;
public final static byte EMPTY_DATA = 0;
public static final short VERTICAL_OFFSET = -2048;
public static int WORLD_HEIGHT = 4096;
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
@@ -67,167 +68,159 @@ public class DataPointUtil
//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 byte BLUE_SHIFT = 0;
public final static byte GREEN_SHIFT = 8;
public final static byte RED_SHIFT = 16;
public final static byte ALPHA_SHIFT = 24;
public final static int COLOR_SHIFT = 36;
//public final static byte 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 byte HEIGHT_SHIFT = 20;
public final static byte DEPTH_SHIFT = 8;
public final static byte BLOCK_LIGHT_SHIFT = 4;
public final static byte SKY_LIGHT_SHIFT = 0;
//public final static byte LIGHTS_SHIFT = SKY_LIGHT_SHIFT;
//public final static byte VERTICAL_INDEX_SHIFT = 6;
public final static byte FLAG_SHIFT = 5;
public final static byte GEN_TYPE_SHIFT = 2;
public final static byte VOID_SHIFT = 1;
public final static byte 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 final static int ALPHA_MASK = 0xFF;
public final static int RED_MASK = 0xFF;
public final static int GREEN_MASK = 0xFF;
public final static int BLUE_MASK = 0xFF;
public final static int COLOR_MASK = 0xFFFFFFFF;
public final static int HEIGHT_MASK = 0xFFF;
public final static int DEPTH_MASK = 0xFFF;
public final static int LIGHTS_MASK = 0xFF;
public final static int BLOCK_LIGHT_MASK = 0xF;
public final static int SKY_LIGHT_MASK = 0xF;
public final static int VERTICAL_INDEX_MASK = 0x3;
public final static byte FLAG_MASK = 0x1;
public final static byte GEN_TYPE_MASK = 0x7;
public final static byte VOID_MASK = 1;
public final static byte EXISTENCE_MASK = 1;
public static long createVoidDataPoint(int generationMode)
/** Returns the Flags byte */
public static byte createVoidDataPoint(byte generationMode)
{
long dataPoint = 0;
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
dataPoint += VOID_MASK << VOID_SHIFT;
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
return dataPoint;
generationMode = (byte) ((generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT);
generationMode |= VOID_MASK << VOID_SHIFT;
generationMode |= EXISTENCE_MASK << EXISTENCE_SHIFT;
return generationMode;
}
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode, boolean flag)
/** Returned datapoint is in ThreadMapUtil */
public static void 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);
int data = (height & HEIGHT_MASK) << HEIGHT_SHIFT;
data += (depth & DEPTH_MASK) << DEPTH_SHIFT;
data += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT;
data += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT;
byte flags = (byte) ((generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT);
if (flag) flags += FLAG_MASK << FLAG_SHIFT;
flags += EXISTENCE_MASK << EXISTENCE_SHIFT;
ThreadMapUtil.saveDataPoint(color, data, flags);
}
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag)
public static void 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;
createDataPoint(
height, depth,
(alpha << ALPHA_SHIFT) | (red << RED_SHIFT) | (green << GREEN_SHIFT) | blue,
lightSky, lightBlock, generationMode, flag);
}
public static short getHeight(long dataPoint)
public static short getHeight(int data)
{
return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK);
return (short) ((data >>> HEIGHT_SHIFT) & HEIGHT_MASK);
}
public static short getDepth(long dataPoint)
public static short getDepth(int data)
{
return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK);
return (short) ((data >>> DEPTH_SHIFT) & DEPTH_MASK);
}
public static short getAlpha(long dataPoint)
public static short getAlpha(int color)
{
return (short) ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111);
return (short) ((color >>> ALPHA_SHIFT) & ALPHA_MASK);
}
public static short getRed(long dataPoint)
public static short getRed(int color)
{
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
return (short) ((color >>> RED_SHIFT) & RED_MASK);
}
public static short getGreen(long dataPoint)
public static short getGreen(int color)
{
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
return (short) ((color >>> GREEN_SHIFT) & GREEN_MASK);
}
public static short getBlue(long dataPoint)
public static short getBlue(int color)
{
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
return (short) ((color >>> BLUE_SHIFT) & BLUE_MASK);
}
public static byte getLightSky(long dataPoint)
public static byte getLightSky(int data)
{
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
return (byte) ((data >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static byte getLightSkyAlt(long dataPoint)
public static byte getLightSkyAlt(int data, byte flags)
{
if (skyLightPlayer == 0 && ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1)
if (skyLightPlayer == 0 && ((flags >>> FLAG_SHIFT) & FLAG_MASK) == 1)
return 0;
else
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
return (byte) ((data >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static byte getLightBlock(long dataPoint)
public static byte getLightBlock(int data)
{
return (byte) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
return (byte) ((data >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
}
public static boolean getFlag(long dataPoint)
public static boolean getFlag(byte flags)
{
return ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1;
return ((flags >>> FLAG_SHIFT) & FLAG_MASK) == 1;
}
public static byte getGenerationMode(long dataPoint)
public static byte getGenerationMode(byte flags)
{
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
return (byte) ((flags >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
}
public static boolean isVoid(long dataPoint)
public static boolean isVoid(byte flags)
{
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
return (((flags >>> VOID_SHIFT) & VOID_MASK) == 1);
}
public static boolean doesItExist(long dataPoint)
public static boolean doesItExist(byte flags)
{
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
return ((flags >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1;
}
public static int getColor(long dataPoint)
@Deprecated
public static int getColor(int color)
{
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24));
return color;
}
/** This is used to convert a dataPoint to string (useful for the print function) */
@SuppressWarnings("unused")
public static String toString(long dataPoint)
public static String toString(int color, int data, byte flags)
{
return getHeight(dataPoint) + " " +
getDepth(dataPoint) + " " +
getAlpha(dataPoint) + " " +
getRed(dataPoint) + " " +
getBlue(dataPoint) + " " +
getGreen(dataPoint) + " " +
getLightBlock(dataPoint) + " " +
getLightSky(dataPoint) + " " +
getGenerationMode(dataPoint) + " " +
isVoid(dataPoint) + " " +
doesItExist(dataPoint) + '\n';
return getHeight(data) + " " +
getDepth(data) + " " +
getAlpha(color) + " " +
getRed(color) + " " +
getBlue(color) + " " +
getGreen(color) + " " +
getLightBlock(data) + " " +
getLightSky(data) + " " +
getGenerationMode(flags) + " " +
isVoid(flags) + " " +
doesItExist(flags) + '\n';
}
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
@@ -257,25 +250,30 @@ public class DataPointUtil
/**
* This method merge column of multiple data together
* @param dataToMerge one or more columns of data
* Returned datapoint is in ThreadMapUtil
* @param dataToMergeColor colors of one or more columns of data
* @param dataToMergeData data of one or more columns of data
* @param dataToMergeFlags flags of 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)
public static void mergeMultiData(int[] dataToMergeColor, int[] dataToMergeData, byte[] dataToMergeFlags, int inputVerticalData, int maxVerticalData)
{
int size = dataToMerge.length / inputVerticalData;
int size = dataToMergeData.length / inputVerticalData;
// We initialize the arrays that are going to be used
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight / 2 + 1) * 2);
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((WORLD_HEIGHT / 2 + 1) * 2);
int[] dataPointColor = ThreadMapUtil.getVerticalDataArrayColor(DetailDistanceUtil.getMaxVerticalData(0));
int[] dataPointData = ThreadMapUtil.getVerticalDataArrayData(DetailDistanceUtil.getMaxVerticalData(0));
byte[] dataPointFlags = ThreadMapUtil.getVerticalDataArrayFlags(DetailDistanceUtil.getMaxVerticalData(0));
int genMode = DistanceGenerationMode.FULL.complexity;
byte genMode = DistanceGenerationMode.FULL.complexity;
boolean allEmpty = true;
boolean allVoid = true;
boolean allDefault;
long singleData;
int singleDataData;
byte singleDataFlags;
short depth;
@@ -289,16 +287,17 @@ public class DataPointUtil
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData))
singleDataData = dataToMergeData[index * inputVerticalData + dataIndex];
singleDataFlags = dataToMergeFlags[index * inputVerticalData + dataIndex];
if (doesItExist(singleDataFlags))
{
genMode = Math.min(genMode, getGenerationMode(singleData));
genMode = (byte) Math.min(genMode, getGenerationMode(singleDataFlags));
allEmpty = false;
if (!isVoid(singleData))
if (!isVoid(singleDataFlags))
{
allVoid = false;
depth = getDepth(singleData);
height = getHeight(singleData);
depth = getDepth(singleDataData);
height = getHeight(singleDataData);
int botPos = -1;
int topPos = -1;
@@ -401,18 +400,18 @@ public class DataPointUtil
//We check if there is any data that's not empty or void
if (allEmpty)
return dataPoint;
return;
if (allVoid)
{
dataPoint[0] = createVoidDataPoint(genMode);
return dataPoint;
dataPointFlags[0] = createVoidDataPoint(genMode);
return;
}
//we limit the vertical portion to maxVerticalData
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)
@@ -450,71 +449,90 @@ public class DataPointUtil
allEmpty = true;
allVoid = true;
allDefault = true;
long data = 0;
int singleDataColor;
int data = EMPTY_DATA;
int color = EMPTY_DATA;
byte flags = EMPTY_DATA;
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData) && !isVoid(singleData))
singleDataColor = dataToMergeColor[index * inputVerticalData + dataIndex];
singleDataData = dataToMergeData[index * inputVerticalData + dataIndex];
singleDataFlags = dataToMergeFlags[index * inputVerticalData + dataIndex];
if (doesItExist(singleDataFlags) && !isVoid(singleDataFlags))
{
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
if ((depth <= getDepth(singleDataData) && getDepth(singleDataData) <= height)
|| (depth <= getHeight(singleDataData) && getHeight(singleDataData) <= height))
{
if (getHeight(singleData) > getHeight(data))
data = singleData;
if (getHeight(singleDataData) > getHeight(data))
{
color = singleDataColor;
data = singleDataData;
flags = singleDataFlags;
}
}
}
else
break;
}
if (!doesItExist(data))
if (!doesItExist(flags))
{
singleData = dataToMerge[index * inputVerticalData];
data = createVoidDataPoint(getGenerationMode(singleData));
singleDataFlags = dataToMergeFlags[index * inputVerticalData];
if (doesItExist(singleDataFlags))
flags = createVoidDataPoint(getGenerationMode(singleDataFlags));
else
flags = createVoidDataPoint((byte) 0);
data = EMPTY_DATA;
color = EMPTY_DATA;
}
if (doesItExist(data))
if (doesItExist(flags))
{
allEmpty = false;
if (!isVoid(data))
if (!isVoid(flags))
{
numberOfChildren++;
allVoid = false;
tempAlpha += getAlpha(data);
tempRed += getRed(data);
tempGreen += getGreen(data);
tempBlue += getBlue(data);
tempAlpha += getAlpha(color);
tempRed += getRed(color);
tempGreen += getGreen(color);
tempBlue += getBlue(color);
tempLightBlock += getLightBlock(data);
tempLightSky += getLightSky(data);
if (!getFlag(data)) allDefault = false;
if (!getFlag(flags))
allDefault = false;
}
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(flags));
}
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
if (!allEmpty)
{
//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);
//child has been initialized
if (allVoid)
{
//all the children are void
dataPointFlags[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;
createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
dataPointColor[j] = ThreadMapUtil.dataPointColor;
dataPointData[j] = ThreadMapUtil.dataPointData;
dataPointFlags[j] = ThreadMapUtil.dataPointFlags;
}
}
}
return dataPoint;
}
}
@@ -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,10 +35,10 @@ 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.
@@ -49,7 +49,7 @@ import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
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);
@@ -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
@@ -38,15 +38,18 @@ import com.seibel.lod.core.objects.Box;
*/
public class ThreadMapUtil
{
public static final ConcurrentMap<String, long[]> threadSingleUpdateMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[][]> threadBuilderArrayMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[][]> threadBuilderVerticalArrayMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[]> threadVerticalAddDataMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[][]> threadBuilderVerticalArrayMapColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[][]> threadBuilderVerticalArrayMapData = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[][]> threadBuilderVerticalArrayMapFlags = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[]> threadVerticalAddDataMapColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[]> threadVerticalAddDataMapData = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[]> threadVerticalAddDataMapFlags = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[][]> saveContainer = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, short[]> projectionArrayMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, short[]> heightAndDepthMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[]> singleDataToMergeMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[][]> verticalUpdate = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[][]> verticalUpdateColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[][]> verticalUpdateData = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[][]> verticalUpdateFlags = new ConcurrentHashMap<>();
//________________________//
@@ -54,8 +57,13 @@ 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, Map<LodDirection, int[]>> adjDataMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, Map<LodDirection, byte[]>> adjFlagsMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, VertexOptimizer> boxMap = new ConcurrentHashMap<>();
public static int dataPointColor = 0;
public static int dataPointData = 0;
public static byte dataPointFlags = 0;
@@ -65,14 +73,14 @@ 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());
}
/** returns the array NOT cleared every time */
public static Map<LodDirection, long[]> getAdjDataArray(int verticalData)
public static Map<LodDirection, int[]> getAdjDataArray(int verticalData)
{
if (!adjDataMap.containsKey(Thread.currentThread().getName())
|| (adjDataMap.get(Thread.currentThread().getName()) == null)
@@ -80,26 +88,47 @@ public class ThreadMapUtil
|| (adjDataMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH).length != verticalData))
{
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)
adjDataMap.get(Thread.currentThread().getName()).put(lodDirection, new long[verticalData]);
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new int[1]);
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new int[1]);
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
adjDataMap.get(Thread.currentThread().getName()).put(lodDirection, new int[verticalData]);
}
else
{
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(lodDirection), DataPointUtil.EMPTY_DATA);
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(lodDirection), 0);
}
return adjDataMap.get(Thread.currentThread().getName());
}
public static Box getBox()
/** returns the array NOT cleared every time */
public static Map<LodDirection, byte[]> getAdjFlagsArray(int verticalData)
{
if (!adjFlagsMap.containsKey(Thread.currentThread().getName())
|| (adjFlagsMap.get(Thread.currentThread().getName()) == null)
|| (adjFlagsMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH) == null)
|| (adjFlagsMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH).length != verticalData))
{
adjFlagsMap.put(Thread.currentThread().getName(), new HashMap<>());
adjFlagsMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new byte[1]);
adjFlagsMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new byte[1]);
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
adjFlagsMap.get(Thread.currentThread().getName()).put(lodDirection, new byte[verticalData]);
}
else
{
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
Arrays.fill(adjFlagsMap.get(Thread.currentThread().getName()).get(lodDirection), (byte) 0);
}
return adjFlagsMap.get(Thread.currentThread().getName());
}
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());
@@ -119,21 +148,57 @@ public class ThreadMapUtil
/** returns the array filled with 0's */
public static long[] getBuilderVerticalArray(int detailLevel)
public static int[] getBuilderVerticalArrayColor(int detailLevel)
{
if (!threadBuilderVerticalArrayMap.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMap.get(Thread.currentThread().getName()) == null))
if (!threadBuilderVerticalArrayMapColor.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMapColor.get(Thread.currentThread().getName()) == null))
{
long[][] array = new long[5][];
int[][] array = new int[5][];
int size;
for (int i = 0; i < 5; i++)
{
size = 1 << i;
array[i] = new long[size * size * (DataPointUtil.worldHeight / 2 + 1)];
array[i] = new int[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
}
threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array);
threadBuilderVerticalArrayMapColor.put(Thread.currentThread().getName(), array);
}
Arrays.fill(threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel], 0);
return threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel];
Arrays.fill(threadBuilderVerticalArrayMapColor.get(Thread.currentThread().getName())[detailLevel], 0);
return threadBuilderVerticalArrayMapColor.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static int[] getBuilderVerticalArrayData(int detailLevel)
{
if (!threadBuilderVerticalArrayMapData.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMapData.get(Thread.currentThread().getName()) == null))
{
int[][] array = new int[5][];
int size;
for (int i = 0; i < 5; i++)
{
size = 1 << i;
array[i] = new int[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
}
threadBuilderVerticalArrayMapData.put(Thread.currentThread().getName(), array);
}
Arrays.fill(threadBuilderVerticalArrayMapData.get(Thread.currentThread().getName())[detailLevel], 0);
return threadBuilderVerticalArrayMapData.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static byte[] getBuilderVerticalArrayFlags(int detailLevel)
{
if (!threadBuilderVerticalArrayMapFlags.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMapFlags.get(Thread.currentThread().getName()) == null))
{
byte[][] array = new byte[5][];
int size;
for (int i = 0; i < 5; i++)
{
size = 1 << i;
array[i] = new byte[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
}
threadBuilderVerticalArrayMapFlags.put(Thread.currentThread().getName(), array);
}
Arrays.fill(threadBuilderVerticalArrayMapFlags.get(Thread.currentThread().getName())[detailLevel], (byte) 0);
return threadBuilderVerticalArrayMapFlags.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array NOT cleared every time */
@@ -145,7 +210,7 @@ public class ThreadMapUtil
int size = 1;
for (int i = LodUtil.DETAIL_OPTIONS - 1; i >= 0; i--)
{
array[i] = new byte[2 + 8 * size * size * DetailDistanceUtil.getMaxVerticalData(i)];
array[i] = new byte[2 + 9 * size * size * DetailDistanceUtil.getMaxVerticalData(i)];
size = size << 1;
}
saveContainer.put(Thread.currentThread().getName(), array);
@@ -156,19 +221,46 @@ public class ThreadMapUtil
/** returns the array filled with 0's */
public static long[] getVerticalDataArray(int arrayLength)
public static int[] getVerticalDataArrayColor(int arrayLength)
{
if (!threadVerticalAddDataMap.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMap.get(Thread.currentThread().getName()) == null))
{
threadVerticalAddDataMap.put(Thread.currentThread().getName(), new long[arrayLength]);
}
if (!threadVerticalAddDataMapColor.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMapColor.get(Thread.currentThread().getName()) == null))
threadVerticalAddDataMapColor.put(Thread.currentThread().getName(), new int[arrayLength]);
else
{
Arrays.fill(threadVerticalAddDataMap.get(Thread.currentThread().getName()), 0);
}
return threadVerticalAddDataMap.get(Thread.currentThread().getName());
Arrays.fill(threadVerticalAddDataMapColor.get(Thread.currentThread().getName()), 0);
return threadVerticalAddDataMapColor.get(Thread.currentThread().getName());
}
public static int[] getRawVerticalDataArrayColor()
{
return threadVerticalAddDataMapColor.get(Thread.currentThread().getName());
}
/** returns the array filled with 0's */
public static int[] getVerticalDataArrayData(int arrayLength)
{
if (!threadVerticalAddDataMapData.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMapData.get(Thread.currentThread().getName()) == null))
threadVerticalAddDataMapData.put(Thread.currentThread().getName(), new int[arrayLength]);
else
Arrays.fill(threadVerticalAddDataMapData.get(Thread.currentThread().getName()), 0);
return threadVerticalAddDataMapData.get(Thread.currentThread().getName());
}
public static int[] getRawVerticalDataArrayData()
{
return threadVerticalAddDataMapData.get(Thread.currentThread().getName());
}
/** returns the array filled with 0's */
public static byte[] getVerticalDataArrayFlags(int arrayLength)
{
if (!threadVerticalAddDataMapFlags.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMapFlags.get(Thread.currentThread().getName()) == null))
threadVerticalAddDataMapFlags.put(Thread.currentThread().getName(), new byte[arrayLength]);
else
Arrays.fill(threadVerticalAddDataMapFlags.get(Thread.currentThread().getName()), (byte) 0);
return threadVerticalAddDataMapFlags.get(Thread.currentThread().getName());
}
public static byte[] getRawVerticalDataArrayFlags()
{
return threadVerticalAddDataMapFlags.get(Thread.currentThread().getName());
}
/** returns the array NOT cleared every time */
@@ -181,22 +273,49 @@ public class ThreadMapUtil
return heightAndDepthMap.get(Thread.currentThread().getName());
}
/** returns the array filled with 0's */
public static long[] getVerticalUpdateArray(int detailLevel)
public static int[] getVerticalUpdateArrayColor(int detailLevel)
{
if (!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null))
if (!verticalUpdateColor.containsKey(Thread.currentThread().getName()) || (verticalUpdateColor.get(Thread.currentThread().getName()) == null))
{
long[][] array = new long[LodUtil.DETAIL_OPTIONS][];
int[][] array = new int[LodUtil.DETAIL_OPTIONS][];
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
array[i] = new long[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdate.put(Thread.currentThread().getName(), array);
array[i] = new int[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdateColor.put(Thread.currentThread().getName(), array);
}
else
Arrays.fill(verticalUpdateColor.get(Thread.currentThread().getName())[detailLevel], 0);
return verticalUpdateColor.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static int[] getVerticalUpdateArrayData(int detailLevel)
{
if (!verticalUpdateData.containsKey(Thread.currentThread().getName()) || (verticalUpdateData.get(Thread.currentThread().getName()) == null))
{
Arrays.fill(verticalUpdate.get(Thread.currentThread().getName())[detailLevel], 0);
int[][] array = new int[LodUtil.DETAIL_OPTIONS][];
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
array[i] = new int[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdateData.put(Thread.currentThread().getName(), array);
}
return verticalUpdate.get(Thread.currentThread().getName())[detailLevel];
else
Arrays.fill(verticalUpdateData.get(Thread.currentThread().getName())[detailLevel], 0);
return verticalUpdateData.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static byte[] getVerticalUpdateArrayFlags(int detailLevel)
{
if (!verticalUpdateFlags.containsKey(Thread.currentThread().getName()) || (verticalUpdateFlags.get(Thread.currentThread().getName()) == null))
{
byte[][] array = new byte[LodUtil.DETAIL_OPTIONS][];
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
array[i] = new byte[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdateFlags.put(Thread.currentThread().getName(), array);
}
else
Arrays.fill(verticalUpdateFlags.get(Thread.currentThread().getName())[detailLevel], (byte) 0);
return verticalUpdateFlags.get(Thread.currentThread().getName())[detailLevel];
}
/** clears all arrays so they will have to be rebuilt */
@@ -205,14 +324,24 @@ public class ThreadMapUtil
adjShadeDisabled.clear();
adjDataMap.clear();
boxMap.clear();
threadSingleUpdateMap.clear();
threadBuilderArrayMap.clear();
threadBuilderVerticalArrayMap.clear();
threadVerticalAddDataMap.clear();
threadBuilderVerticalArrayMapColor.clear();
threadBuilderVerticalArrayMapData.clear();
threadBuilderVerticalArrayMapFlags.clear();
threadVerticalAddDataMapColor.clear();
threadVerticalAddDataMapData.clear();
threadVerticalAddDataMapFlags.clear();
saveContainer.clear();
projectionArrayMap.clear();
heightAndDepthMap.clear();
singleDataToMergeMap.clear();
verticalUpdate.clear();
verticalUpdateColor.clear();
verticalUpdateData.clear();
verticalUpdateFlags.clear();
}
public static void saveDataPoint(int color, int data, byte flags)
{
dataPointColor = color;
dataPointData = data;
dataPointFlags = flags;
}
}
@@ -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,22 @@ public interface IBlockColorWrapper
//Colors getters//
//--------------//
public boolean hasColor();
boolean hasColor();
public int getColor();
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,7 @@ package com.seibel.lod.core.wrapperInterfaces.chunk;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.forge.wrappers.world.BiomeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
/**
* @author James Seibel
@@ -30,23 +30,23 @@ import com.seibel.lod.forge.wrappers.world.BiomeWrapper;
*/
public interface IChunkWrapper
{
public int getHeight();
int getHeight();
public boolean isPositionInWater(AbstractBlockPosWrapper blockPos);
boolean isPositionInWater(AbstractBlockPosWrapper blockPos);
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 xRel, int yAbs, int zRel);
public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos);
IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos);
public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos);
IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos);
public AbstractChunkPosWrapper getPos();
AbstractChunkPosWrapper getPos();
public boolean isLightCorrect();
boolean isLightCorrect();
public boolean isWaterLogged(AbstractBlockPosWrapper blockPos);
boolean isWaterLogged(AbstractBlockPosWrapper blockPos);
public int getEmittedBrightness(AbstractBlockPosWrapper blockPos);
int getEmittedBrightness(AbstractBlockPosWrapper blockPos);
}
@@ -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,120 +43,144 @@ 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.";
@@ -168,66 +193,58 @@ public interface ILodConfigWrapperSingleton
+ " " + 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);
LodTemplate getLodTemplate();
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 +254,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 +338,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 +369,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 +387,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 +433,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"
+ " 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,37 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
* rendering in Minecraft.
*
* @author James Seibel
* @version 11-18-2021
* @version 11-26-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();
}
@@ -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();
@@ -80,7 +80,7 @@ public interface IMinecraftWrapper
* @param u x location in texture space
* @param v z location in texture space
*/
public int getColorIntFromLightMap(int u, int v);
int getColorIntFromLightMap(int u, int v);
/**
* Returns the Color at the given pixel coordinates
@@ -88,7 +88,7 @@ public interface IMinecraftWrapper
* @param u x location in texture space
* @param v z location in texture space
*/
public Color getColorFromLightMap(int u, int v);
Color getColorFromLightMap(int u, int v);
@@ -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,12 @@ 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);
int getGrassTint(int x, int z);
public int getFolliageTint();
int getFolliageTint();
public int getWaterTint();
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,30 @@ 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(AbstractBlockPosWrapper blockPos);
public int getSkyLight(AbstractBlockPosWrapper blockPos);
int getSkyLight(AbstractBlockPosWrapper blockPos);
public IBiomeWrapper getBiome(AbstractBlockPosWrapper blockPos);
IBiomeWrapper getBiome(AbstractBlockPosWrapper blockPos);
public boolean hasCeiling();
boolean hasCeiling();
public boolean hasSkyLight();
boolean hasSkyLight();
public boolean isEmpty();
// Pls don't use this
// If the world is null then this can't be called and gives an error
boolean isEmpty();
public int getHeight();
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_
-51
View File
@@ -1,51 +0,0 @@
#// This is an example mods.toml file. It contains the data relating to the loading mods.
#// There are several mandatory fields (#mandatory), and many more that are optional (#optional).
#// The overall format is standard TOML format, v0.5.0.
#// Note that there are a couple of TOML lists in this file.
#// Find more information on toml format here: https://github.com/toml-lang/toml
#// The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
modLoader="javafml" #mandatory
#// A version range to match for said mod loader - for regular FML @Mod it will be the forge version
loaderVersion="[36,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions.
#// The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
#// Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
license="GNU GPLv3"
#// A URL to refer people to when problems occur with this mod
issueTrackerURL="https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues" #optional
#// A list of mods - how many allowed here is determined by the individual mod loader
[[mods]] #mandatory
#// The modid of the mod
modId="lod" #mandatory
#// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
#//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
#// see the associated build.gradle script for how to populate this completely automatically during a build
version="a1.5.3" #mandatory
#// A display name for the mod
displayName="Distant Horizons" #mandatory
#// A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/
#//updateJSONURL="https://change.me.example.invalid/updates.json" #optional
#// A URL for the "homepage" for this mod, displayed in the mod UI
displayURL="https://www.curseforge.com/minecraft/mc-mods/lod-level-of-detail" #optional
#// A file name (in the root of the mod JAR) containing a logo for display
logoFile="logo.png" #optional
#// A file name (in the root of the mod JAR) containing a icon for display by catalogue
catalogueImageIcon="icon.png"
#// A text field displayed in the mod UI
credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional
#// A text field displayed in the mod UI
authors="James Seibel, Leonardo Amato, and Cola" #optional
#// The description text for the mod (multi line!) (#mandatory)
description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.'''
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

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