Compare commits
185 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 814ac5df84 | |||
| 55f9d142e6 | |||
| 58607ab1fc | |||
| 433ab4f92f | |||
| 8840973a1e | |||
| 4460789ab1 | |||
| a232f449ed | |||
| 1c5988fbfb | |||
| b386547616 | |||
| 03ea7ee460 | |||
| 752a2e4a69 | |||
| 3e3e30c4b1 | |||
| f5cc5c2846 | |||
| 609d471346 | |||
| 5f921ffcef | |||
| 4aebf36f02 | |||
| 0b1fdba6ea | |||
| 475324ed73 | |||
| d36f97d765 | |||
| ee5dc0a6b3 | |||
| 05ed61b0a1 | |||
| 2ef6c59dfc | |||
| fd90f761a1 | |||
| 7ec7d6bdc5 | |||
| f9ed9a472c | |||
| b542738097 | |||
| bceefb5717 | |||
| f977687ad6 | |||
| 31ef1d4959 | |||
| 86d07eb62b | |||
| 39c7ae8d17 | |||
| 5c369fd515 | |||
| 2b0926a097 | |||
| 9b6b67b178 | |||
| bb07e3db9a | |||
| 46e65704d7 | |||
| d5ec17551e | |||
| 8390cfc6fb | |||
| 2711fbf01a | |||
| e4f64df660 | |||
| a6c9d0df98 | |||
| 136dd5b47a | |||
| 277def2acc | |||
| e941b553e7 | |||
| 92e651c779 | |||
| 2b82eb88c8 | |||
| 13fecf9d1d | |||
| cdd1529a13 | |||
| 7915444713 | |||
| 2135ae769d | |||
| b98cfb9666 | |||
| a28da86d44 | |||
| 799b0e2481 | |||
| e380c7c069 | |||
| 5a3b8f5baa | |||
| a1c547b72e | |||
| bb5613e3fe | |||
| a29060bf4f | |||
| 79ee23e0b0 | |||
| d3231a480d | |||
| 3694dcba4c | |||
| bf6db89a4b | |||
| 38c644739a | |||
| a0fe977976 | |||
| 739d6d5856 | |||
| 84125735a1 | |||
| 884f6811a1 | |||
| 402abb0963 | |||
| 2f7f489e14 | |||
| 4de5c287fc | |||
| ccb58024a1 | |||
| 0381d56511 | |||
| 7bbd3fd815 | |||
| 4e243252e9 | |||
| b488d21a14 | |||
| 3e8dbf7ac7 | |||
| 695b73f9d3 | |||
| 6e37bce38a | |||
| 06232f65b2 | |||
| bebe4b7436 | |||
| 7d9f04d54c | |||
| 70336acc75 | |||
| e1ef08a783 | |||
| 648a70097a | |||
| 280892f5f4 | |||
| 3c57b124d5 | |||
| 04d6bae479 | |||
| 98096a5db7 | |||
| 7e4f3a3185 | |||
| 9fe6be10f6 | |||
| 76b356d38e | |||
| 811e24ee5e | |||
| 5002db15d6 | |||
| a3357c1193 | |||
| af36224a57 | |||
| 630ff361d3 | |||
| 34c6f29a18 | |||
| 909718e491 | |||
| 8a3d199247 | |||
| fa13c981b7 | |||
| 815b00c3ca | |||
| ae9144a6c4 | |||
| 359fde3b6b | |||
| 92fa904cc6 | |||
| 2583ae34d4 | |||
| e36b3394f4 | |||
| 7af38df92c | |||
| 51add24110 | |||
| 9e5aac3bf7 | |||
| 5738a5b7cd | |||
| e1216966a3 | |||
| 222c0de7f1 | |||
| b4f1fb6d28 | |||
| a32082ad20 | |||
| eeb5fb6c3c | |||
| cb50f24c86 | |||
| 5ca5764c0e | |||
| fccd1db045 | |||
| 6c515350bc | |||
| 50aee9dfb2 | |||
| 0649504770 | |||
| 29068f9550 | |||
| 18c08ccd88 | |||
| b71d6a5e3f | |||
| 8f619f3fa1 | |||
| eab16ff20a | |||
| d913ed9621 | |||
| a649cf179f | |||
| cf5de39250 | |||
| bab3cd9656 | |||
| cafd4f0c47 | |||
| e20833225f | |||
| 14a06c220b | |||
| e5a5ba327e | |||
| fedc8f7b66 | |||
| 7a3497d44c | |||
| 873034f7e4 | |||
| a151054b48 | |||
| 14c69971f6 | |||
| edc3858699 | |||
| bdaf33b80b | |||
| e1e63d4981 | |||
| 12f4a2d159 | |||
| fa2f12e4e0 | |||
| 520e2e99d9 | |||
| 759d6a0a94 | |||
| 7983f59ff1 | |||
| cd33b4c33e | |||
| 49bbc56941 | |||
| bf6813b6a5 | |||
| f96a6dcecd | |||
| 306f575edd | |||
| 8babc5aa65 | |||
| 6abbf328fb | |||
| 6bedfa5136 | |||
| e02156b1a4 | |||
| 673474cd64 | |||
| 807818e078 | |||
| 22840bd4e3 | |||
| 4c71c9aad5 | |||
| cc1683f573 | |||
| 62dc86d64e | |||
| ef65f87777 | |||
| 055f64e7c6 | |||
| a0fc9835b6 | |||
| b127ad0538 | |||
| e1cf190a7f | |||
| 27caab932c | |||
| d06415bd3e | |||
| 14e0fca1ed | |||
| 3d6ba0fad9 | |||
| 84bdd3dd90 | |||
| e81cd17ecf | |||
| 58b0eafe29 | |||
| 336cfb0749 | |||
| 0f5990e2f8 | |||
| 7acad77eda | |||
| fb0ff2a00c | |||
| ab7157476b | |||
| 40bc930d34 | |||
| 9500805243 | |||
| f637e5fd44 | |||
| cfda8c9655 | |||
| cb04b2df09 | |||
| f754467450 |
@@ -0,0 +1,5 @@
|
||||
# Disable autocrlf on generated files, they always generate with LF
|
||||
# Add any extra files or paths here to make git stop saying they
|
||||
# are changed when only line endings change.
|
||||
src/generated/**/.cache/cache text eol=lf
|
||||
src/generated/**/*.json text eol=lf
|
||||
+4
-4
@@ -19,8 +19,8 @@ build
|
||||
|
||||
# other
|
||||
eclipse
|
||||
run
|
||||
|
||||
# minecraft run folder,
|
||||
# ignore everything but the mods folder
|
||||
run/*
|
||||
!run/mods
|
||||
# Files from Forge MDK
|
||||
logs
|
||||
forge*changelog.txt
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[submodule "ASMHelper"]
|
||||
path = ASMHelper
|
||||
url = https://github.com/squeek502/ASMHelper.git
|
||||
branch = 1.12.x
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
Minecraft Forge: Credits/Thank You
|
||||
|
||||
Forge is a set of tools and modifications to the Minecraft base game code to assist
|
||||
mod developers in creating new and exciting content. It has been in development for
|
||||
several years now, but I would like to take this time thank a few people who have
|
||||
helped it along it's way.
|
||||
|
||||
First, the people who originally created the Forge projects way back in Minecraft
|
||||
alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance
|
||||
of me taking over the project, who knows what Minecraft modding would be today.
|
||||
|
||||
Secondly, someone who has worked with me, and developed some of the core features
|
||||
that allow modding to he as functional, and as simple as it is, cpw. For developing
|
||||
FML, which stabelized the client and server modding ecosystem. As well as the base
|
||||
loading system that allows us to modify Minecraft's code as elegently as possible.
|
||||
|
||||
Mezz, who has stepped up as the issue and pull request manager. Helping to keep me
|
||||
sane as well as guiding the community into creating better additions to Forge.
|
||||
|
||||
Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which
|
||||
I am a part}. For creating some of the core tools needed to make Minecraft modding
|
||||
both possible, and as stable as can be.
|
||||
On that note, here is some specific information of the MCP data we use:
|
||||
* Minecraft Coder Pack (MCP) *
|
||||
Forge Mod Loader and Minecraft Forge have permission to distribute and automatically
|
||||
download components of MCP and distribute MCP data files. This permission is not
|
||||
transitive and others wishing to redistribute the Minecraft Forge source independently
|
||||
should seek permission of MCP or remove the MCP data files and request their users
|
||||
to download MCP separately.
|
||||
|
||||
And lastly, the countless community members who have spent time submitting bug reports,
|
||||
pull requests, and just helping out the community in general. Thank you.
|
||||
|
||||
--LexManos
|
||||
|
||||
=========================================================================
|
||||
|
||||
This is Forge Mod Loader.
|
||||
|
||||
You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml
|
||||
|
||||
This minecraft mod is a clean open source implementation of a mod loader for minecraft servers
|
||||
and minecraft clients.
|
||||
|
||||
The code is authored by cpw.
|
||||
|
||||
It began by partially implementing an API defined by the client side ModLoader, authored by Risugami.
|
||||
http://www.minecraftforum.net/topic/75440-
|
||||
This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader.
|
||||
|
||||
It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge.
|
||||
http://www.minecraftforge.net/
|
||||
|
||||
Additionally, it contains an implementation of topological sort based on that
|
||||
published at http://keithschwarz.com/interesting/code/?dir=topological-sort
|
||||
|
||||
It also contains code from the Maven project for performing versioned dependency
|
||||
resolution. http://maven.apache.org/
|
||||
|
||||
It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/
|
||||
with credit to it's authors.
|
||||
|
||||
Forge Mod Loader downloads components from the Minecraft Coder Pack
|
||||
(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
SoundSystem CodecIBXM Class License:
|
||||
|
||||
You are free to use this class for any purpose, commercial or otherwise.
|
||||
You may modify this class or source code, and distribute it any way you
|
||||
like, provided the following conditions are met:
|
||||
|
||||
1) You may not falsely claim to be the author of this class or any
|
||||
unmodified portion of it.
|
||||
2) You may not copyright this class or a modified version of it and then
|
||||
sue me for copyright infringement.
|
||||
3) If you modify the source code, you must clearly document the changes
|
||||
made before redistributing the modified source code, so other users know
|
||||
it is not the original code.
|
||||
4) You are not required to give me credit for this class in any derived
|
||||
work, but if you do, you must also mention my website:
|
||||
http://www.paulscode.com
|
||||
5) I the author will not be responsible for any damages (physical,
|
||||
financial, or otherwise) caused by the use if this class or any
|
||||
portion of it.
|
||||
6) I the author do not guarantee, warrant, or make any representations,
|
||||
either expressed or implied, regarding the use of this class or any
|
||||
portion of it.
|
||||
|
||||
Author: Paul Lamb
|
||||
http://www.paulscode.com
|
||||
|
||||
|
||||
This software is based on or using the IBXM library available from
|
||||
http://www.geocities.com/sunet2000/
|
||||
|
||||
|
||||
IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+555
-460
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+49
-20
@@ -1,8 +1,17 @@
|
||||
This program is an attempt to create Level Of Details (LODs) in Minecraft.
|
||||
The purpose is to increase the maximum view distance in game
|
||||
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.
|
||||
|
||||
Used in congunction with:
|
||||
https://gitlab.com/jeseibel/minecraft-lod-core-mod
|
||||
Or in other words: this mod let's you see farther without turning your game into a slide show.
|
||||
If you want to see a quick demo, check out the video I made here:
|
||||
https://youtu.be/CCT-3s02tYA
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
========================
|
||||
@@ -12,30 +21,50 @@ source code installation
|
||||
See the Forge Documentation online for more detailed instructions:
|
||||
http://mcforge.readthedocs.io/en/latest/gettingstarted/
|
||||
|
||||
Step 1: open a command line in the project folder
|
||||
Step 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)
|
||||
|
||||
Step 2: run the command: "./gradlew setupDecompWorkspace"
|
||||
Step 2: replace JAVA_HOME with JAVA_MC_HOME in gradle.bat
|
||||
|
||||
Step 3: run the command: "./gradlew eclipse"
|
||||
Step 3: open a command line in the project folder
|
||||
|
||||
Step 4: Import project
|
||||
Step 4: run the command: "./gradlew geneclipseruns"
|
||||
|
||||
Step 5: 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)
|
||||
And make sure it is used in the build.gradle file.
|
||||
|
||||
Step 6: Import the lodcore and lodcore_source jar files into the referenced libraries.
|
||||
Step 5: run the command: "./gradlew eclipse"
|
||||
|
||||
Step 6: Make sure the eclipse has the JDK 1.8.0_251 installed. (This is needed so that eclipse can run minecraft)
|
||||
|
||||
|
||||
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.
|
||||
Step 7: Import the project into eclipse
|
||||
|
||||
|
||||
|
||||
Tip:
|
||||
The Minecraft source code is NOT added to your workspace in a editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only.
|
||||
=========
|
||||
compiling
|
||||
=========
|
||||
|
||||
Current location of mcp-srg.srg:
|
||||
"C:/Users/James Seibel/.gradle/caches/minecraft/de/oceanlabs/mcp/mcp_snapshot/20171003/1.12.2/srgs/"
|
||||
Step 1: open a command line in the project folder
|
||||
|
||||
Step 2: run the command: "./gradlew build"
|
||||
|
||||
Step 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 a editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only.
|
||||
|
||||
Source code uses mcp mappings not Mojangs.
|
||||
|
||||
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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
these are tools needed for looking at obfuscated minecraft code.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
these are tools needed for deobfuscating and looking at the code of optifine.
|
||||
@@ -1,3 +0,0 @@
|
||||
java -jar ./simpledeobf-0.6.jar --input ./OptiFine_1.12.2_HD_U_F5.jar --output ./OptiFine_1.12.2_HD_U_F5_dev.jar --mapFile C:/Users/James_Seibel/.gradle/caches/minecraft/de/oceanlabs/mcp/mcp_snapshot/20171003/1.12.2/srgs/notch-mcp.srg --ref C:/Users/James_Seibel/.gradle/caches/minecraft/net/minecraft/minecraft/1.12.2/minecraft-1.12.2.jar
|
||||
|
||||
pause
|
||||
Binary file not shown.
+167
-50
@@ -1,77 +1,194 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url = 'https://files.minecraftforge.net/maven' }
|
||||
jcenter()
|
||||
maven { url = "https://files.minecraftforge.net/maven" }
|
||||
mavenCentral()
|
||||
maven { url = 'https://repo.spongepowered.org/maven/' }
|
||||
// potential replacement in case of problems:
|
||||
// https://dist.creeper.host/Sponge/maven
|
||||
}
|
||||
dependencies {
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT'
|
||||
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
|
||||
classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT'
|
||||
}
|
||||
}
|
||||
apply plugin: 'net.minecraftforge.gradle.forge'
|
||||
//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
|
||||
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'
|
||||
|
||||
version = 'a1.3.1'
|
||||
group = 'com.backsun.lod'
|
||||
archivesBaseName = 'lod_1.16.5'
|
||||
|
||||
version = "1.0"
|
||||
group = "com.backsun.lod" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
archivesBaseName = "lod"
|
||||
|
||||
sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
|
||||
compileJava {
|
||||
sourceCompatibility = targetCompatibility = '1.8'
|
||||
}
|
||||
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 {
|
||||
version = "1.12.2-14.23.5.2847"
|
||||
runDir = "run"
|
||||
|
||||
// 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.
|
||||
// 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 = "snapshot_20171003"
|
||||
// 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' }
|
||||
|
||||
dependencies {
|
||||
// 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"
|
||||
// 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'
|
||||
|
||||
// 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
|
||||
|
||||
// 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"
|
||||
|
||||
// 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'
|
||||
// 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 deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided,
|
||||
// except that these dependencies get remapped to your current MCP mappings
|
||||
//deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
//deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
// 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'
|
||||
|
||||
// for more info...
|
||||
// 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
|
||||
|
||||
}
|
||||
|
||||
processResources {
|
||||
// this will ensure that this task is redone when the versions change.
|
||||
inputs.property "version", project.version
|
||||
inputs.property "mcversion", project.minecraft.version
|
||||
|
||||
// replace stuff in mcmod.info, nothing else
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include 'mcmod.info'
|
||||
|
||||
// replace version and mcversion
|
||||
expand 'version':project.version, 'mcversion':project.minecraft.version
|
||||
}
|
||||
|
||||
// copy everything else except the mcmod.info
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude 'mcmod.info'
|
||||
// Example for how to get properties into the manifest for reading by the runtime..
|
||||
jar {
|
||||
manifest {
|
||||
attributes([
|
||||
"Specification-Title": "Level of Detail",
|
||||
"Specification-Version": "1", // We are version 1 of ourselves
|
||||
"Implementation-Title": project.name,
|
||||
"Implementation-Version": "{version}",
|
||||
"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"
|
||||
}
|
||||
|
||||
-467
@@ -1,467 +0,0 @@
|
||||
=========
|
||||
Build: 1.12.2-14.23.5.2846 - Tue Sep 03 21:22:46 GMT 2019
|
||||
ichttt:
|
||||
Fix CME when removing ticket managers (#5861)
|
||||
|
||||
=========
|
||||
Build: 1.12.2-14.23.5.2844 - Thu Aug 29 02:33:00 GMT 2019
|
||||
Barteks2x:
|
||||
Fix client sometimes generating biomes, causing incorrect biome
|
||||
generation on integrated server (#5720)
|
||||
|
||||
bs2609:
|
||||
Add checks for tile entities in now-unloaded chunks (#5724)
|
||||
|
||||
=========
|
||||
Build: 1.12.2-14.23.5.2843 - Thu Aug 29 02:23:04 GMT 2019
|
||||
bs2609:
|
||||
Fix invalid placeholder entity attributes (MC-150405) (#5718)
|
||||
|
||||
=========
|
||||
Build: 1.12.2-14.23.5.2842 - Thu Aug 29 02:08:57 GMT 2019
|
||||
bs2609:
|
||||
Allow conditional loading of advancements (#5255)
|
||||
|
||||
=========
|
||||
Build: 1.12.2-14.23.5.2841 - Thu Aug 29 01:58:50 GMT 2019
|
||||
bs2609:
|
||||
Improve performance of persistent chunk checks (#5706)
|
||||
|
||||
jensen.derik:
|
||||
Fix lightning not triggering EntityJoinWorldEvent (#5290)
|
||||
|
||||
=========
|
||||
Build: 1.12.2-14.23.5.2840 - Thu Aug 29 01:19:55 GMT 2019
|
||||
lex:
|
||||
Fix copy paste derp
|
||||
|
||||
|
||||
Build 2838:
|
||||
bs2609: Fix vanilla handling of options file (MC-117449, MC-151173) (#5725)
|
||||
|
||||
Build 2837:
|
||||
clienthax: Update EnumHelper to be compatible with Eclipse's OpenJ9 JVM. (#5712)
|
||||
|
||||
Build 2836:
|
||||
tterrag:
|
||||
Revert "Invalidate tile entities that are queued for removal (#5512)"
|
||||
This reverts commit 75788f63eea6c33ccef7e5cbcab27ad9ad2c2a04.
|
||||
This solution is invalid as TEs are free to modify the world
|
||||
tileEntities list, usually indirectly via chunkloading, from inside
|
||||
invalidate().
|
||||
This happens in vanilla, in TileEntityChest#invalidate, where it calls
|
||||
checkForAdjacentChests(), which has the potential to load neighboring
|
||||
chunks and cause a CME.
|
||||
A more sophisticated solution is needed.
|
||||
|
||||
Build 2835:
|
||||
bs2609: Fix some vertex format changes not being handled correctly (#5368)
|
||||
|
||||
Build 2834:
|
||||
bs2609:
|
||||
Transform vertex normals as well as positions when generating quads
|
||||
(#5242)
|
||||
|
||||
Build 2833:
|
||||
rseifert.phone: Fix for SidedInvWrapper isItemValid using wrong slot (#5642)
|
||||
|
||||
Build 2832:
|
||||
bs2609: Invalidate tile entities that are queued for removal (#5512)
|
||||
|
||||
Build 2831:
|
||||
lclc98: Added Wool to OreDictionary (#5414)
|
||||
|
||||
Build 2830:
|
||||
CreativeMD: Fixed boat not taking care of block-liquid hooks (#5086)
|
||||
|
||||
Build 2829:
|
||||
bs2609: Add Forge dimension-changing hooks to spectator handling code (#5212)
|
||||
|
||||
Build 2828:
|
||||
ichttt:
|
||||
Minor performace improvement when building chunks and rendering blocks
|
||||
(#5286)
|
||||
|
||||
Build 2827:
|
||||
bs2609: Catch json parsing errors from constants/factories files (#5258)
|
||||
|
||||
Build 2826:
|
||||
bs2609: Allow custom DataSerializers to be registered safely (#5245)
|
||||
|
||||
Build 2825:
|
||||
cpw:
|
||||
Try and make 1.13 mods more obviously wrong in 1.12..
|
||||
Signed-off-by: cpw <cpw+github@weeksfamily.ca>
|
||||
|
||||
Build 2824:
|
||||
tterrag: Fix #5651 Re-add canPlaceBlockOnSide check in World#mayPlace
|
||||
|
||||
Build 2823:
|
||||
Pokechu022:
|
||||
Fail fast when null is used with setTag instead of crashing in
|
||||
writeEntry (#5257)
|
||||
|
||||
Build 2822:
|
||||
tterrag: Fix block placement not checking for player collision
|
||||
|
||||
Build 2821:
|
||||
wynprice999: Added more Constants (#5323)
|
||||
|
||||
Build 2820:
|
||||
python0429: [1.12.2] Add a few events pertaining to villages (#5302)
|
||||
|
||||
Build 2819:
|
||||
Tyler Hancock: [1.12] Fix special spawn event not firing in many cases. (#5389)
|
||||
|
||||
Build 2818:
|
||||
bs2609: Only prompt for missing registries on local worlds (#5348)
|
||||
|
||||
Build 2817:
|
||||
ckrier.3000: Add EntityPlaceEvent (#5057)
|
||||
|
||||
Build 2816:
|
||||
bs2609: Generalise EnumRarity to an interface (#5182)
|
||||
|
||||
Build 2815:
|
||||
bs2609: Apply access-level changes to inner class attributes (#5468)
|
||||
|
||||
Build 2814:
|
||||
bs2609: Fix small logic error in ItemTextureQuadConverter (#5463)
|
||||
|
||||
Build 2813:
|
||||
bs2609: Better support for custom bows (#5209)
|
||||
|
||||
Build 2812:
|
||||
CovertJaguar: Remove FluidStack amount from hashcode calculation (#5272)
|
||||
molecularphylo:
|
||||
Fixed incorrect string representation of string list config property
|
||||
default values in their comments.
|
||||
bs2609:
|
||||
Improve tracking of used dimension IDs (#5249)
|
||||
Closes #5378 Large dimension IDs bloat level.data
|
||||
|
||||
Build 2811:
|
||||
bs2609:
|
||||
Fix up torch placement logic to handle more vanilla special-casing
|
||||
(#5426)
|
||||
|
||||
Build 2810:
|
||||
LexManos: Written size does not include int bytes.
|
||||
|
||||
Build 2809:
|
||||
LexManos:
|
||||
Extend Region files to support >1MB per chunk. If the 'sector count' is
|
||||
255, ask the compressed data header for the proper length.
|
||||
|
||||
Build 2808:
|
||||
tterrag1098: Add default impl to IConfigElement#getValidValuesDisplay
|
||||
|
||||
Build 2807:
|
||||
tterrag: Simplify custom item rendering by removing GL emissivity hacks
|
||||
|
||||
Build 2806:
|
||||
tterrag: Support diffuse lighting flag in item rendering
|
||||
|
||||
Build 2805:
|
||||
bs2609:
|
||||
Add a hook to allow continuously using items through stack changes
|
||||
(#4834)
|
||||
* Add a hook to allow continuously using items through stack changes
|
||||
|
||||
* Update licences
|
||||
|
||||
Build 2804:
|
||||
oOMitchOo:
|
||||
Added an additional constructor to every implementation of IFluidBlock.
|
||||
It is now possible to create a fluid block with a Fluid, Material and
|
||||
MapColor, so that the Material's MapColor isn't used for the blocks
|
||||
MapColor. (#5293)
|
||||
|
||||
Build 2803:
|
||||
bs2609: Add redirects to PotionEffect to respect registry replacement (#5213)
|
||||
|
||||
Build 2802:
|
||||
alexiy.ov:
|
||||
Add an annotation for @Config elements which will automatically create a
|
||||
slider control (#5026)
|
||||
|
||||
Build 2801:
|
||||
bs2609: Fix small logic error in emissive item rendering code (#5320)
|
||||
|
||||
Build 2800:
|
||||
molecularphylo:
|
||||
Allow config GUI cycling button elements generated from enums to display
|
||||
toString return values, rather than actual values. (#5125)
|
||||
|
||||
Build 2799:
|
||||
bs2609: Fix incorrect indexing in mipmap generation code (#5201)
|
||||
|
||||
Build 2798:
|
||||
bs2609:
|
||||
Ensure slave maps are cleaned up when handling registry overrides
|
||||
(#5250)
|
||||
|
||||
Build 2797:
|
||||
d_scalzi: Fix issue with --modListFile. (#5316)
|
||||
|
||||
Build 2796:
|
||||
LexManos:
|
||||
Fix potential issues with the Minecraft FakePlayer lingering around
|
||||
after world unloads.
|
||||
|
||||
Build 2795:
|
||||
tterrag: Fix potion remove event not always firing, add expiry event
|
||||
|
||||
Build 2794:
|
||||
tterrag: Clean up CraftingHelper constants loading API
|
||||
|
||||
Build 2793:
|
||||
tterrag: Fix crash from CraftingHelper due to FileSystem being closed early
|
||||
|
||||
Build 2792:
|
||||
tterrag:
|
||||
added PotionHooks, closes #3867, #4375 (#4614)
|
||||
* solved merge confilct
|
||||
|
||||
* improved var names & removed tabs
|
||||
|
||||
* Added spaces around !=
|
||||
|
||||
* fixed typo
|
||||
|
||||
Build 2791:
|
||||
tterrag:
|
||||
Add a hook for farmland watering (#4891)
|
||||
* Add a FarmlandWaterCheckEvent to allow mods to override when Farmland
|
||||
is watered or not
|
||||
|
||||
* revert 1.12.2 json
|
||||
|
||||
* Move the farmland patch to a ticket based instead of a event based
|
||||
system
|
||||
|
||||
* Minor changes
|
||||
|
||||
* Faster isValid checks, faster validation/invalidation if the state did
|
||||
not change, expand test mod to include a test for the validation system
|
||||
|
||||
* remove isValid boolean flag, we can express it with the tick counter
|
||||
|
||||
* Fix test mod resource warnings
|
||||
|
||||
* Remove tick timeout, add a javadoc note to invalidate on chunk unload,
|
||||
cleaned up test mod
|
||||
|
||||
* Allow mods to provide custom handling to determine if a pos is valid
|
||||
or not
|
||||
|
||||
* Make SimpleTicket more simple, make register public so custom tickets
|
||||
can be registered
|
||||
|
||||
* Fixes for review
|
||||
|
||||
* Add missing license headers
|
||||
|
||||
* Use a weak hash set
|
||||
|
||||
* Split up the map into smaller chunk based maps
|
||||
|
||||
* Add missing license headers
|
||||
|
||||
* Make MultiTicketManager more universal for custom implementations,
|
||||
cleanup imports
|
||||
|
||||
Build 2790:
|
||||
tterrag:
|
||||
Add methods to allow loading json constants outside of _constants
|
||||
(#4975)
|
||||
* add interface methods for loading json constants from an arbitary file
|
||||
|
||||
|
||||
* use try-with-resources
|
||||
|
||||
* don't make modders create jsoncontext, clean up resource use
|
||||
|
||||
* very minor cleanup
|
||||
|
||||
Build 2789:
|
||||
tterrag:
|
||||
Compute ASMDataTable submaps parallel, speeds up contructing mods
|
||||
(#5246)
|
||||
* Compute submaps parallel, speeds up contructing mods by a lot
|
||||
|
||||
* Use stream API better
|
||||
|
||||
Build 2788:
|
||||
mezz: Add logging for data manager key registration errors (#5129)
|
||||
|
||||
Build 2787:
|
||||
mezz: Improve support for custom block path types (#5203)
|
||||
|
||||
Build 2786:
|
||||
mezz: Fix missing comments in configs created with annotations (#5189)
|
||||
|
||||
Build 2785:
|
||||
mezz: Allow items to control the rate of repair from mending (#5102)
|
||||
|
||||
Build 2784:
|
||||
mezz: Improve exception handling from server starting events (#5226)
|
||||
|
||||
Build 2783:
|
||||
mezz: Improve context provided by potion icon rendering hooks (#5111)
|
||||
|
||||
Build 2782:
|
||||
mezz: Fix an ObjectHolderRef internal error message (#5214)
|
||||
|
||||
Build 2781:
|
||||
mezz: Implement rendering for item models with emissive quads (#5047)
|
||||
|
||||
Build 2780:
|
||||
mezz: Clean up logged mod states (#5227)
|
||||
mezz: Fix minor issue in getFilledPercentage for Fluid rendering (#5206)
|
||||
|
||||
Build 2779:
|
||||
mezz: Improve reflection helper methods (#4853)
|
||||
mezz: Fix inaccurate main thread name shown in client log (#5078)
|
||||
|
||||
Build 2778:
|
||||
mezz: Fix Baked Item models with transformations (#5241)
|
||||
|
||||
Build 2777:
|
||||
mezz:
|
||||
Re-add some missing villager profession patches (#5200)
|
||||
* Fix zombie villagers only spawning with vanilla professions
|
||||
* Fix spawning modded villagers that do not have their own building
|
||||
mezz: Fix missing string parameters in some log messages (#5210)
|
||||
mezz: Stop firing LivingSetAttackTargetEvent for setRevengeTarget (#5217)
|
||||
mezz: Prevent RecipeBook from crashing on empty modded ingredients (#5234)
|
||||
|
||||
Build 2776:
|
||||
mezz: Fix the bed position given to the SleepingTimeCheck event (#5107)
|
||||
|
||||
Build 2775:
|
||||
LexManos: Fix some null returns from defaulted registries (#5235)
|
||||
|
||||
Build 2774:
|
||||
LexManos:
|
||||
Only remove synthetic lambda methods referenced in body of SideOnly
|
||||
methods (#5127)
|
||||
|
||||
Build 2773:
|
||||
LexManos:
|
||||
A different approach to my changes in
|
||||
https://github.com/MinecraftForge/MinecraftForge/commit/8ace535995522bec0557d4217e0d98b3dc76cf1e
|
||||
to fix #5207
|
||||
LexManos: Fix patches from #5160 setting rotation as well as position (#5233)
|
||||
LexManos: Use HTTPS for files website.
|
||||
|
||||
Build 2772:
|
||||
tterrag: Make Forge-provided default transforms accessible to custom models
|
||||
tterrag:
|
||||
Make Forge blockstate variants correctly inherit AO setting from vanilla
|
||||
models (#5190)
|
||||
* Make Forge blockstate variants correctly inherit AO setting
|
||||
|
||||
* Move variant format checks into variant, check for added properties
|
||||
|
||||
* Small code cleanup
|
||||
tterrag: fixed visual bug with guislider
|
||||
tterrag:
|
||||
Allow IModel to express itself as a vanilla parent (#5195)
|
||||
* Fix errors caused by fancy missing model being non-vanilla parent
|
||||
|
||||
* Switch instanceof checks to a default IModel method
|
||||
|
||||
* Small code tweaks
|
||||
|
||||
Build 2771:
|
||||
tterrag: Add CreativeTabs#getLabelColor
|
||||
|
||||
Build 2770:
|
||||
tterrag:
|
||||
Allow providing a BufferedImage for banner render generation (#5041)
|
||||
* Adds an Event to allow providing a BufferedImage for the banner render
|
||||
generation (cached)
|
||||
textures, since banners don't use an atlas.
|
||||
implementation.
|
||||
|
||||
* Missed some copyright; might as well absolut text match, I guess
|
||||
|
||||
* Remove unneeded patch change
|
||||
|
||||
* Fix event variable access convention
|
||||
|
||||
* Formatting fix
|
||||
|
||||
* Improve event handling registration
|
||||
|
||||
* Import cleanup
|
||||
|
||||
* Replace event approach with Supplier approach
|
||||
|
||||
* Better name for test mod (now that it's no longer an event); adds
|
||||
ENABLE flag
|
||||
|
||||
* Moves MC code into
|
||||
net.minecraftforge.client.MinecraftForgeClient.getImageLayer to simplify
|
||||
patch
|
||||
|
||||
* Generalize naming
|
||||
|
||||
Build 2769:
|
||||
mezz: Fix Mesa biome entry tags in the BiomeDictionary (#5177)
|
||||
|
||||
Build 2768:
|
||||
LexManos: Bump version number for RB.
|
||||
|
||||
Build 2767:
|
||||
LexManos:
|
||||
Change biome spawn list entries to use factory method where possible
|
||||
(#5075)
|
||||
LexManos: Prevent some texture loading errors from crashing the game (#5121)
|
||||
LexManos: Patch PotionHelper to use registry delegates (#5142)
|
||||
LexManos: Add a notification event for handling game rule changes (#5152)
|
||||
|
||||
Build 2766:
|
||||
LexManos:
|
||||
Change universal bucket support to use fluid names instead of instances
|
||||
(#5031)
|
||||
|
||||
Build 2765:
|
||||
LexManos: Fix NPE on clientside entities constructed with null world (#5170)
|
||||
|
||||
Build 2764:
|
||||
tterrag: Fix patches from #5160 running on the client and causing stutter
|
||||
|
||||
Build 2763:
|
||||
LexManos:
|
||||
Class transformer optimizations (#5159)
|
||||
* Filter packages for deobf transformation
|
||||
* Only serialize transformed class with TerminalTransformer if bytecode
|
||||
changed
|
||||
|
||||
Build 2762:
|
||||
github: Update github stale so issues can be Assigned
|
||||
|
||||
Build 2761:
|
||||
LexManos:
|
||||
Fix MC-136995 - Chunk loading and unloading issue with entities placed
|
||||
in exact positions. (#5160)
|
||||
Scatter gun patches to improve entity tracking and position tracking.
|
||||
Provided by Aikar through the Paper project, this commit of patches
|
||||
combines the following patches:
|
||||
|
||||
https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0306-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch
|
||||
|
||||
https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0315-Always-process-chunk-registration-after-moving.patch
|
||||
|
||||
https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0335-Ensure-chunks-are-always-loaded-on-hard-position-set.patch
|
||||
|
||||
https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0378-Sync-Player-Position-to-Vehicles.patch
|
||||
|
||||
Build 2760:
|
||||
LexManos: Fix --mods and --modListFile arguments not making it past LaunchWrapper.
|
||||
|
||||
Build 2759:
|
||||
LexManos: Remove BlamingTransformer (#5115)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=false
|
||||
Vendored
BIN
Binary file not shown.
+1
-2
@@ -1,6 +1,5 @@
|
||||
#Mon Sep 14 12:28:28 PDT 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,47 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# 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
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
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
|
||||
|
||||
@@ -90,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
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
|
||||
@@ -114,6 +113,7 @@ fi
|
||||
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`
|
||||
@@ -154,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# 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
+4
-10
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@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=
|
||||
|
||||
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
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
include ":ASMHelper"
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.backsun.lod;
|
||||
|
||||
import com.backsun.lod.proxy.ClientProxy;
|
||||
import com.backsun.lod.proxy.CommonProxy;
|
||||
import com.backsun.lod.util.Reference;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.Mod.EventHandler;
|
||||
import net.minecraftforge.fml.common.Mod.Instance;
|
||||
import net.minecraftforge.fml.common.SidedProxy;
|
||||
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-07-2021
|
||||
*/
|
||||
@IFMLLoadingPlugin.MCVersion("1.12.2")
|
||||
@IFMLLoadingPlugin.TransformerExclusions({"com.backsun.lod.asm"})
|
||||
@Mod(modid = Reference.MOD_ID, name = Reference.NAME, version = Reference.VERSION, dependencies = "required-after:lodcore@[1.0,)")
|
||||
public class LodMain
|
||||
{
|
||||
@Instance
|
||||
public static LodMain instance;
|
||||
|
||||
@SidedProxy(clientSide = Reference.CLIENT_PROXY_CLASS, serverSide = Reference.COMMON_PROXY_CLASS)
|
||||
public static CommonProxy common_proxy;
|
||||
public static ClientProxy client_proxy;
|
||||
|
||||
@EventHandler
|
||||
public static void PreInit(FMLPreInitializationEvent event)
|
||||
{
|
||||
Minecraft.getMinecraft().getFramebuffer().enableStencil();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public static void Init(FMLInitializationEvent event)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.register(common_proxy);
|
||||
client_proxy = new ClientProxy();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public static void PostInit(FMLPostInitializationEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,755 +0,0 @@
|
||||
package com.backsun.lod.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.backsun.lod.util.enums.ColorDirection;
|
||||
import com.backsun.lod.util.enums.LodLocation;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.color.BlockColors;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
|
||||
/**
|
||||
* This object contains position
|
||||
* and color data for an LOD object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-13-2021
|
||||
*/
|
||||
public class LodChunk
|
||||
{
|
||||
/** how many different pieces of data are in one line */
|
||||
private static final int DATA_DELIMITER_COUNT = 28;
|
||||
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
|
||||
public static final int WIDTH = 16;
|
||||
|
||||
private static final int CHUNK_DATA_WIDTH = WIDTH;
|
||||
private static final int CHUNK_DATA_HEIGHT = WIDTH;
|
||||
|
||||
private final int airBlockId = Block.getIdFromBlock(Block.getBlockFromName("air"));
|
||||
private final int waterBlockId = Block.getIdFromBlock(Block.getBlockFromName("water"));
|
||||
private final int waterColor = colorToInt(new Color(36, 50, 171));
|
||||
|
||||
/**
|
||||
* This is how many blocks are
|
||||
* required at a specific y-value
|
||||
* to constitute a LOD point
|
||||
*/
|
||||
private final int LOD_BLOCK_REQ = 16;
|
||||
// the max number of blocks per layer = 64 (8*8)
|
||||
// since each layer is 1/4 the chunk
|
||||
|
||||
|
||||
/** The x coordinate of the chunk. */
|
||||
public int x;
|
||||
/** The z coordinate of the chunk. */
|
||||
public int z;
|
||||
|
||||
// each short is the height of
|
||||
// that 8th of the chunk.
|
||||
public short top[];
|
||||
public short bottom[];
|
||||
|
||||
/** The average color of each 6 cardinal directions */
|
||||
public Color colors[];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
/**
|
||||
* Create an empty LodChunk
|
||||
*/
|
||||
public LodChunk()
|
||||
{
|
||||
x = 0;
|
||||
z = 0;
|
||||
|
||||
top = new short[4];
|
||||
bottom = new short[4];
|
||||
colors = new Color[6];
|
||||
|
||||
// by default have the colors invisible
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
{
|
||||
colors[dir.value] = new Color(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an LodChunk from the string
|
||||
* generated by the toData method.
|
||||
*
|
||||
* @throws IllegalArgumentException if the data isn't valid to create a LodChunk
|
||||
* @throws NumberFormatException if the data can't be converted into an int at any point
|
||||
*/
|
||||
public LodChunk(String data) throws IllegalArgumentException, NumberFormatException
|
||||
{
|
||||
/*
|
||||
* data format:
|
||||
* x, z, top data, bottom data, rgb color data
|
||||
*
|
||||
* example:
|
||||
* 5,8, 4,4,4,4, 0,0,0,0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
||||
*/
|
||||
|
||||
// make sure there are the correct number of entries
|
||||
// in the data string (28)
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < data.length(); i++)
|
||||
{
|
||||
if(data.charAt(i) == DATA_DELIMITER)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if(count != DATA_DELIMITER_COUNT)
|
||||
{
|
||||
throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + DATA_DELIMITER_COUNT + ".");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// index we will use when going through the String
|
||||
int index = 0;
|
||||
int lastIndex = 0;
|
||||
|
||||
|
||||
|
||||
// x and z position
|
||||
index = data.indexOf(DATA_DELIMITER, 0);
|
||||
x = Integer.parseInt(data.substring(0,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
z = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
|
||||
|
||||
// top
|
||||
top = new short[4];
|
||||
for(LodLocation loc : LodLocation.values())
|
||||
{
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
|
||||
top[loc.value] = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
}
|
||||
|
||||
|
||||
// bottom
|
||||
bottom = new short[4];
|
||||
for(LodLocation loc : LodLocation.values())
|
||||
{
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
|
||||
bottom[loc.value] = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
}
|
||||
|
||||
|
||||
// color
|
||||
colors = new Color[6];
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
{
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
// get r,g,b
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
|
||||
String raw = "";
|
||||
switch(i)
|
||||
{
|
||||
case 0:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
red = Short.parseShort(raw);
|
||||
break;
|
||||
case 1:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
green = Short.parseShort(raw);
|
||||
break;
|
||||
case 2:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
blue = Short.parseShort(raw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
colors[dir.value] = new Color(red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Illegal argument is thrown if either the
|
||||
* chunk or world is null. The reason the world
|
||||
* can't be null is because it's required to determine
|
||||
* a block's color.
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public LodChunk(Chunk chunk, World world) throws IllegalArgumentException
|
||||
{
|
||||
if(chunk == null)
|
||||
{
|
||||
throw new IllegalArgumentException("LodChunk constructor given a null chunk");
|
||||
}
|
||||
if(world == null)
|
||||
{
|
||||
throw new IllegalArgumentException("LodChunk constructor given a null world");
|
||||
}
|
||||
|
||||
|
||||
x = chunk.x;
|
||||
z = chunk.z;
|
||||
|
||||
top = new short[4];
|
||||
bottom = new short[4];
|
||||
colors = new Color[6];
|
||||
|
||||
// generate the top and bottom points of this LOD
|
||||
for(LodLocation loc : LodLocation.values())
|
||||
{
|
||||
top[loc.value] = generateLodSection(chunk, true, loc);
|
||||
bottom[loc.value] = generateLodSection(chunk, false, loc);
|
||||
}
|
||||
|
||||
// determine the average color for each direction
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
{
|
||||
colors[dir.value] = generateLodColorSection(chunk, world, dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// constructor helpers //
|
||||
//=====================//
|
||||
|
||||
|
||||
/**
|
||||
* If invalid/null/empty chunks are given
|
||||
* crashes may occur.
|
||||
*/
|
||||
public short generateLodSection(Chunk chunk, boolean getTopSection, LodLocation lodLoc)
|
||||
{
|
||||
// should have a length of 16
|
||||
// (each storage is 16x16x16 and the
|
||||
// world height is 256)
|
||||
ExtendedBlockStorage[] data = chunk.getBlockStorageArray();
|
||||
|
||||
|
||||
|
||||
int startX = 0;
|
||||
int endX = 0;
|
||||
|
||||
int startZ = 0;
|
||||
int endZ = 0;
|
||||
|
||||
// determine where we should look in this
|
||||
// chunk
|
||||
switch(lodLoc)
|
||||
{
|
||||
case NE:
|
||||
// -N
|
||||
startZ = 0;
|
||||
endZ = (CHUNK_DATA_WIDTH / 2) - 1;
|
||||
// +E
|
||||
startX = CHUNK_DATA_WIDTH / 2;
|
||||
endX = CHUNK_DATA_WIDTH - 1;
|
||||
break;
|
||||
|
||||
case SE:
|
||||
// +S
|
||||
startZ = CHUNK_DATA_WIDTH / 2;
|
||||
endZ = CHUNK_DATA_WIDTH;
|
||||
// +E
|
||||
startX = CHUNK_DATA_WIDTH / 2;
|
||||
endX = CHUNK_DATA_WIDTH;
|
||||
break;
|
||||
|
||||
case SW:
|
||||
// +S
|
||||
startZ = CHUNK_DATA_WIDTH / 2;
|
||||
endZ = CHUNK_DATA_WIDTH;
|
||||
// -W
|
||||
startX = 0;
|
||||
endX = (CHUNK_DATA_WIDTH / 2) - 1;
|
||||
break;
|
||||
|
||||
case NW:
|
||||
// -N
|
||||
startZ = 0;
|
||||
endZ = CHUNK_DATA_WIDTH / 2;
|
||||
// -W
|
||||
startX = 0;
|
||||
endX = CHUNK_DATA_WIDTH / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(getTopSection)
|
||||
return determineTopPoint(data, startX, endX, startZ, endZ);
|
||||
else
|
||||
return determineBottomPoint(data, startX, endX, startZ, endZ);
|
||||
}
|
||||
|
||||
private short determineBottomPoint(ExtendedBlockStorage[] data, int startX, int endX, int startZ, int endZ)
|
||||
{
|
||||
// search from the bottom up
|
||||
for(int i = 0; i < data.length; i++)
|
||||
{
|
||||
for(int y = 0; y < CHUNK_DATA_HEIGHT; y++)
|
||||
{
|
||||
|
||||
if(isLayerValidLodPoint(data, startX, endX, startZ, endZ, i, y))
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (i * CHUNK_DATA_HEIGHT));
|
||||
}
|
||||
|
||||
} // y
|
||||
} // data
|
||||
|
||||
|
||||
// we never found a valid LOD point
|
||||
return -1;
|
||||
}
|
||||
|
||||
private short determineTopPoint(ExtendedBlockStorage[] data, int startX, int endX, int startZ, int endZ)
|
||||
{
|
||||
// search from the top down
|
||||
for(int i = data.length - 1; i >= 0; i--)
|
||||
{
|
||||
for(int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
|
||||
{
|
||||
if(isLayerValidLodPoint(data, startX, endX, startZ, endZ, i, y))
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (i * CHUNK_DATA_HEIGHT));
|
||||
}
|
||||
} // y
|
||||
} // data
|
||||
|
||||
|
||||
|
||||
// we never found a valid LOD point
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the layer between the given X, Z, and dataIndex
|
||||
* values a valid LOD point?
|
||||
*/
|
||||
private boolean isLayerValidLodPoint(
|
||||
ExtendedBlockStorage[] data,
|
||||
int startX, int endX,
|
||||
int startZ, int endZ,
|
||||
int dataIndex, int y)
|
||||
{
|
||||
// search through this layer
|
||||
int layerBlocks = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
if(data[dataIndex] == null)
|
||||
{
|
||||
// this section doesn't have any blocks,
|
||||
// it is not a valid section
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(data[dataIndex].get(x, y, z) != null && Block.getIdFromBlock(data[dataIndex].get(x, y, z).getBlock()) != airBlockId)
|
||||
{
|
||||
// we found a valid block in
|
||||
// in this layer
|
||||
layerBlocks++;
|
||||
|
||||
if(layerBlocks >= LOD_BLOCK_REQ)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // z
|
||||
} // x
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Color generateLodColorSection(Chunk chunk, World world, ColorDirection colorDir)
|
||||
{
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
BlockColors bc = mc.getBlockColors();
|
||||
|
||||
switch (colorDir)
|
||||
{
|
||||
case TOP:
|
||||
return generateLodColorVertical(chunk, colorDir, world, bc);
|
||||
case BOTTOM:
|
||||
return generateLodColorVertical(chunk, colorDir, world, bc);
|
||||
|
||||
case N:
|
||||
return generateLodColorHorizontal(chunk, colorDir, world, bc);
|
||||
case S:
|
||||
return generateLodColorHorizontal(chunk, colorDir, world, bc);
|
||||
|
||||
case E:
|
||||
return generateLodColorHorizontal(chunk, colorDir, world, bc);
|
||||
case W:
|
||||
return generateLodColorHorizontal(chunk, colorDir, world, bc);
|
||||
}
|
||||
|
||||
return new Color(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only accepts TOP and BOTTOM as ColorPositions
|
||||
*/
|
||||
private Color generateLodColorVertical(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
|
||||
{
|
||||
ExtendedBlockStorage[] data = chunk.getBlockStorageArray();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
boolean goTopDown = (colorDir == ColorDirection.TOP);
|
||||
|
||||
|
||||
// either go top down or bottom up
|
||||
int dataStart = goTopDown? data.length - 1 : 0;
|
||||
int dataMax = data.length;
|
||||
int dataMin = 0;
|
||||
int dataIncrement = goTopDown? -1 : 1;
|
||||
|
||||
int topStart = goTopDown? CHUNK_DATA_HEIGHT - 1 : 0;
|
||||
int topMax = CHUNK_DATA_HEIGHT;
|
||||
int topMin = 0;
|
||||
int topIncrement = goTopDown? -1 : 1;
|
||||
|
||||
for(int x = 0; x < CHUNK_DATA_WIDTH; x++)
|
||||
{
|
||||
for(int z = 0; z < CHUNK_DATA_WIDTH; z++)
|
||||
{
|
||||
boolean foundBlock = false;
|
||||
|
||||
for(int di = dataStart; !foundBlock && di >= dataMin && di < dataMax; di += dataIncrement)
|
||||
{
|
||||
if(!foundBlock && data[di] != null)
|
||||
{
|
||||
for(int y = topStart; !foundBlock && y >= topMin && y < topMax; y += topIncrement)
|
||||
{
|
||||
int ci;
|
||||
if(Block.getIdFromBlock(data[di].get(x, y, z).getBlock()) == waterBlockId)
|
||||
// this is a special case since getColor on water generally returns white
|
||||
ci = waterColor;
|
||||
else
|
||||
ci = bc.getColor(data[di].get(x, y, z), world, new BlockPos(x,y,z));
|
||||
|
||||
if(ci == 0)
|
||||
{
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
Color c = intToColor(ci);
|
||||
|
||||
red += c.getRed();
|
||||
green += c.getGreen();
|
||||
blue += c.getBlue();
|
||||
|
||||
numbOfBlocks++;
|
||||
|
||||
|
||||
// we found a valid block, skip to the
|
||||
// next x and z
|
||||
foundBlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(numbOfBlocks == 0)
|
||||
numbOfBlocks = 1;
|
||||
|
||||
red /= numbOfBlocks;
|
||||
green /= numbOfBlocks;
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
private Color generateLodColorHorizontal(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
|
||||
{
|
||||
ExtendedBlockStorage[] data = chunk.getBlockStorageArray();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
|
||||
// these don't change since the over direction doesn't matter
|
||||
int overStart = 0;
|
||||
int overIncrement = 1;
|
||||
|
||||
// determine which direction is "in"
|
||||
int inStart = 0;
|
||||
int inIncrement = 1;
|
||||
switch (colorDir)
|
||||
{
|
||||
case N:
|
||||
inStart = 0;
|
||||
inIncrement = 1;
|
||||
break;
|
||||
case S:
|
||||
inStart = CHUNK_DATA_WIDTH - 1;
|
||||
inIncrement = -1;
|
||||
break;
|
||||
case E:
|
||||
inStart = 0;
|
||||
inIncrement = 1;
|
||||
break;
|
||||
case W:
|
||||
inStart = CHUNK_DATA_WIDTH - 1;
|
||||
inIncrement = -1;
|
||||
break;
|
||||
default:
|
||||
// we were given an invalid position, return invisible.
|
||||
// this shouldn't happen and is mostly here to make the
|
||||
// compiler happy
|
||||
return new Color(0,0,0,0);
|
||||
}
|
||||
|
||||
|
||||
for (int di = 0; di < data.length; di++)
|
||||
{
|
||||
if (data[di] != null)
|
||||
{
|
||||
for (int y = 0; y < CHUNK_DATA_HEIGHT; y++)
|
||||
{
|
||||
boolean foundBlock = false;
|
||||
|
||||
// over moves "over" the side of the chunk
|
||||
// in moves "into" the chunk until it finds a block
|
||||
|
||||
for (int over = overStart; !foundBlock && over >= 0 && over < CHUNK_DATA_WIDTH; over += overIncrement)
|
||||
{
|
||||
for (int in = inStart; !foundBlock && in >= 0 && in < CHUNK_DATA_WIDTH; in += inIncrement)
|
||||
{
|
||||
int x = -1;
|
||||
int z = -1;
|
||||
|
||||
// determine which should be X and Z
|
||||
switch(colorDir)
|
||||
{
|
||||
case N:
|
||||
x = over;
|
||||
z = in;
|
||||
break;
|
||||
case S:
|
||||
x = over;
|
||||
z = in;
|
||||
break;
|
||||
case E:
|
||||
x = in;
|
||||
z = over;
|
||||
break;
|
||||
case W:
|
||||
x = in;
|
||||
z = over;
|
||||
break;
|
||||
default:
|
||||
// this will never happen, it would have
|
||||
// been caught by the switch before the loops
|
||||
break;
|
||||
}
|
||||
|
||||
int ci;
|
||||
if(Block.getIdFromBlock(data[di].get(x, y, z).getBlock()) == waterBlockId)
|
||||
// this is a special case since getColor on water generally returns white
|
||||
ci = waterColor;
|
||||
else
|
||||
ci = bc.getColor(data[di].get(x, y, z), world, new BlockPos(x,y,z));
|
||||
|
||||
if (ci == 0) {
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
Color c = intToColor(ci);
|
||||
|
||||
red += c.getRed();
|
||||
green += c.getGreen();
|
||||
blue += c.getBlue();
|
||||
|
||||
numbOfBlocks++;
|
||||
|
||||
// we found a valid block, skip to the
|
||||
// next x and z
|
||||
foundBlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(numbOfBlocks == 0)
|
||||
numbOfBlocks = 1;
|
||||
|
||||
red /= numbOfBlocks;
|
||||
green /= numbOfBlocks;
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a BlockColors int into a Color object.
|
||||
*/
|
||||
private Color intToColor(int num)
|
||||
{
|
||||
int filter = 0b11111111;
|
||||
|
||||
int red = (num >> 16 ) & filter;
|
||||
int green = (num >> 8 ) & filter;
|
||||
int blue = num & filter;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Color into a BlockColors object.
|
||||
*/
|
||||
private int colorToInt(Color color)
|
||||
{
|
||||
return color.getRGB();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// output //
|
||||
//========//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Outputs all data in csv format
|
||||
* with the given delimiter.
|
||||
* <br>
|
||||
* Exports data in the form:
|
||||
* <br>
|
||||
* x, z, top data, bottom data, rgb color data
|
||||
*
|
||||
* <br>
|
||||
* example output:
|
||||
* <br>
|
||||
* 5,8, 4,4,4,4, 0,0,0,0 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
||||
*/
|
||||
public String toData()
|
||||
{
|
||||
String s = "";
|
||||
|
||||
s += Integer.toString(x) + DATA_DELIMITER + Integer.toString(z) + DATA_DELIMITER;
|
||||
|
||||
for(int i = 0; i < top.length; i++)
|
||||
{
|
||||
s += Short.toString(top[i]) + DATA_DELIMITER;
|
||||
}
|
||||
|
||||
for(int i = 0; i < bottom.length; i++)
|
||||
{
|
||||
s += Short.toString(bottom[i]) + DATA_DELIMITER;
|
||||
}
|
||||
|
||||
for(int i = 0; i < colors.length; i++)
|
||||
{
|
||||
s += Integer.toString(colors[i].getRed()) + DATA_DELIMITER + Integer.toString(colors[i].getGreen()) + DATA_DELIMITER + Integer.toString(colors[i].getBlue()) + DATA_DELIMITER;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "";
|
||||
|
||||
s += "x: " + x + " z: " + z + "\t";
|
||||
|
||||
// s += "top: ";
|
||||
// for(int i = 0; i < top.length; i++)
|
||||
// {
|
||||
// s += top[i] + " ";
|
||||
// }
|
||||
// s += "\t";
|
||||
|
||||
// s += "bottom: ";
|
||||
// for(int i = 0; i < bottom.length; i++)
|
||||
// {
|
||||
// s += bottom[i] + " ";
|
||||
// }
|
||||
// s += "\t";
|
||||
|
||||
// s += "colors ";
|
||||
// for(int i = 0; i < colors.length; i++)
|
||||
// {
|
||||
// if(colors[i] != null)
|
||||
// s += "(" + colors[i].getRed() + ", " + colors[i].getGreen() + ", " + colors[i].getBlue() + "), ";
|
||||
// }
|
||||
|
||||
s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + "), ";
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.backsun.lod.objects;
|
||||
|
||||
import java.util.Dictionary;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* This stores all LODs for a given world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 01-31-2021
|
||||
*/
|
||||
public class LodWorld
|
||||
{
|
||||
public String worldName;
|
||||
|
||||
/**
|
||||
* Key = Dimension id (as an int)
|
||||
*/
|
||||
private Dictionary<Integer, LodDimension> lodDimensions;
|
||||
|
||||
|
||||
public LodWorld(String newWorldName)
|
||||
{
|
||||
worldName = newWorldName;
|
||||
lodDimensions = new Hashtable<Integer, LodDimension>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void addLodDimension(LodDimension newStorage)
|
||||
{
|
||||
lodDimensions.put(newStorage.dimension.getId(), newStorage);
|
||||
}
|
||||
|
||||
public LodDimension getLodDimension(int dimensionId)
|
||||
{
|
||||
return lodDimensions.get(dimensionId);
|
||||
}
|
||||
|
||||
|
||||
public void resizeDimensionRegionWidth(int newWidth)
|
||||
{
|
||||
Enumeration<Integer> keys = lodDimensions.keys();
|
||||
|
||||
while(keys.hasMoreElements())
|
||||
lodDimensions.get(keys.nextElement()).setRegionWidth(newWidth);
|
||||
}
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
package com.backsun.lod.proxy;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.backsun.lod.objects.LodChunk;
|
||||
import com.backsun.lod.objects.LodDimension;
|
||||
import com.backsun.lod.objects.LodRegion;
|
||||
import com.backsun.lod.objects.LodWorld;
|
||||
import com.backsun.lod.renderer.LodRenderer;
|
||||
import com.backsun.lod.util.LodConfig;
|
||||
import com.backsun.lod.util.LodFileHandler;
|
||||
import com.backsun.lodCore.util.RenderGlobalHook;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.event.terraingen.PopulateChunkEvent;
|
||||
import net.minecraftforge.event.world.ChunkEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
//TODO Find a way to replace getIntegratedServer so this mod could be used on non-local worlds.
|
||||
// Minecraft.getMinecraft().getIntegratedServer()
|
||||
|
||||
/**
|
||||
* This is used by the client.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 01-31-2021
|
||||
*/
|
||||
public class ClientProxy extends CommonProxy
|
||||
{
|
||||
private LodRenderer renderer;
|
||||
private LodWorld lodWorld;
|
||||
private ExecutorService lodGenThreadPool = Executors.newFixedThreadPool(1);
|
||||
|
||||
/** Default size of any LOD regions we use */
|
||||
private int regionWidth = 5;
|
||||
|
||||
public ClientProxy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// render event //
|
||||
//==============//
|
||||
|
||||
@SubscribeEvent
|
||||
public void renderWorldLast(RenderWorldLastEvent event)
|
||||
{
|
||||
RenderGlobalHook.endRenderingStencil();
|
||||
GL11.glStencilFunc(GL11.GL_EQUAL, 0, 0xFF);
|
||||
|
||||
if (LodConfig.drawLODs)
|
||||
renderLods(event.getPartialTicks());
|
||||
|
||||
GL11.glDisable(GL11.GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
public void renderLods(float partialTicks)
|
||||
{
|
||||
int newWidth = Math.max(4, (Minecraft.getMinecraft().gameSettings.renderDistanceChunks * LodChunk.WIDTH * 2) / LodRegion.SIZE);
|
||||
if (lodWorld != null && regionWidth != newWidth)
|
||||
{
|
||||
lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
regionWidth = newWidth;
|
||||
|
||||
// skip this frame, hopefully the lodWorld
|
||||
// should have everything set up by then
|
||||
return;
|
||||
}
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc == null || mc.player == null || lodWorld == null)
|
||||
return;
|
||||
|
||||
int dimId = mc.player.dimension;
|
||||
LodDimension lodDim = lodWorld.getLodDimension(dimId);
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
|
||||
double playerX = mc.player.posX;
|
||||
double playerZ = mc.player.posZ;
|
||||
|
||||
int xOffset = ((int)playerX / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterX();
|
||||
int zOffset = ((int)playerZ / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterZ();
|
||||
|
||||
if (xOffset != 0 || zOffset != 0)
|
||||
{
|
||||
lodDim.move(xOffset, zOffset);
|
||||
}
|
||||
|
||||
|
||||
// we wait to create the renderer until the first frame
|
||||
// to make sure that the EntityRenderer has
|
||||
// been created, that way we can get the fovModifer
|
||||
// method from it through reflection.
|
||||
if (renderer == null)
|
||||
{
|
||||
renderer = new LodRenderer();
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.drawLODs(lodDim, partialTicks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// update events //
|
||||
//===============//
|
||||
|
||||
@SubscribeEvent
|
||||
public void chunkLoadEvent(ChunkEvent event)
|
||||
{
|
||||
generateLodChunk(event.getChunk());
|
||||
}
|
||||
|
||||
/**
|
||||
* this event is called whenever a chunk is created for the first time.
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public void onChunkPopulate(PopulateChunkEvent event)
|
||||
{
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc != null && event != null)
|
||||
{
|
||||
WorldClient world = mc.world;
|
||||
|
||||
if(world != null)
|
||||
{
|
||||
generateLodChunk(world.getChunkFromChunkCoords(event.getChunkX(), event.getChunkZ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
Use this for generating chunks and maybe determining if they are loaded at all?
|
||||
|
||||
Could I create my own chunk generator and multithread it? It wouldn't save to the world, but could I save it for LODs?
|
||||
|
||||
chunk = Minecraft.getMinecraft().getIntegratedServer().getWorld(0).getChunkProvider().chunkGenerator.generateChunk(chunk.x, chunk.z);
|
||||
|
||||
System.out.println(chunk.x + " " + chunk.z + "\tloaded: " + chunk.isLoaded() + "\tpop: " + chunk.isPopulated() + "\tter pop: " + chunk.isTerrainPopulated());
|
||||
*/
|
||||
|
||||
private void generateLodChunk(Chunk chunk)
|
||||
{
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
// given a valid chunk object
|
||||
// (Minecraft often gives back empty
|
||||
// or null chunks in this method)
|
||||
if (chunk == null || !isValidChunk(chunk))
|
||||
return;
|
||||
|
||||
int dimId = chunk.getWorld().provider.getDimension();
|
||||
World world = mc.getIntegratedServer().getWorld(dimId);
|
||||
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
LodChunk lod = new LodChunk(chunk, world);
|
||||
LodDimension lodDim;
|
||||
|
||||
if (lodWorld == null)
|
||||
{
|
||||
lodWorld = new LodWorld(LodFileHandler.getWorldName());
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we have a lodWorld make sure
|
||||
// it is for this minecraft world
|
||||
if (!lodWorld.worldName.equals(LodFileHandler.getWorldName()))
|
||||
{
|
||||
// this lodWorld isn't for this minecraft world
|
||||
// delete it so we can get a new one
|
||||
lodWorld = null;
|
||||
|
||||
// skip this frame
|
||||
// we'll get this set up next time
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (lodWorld.getLodDimension(dimId) == null)
|
||||
{
|
||||
DimensionType dim = DimensionType.getById(dimId);
|
||||
lodDim = new LodDimension(dim, regionWidth);
|
||||
lodWorld.addLodDimension(lodDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
lodDim = lodWorld.getLodDimension(dimId);
|
||||
}
|
||||
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
catch(IllegalArgumentException | NullPointerException e)
|
||||
{
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
lodGenThreadPool.execute(thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given chunk
|
||||
* has any data in it.
|
||||
*/
|
||||
private boolean isValidChunk(Chunk chunk)
|
||||
{
|
||||
ExtendedBlockStorage[] data = chunk.getBlockStorageArray();
|
||||
|
||||
for(ExtendedBlockStorage e : data)
|
||||
{
|
||||
if(e != null && !e.isEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.backsun.lod.proxy;
|
||||
|
||||
/**
|
||||
* This is used by the server.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 08-31-2020
|
||||
*/
|
||||
public class CommonProxy
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,333 +0,0 @@
|
||||
package com.backsun.lod.renderer;
|
||||
import java.awt.Color;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.backsun.lod.util.enums.FogDistance;
|
||||
|
||||
import net.minecraft.client.renderer.GLAllocation;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-13-2021
|
||||
*/
|
||||
public class BuildBufferThread implements Callable<NearFarBuffer>
|
||||
{
|
||||
public ByteBuffer nearBuffer;
|
||||
public ByteBuffer farBuffer;
|
||||
public FogDistance distanceMode;
|
||||
public AxisAlignedBB[][] lods;
|
||||
public Color[][] colors;
|
||||
|
||||
private int start = 0;
|
||||
private int end = -1;
|
||||
|
||||
private int vertexCount = 0;
|
||||
private VertexFormat vertexFormat = null;
|
||||
private int vertexFormatIndex = 0;
|
||||
private VertexFormatElement vertexFormatElement = null;
|
||||
|
||||
|
||||
|
||||
BuildBufferThread()
|
||||
{
|
||||
vertexCount = 0;
|
||||
vertexFormat = DefaultVertexFormats.POSITION_COLOR;
|
||||
vertexFormatIndex = 0;
|
||||
vertexFormatElement = vertexFormat.getElement(vertexFormatIndex);
|
||||
}
|
||||
|
||||
BuildBufferThread(ByteBuffer newNearByteBuffer, ByteBuffer newFarByteBuffer, AxisAlignedBB[][] newLods, Color[][] newColors, FogDistance newDistanceMode, int threadNumber, int totalThreads)
|
||||
{
|
||||
setNewData(newNearByteBuffer, newFarByteBuffer, distanceMode, newLods, newColors, threadNumber, totalThreads);
|
||||
|
||||
vertexCount = 0;
|
||||
vertexFormat = DefaultVertexFormats.POSITION_COLOR;
|
||||
vertexFormatIndex = 0;
|
||||
vertexFormatElement = vertexFormat.getElement(vertexFormatIndex);
|
||||
}
|
||||
|
||||
public void setNewData(ByteBuffer newNearByteBuffer, ByteBuffer newFarByteBuffer, FogDistance newDistanceMode, AxisAlignedBB[][] newLods, Color[][] newColors, int threadNumber, int totalThreads)
|
||||
{
|
||||
vertexCount = 0;
|
||||
vertexFormatIndex = 0;
|
||||
|
||||
nearBuffer = newNearByteBuffer;
|
||||
farBuffer = newFarByteBuffer;
|
||||
distanceMode = newDistanceMode;
|
||||
lods = newLods;
|
||||
colors = newColors;
|
||||
|
||||
int numbChunksWide = lods.length;
|
||||
int rowsToRender = numbChunksWide / totalThreads;
|
||||
start = threadNumber * rowsToRender;
|
||||
end = (threadNumber + 1) * rowsToRender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NearFarBuffer call()
|
||||
{
|
||||
int numbChunksWide = lods.length;
|
||||
|
||||
ByteBuffer currentBuffer;
|
||||
AxisAlignedBB bb;
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
|
||||
if (distanceMode == FogDistance.NEAR)
|
||||
{
|
||||
currentBuffer = nearBuffer;
|
||||
}
|
||||
else // if (distanceMode == FogDistance.FAR)
|
||||
{
|
||||
currentBuffer = farBuffer;
|
||||
}
|
||||
|
||||
|
||||
// x axis
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
// z axis
|
||||
for (int j = 0; j < numbChunksWide; j++)
|
||||
{
|
||||
if (lods[i][j] == null || colors[i][j] == null)
|
||||
continue;
|
||||
|
||||
bb = lods[i][j];
|
||||
|
||||
// get the color of this LOD object
|
||||
red = colors[i][j].getRed();
|
||||
green = colors[i][j].getGreen();
|
||||
blue = colors[i][j].getBlue();
|
||||
alpha = colors[i][j].getAlpha();
|
||||
|
||||
// choose which buffer to add these LODs too
|
||||
if (distanceMode == FogDistance.NEAR_AND_FAR)
|
||||
{
|
||||
if (RenderUtil.isCoordinateInNearFogArea(i, j, numbChunksWide / 2))
|
||||
currentBuffer = nearBuffer;
|
||||
else
|
||||
currentBuffer = farBuffer;
|
||||
}
|
||||
|
||||
|
||||
if (bb.minY != bb.maxY)
|
||||
{
|
||||
// top (facing up)
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
// bottom (facing down)
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
|
||||
// south (facing -Z)
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
// north (facing +Z)
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
|
||||
// west (facing -X)
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
// east (facing +X)
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
// render this LOD as one block thick
|
||||
|
||||
// top (facing up)
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
|
||||
// bottom (facing down)
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
|
||||
// south (facing -Z)
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
// north (facing +Z)
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
|
||||
// west (facing -X)
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
|
||||
// east (facing +X)
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
|
||||
} // z axis
|
||||
} // x axis
|
||||
|
||||
return new NearFarBuffer(nearBuffer, farBuffer);
|
||||
}
|
||||
|
||||
private void addPosAndColor(ByteBuffer buffer, double x, double y, double z, int red, int green, int blue, int alpha)
|
||||
{
|
||||
addPos(buffer, x, y, z);
|
||||
addColor(buffer, red, green, blue, alpha);
|
||||
endVertex();
|
||||
}
|
||||
|
||||
private void addPos(ByteBuffer byteBuffer, double x, double y, double z)
|
||||
{
|
||||
int i = this.vertexCount * this.vertexFormat.getNextOffset() + this.vertexFormat.getOffset(this.vertexFormatIndex);
|
||||
|
||||
switch (this.vertexFormatElement.getType())
|
||||
{
|
||||
case FLOAT: // This is the one currently used
|
||||
byteBuffer.putFloat(i, (float)(x));
|
||||
byteBuffer.putFloat(i + 4, (float)(y));
|
||||
byteBuffer.putFloat(i + 8, (float)(z));
|
||||
break;
|
||||
case UINT:
|
||||
case INT:
|
||||
byteBuffer.putInt(i, Float.floatToRawIntBits((float)(x)));
|
||||
byteBuffer.putInt(i + 4, Float.floatToRawIntBits((float)(y)));
|
||||
byteBuffer.putInt(i + 8, Float.floatToRawIntBits((float)(z)));
|
||||
break;
|
||||
case USHORT:
|
||||
case SHORT:
|
||||
byteBuffer.putShort(i, (short)((int)(x)));
|
||||
byteBuffer.putShort(i + 2, (short)((int)(y)));
|
||||
byteBuffer.putShort(i + 4, (short)((int)(z)));
|
||||
break;
|
||||
case UBYTE:
|
||||
case BYTE:
|
||||
byteBuffer.put(i, (byte)((int)(x)));
|
||||
byteBuffer.put(i + 1, (byte)((int)(y)));
|
||||
byteBuffer.put(i + 2, (byte)((int)(z)));
|
||||
}
|
||||
|
||||
nextVertexFormatIndex();
|
||||
}
|
||||
|
||||
private void addColor(ByteBuffer byteBuffer, int red, int green, int blue, int alpha)
|
||||
{
|
||||
int i = this.vertexCount * this.vertexFormat.getNextOffset() + this.vertexFormat.getOffset(this.vertexFormatIndex);
|
||||
|
||||
switch (this.vertexFormatElement.getType())
|
||||
{
|
||||
case FLOAT:
|
||||
byteBuffer.putFloat(i, red / 255.0F);
|
||||
byteBuffer.putFloat(i + 4, green / 255.0F);
|
||||
byteBuffer.putFloat(i + 8, blue / 255.0F);
|
||||
byteBuffer.putFloat(i + 12, alpha / 255.0F);
|
||||
break;
|
||||
case UINT:
|
||||
case INT:
|
||||
byteBuffer.putFloat(i, red);
|
||||
byteBuffer.putFloat(i + 4, green);
|
||||
byteBuffer.putFloat(i + 8, blue);
|
||||
byteBuffer.putFloat(i + 12, alpha);
|
||||
break;
|
||||
case USHORT:
|
||||
case SHORT:
|
||||
byteBuffer.putShort(i, (short)red);
|
||||
byteBuffer.putShort(i + 2, (short)green);
|
||||
byteBuffer.putShort(i + 4, (short)blue);
|
||||
byteBuffer.putShort(i + 6, (short)alpha);
|
||||
break;
|
||||
case UBYTE:
|
||||
case BYTE:
|
||||
|
||||
if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN)
|
||||
{
|
||||
// this is the one used currently
|
||||
byteBuffer.put(i, (byte)red);
|
||||
byteBuffer.put(i + 1, (byte)green);
|
||||
byteBuffer.put(i + 2, (byte)blue);
|
||||
byteBuffer.put(i + 3, (byte)alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
byteBuffer.put(i, (byte)alpha);
|
||||
byteBuffer.put(i + 1, (byte)blue);
|
||||
byteBuffer.put(i + 2, (byte)green);
|
||||
byteBuffer.put(i + 3, (byte)red);
|
||||
}
|
||||
}
|
||||
|
||||
nextVertexFormatIndex();
|
||||
}
|
||||
|
||||
private void nextVertexFormatIndex()
|
||||
{
|
||||
++this.vertexFormatIndex;
|
||||
this.vertexFormatIndex %= this.vertexFormat.getElementCount();
|
||||
this.vertexFormatElement = this.vertexFormat.getElement(this.vertexFormatIndex);
|
||||
|
||||
if (this.vertexFormatElement.getUsage() == VertexFormatElement.EnumUsage.PADDING)
|
||||
{
|
||||
this.nextVertexFormatIndex();
|
||||
}
|
||||
}
|
||||
|
||||
private void endVertex()
|
||||
{
|
||||
++this.vertexCount;
|
||||
growBuffer(this.vertexFormat.getNextOffset());
|
||||
}
|
||||
|
||||
private void growBuffer(int p_181670_1_)
|
||||
{
|
||||
//if (MathHelper.roundUp(p_181670_1_, 4) / 4 > this.rawIntBuffer.remaining() || this.vertexCount * this.vertexFormat.getNextOffset() + p_181670_1_ > this.byteBuffer.capacity())
|
||||
if (this.vertexCount * this.vertexFormat.getNextOffset() + p_181670_1_ > nearBuffer.capacity())
|
||||
{
|
||||
int i = nearBuffer.capacity();
|
||||
int j = i + MathHelper.roundUp(p_181670_1_, 2097152);
|
||||
// int k = this.rawIntBuffer.position();
|
||||
ByteBuffer directBytebuffer = GLAllocation.createDirectByteBuffer(j);
|
||||
nearBuffer.position(0);
|
||||
directBytebuffer.put(nearBuffer);
|
||||
directBytebuffer.rewind();
|
||||
nearBuffer = directBytebuffer;
|
||||
// this.rawFloatBuffer = buffer.asFloatBuffer().asReadOnlyBuffer();
|
||||
// this.rawIntBuffer = buffer.asIntBuffer();
|
||||
// this.rawIntBuffer.position(k);
|
||||
// this.rawShortBuffer = buffer.asShortBuffer();
|
||||
// this.rawShortBuffer.position(k << 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,667 +0,0 @@
|
||||
package com.backsun.lod.renderer;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.util.glu.Project;
|
||||
|
||||
import com.backsun.lod.objects.LodChunk;
|
||||
import com.backsun.lod.objects.LodDimension;
|
||||
import com.backsun.lod.util.LodConfig;
|
||||
import com.backsun.lod.util.ReflectionHandler;
|
||||
import com.backsun.lod.util.enums.ColorDirection;
|
||||
import com.backsun.lod.util.enums.FogDistance;
|
||||
import com.backsun.lod.util.enums.FogQuality;
|
||||
import com.backsun.lod.util.enums.LodLocation;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 2-13-2021
|
||||
*/
|
||||
public class LodRenderer
|
||||
{
|
||||
/** If true the LODs colors will be replaced with
|
||||
* a checkerboard, this can be used for debugging. */
|
||||
public boolean debugging = false;
|
||||
|
||||
private Minecraft mc;
|
||||
private float farPlaneDistance;
|
||||
// make sure this is an even number, or else it won't align with the chunk grid
|
||||
/** this is the total width of the LODs (I.E the diameter, not the radius) */
|
||||
private static final int LOD_CHUNK_DISTANCE_RADIUS = 6;
|
||||
|
||||
private Tessellator tessellator;
|
||||
private BufferBuilder bufferBuilder;
|
||||
|
||||
/**
|
||||
* This is an array of 0's used to clear old
|
||||
* ByteBuffers when they need to be rebuilt.
|
||||
*/
|
||||
byte[] clearBytes;
|
||||
|
||||
private ReflectionHandler reflectionHandler;
|
||||
|
||||
public LodDimension lodDimension = null;
|
||||
|
||||
|
||||
|
||||
private int maxNumbThreads = Runtime.getRuntime().availableProcessors();
|
||||
/** How many threads should be used for building the render buffer. */
|
||||
private int numbBufferThreads = maxNumbThreads;
|
||||
private ArrayList<BuildBufferThread> bufferThreads = new ArrayList<BuildBufferThread>();
|
||||
private volatile ByteBuffer[] nearBuffers = new ByteBuffer[maxNumbThreads];
|
||||
private volatile ByteBuffer[] farBuffers = new ByteBuffer[maxNumbThreads];
|
||||
private ExecutorService bufferThreadPool = Executors.newFixedThreadPool(maxNumbThreads);
|
||||
/*
|
||||
* this is the maximum number of bytes a buffer
|
||||
* would ever have to hold at once (this prevents the buffer
|
||||
* from having to resize and thus save performance)
|
||||
*/
|
||||
private int bufferMaxCapacity = 0;
|
||||
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int previousChunkRenderDistance = 0;
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int prevChunkX = 0;
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int prevChunkZ = 0;
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR;
|
||||
|
||||
/** if this is true the LODs should be regenerated */
|
||||
private boolean regen = false;
|
||||
|
||||
|
||||
|
||||
|
||||
public LodRenderer()
|
||||
{
|
||||
mc = Minecraft.getMinecraft();
|
||||
|
||||
// for some reason "Tessellator.getInstance()" won't work here, we have to create a new one
|
||||
tessellator = new Tessellator(2097152);
|
||||
bufferBuilder = tessellator.getBuffer();
|
||||
|
||||
reflectionHandler = new ReflectionHandler();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void drawLODs(LodDimension newDimension, float partialTicks)
|
||||
{
|
||||
if (reflectionHandler.fovMethod == null)
|
||||
{
|
||||
// don't continue if we can't get the
|
||||
// user's FOV
|
||||
return;
|
||||
}
|
||||
|
||||
if (reflectionHandler.fovMethod == null)
|
||||
{
|
||||
// we aren't able to get the user's
|
||||
// FOV, don't render anything
|
||||
return;
|
||||
}
|
||||
|
||||
// should the LODs be regenerated?
|
||||
if ((int)Minecraft.getMinecraft().player.posX / LodChunk.WIDTH != prevChunkX ||
|
||||
(int)Minecraft.getMinecraft().player.posZ / LodChunk.WIDTH != prevChunkZ ||
|
||||
previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks ||
|
||||
prevFogDistance != LodConfig.fogDistance ||
|
||||
lodDimension != newDimension)
|
||||
{
|
||||
regen = true;
|
||||
|
||||
prevChunkX = (int)Minecraft.getMinecraft().player.posX / LodChunk.WIDTH;
|
||||
prevChunkZ = (int)Minecraft.getMinecraft().player.posZ / LodChunk.WIDTH;
|
||||
prevFogDistance = LodConfig.fogDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nope, the player hasn't moved, the
|
||||
// render distance hasn't changed, and
|
||||
// the dimension is the same
|
||||
regen = false;
|
||||
}
|
||||
|
||||
lodDimension = newDimension;
|
||||
if (lodDimension == null)
|
||||
{
|
||||
// if there aren't any loaded LodChunks
|
||||
// don't try drawing anything
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// used for debugging and viewing how long different processes take
|
||||
mc.mcProfiler.endSection();
|
||||
mc.mcProfiler.startSection("LOD");
|
||||
mc.mcProfiler.startSection("LOD setup");
|
||||
@SuppressWarnings("unused")
|
||||
long startTime = System.nanoTime();
|
||||
if (LodConfig.drawCheckerBoard)
|
||||
{
|
||||
if (debugging != LodConfig.drawCheckerBoard)
|
||||
regen = true;
|
||||
debugging = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugging != LodConfig.drawCheckerBoard)
|
||||
regen = true;
|
||||
debugging = false;
|
||||
}
|
||||
|
||||
|
||||
// color setup
|
||||
int alpha = 255; // 0 - 255
|
||||
|
||||
Color red = new Color(255, 0, 0, alpha);
|
||||
Color black = new Color(0, 0, 0, alpha);
|
||||
Color white = new Color(255, 255, 255, alpha);
|
||||
@SuppressWarnings("unused")
|
||||
Color invisible = new Color(0,0,0,0);
|
||||
@SuppressWarnings("unused")
|
||||
Color error = new Color(255, 0, 225, alpha); // bright pink
|
||||
|
||||
|
||||
|
||||
// get the camera location
|
||||
Entity player = mc.player;
|
||||
double cameraX = player.lastTickPosX + (player.posX - player.lastTickPosX) * partialTicks;
|
||||
double cameraY = player.lastTickPosY + (player.posY - player.lastTickPosY) * partialTicks;
|
||||
double cameraZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialTicks;
|
||||
|
||||
|
||||
|
||||
|
||||
// determine how far the game's render distance is currently set
|
||||
int renderDistWidth = mc.gameSettings.renderDistanceChunks;
|
||||
farPlaneDistance = renderDistWidth * LodChunk.WIDTH;
|
||||
|
||||
// set how big the LODs will be and how far they will go
|
||||
int totalLength = (int) farPlaneDistance * LOD_CHUNK_DISTANCE_RADIUS * 2;
|
||||
int numbChunksWide = (totalLength / LodChunk.WIDTH);
|
||||
|
||||
// this seemingly useless math is required,
|
||||
// just using (int) camera doesn't work
|
||||
int playerXChunkOffset = ((int) cameraX / LodChunk.WIDTH) * LodChunk.WIDTH;
|
||||
int playerZChunkOffset = ((int) cameraZ / LodChunk.WIDTH) * LodChunk.WIDTH;
|
||||
// this where we will start drawing squares
|
||||
// (exactly half the total width)
|
||||
int startX = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerXChunkOffset;
|
||||
int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset;
|
||||
|
||||
|
||||
// this is where we store the LOD objects
|
||||
AxisAlignedBB lodArray[][] = new AxisAlignedBB[numbChunksWide][numbChunksWide];
|
||||
// this is where we store the color for each LOD object
|
||||
Color colorArray[][] = new Color[numbChunksWide][numbChunksWide];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// create the LODs //
|
||||
//=================//
|
||||
|
||||
if (regen)
|
||||
{
|
||||
mc.mcProfiler.endStartSection("LOD generation");
|
||||
|
||||
// x axis
|
||||
for (int i = 0; i < numbChunksWide; i++)
|
||||
{
|
||||
// z axis
|
||||
for (int j = 0; j < numbChunksWide; j++)
|
||||
{
|
||||
// skip the middle
|
||||
// (As the player moves some chunks will overlap or be missing,
|
||||
// this is just how chunk loading/unloading works. This can hopefully
|
||||
// be hidden with careful use of fog)
|
||||
int middle = (numbChunksWide / 2);
|
||||
if (RenderUtil.isCoordinateInLoadedArea(i, j, middle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// set where this square will be drawn in the world
|
||||
double xOffset = (LodChunk.WIDTH * i) + // offset by the number of LOD blocks
|
||||
startX; // offset so the center LOD block is centered underneath the player
|
||||
double yOffset = 0;
|
||||
double zOffset = (LodChunk.WIDTH * j) + startZ;
|
||||
|
||||
int chunkX = i + (startX / LodChunk.WIDTH);
|
||||
int chunkZ = j + (startZ / LodChunk.WIDTH);
|
||||
|
||||
LodChunk lod = lodDimension.getLodFromCoordinates(chunkX, chunkZ); // new LodChunk(); //
|
||||
if (lod == null)
|
||||
{
|
||||
// note: for some reason if any color or lod object are set here
|
||||
// it causes the game to use 100% gpu, all of it undefined in the debug menu
|
||||
// and drop to ~6 fps.
|
||||
// colorArray[i][j] = null;
|
||||
// lodArray[i][j] = null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Color c = new Color(
|
||||
(lod.colors[ColorDirection.TOP.value].getRed()),
|
||||
(lod.colors[ColorDirection.TOP.value].getGreen()),
|
||||
(lod.colors[ColorDirection.TOP.value].getBlue()),
|
||||
lod.colors[ColorDirection.TOP.value].getAlpha());
|
||||
|
||||
|
||||
|
||||
if (!debugging)
|
||||
{
|
||||
// add the color to the array
|
||||
colorArray[i][j] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if debugging draw the squares as a black and white checker board
|
||||
if ((chunkX + chunkZ) % 2 == 0)
|
||||
c = white;
|
||||
else
|
||||
c = black;
|
||||
// draw the first square as red
|
||||
if (i == 0 && j == 0)
|
||||
c = red;
|
||||
|
||||
colorArray[i][j] = c;
|
||||
}
|
||||
|
||||
|
||||
// add the new box to the array
|
||||
int topPoint = getLodHeightPoint(lod.top);
|
||||
int bottomPoint = getLodHeightPoint(lod.bottom);
|
||||
|
||||
// don't draw an LOD if it is empty
|
||||
if (topPoint == -1 && bottomPoint == -1)
|
||||
continue;
|
||||
|
||||
lodArray[i][j] = new AxisAlignedBB(0, bottomPoint, 0, LodChunk.WIDTH, topPoint, LodChunk.WIDTH).offset(xOffset, yOffset, zOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===========================//
|
||||
// GL settings for rendering //
|
||||
//===========================//
|
||||
|
||||
// set the required open GL settings
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glLineWidth(2.0f);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glEnable(GL11.GL_CULL_FACE);
|
||||
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
|
||||
GlStateManager.translate(-cameraX, -cameraY, -cameraZ);
|
||||
|
||||
setProjectionMatrix(partialTicks);
|
||||
setupLighting(partialTicks);
|
||||
setupBufferThreads(lodArray);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// rendering //
|
||||
//===========//
|
||||
|
||||
mc.mcProfiler.endStartSection("LOD build buffer");
|
||||
if (regen)
|
||||
generateLodBuffers(lodArray, colorArray, LodConfig.fogDistance);
|
||||
|
||||
switch(LodConfig.fogDistance)
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
mc.mcProfiler.endStartSection("LOD draw setup");
|
||||
setupFog(FogDistance.NEAR, reflectionHandler.getFogQuality());
|
||||
sendLodsToGpuAndDraw(nearBuffers);
|
||||
|
||||
mc.mcProfiler.endStartSection("LOD draw setup");
|
||||
setupFog(FogDistance.FAR, reflectionHandler.getFogQuality());
|
||||
sendLodsToGpuAndDraw(farBuffers);
|
||||
break;
|
||||
case NEAR:
|
||||
mc.mcProfiler.endStartSection("LOD draw setup");
|
||||
setupFog(FogDistance.NEAR, reflectionHandler.getFogQuality());
|
||||
sendLodsToGpuAndDraw(nearBuffers);
|
||||
break;
|
||||
case FAR:
|
||||
mc.mcProfiler.endStartSection("LOD draw setup");
|
||||
setupFog(FogDistance.FAR, reflectionHandler.getFogQuality());
|
||||
sendLodsToGpuAndDraw(farBuffers);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// cleanup //
|
||||
//=========//
|
||||
|
||||
mc.mcProfiler.endStartSection("LOD cleanup");
|
||||
|
||||
|
||||
// this must be done otherwise other parts of the screen may be drawn with a fog effect
|
||||
// IE the GUI
|
||||
GlStateManager.disableFog();
|
||||
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glDisable(GL11.GL_LIGHT1);
|
||||
GL11.glDisable(GL11.GL_COLOR_MATERIAL);
|
||||
|
||||
// change the perspective matrix back to prevent incompatibilities
|
||||
// with other mods that may render during forgeRenderLast
|
||||
Project.gluPerspective(reflectionHandler.getFov(mc, partialTicks, true), (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, this.farPlaneDistance * MathHelper.SQRT_2);
|
||||
|
||||
// this can't be called until after the buffers are built
|
||||
// because otherwise the buffers may be set to the wrong size
|
||||
previousChunkRenderDistance = mc.gameSettings.renderDistanceChunks;
|
||||
|
||||
|
||||
|
||||
// This is about how long this whole process should take
|
||||
// 16 ms = 60 hz
|
||||
@SuppressWarnings("unused")
|
||||
long endTime = System.nanoTime();
|
||||
|
||||
// end of profiler tracking
|
||||
mc.mcProfiler.endSection();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* draw an array of cubes (or squares) with the given colors.
|
||||
* @param lods bounding boxes to draw
|
||||
* @param colors color of each box to draw
|
||||
*/
|
||||
private void generateLodBuffers(AxisAlignedBB[][] lods, Color[][] colors, FogDistance fogDistance)
|
||||
{
|
||||
List<Future<NearFarBuffer>> bufferFutures = new ArrayList<>();
|
||||
// TODO this should change based on whether we are using near/far or both fog settings
|
||||
bufferMaxCapacity = (lods.length * lods.length * (6 * 4 * ((3 * 4) + (4 * 4)))) / numbBufferThreads;
|
||||
|
||||
for(int i = 0; i < numbBufferThreads; i++)
|
||||
{
|
||||
if (nearBuffers[i] == null || previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks)
|
||||
{
|
||||
nearBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity);
|
||||
nearBuffers[i].order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
farBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity);
|
||||
farBuffers[i].order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
clearBytes = new byte[bufferMaxCapacity];
|
||||
}
|
||||
|
||||
if (regen)
|
||||
{
|
||||
// this is the best way I could find to
|
||||
// overwrite the old data
|
||||
// (which needs to be done otherwise old
|
||||
// LODs may be drawn)
|
||||
nearBuffers[i].clear();
|
||||
nearBuffers[i].put(clearBytes);
|
||||
nearBuffers[i].clear();
|
||||
|
||||
farBuffers[i].clear();
|
||||
farBuffers[i].put(clearBytes);
|
||||
farBuffers[i].clear();
|
||||
}
|
||||
|
||||
int pos = bufferBuilder.getByteBuffer().position();
|
||||
nearBuffers[i].position(pos);
|
||||
farBuffers[i].position(pos);
|
||||
|
||||
bufferThreads.get(i).setNewData(nearBuffers[i], farBuffers[i], fogDistance, lods, colors, i, numbBufferThreads);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bufferFutures = bufferThreadPool.invokeAll(bufferThreads);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
// this should never happen, but just in case
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for(int i = 0; i < numbBufferThreads; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
nearBuffers[i] = bufferFutures.get(i).get().nearBuffer;
|
||||
farBuffers[i] = bufferFutures.get(i).get().farBuffer;
|
||||
}
|
||||
catch(CancellationException | ExecutionException| InterruptedException e)
|
||||
{
|
||||
// this should never happen, but just in case
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void sendLodsToGpuAndDraw(ByteBuffer[] buffers)
|
||||
{
|
||||
for(int i = 0; i < numbBufferThreads; i++)
|
||||
{
|
||||
int pos = bufferBuilder.getByteBuffer().position();
|
||||
buffers[i].position(pos);
|
||||
|
||||
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
|
||||
bufferBuilder.getByteBuffer().clear();
|
||||
bufferBuilder.putBulkData(buffers[i]);
|
||||
|
||||
mc.mcProfiler.endStartSection("LOD draw");
|
||||
tessellator.draw();
|
||||
mc.mcProfiler.endStartSection("LOD draw setup");
|
||||
|
||||
bufferBuilder.getByteBuffer().clear(); // this is required otherwise nothing is drawn
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// Setup Functions //
|
||||
//=================//
|
||||
|
||||
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
|
||||
{
|
||||
if(fogQuality == FogQuality.OFF)
|
||||
{
|
||||
GlStateManager.disableFog();
|
||||
return;
|
||||
}
|
||||
|
||||
if(fogDistance == FogDistance.NEAR_AND_FAR)
|
||||
{
|
||||
throw new IllegalArgumentException("setupFog only accepts NEAR or FAR fog distances.");
|
||||
}
|
||||
|
||||
// the multipliers are percentages
|
||||
// of the regular view distance.
|
||||
|
||||
if(fogDistance == FogDistance.NEAR)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
GlStateManager.setFogEnd(farPlaneDistance * 0.3f * LOD_CHUNK_DISTANCE_RADIUS);
|
||||
GlStateManager.setFogStart(farPlaneDistance * 0.35f * LOD_CHUNK_DISTANCE_RADIUS);
|
||||
}
|
||||
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
|
||||
|
||||
GlStateManager.setFogEnd(farPlaneDistance * 1.5f);
|
||||
GlStateManager.setFogStart(farPlaneDistance * 2.0f);
|
||||
}
|
||||
}
|
||||
else if(fogDistance == FogDistance.FAR)
|
||||
{
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
{
|
||||
GlStateManager.setFogStart(farPlaneDistance * 0.78f * LOD_CHUNK_DISTANCE_RADIUS);
|
||||
GlStateManager.setFogEnd(farPlaneDistance * 1.0f * LOD_CHUNK_DISTANCE_RADIUS);
|
||||
}
|
||||
else if(fogQuality == FogQuality.FAST)
|
||||
{
|
||||
GlStateManager.setFogStart(farPlaneDistance * 0.5f * LOD_CHUNK_DISTANCE_RADIUS);
|
||||
GlStateManager.setFogEnd(farPlaneDistance * 0.75f * LOD_CHUNK_DISTANCE_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.setFogDensity(0.1f);
|
||||
GlStateManager.enableFog();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create a new projection matrix and send it over to the GPU
|
||||
* @param partialTicks how many ticks into the frame we are
|
||||
* @return true if the matrix was successfully created and sent to the GPU, false otherwise
|
||||
*/
|
||||
private void setProjectionMatrix(float partialTicks)
|
||||
{
|
||||
// create a new view frustum so that the squares can be drawn outside the normal view distance
|
||||
GlStateManager.matrixMode(GL11.GL_PROJECTION);
|
||||
GlStateManager.loadIdentity();
|
||||
|
||||
// only continue if we can get the FOV
|
||||
if (reflectionHandler.fovMethod != null)
|
||||
{
|
||||
Project.gluPerspective(reflectionHandler.getFov(mc, partialTicks, true), (float) mc.displayWidth / (float) mc.displayHeight, 0.5F, farPlaneDistance * 12);
|
||||
}
|
||||
|
||||
// we weren't able to set up the projection matrix
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* setup the lighting to be used for the LODs
|
||||
*/
|
||||
private void setupLighting(float partialTicks)
|
||||
{
|
||||
GL11.glEnable(GL11.GL_COLOR_MATERIAL); // set the color to be used as the material (this allows lighting to be enabled)
|
||||
|
||||
// this isn't perfect right now, but it looks pretty good at 50% brightness
|
||||
float sunBrightness = mc.world.getSunBrightness(partialTicks) * mc.world.provider.getSunBrightnessFactor(partialTicks);
|
||||
float skyHasLight = mc.world.provider.hasSkyLight()? 1.0f : 0.15f;
|
||||
float gammaMultiplyer = (mc.gameSettings.gammaSetting * 0.5f + 0.5f);
|
||||
float lightStrength = sunBrightness * skyHasLight * gammaMultiplyer;
|
||||
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
|
||||
|
||||
ByteBuffer temp = ByteBuffer.allocateDirect(16);
|
||||
temp.order(ByteOrder.nativeOrder());
|
||||
GL11.glLight(GL11.GL_LIGHT1, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
|
||||
GL11.glEnable(GL11.GL_LIGHT1); // Enable the above lighting
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
|
||||
private void setupBufferThreads(AxisAlignedBB[][] lods)
|
||||
{
|
||||
if (numbBufferThreads != bufferThreads.size())
|
||||
{
|
||||
bufferMaxCapacity = (lods.length * lods.length * (6 * 4 * ((3 * 4) + (4 * 4)))) / numbBufferThreads;
|
||||
clearBytes = new byte[bufferMaxCapacity];
|
||||
|
||||
bufferThreads.clear();
|
||||
for(int i = 0; i < numbBufferThreads; i++)
|
||||
bufferThreads.add(new BuildBufferThread());
|
||||
regen = true;
|
||||
|
||||
for(int i = 0; i < maxNumbThreads; i++)
|
||||
{
|
||||
nearBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity);
|
||||
nearBuffers[i].order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
farBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity);
|
||||
farBuffers[i].order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns -1 if there are no valid points
|
||||
*/
|
||||
private int getLodHeightPoint(short[] heightPoints)
|
||||
{
|
||||
if (heightPoints[LodLocation.NE.value] != -1)
|
||||
return heightPoints[LodLocation.NE.value];
|
||||
if (heightPoints[LodLocation.NW.value] != -1)
|
||||
return heightPoints[LodLocation.NW.value];
|
||||
if (heightPoints[LodLocation.SE.value] != -1)
|
||||
return heightPoints[LodLocation.NE.value];
|
||||
return heightPoints[LodLocation.NE.value];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.backsun.lod.renderer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* This object is just a replacement for an array
|
||||
* to make things easier to understand in the LodRenderer
|
||||
* and BuildBufferThread.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-13-2021
|
||||
*/
|
||||
public class NearFarBuffer
|
||||
{
|
||||
public ByteBuffer nearBuffer;
|
||||
|
||||
public ByteBuffer farBuffer;
|
||||
|
||||
|
||||
NearFarBuffer(ByteBuffer newNearBuffer, ByteBuffer newFarBuffer)
|
||||
{
|
||||
nearBuffer = newNearBuffer;
|
||||
farBuffer = newFarBuffer;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.backsun.lod.renderer;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* This holds miscellaneous helper code
|
||||
* to be used in the rendering process.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2-13-2021
|
||||
*/
|
||||
public class RenderUtil
|
||||
{
|
||||
/**
|
||||
* Returns if the given coordinate is in the loaded area of the world.
|
||||
* @param centerCoordinate the center of the loaded world
|
||||
*/
|
||||
public static boolean isCoordinateInLoadedArea(int i, int j, int centerCoordinate)
|
||||
{
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
return (i >= centerCoordinate - mc.gameSettings.renderDistanceChunks
|
||||
&& i <= centerCoordinate + mc.gameSettings.renderDistanceChunks)
|
||||
&&
|
||||
(j >= centerCoordinate - mc.gameSettings.renderDistanceChunks
|
||||
&& j <= centerCoordinate + mc.gameSettings.renderDistanceChunks);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the coordinates that are in the center half of the given
|
||||
* 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius).
|
||||
*/
|
||||
public static boolean isCoordinateInNearFogArea(int i, int j, int lodRadius)
|
||||
{
|
||||
int halfRadius = lodRadius / 2;
|
||||
|
||||
return (i >= lodRadius - halfRadius
|
||||
&& i <= lodRadius + halfRadius)
|
||||
&&
|
||||
(j >= lodRadius - halfRadius
|
||||
&& j <= lodRadius + halfRadius);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.backsun.lod.util;
|
||||
|
||||
import com.backsun.lod.util.enums.FogDistance;
|
||||
|
||||
import net.minecraftforge.common.config.Config;
|
||||
import net.minecraftforge.common.config.ConfigManager;
|
||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
*/
|
||||
@Config(modid = Reference.MOD_ID)
|
||||
public class LodConfig
|
||||
{
|
||||
// save the config file when it is changed
|
||||
@Mod.EventBusSubscriber(modid = Reference.MOD_ID)
|
||||
private static class EventHandler
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void onConfigChanged(final ConfigChangedEvent.OnConfigChangedEvent event)
|
||||
{
|
||||
if (event.getModID().equals(Reference.MOD_ID))
|
||||
{
|
||||
ConfigManager.sync(Reference.MOD_ID, Config.Type.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Config.Comment(
|
||||
{"Enable LODs",
|
||||
"If true LODs will be drawn, if false LODs will "
|
||||
+ "not be rendered. However they will "
|
||||
+ "still be generated and stored in your world's save folder."})
|
||||
public static boolean drawLODs = true;
|
||||
|
||||
@Config.Comment(
|
||||
{"Fog Distance",
|
||||
"What distance should Fog be drawn on the LODs?"})
|
||||
public static FogDistance fogDistance = FogDistance.NEAR_AND_FAR;
|
||||
|
||||
@Config.Comment(
|
||||
{"Draw Debugging Checkerboard",
|
||||
"If false the LODs will draw with their normal world colors."
|
||||
+ "If true they will draw as a black and white checkerboard."
|
||||
+ "This can be used for debugging or imagining you are playing a "
|
||||
+ "giant game of chess ;)"})
|
||||
public static boolean drawCheckerBoard = false;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
package com.backsun.lod.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.backsun.lod.objects.LodChunk;
|
||||
import com.backsun.lod.objects.LodDimension;
|
||||
import com.backsun.lod.objects.LodRegion;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.storage.ISaveHandler;
|
||||
|
||||
/**
|
||||
* This object handles creating LodRegions
|
||||
* from files and saving LodRegion objects
|
||||
* to file.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 01-30-2021
|
||||
*/
|
||||
public class LodFileHandler
|
||||
{
|
||||
private LodDimension loadedRegion = null;
|
||||
public long regionLastWriteTime[][];
|
||||
|
||||
// String s = Minecraft.getMinecraftDir().getCanonicalPath() + "/saves/" + world.getSaveHandler().getSaveDirectoryName() + "/data/AA/World" + world.provider.dimensionId + ".dat";
|
||||
private String save_dir;
|
||||
public ISaveHandler saveHandler;
|
||||
|
||||
private final String FILE_NAME_PREFIX = "lod";
|
||||
private final String FILE_EXTENSION = ".txt";
|
||||
|
||||
private ExecutorService fileWritingThreadPool = Executors.newFixedThreadPool(1);
|
||||
/** Is true if the readyToReadAndWrite is false */
|
||||
private boolean waitingToSaveRegions = false;
|
||||
|
||||
|
||||
public LodFileHandler(ISaveHandler newSaveHandler, LodDimension newLoadedRegion)
|
||||
{
|
||||
saveHandler = newSaveHandler;
|
||||
|
||||
loadedRegion = newLoadedRegion;
|
||||
// these two variable are used in sync with the LodDimension
|
||||
regionLastWriteTime = new long[loadedRegion.getWidth()][loadedRegion.getWidth()];
|
||||
for(int i = 0; i < loadedRegion.getWidth(); i++)
|
||||
for(int j = 0; j < loadedRegion.getWidth(); j++)
|
||||
regionLastWriteTime[i][j] = -1;
|
||||
|
||||
if (saveHandler != null && saveHandler.getWorldDirectory() != null)
|
||||
save_dir = getWorldSaveDirectory();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// read from file //
|
||||
//================//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the LodRegion at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(int regionX, int regionZ)
|
||||
{
|
||||
// we don't currently support reading or writing
|
||||
// files when connected to a server
|
||||
if (!Minecraft.getMinecraft().isIntegratedServerRunning())
|
||||
return null;
|
||||
|
||||
if (!readyToReadAndWrite())
|
||||
return null;
|
||||
|
||||
String fileName = getFileNameForRegion(regionX, regionZ);
|
||||
|
||||
File f = new File(fileName);
|
||||
|
||||
if (!f.exists())
|
||||
{
|
||||
// there wasn't a file, don't
|
||||
// return anything
|
||||
return null;
|
||||
}
|
||||
|
||||
LodRegion region = new LodRegion(regionX, regionZ);
|
||||
|
||||
try
|
||||
{
|
||||
BufferedReader br = new BufferedReader(new FileReader(f));
|
||||
String s = br.readLine();
|
||||
|
||||
while(s != null && !s.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
// convert each line into an LOD object and add it to the region
|
||||
LodChunk lod = new LodChunk(s);
|
||||
|
||||
region.addLod(lod);
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
// we were unable to create this chunk
|
||||
// for whatever reason.
|
||||
// skip to the next chunk
|
||||
}
|
||||
|
||||
s = br.readLine();
|
||||
}
|
||||
|
||||
br.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// File not found
|
||||
|
||||
// or the buffered reader encountered a
|
||||
// problem reading the file
|
||||
return null;
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// Save to File //
|
||||
//==============//
|
||||
|
||||
|
||||
public synchronized void saveDirtyRegionsToFile()
|
||||
{
|
||||
// we don't currently support reading or writing
|
||||
// files when connected to a server
|
||||
if (!Minecraft.getMinecraft().isIntegratedServerRunning())
|
||||
return;
|
||||
|
||||
if (!readyToReadAndWrite())
|
||||
{
|
||||
// we aren't ready to read and write yet
|
||||
if(!waitingToSaveRegions)
|
||||
{
|
||||
waitingToSaveRegions = true;
|
||||
|
||||
// retry until we are able to read and write
|
||||
// then wake up the fileWritingThreadPool
|
||||
Thread retryReady = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
// check once every so often so see
|
||||
// if anything has changed so we can
|
||||
// start reading and writing files
|
||||
while(!readyToReadAndWrite())
|
||||
{
|
||||
this.wait(1000);
|
||||
// get the save handler again, if for some
|
||||
// reason the original handler was null
|
||||
saveHandler = Minecraft.getMinecraft().getIntegratedServer().getWorld(0).getSaveHandler();
|
||||
save_dir = getWorldSaveDirectory();
|
||||
}
|
||||
|
||||
// we can start writing files now
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||
waitingToSaveRegions = false;
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{ /* should never be called */}
|
||||
});
|
||||
|
||||
retryReady.run();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||
}
|
||||
private Thread saveDirtyRegionsThread = new Thread(() ->
|
||||
{
|
||||
for(int i = 0; i < loadedRegion.getWidth(); i++)
|
||||
{
|
||||
for(int j = 0; j < loadedRegion.getWidth(); j++)
|
||||
{
|
||||
if(loadedRegion.isRegionDirty[i][j])
|
||||
{
|
||||
saveRegionToDisk(loadedRegion.regions[i][j]);
|
||||
loadedRegion.isRegionDirty[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
waitingToSaveRegions = false;
|
||||
});
|
||||
|
||||
|
||||
private void saveRegionToDisk(LodRegion region)
|
||||
{
|
||||
if (!readyToReadAndWrite() || region == null)
|
||||
return;
|
||||
|
||||
// convert chunk coordinates to region
|
||||
// coordinates
|
||||
int x = region.x;
|
||||
int z = region.z;
|
||||
|
||||
File f = new File(getFileNameForRegion(x, z));
|
||||
|
||||
try
|
||||
{
|
||||
// make sure the file and folder exists
|
||||
if (!f.exists())
|
||||
if(!f.getParentFile().exists())
|
||||
f.getParentFile().mkdirs();
|
||||
f.createNewFile();
|
||||
|
||||
FileWriter fw = new FileWriter(f);
|
||||
|
||||
for(LodChunk[] chunkArray : region.getAllLods())
|
||||
for(LodChunk chunk : chunkArray)
|
||||
if(chunk != null)
|
||||
fw.write(chunk.toData() + "\n");
|
||||
|
||||
fw.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
System.err.println("LOD file write error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't ready to read and write.
|
||||
* @param regionX
|
||||
* @param regionZ
|
||||
*/
|
||||
private String getFileNameForRegion(int regionX, int regionZ)
|
||||
{
|
||||
if (!readyToReadAndWrite())
|
||||
return null;
|
||||
|
||||
return save_dir + "\\lod_data\\DIM" + loadedRegion.dimension.getId() + "\\" +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if this FileHandler is ready to read
|
||||
* and write files.
|
||||
*/
|
||||
public boolean readyToReadAndWrite()
|
||||
{
|
||||
return saveHandler != null && saveHandler.getWorldDirectory() != null &&
|
||||
save_dir != null && !save_dir.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If on single player this will return the name of the user's
|
||||
* world, if in multiplayer it will return the server name
|
||||
* and game version.
|
||||
*/
|
||||
public static String getWorldName()
|
||||
{
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
if(mc.isIntegratedServerRunning())
|
||||
{
|
||||
return mc.getIntegratedServer().getWorldName();
|
||||
}
|
||||
else
|
||||
{
|
||||
return mc.getCurrentServerData().serverName + "_version_" + mc.getCurrentServerData().gameVersion;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns null if there was an IO Exception
|
||||
*/
|
||||
private String getWorldSaveDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
return saveHandler.getWorldDirectory().getCanonicalPath();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.backsun.lod.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 04-16-2020
|
||||
*/
|
||||
public class Reference
|
||||
{
|
||||
/** the mod's identifier */
|
||||
public static final String MOD_ID = "lod";
|
||||
/** the mod's name */
|
||||
public static final String NAME = "LOD Mod";
|
||||
/** the mod's version */
|
||||
public static final String VERSION = "1.0";
|
||||
/** the version of minecraft this mod is built for */
|
||||
public static final String ACCEPTED_VERSIONS = "[1.12.2]";
|
||||
|
||||
/** where the client proxy class is */
|
||||
public static final String CLIENT_PROXY_CLASS = "com.backsun.lod.proxy.ClientProxy";
|
||||
/** where the common proxy class is*/
|
||||
public static final String COMMON_PROXY_CLASS = "com.backsun.lod.proxy.CommonProxy";
|
||||
|
||||
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package com.backsun.lod.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
|
||||
import com.backsun.lod.util.enums.FogQuality;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* This object is used to get variables from methods
|
||||
* where they are private.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 09-21-2020
|
||||
*/
|
||||
public class ReflectionHandler
|
||||
{
|
||||
public Method fovMethod = null;
|
||||
public Field ofFogField = null;
|
||||
|
||||
|
||||
public ReflectionHandler()
|
||||
{
|
||||
setupFovMethod();
|
||||
setupFogField();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This sets the "getFOVModifier" method from the
|
||||
* minecraft "EntityRenderer" class, so that we can get
|
||||
* the FOV of the player at any time.
|
||||
*
|
||||
* This is required since Minecraft is obfuscated so
|
||||
* we can't just look for 'getFOVModifier'
|
||||
* we have to search for it based on its parameters and
|
||||
* return type; which luckily are unique in the EntityRenderer
|
||||
* class.
|
||||
*/
|
||||
private void setupFovMethod()
|
||||
{
|
||||
// get every method from the entity renderer
|
||||
Method[] methods = Minecraft.getMinecraft().entityRenderer.getClass().getDeclaredMethods();
|
||||
|
||||
Class<?> returnType;
|
||||
Parameter[] params;
|
||||
Method returnMethod = null;
|
||||
|
||||
for(Method m : methods)
|
||||
{
|
||||
returnType = m.getReturnType();
|
||||
params = m.getParameters();
|
||||
|
||||
// see if this method has the same return type
|
||||
// and parameters as the 'getFOVModifier' method.
|
||||
if (returnType.equals(float.class) &&
|
||||
params.length == 2 &&
|
||||
params[0].getType().equals(float.class) &&
|
||||
params[1].getType().equals(boolean.class))
|
||||
{
|
||||
|
||||
// only accept the first method that we find
|
||||
if (returnMethod == null)
|
||||
{
|
||||
returnMethod = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we found a second method that matches the
|
||||
// outline we were looking for,
|
||||
// to prevent unexpected behavior
|
||||
// dont't set fovMethod.
|
||||
|
||||
// Since we aren't sure that
|
||||
// this method is the right
|
||||
// one, we may accidently mess
|
||||
// up the entityRender by invoking
|
||||
// it and we probably wouldn't get
|
||||
// the FOV from it anyway.
|
||||
|
||||
System.err.println("Error: a second method that matches the parameters and return typ of 'getFOVModifier' was found, LODs won't be rendered.");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only set the method once we have gone through
|
||||
// the whole array of methods, just to
|
||||
// make sure we have the right one.
|
||||
fovMethod = returnMethod;
|
||||
// set up the method so we can invoke it later
|
||||
fovMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to setupFovMethod.
|
||||
*/
|
||||
private void setupFogField()
|
||||
{
|
||||
// get every variable from the entity renderer
|
||||
Field[] vars = Minecraft.getMinecraft().gameSettings.getClass().getDeclaredFields();
|
||||
|
||||
// try and find the ofFogType variable in gameSettings
|
||||
for(Field f : vars)
|
||||
{
|
||||
if(f.getName().equals("ofFogType"))
|
||||
{
|
||||
ofFogField = f;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find the field,
|
||||
// either optifine isn't installed, or
|
||||
// optifine changed the name of the variable
|
||||
ofFogField = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get what type of fog optifine is currently set to render.
|
||||
*/
|
||||
public FogQuality getFogQuality()
|
||||
{
|
||||
if (ofFogField == null)
|
||||
{
|
||||
// either optifine isn't installed,
|
||||
// the variable name was changed, or
|
||||
// the setup method wasn't called yet.
|
||||
return FogQuality.OFF;
|
||||
}
|
||||
|
||||
int returnNum = 0;
|
||||
|
||||
try
|
||||
{
|
||||
returnNum = (int)ofFogField.get(Minecraft.getMinecraft().gameSettings);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
switch (returnNum)
|
||||
{
|
||||
case 0:
|
||||
return FogQuality.FAST;
|
||||
case 1:
|
||||
return FogQuality.FAST;
|
||||
case 2:
|
||||
return FogQuality.FANCY;
|
||||
case 3:
|
||||
return FogQuality.OFF;
|
||||
|
||||
default:
|
||||
return FogQuality.FAST;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the FOV used by the EntityRender.
|
||||
*/
|
||||
public float getFov(Minecraft mc, float partialTicks, boolean useFovSetting)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (float)fovMethod.invoke(mc.entityRenderer, new Object[]{partialTicks, useFovSetting});
|
||||
}
|
||||
catch(InvocationTargetException | IllegalAccessException | IllegalArgumentException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.backsun.lod.util.enums;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 10-17-2020
|
||||
*
|
||||
* TOP, N, S, E, W, BOTTOM
|
||||
*/
|
||||
public enum ColorDirection
|
||||
{
|
||||
// used for colors
|
||||
/** +Y */
|
||||
TOP(0),
|
||||
|
||||
/** -Z */
|
||||
N(1),
|
||||
/** +Z */
|
||||
S(2),
|
||||
|
||||
/** +X */
|
||||
E(3),
|
||||
/** -X */
|
||||
W(4),
|
||||
|
||||
/** -Y */
|
||||
BOTTOM(5);
|
||||
|
||||
public final int value;
|
||||
|
||||
private ColorDirection(int newValue)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.backsun.lod.util.enums;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 1-23-2021
|
||||
*/
|
||||
public enum DrawMode
|
||||
{
|
||||
/** Draw the LOD objects in groups.
|
||||
* <br>
|
||||
* <br>
|
||||
* Fancy fog: render the center and outside LOD
|
||||
* objects in 2 different groups.
|
||||
* <br>
|
||||
* Fast fog: render all LOD objects at one time.
|
||||
*/
|
||||
BATCH(0),
|
||||
|
||||
/** Draw each LOD objects separately.
|
||||
* <br>
|
||||
* <br>
|
||||
* Not suggested normally since draw calls are GPU expensive.
|
||||
*/
|
||||
INDIVIDUAL(5);
|
||||
|
||||
public final int value;
|
||||
|
||||
private DrawMode(int newValue)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.backsun.lod.util.enums;
|
||||
|
||||
/**
|
||||
* Near, far, or NEAR_AND_FAR.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
*/
|
||||
public enum FogDistance
|
||||
{
|
||||
/** valid for both fast and fancy fog qualities. */
|
||||
NEAR,
|
||||
/** valid for both fast and fancy fog qualities. */
|
||||
FAR,
|
||||
/** only looks good if the fog quality is set to Fancy. */
|
||||
NEAR_AND_FAR;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.backsun.lod.util.enums;
|
||||
|
||||
/**
|
||||
* fast, fancy, or off
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
*/
|
||||
public enum FogQuality
|
||||
{
|
||||
FAST,
|
||||
FANCY,
|
||||
OFF;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.backsun.lod.util.enums;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 1-20-2020
|
||||
*
|
||||
* NE, SE, SW, NW
|
||||
*/
|
||||
public enum LodLocation
|
||||
{
|
||||
// used for position
|
||||
|
||||
/** -Z, +X */
|
||||
NE(0),
|
||||
/** +Z, +X */
|
||||
SE(1),
|
||||
/** +Z, -X */
|
||||
SW(2),
|
||||
/** -Z, -X */
|
||||
NW(3);
|
||||
|
||||
public final int value;
|
||||
|
||||
private LodLocation(int newValue)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of 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;
|
||||
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.DeferredWorkQueue;
|
||||
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 7-3-2021
|
||||
*/
|
||||
@Mod(ModInfo.MODID)
|
||||
public class LodMain
|
||||
{
|
||||
public static LodMain instance;
|
||||
|
||||
public static ClientProxy client_proxy;
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void init(final FMLCommonSetupEvent event)
|
||||
{
|
||||
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, LodConfig.clientSpec);
|
||||
|
||||
|
||||
Thread setFancyFog = new Thread(() ->
|
||||
{
|
||||
LodRenderer.fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
|
||||
|
||||
if (!LodRenderer.fancyFogAvailable)
|
||||
{
|
||||
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that fancy fog options will not be available.");
|
||||
}
|
||||
});
|
||||
|
||||
// This will be run on the main thread when it is able.
|
||||
// If it wasn't run on the main thread GL.getCapabilities()
|
||||
// would fail.
|
||||
DeferredWorkQueue.runLater(setFancyFog);
|
||||
}
|
||||
|
||||
|
||||
public LodMain()
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
client_proxy = new ClientProxy();
|
||||
MinecraftForge.EVENT_BUS.register(client_proxy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void onServerStarting(FMLServerStartingEvent event)
|
||||
{
|
||||
// this is called when the server starts
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of 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;
|
||||
|
||||
/**
|
||||
* This file is similar to mcmod.info
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-31-2021
|
||||
*/
|
||||
public final class ModInfo
|
||||
{
|
||||
public static final String MODID = "lod";
|
||||
public static final String MODNAME = "Level of Detail";
|
||||
public static final String MODAPI = "LodAPI";
|
||||
public static final String VERSION = "a1.3.1";
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* This file is part of 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.builders;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.seibel.lod.builders.worldGeneration.LodChunkGenWorker;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.NearFarBuffer;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.biome.BiomeContainer;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.common.WorldWorkerManager;
|
||||
|
||||
/**
|
||||
* This object is used to create NearFarBuffer objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-19-2021
|
||||
*/
|
||||
public class LodBufferBuilder
|
||||
{
|
||||
private Minecraft mc;
|
||||
|
||||
/** This holds the thread used to generate new LODs off the main thread. */
|
||||
private ExecutorService genThread = Executors.newSingleThreadExecutor();
|
||||
|
||||
private LodChunkBuilder lodChunkBuilder;
|
||||
|
||||
/** The buffers that are used to create LODs using near fog */
|
||||
public volatile BufferBuilder buildableNearBuffer;
|
||||
/** The buffers that are used to create LODs using far fog */
|
||||
public volatile BufferBuilder buildableFarBuffer;
|
||||
|
||||
/** if this is true the LOD buffers are currently being
|
||||
* regenerated. */
|
||||
public volatile boolean generatingBuffers = false;
|
||||
|
||||
/** if this is true new LOD buffers have been generated
|
||||
* and are waiting to be swapped with the drawable buffers*/
|
||||
private volatile boolean switchBuffers = false;
|
||||
|
||||
/** This keeps track of how many chunk generation requests are on going.
|
||||
* This is to prevent chunks from being generated for a long time in an area
|
||||
* the player is no longer in. */
|
||||
public volatile AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0);
|
||||
|
||||
|
||||
|
||||
/** how many chunks to generate outside of the player's
|
||||
* view distance at one time. (or more specifically how
|
||||
* many requests to make at one time).
|
||||
* I multiply by 8 to make sure there is always a buffer of chunk requests,
|
||||
* to make sure the CPU is always busy and we can generate LODs as quickly as
|
||||
* possible. */
|
||||
public int maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8;
|
||||
|
||||
|
||||
public LodBufferBuilder(LodChunkBuilder newLodBuilder)
|
||||
{
|
||||
mc = Minecraft.getInstance();
|
||||
lodChunkBuilder = newLodBuilder;
|
||||
}
|
||||
|
||||
|
||||
private BiomeContainer biomeContainer = null;
|
||||
private LodDimension previousDimension = null;
|
||||
|
||||
|
||||
/**
|
||||
* Create a thread to asynchronously generate LOD buffers
|
||||
* centered around the given camera X and Z.
|
||||
* <br>
|
||||
* This method will write to the drawableNearBuffers and drawableFarBuffers.
|
||||
* <br>
|
||||
* After the buildable buffers have been generated they must be
|
||||
* swapped with the drawable buffers in the LodRenderer to be drawn.
|
||||
*/
|
||||
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
|
||||
double playerX, double playerZ, int numbChunksWide)
|
||||
{
|
||||
// only allow one generation process to happen at a time
|
||||
if (generatingBuffers)
|
||||
return;
|
||||
|
||||
if (buildableNearBuffer == null || buildableFarBuffer == null)
|
||||
throw new IllegalStateException("generateLodBuffersAsync was called before the buildableNearBuffer and buildableFarBuffer were created.");
|
||||
|
||||
if (previousDimension != lodDim)
|
||||
{
|
||||
biomeContainer = LodUtil.getServerWorldFromDimension(lodDim.dimension).getChunk(0, 0, ChunkStatus.EMPTY).getBiomes();
|
||||
previousDimension = lodDim;
|
||||
}
|
||||
|
||||
generatingBuffers = true;
|
||||
|
||||
|
||||
// this seemingly useless math is required,
|
||||
// just using (int) playerX/Z doesn't work
|
||||
int playerXChunkOffset = ((int) playerX / LodChunk.WIDTH) * LodChunk.WIDTH;
|
||||
int playerZChunkOffset = ((int) playerZ / LodChunk.WIDTH) * LodChunk.WIDTH;
|
||||
// this is where we will start drawing squares
|
||||
// (exactly half the total width)
|
||||
int startX = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerXChunkOffset;
|
||||
int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset;
|
||||
|
||||
|
||||
Thread thread = new Thread(()->
|
||||
{
|
||||
// index of the chunk currently being added to the
|
||||
// generation list
|
||||
int chunkGenIndex = 0;
|
||||
|
||||
ChunkPos[] chunksToGen = new ChunkPos[maxChunkGenRequests];
|
||||
// if we don't have a full number of chunks to generate in chunksToGen
|
||||
// we can top it off from the reserve
|
||||
ChunkPos[] chunksToGenReserve = new ChunkPos[maxChunkGenRequests];
|
||||
int minChunkDist = Integer.MAX_VALUE;
|
||||
ChunkPos playerChunkPos = new ChunkPos((int)playerX / LodChunk.WIDTH, (int)playerZ / LodChunk.WIDTH);
|
||||
|
||||
|
||||
// generate our new buildable buffers
|
||||
buildableNearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
buildableFarBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
|
||||
|
||||
|
||||
// x axis
|
||||
for (int i = 0; i < numbChunksWide; i++)
|
||||
{
|
||||
// z axis
|
||||
for (int j = 0; j < numbChunksWide; j++)
|
||||
{
|
||||
int chunkX = i + (startX / LodChunk.WIDTH);
|
||||
int chunkZ = j + (startZ / LodChunk.WIDTH);
|
||||
|
||||
// skip any chunks that Minecraft is going to render
|
||||
if(isCoordInCenterArea(i, j, (numbChunksWide / 2))
|
||||
&& renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// set where this square will be drawn in the world
|
||||
double xOffset = (LodChunk.WIDTH * i) + // offset by the number of LOD blocks
|
||||
startX; // offset so the center LOD block is centered underneath the player
|
||||
double yOffset = 0;
|
||||
double zOffset = (LodChunk.WIDTH * j) + startZ;
|
||||
|
||||
LodChunk lod = lodDim.getLodFromCoordinates(chunkX, chunkZ);
|
||||
|
||||
if (lod == null || lod.isLodEmpty())
|
||||
{
|
||||
// generate a new chunk if no chunk currently exists
|
||||
// and we aren't waiting on any other chunks to generate
|
||||
if (lod == null && numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
|
||||
{
|
||||
ChunkPos pos = new ChunkPos(chunkX, chunkZ);
|
||||
|
||||
|
||||
// can be used for debugging
|
||||
// if (chunksToGen == null)
|
||||
// {
|
||||
// chunkGenIndex = 0;
|
||||
// chunksToGen = new ChunkPos[maxChunkGenRequests];
|
||||
// }
|
||||
//
|
||||
// if (chunkGenIndex < maxChunkGenRequests)
|
||||
// {
|
||||
// chunksToGen[chunkGenIndex] = pos;
|
||||
// chunkGenIndex++;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
// determine if this position is closer to the player
|
||||
// than the previous
|
||||
int newDistance = playerChunkPos.getChessboardDistance(pos);
|
||||
|
||||
// issue #40
|
||||
// TODO optimize this code,
|
||||
// using the purely optimized code above we can achieve close to
|
||||
// 100% CPU utilization, this code generally achieves 40 - 50 %
|
||||
// I'm sure there is a better data structure for this.
|
||||
if (newDistance < minChunkDist)
|
||||
{
|
||||
// this chunk is closer, clear any previous
|
||||
// positions and update the new minimum distance
|
||||
minChunkDist = newDistance;
|
||||
|
||||
|
||||
// move all the old chunks into the reserve
|
||||
ChunkPos[] newReserve = new ChunkPos[maxChunkGenRequests];
|
||||
int oldToGenIndex = 0;
|
||||
int oldReserveIndex = 0;
|
||||
for(int tmpIndex = 0; tmpIndex < newReserve.length; tmpIndex++)
|
||||
{
|
||||
// we don't check if the boundaries are good since
|
||||
// the tmp array will always be the same length
|
||||
// as chunksToGen and chunksToGenReserve
|
||||
|
||||
if (chunksToGen[oldToGenIndex] != null)
|
||||
{
|
||||
// add all the closest chunks...
|
||||
newReserve[tmpIndex] = chunksToGen[oldToGenIndex];
|
||||
oldToGenIndex++;
|
||||
}
|
||||
else if (chunksToGenReserve[oldReserveIndex] != null)
|
||||
{
|
||||
// ...then add all the previous reserve chunks
|
||||
// (which are farther away)
|
||||
newReserve[tmpIndex] = chunksToGenReserve[oldToGenIndex];
|
||||
oldReserveIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have moved all the items from
|
||||
// the old chunksToGen and reserve
|
||||
break;
|
||||
}
|
||||
}
|
||||
chunksToGenReserve = newReserve;
|
||||
|
||||
|
||||
|
||||
chunkGenIndex = 0;
|
||||
chunksToGen = new ChunkPos[maxChunkGenRequests];
|
||||
chunksToGen[chunkGenIndex] = pos;
|
||||
chunkGenIndex++;
|
||||
}
|
||||
else if (newDistance <= minChunkDist)
|
||||
{
|
||||
// this chunk position is as close or closers than the
|
||||
// minimum distance
|
||||
if(chunkGenIndex < maxChunkGenRequests)
|
||||
{
|
||||
// we are still under the number of chunks to generate
|
||||
// add this position to the list
|
||||
chunksToGen[chunkGenIndex] = pos;
|
||||
chunkGenIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// don't render this null chunk
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
BufferBuilder currentBuffer = null;
|
||||
if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2))
|
||||
currentBuffer = buildableNearBuffer;
|
||||
else
|
||||
currentBuffer = buildableFarBuffer;
|
||||
|
||||
// get the desired LodTemplate and
|
||||
// add this LOD to the buffer
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
template.addLodToBuffer(currentBuffer, lodDim, lod,
|
||||
xOffset, yOffset, zOffset, renderer.debugging);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add a way for a server side mod to generate chunks requested here
|
||||
if(mc.hasSingleplayerServer())
|
||||
{
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
// make sure we have as many chunks to generate as we are allowed
|
||||
if (chunkGenIndex < maxChunkGenRequests)
|
||||
{
|
||||
for(int i = chunkGenIndex, j = 0; i < maxChunkGenRequests; i++, j++)
|
||||
{
|
||||
chunksToGen[i] = chunksToGenReserve[j];
|
||||
}
|
||||
}
|
||||
|
||||
// start chunk generation
|
||||
for(ChunkPos chunkPos : chunksToGen)
|
||||
{
|
||||
if(chunkPos == null)
|
||||
break;
|
||||
|
||||
// make sure we don't generate this chunk again
|
||||
lodDim.addLod(new LodChunk(chunkPos));
|
||||
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
|
||||
LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, renderer, lodChunkBuilder, this, lodDim, serverWorld, biomeContainer);
|
||||
WorldWorkerManager.addWorker(genWorker);
|
||||
}
|
||||
}
|
||||
|
||||
// finish the buffer building
|
||||
buildableNearBuffer.end();
|
||||
buildableFarBuffer.end();
|
||||
|
||||
// mark that the buildable buffers as ready to swap
|
||||
generatingBuffers = false;
|
||||
switchBuffers = true;
|
||||
});
|
||||
|
||||
genThread.execute(thread);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//====================//
|
||||
// generation helpers //
|
||||
//====================//
|
||||
|
||||
/**
|
||||
* Returns if the given coordinate is in the loaded area of the world.
|
||||
* @param centerCoordinate the center of the loaded world
|
||||
*/
|
||||
private boolean isCoordInCenterArea(int i, int j, int centerCoordinate)
|
||||
{
|
||||
return (i >= centerCoordinate - mc.options.renderDistance
|
||||
&& i <= centerCoordinate + mc.options.renderDistance)
|
||||
&&
|
||||
(j >= centerCoordinate - mc.options.renderDistance
|
||||
&& j <= centerCoordinate + mc.options.renderDistance);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the coordinates that are in the center half of the given
|
||||
* 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius).
|
||||
*/
|
||||
private static boolean isCoordinateInNearFogArea(int chunkX, int chunkZ, int lodRadius)
|
||||
{
|
||||
int halfRadius = lodRadius / 2;
|
||||
|
||||
return (chunkX >= lodRadius - halfRadius
|
||||
&& chunkX <= lodRadius + halfRadius)
|
||||
&&
|
||||
(chunkZ >= lodRadius - halfRadius
|
||||
&& chunkZ <= lodRadius + halfRadius);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===============================//
|
||||
// BufferBuilder related methods //
|
||||
//===============================//
|
||||
|
||||
|
||||
/**
|
||||
* Called from the LodRenderer to create the
|
||||
* BufferBuilders at the right size.
|
||||
*
|
||||
* @param bufferMaxCapacity
|
||||
*/
|
||||
public void setupBuffers(int bufferMaxCapacity)
|
||||
{
|
||||
buildableNearBuffer = new BufferBuilder(bufferMaxCapacity);
|
||||
buildableFarBuffer = new BufferBuilder(bufferMaxCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the drawable and buildable buffers and return
|
||||
* the old drawable buffers.
|
||||
* @param drawableNearBuffer
|
||||
* @param drawableFarBuffer
|
||||
*/
|
||||
public NearFarBuffer swapBuffers(BufferBuilder drawableNearBuffer, BufferBuilder drawableFarBuffer)
|
||||
{
|
||||
// swap the BufferBuilders
|
||||
BufferBuilder tmp = buildableNearBuffer;
|
||||
buildableNearBuffer = drawableNearBuffer;
|
||||
drawableNearBuffer = tmp;
|
||||
|
||||
tmp = buildableFarBuffer;
|
||||
buildableFarBuffer = drawableFarBuffer;
|
||||
drawableFarBuffer = tmp;
|
||||
|
||||
|
||||
// the buffers have been swapped
|
||||
switchBuffers = false;
|
||||
|
||||
return new NearFarBuffer(drawableNearBuffer, drawableFarBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is true the buildable near and far
|
||||
* buffers have been generated and are ready to be
|
||||
* sent to the LodRenderer.
|
||||
*/
|
||||
public boolean newBuffersAvaliable()
|
||||
{
|
||||
return switchBuffers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of 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.builders;
|
||||
|
||||
/**
|
||||
* This is used to easily configure how LodChunks are generated.
|
||||
* Generally this will only be used if we want to generate a
|
||||
* LodChunk using a incomplete Chunk, otherwise the defaults
|
||||
* work best for a fully generated chunk (IE has correct surface blocks).
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodBuilderConfig
|
||||
{
|
||||
/** default false */
|
||||
public boolean useHeightmap;
|
||||
/** default false */
|
||||
public boolean useBiomeColors;
|
||||
/** default true */
|
||||
public boolean useSolidBlocksInColorGen;
|
||||
|
||||
/** default settings for a normal chunk
|
||||
* useHeightmap = false
|
||||
* useBiomeColors = false
|
||||
* useSolidBlocksInColorGen = true
|
||||
*/
|
||||
public LodBuilderConfig()
|
||||
{
|
||||
useHeightmap = false;
|
||||
useBiomeColors = false;
|
||||
useSolidBlocksInColorGen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newUseHeightmap default = false
|
||||
* @param newUseBiomeColors default = false
|
||||
* @param newUseSolidBlocksInBiomeColor default = true
|
||||
*/
|
||||
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
|
||||
{
|
||||
useHeightmap = newUseHeightmap;
|
||||
useBiomeColors = newUseBiomeColors;
|
||||
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* This file is part of 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.builders;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.block.AbstractPlantBlock;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.BushBlock;
|
||||
import net.minecraft.block.FlowingFluidBlock;
|
||||
import net.minecraft.block.GrassBlock;
|
||||
import net.minecraft.block.IGrowable;
|
||||
import net.minecraft.block.LeavesBlock;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.ChunkSection;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
|
||||
/**
|
||||
* This object is in charge of creating Lod
|
||||
* related objects.
|
||||
* (specifically: Lod World, Dimension, Region, and Chunk objects)
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodChunkBuilder
|
||||
{
|
||||
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor();
|
||||
|
||||
/** Default size of any LOD regions we use */
|
||||
public int regionWidth = 5;
|
||||
|
||||
|
||||
public static final int CHUNK_DATA_WIDTH = LodChunk.WIDTH;
|
||||
public static final int CHUNK_SECTION_HEIGHT = LodChunk.WIDTH;
|
||||
|
||||
|
||||
|
||||
public LodChunkBuilder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void generateLodChunkAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
generateLodChunkAsync(chunk, new LodBuilderConfig(), lodWorld, world);
|
||||
}
|
||||
|
||||
public void generateLodChunkAsync(IChunk chunk, LodBuilderConfig config, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
// given a valid chunk object
|
||||
if (chunk == null)
|
||||
return;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
DimensionType dim = world.dimensionType();
|
||||
|
||||
LodChunk lod = generateLodFromChunk(chunk, config);
|
||||
|
||||
LodDimension lodDim;
|
||||
|
||||
if (lodWorld.getLodDimension(dim) == null)
|
||||
{
|
||||
lodDim = new LodDimension(dim, lodWorld, regionWidth);
|
||||
lodWorld.addLodDimension(lodDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
lodDim = lodWorld.getLodDimension(dim);
|
||||
}
|
||||
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
catch(IllegalArgumentException | NullPointerException e)
|
||||
{
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
}
|
||||
});
|
||||
lodGenThreadPool.execute(thread);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a LodChunk for a chunk in the given world.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* thrown if either the chunk or world is null.
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk) throws IllegalArgumentException
|
||||
{
|
||||
return generateLodFromChunk(chunk, new LodBuilderConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LodChunk for a chunk in the given world.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* thrown if either the chunk or world is null.
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException
|
||||
{
|
||||
if(chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
|
||||
|
||||
LodDetail detail = LodConfig.CLIENT.lodDetail.get();
|
||||
LodDataPoint[][] dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount];
|
||||
|
||||
for(int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||
{
|
||||
int startX = detail.startX[i];
|
||||
int startZ = detail.startZ[i];
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
Color color;
|
||||
|
||||
color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
|
||||
|
||||
|
||||
short height;
|
||||
short depth;
|
||||
|
||||
if (!config.useHeightmap)
|
||||
{
|
||||
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ);
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
int x = i / detail.dataPointLengthCount;
|
||||
int z = i % detail.dataPointLengthCount;
|
||||
|
||||
dataPoints[x][z] = new LodDataPoint(height, depth, color);
|
||||
}
|
||||
|
||||
return new LodChunk(chunk.getPos(), dataPoints, detail);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// constructor helpers //
|
||||
//=====================//
|
||||
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
* @param chunkSections
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
*/
|
||||
private short determineBottomPointForArea(ChunkSection[] chunkSections,
|
||||
int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
int numberOfBlocksRequired = ((endX-startX) * (endZ-startZ) / 2);
|
||||
|
||||
// search from the bottom up
|
||||
for(int section = 0; section < CHUNK_DATA_WIDTH; section++)
|
||||
{
|
||||
for(int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
|
||||
{
|
||||
int numberOfBlocksFound = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
if(isLayerValidLodPoint(chunkSections, section, y, x, z))
|
||||
{
|
||||
numberOfBlocksFound++;
|
||||
|
||||
if (numberOfBlocksFound >= numberOfBlocksRequired)
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we never found a valid LOD point
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private short determineBottomPoint(Heightmap heightmap)
|
||||
{
|
||||
// the heightmap only shows how high the blocks go, it
|
||||
// doesn't have any info about how low they go
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Find the highest valid point from the Top
|
||||
* @param chunkSections
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
*/
|
||||
private short determineHeightPointForArea(ChunkSection[] chunkSections,
|
||||
int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
int numberOfBlocksRequired = ((endX-startX) * (endZ-startZ) / 2);
|
||||
|
||||
// search from the top down
|
||||
for(int section = chunkSections.length - 1; section >= 0; section--)
|
||||
{
|
||||
for(int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
|
||||
{
|
||||
int numberOfBlocksFound = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
if(isLayerValidLodPoint(chunkSections, section, y, x, z))
|
||||
{
|
||||
numberOfBlocksFound++;
|
||||
|
||||
if (numberOfBlocksFound >= numberOfBlocksRequired)
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we never found a valid LOD point
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the highest point from the Top
|
||||
*/
|
||||
private short determineHeightPoint(Heightmap heightmap,
|
||||
int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
short highest = 0;
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
short newHeight = (short) heightmap.getFirstAvailable(x, z);
|
||||
if (newHeight > highest)
|
||||
highest = newHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the color for the given chunk using biome
|
||||
* water color, foliage color, and grass color.
|
||||
*
|
||||
* @param config_useSolidBlocksInColorGen <br>
|
||||
* If true we look down from the top of the <br>
|
||||
* chunk until we find a non-invisible block, and then use <br>
|
||||
* its color. If false we generate the color immediately for <br>
|
||||
* each x and z.
|
||||
* @param config_useBiomeColors <br>
|
||||
* If true use biome foliage, water, and grass colors, <br>
|
||||
* otherwise only use the block's material color
|
||||
*/
|
||||
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
boolean foundBlock = false;
|
||||
|
||||
// go top down
|
||||
for(int i = chunkSections.length - 1; !foundBlock && i >= 0; i--)
|
||||
{
|
||||
if( !foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen))
|
||||
{
|
||||
for(int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--)
|
||||
{
|
||||
int colorInt = 0;
|
||||
BlockState blockState = null;
|
||||
|
||||
if (chunkSections[i] != null)
|
||||
{
|
||||
blockState = chunkSections[i].getBlockState(x, y, z);
|
||||
colorInt = blockState.materialColor.col;
|
||||
}
|
||||
|
||||
if(colorInt == 0 && config.useSolidBlocksInColorGen)
|
||||
{
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.useBiomeColors)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z);
|
||||
|
||||
if (biome.getBiomeCategory() == Biome.Category.OCEAN ||
|
||||
biome.getBiomeCategory() == Biome.Category.RIVER)
|
||||
{
|
||||
if (config.useSolidBlocksInColorGen &&
|
||||
blockState != Blocks.WATER.defaultBlockState())
|
||||
{
|
||||
// non-water block
|
||||
colorInt = getColorForBlock(x, z, blockState, biome);
|
||||
}
|
||||
else
|
||||
{
|
||||
// water block
|
||||
colorInt = biome.getWaterColor();
|
||||
}
|
||||
}
|
||||
else if (biome.getBiomeCategory() == Biome.Category.EXTREME_HILLS)
|
||||
{
|
||||
colorInt = Blocks.STONE.defaultMaterialColor().col;
|
||||
}
|
||||
else if (biome.getBiomeCategory() == Biome.Category.ICY)
|
||||
{
|
||||
colorInt = LodUtil.colorToInt(Color.WHITE);
|
||||
}
|
||||
else if (biome.getBiomeCategory() == Biome.Category.THEEND)
|
||||
{
|
||||
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
|
||||
}
|
||||
else if (config.useSolidBlocksInColorGen)
|
||||
{
|
||||
colorInt = getColorForBlock(x, z, blockState, biome);
|
||||
}
|
||||
else
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z);
|
||||
colorInt = getColorForBlock(x,z, blockState, biome);
|
||||
}
|
||||
|
||||
|
||||
Color c = LodUtil.intToColor(colorInt);
|
||||
|
||||
red += c.getRed();
|
||||
green += c.getGreen();
|
||||
blue += c.getBlue();
|
||||
|
||||
numbOfBlocks++;
|
||||
|
||||
|
||||
// we found a valid block, skip to the
|
||||
// next x and z
|
||||
foundBlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(numbOfBlocks == 0)
|
||||
numbOfBlocks = 1;
|
||||
|
||||
red /= numbOfBlocks;
|
||||
green /= numbOfBlocks;
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns a color int for a given block.
|
||||
*/
|
||||
private int getColorForBlock(int x, int z, BlockState blockState, Biome biome)
|
||||
{
|
||||
int colorInt = 0;
|
||||
|
||||
if (blockState == Blocks.AIR.defaultBlockState())
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
|
||||
|
||||
else if (blockState.getBlock() instanceof LeavesBlock)
|
||||
{
|
||||
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
|
||||
colorInt = LodUtil.colorToInt(leafColor);
|
||||
}
|
||||
else if (blockState.getBlock() instanceof GrassBlock ||
|
||||
blockState.getBlock() instanceof AbstractPlantBlock ||
|
||||
blockState.getBlock() instanceof BushBlock ||
|
||||
blockState.getBlock() instanceof IGrowable)
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
else if (blockState.getBlock() instanceof FlowingFluidBlock)
|
||||
{
|
||||
colorInt = biome.getWaterColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
colorInt = blockState.materialColor.col;
|
||||
}
|
||||
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Is the layer between the given X, Z, and dataIndex
|
||||
* values a valid LOD point?
|
||||
*/
|
||||
private boolean isLayerValidLodPoint(
|
||||
ChunkSection[] chunkSections,
|
||||
int sectionIndex, int y,
|
||||
int x, int z)
|
||||
{
|
||||
if(chunkSections[sectionIndex] == null)
|
||||
{
|
||||
// this section doesn't have any blocks,
|
||||
// it is not a valid section
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(chunkSections[sectionIndex].getBlockState(x, y, z) != null &&
|
||||
chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of 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.builders.lodTemplates;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
* This is the abstract class used to create different
|
||||
* BufferBuilders.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public abstract class AbstractLodTemplate
|
||||
{
|
||||
public abstract void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging);
|
||||
|
||||
/** add the given position and color to the buffer */
|
||||
protected void addPosAndColor(BufferBuilder buffer,
|
||||
double x, double y, double z,
|
||||
int red, int green, int blue, int alpha)
|
||||
{
|
||||
buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex();
|
||||
}
|
||||
|
||||
/** Returns in bytes how much buffer memory is required
|
||||
* for one LOD object */
|
||||
public abstract int getBufferMemoryForSingleLod(LodDetail detail);
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This file is part of 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.builders.lodTemplates;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.enums.ColorDirection;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
|
||||
/**
|
||||
* Builds LODs as rectangular prisms.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class CubicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
|
||||
public CubicLodTemplate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk centerLod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
{
|
||||
AxisAlignedBB bbox;
|
||||
|
||||
// Add this LOD to the BufferBuilder
|
||||
// using the quality setting set by the config
|
||||
LodDetail detail = LodConfig.CLIENT.lodDetail.get();
|
||||
|
||||
int halfWidth = detail.dataPointWidth / 2;
|
||||
|
||||
for(int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||
{
|
||||
int startX = detail.startX[i];
|
||||
int startZ = detail.startZ[i];
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
// returns null if the lod is empty at the given location
|
||||
bbox = generateBoundingBox(
|
||||
centerLod.getAverageHeightOverArea(startX, startZ, endX, endZ),
|
||||
centerLod.getAverageDepthOverArea(startX, startZ, endX, endZ),
|
||||
detail.dataPointWidth,
|
||||
xOffset - (halfWidth / 2) + detail.startX[i],
|
||||
yOffset,
|
||||
zOffset - (halfWidth / 2) + detail.startZ[i]);
|
||||
|
||||
if (bbox != null)
|
||||
{
|
||||
addBoundingBoxToBuffer(buffer, bbox, centerLod.getAverageColorOverArea(startX, startZ, endX, endZ, debugging));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset)
|
||||
{
|
||||
// don't add an LOD if it is empty
|
||||
if (height == -1 && depth == -1)
|
||||
return null;
|
||||
|
||||
if (depth == height)
|
||||
{
|
||||
// if the top and bottom points are at the same height
|
||||
// render this LOD as 1 block thick
|
||||
height++;
|
||||
}
|
||||
|
||||
return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c)
|
||||
{
|
||||
// top (facing up)
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
// bottom (facing down)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
|
||||
// south (facing -Z)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
// north (facing +Z)
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
|
||||
// west (facing -X)
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
// east (facing +X)
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color[] c)
|
||||
{
|
||||
// top (facing up)
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c[ColorDirection.TOP.value].getRed(), c[ColorDirection.TOP.value].getGreen(), c[ColorDirection.TOP.value].getBlue(), c[ColorDirection.TOP.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c[ColorDirection.TOP.value].getRed(), c[ColorDirection.TOP.value].getGreen(), c[ColorDirection.TOP.value].getBlue(), c[ColorDirection.TOP.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c[ColorDirection.TOP.value].getRed(), c[ColorDirection.TOP.value].getGreen(), c[ColorDirection.TOP.value].getBlue(), c[ColorDirection.TOP.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c[ColorDirection.TOP.value].getRed(), c[ColorDirection.TOP.value].getGreen(), c[ColorDirection.TOP.value].getBlue(), c[ColorDirection.TOP.value].getAlpha());
|
||||
// bottom (facing down)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c[ColorDirection.BOTTOM.value].getRed(), c[ColorDirection.BOTTOM.value].getGreen(), c[ColorDirection.BOTTOM.value].getBlue(), c[ColorDirection.BOTTOM.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c[ColorDirection.BOTTOM.value].getRed(), c[ColorDirection.BOTTOM.value].getGreen(), c[ColorDirection.BOTTOM.value].getBlue(), c[ColorDirection.BOTTOM.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c[ColorDirection.BOTTOM.value].getRed(), c[ColorDirection.BOTTOM.value].getGreen(), c[ColorDirection.BOTTOM.value].getBlue(), c[ColorDirection.BOTTOM.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c[ColorDirection.BOTTOM.value].getRed(), c[ColorDirection.BOTTOM.value].getGreen(), c[ColorDirection.BOTTOM.value].getBlue(), c[ColorDirection.BOTTOM.value].getAlpha());
|
||||
|
||||
// south (facing -Z)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c[ColorDirection.SOUTH.value].getRed(), c[ColorDirection.SOUTH.value].getGreen(), c[ColorDirection.SOUTH.value].getBlue(), c[ColorDirection.SOUTH.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c[ColorDirection.SOUTH.value].getRed(), c[ColorDirection.SOUTH.value].getGreen(), c[ColorDirection.SOUTH.value].getBlue(), c[ColorDirection.SOUTH.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c[ColorDirection.SOUTH.value].getRed(), c[ColorDirection.SOUTH.value].getGreen(), c[ColorDirection.SOUTH.value].getBlue(), c[ColorDirection.SOUTH.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c[ColorDirection.SOUTH.value].getRed(), c[ColorDirection.SOUTH.value].getGreen(), c[ColorDirection.SOUTH.value].getBlue(), c[ColorDirection.SOUTH.value].getAlpha());
|
||||
// north (facing +Z)
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c[ColorDirection.NORTH.value].getRed(), c[ColorDirection.NORTH.value].getGreen(), c[ColorDirection.NORTH.value].getBlue(), c[ColorDirection.NORTH.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c[ColorDirection.NORTH.value].getRed(), c[ColorDirection.NORTH.value].getGreen(), c[ColorDirection.NORTH.value].getBlue(), c[ColorDirection.NORTH.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c[ColorDirection.NORTH.value].getRed(), c[ColorDirection.NORTH.value].getGreen(), c[ColorDirection.NORTH.value].getBlue(), c[ColorDirection.NORTH.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c[ColorDirection.NORTH.value].getRed(), c[ColorDirection.NORTH.value].getGreen(), c[ColorDirection.NORTH.value].getBlue(), c[ColorDirection.NORTH.value].getAlpha());
|
||||
|
||||
// west (facing -X)
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c[ColorDirection.WEST.value].getRed(), c[ColorDirection.WEST.value].getGreen(), c[ColorDirection.WEST.value].getBlue(), c[ColorDirection.WEST.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c[ColorDirection.WEST.value].getRed(), c[ColorDirection.WEST.value].getGreen(), c[ColorDirection.WEST.value].getBlue(), c[ColorDirection.WEST.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c[ColorDirection.WEST.value].getRed(), c[ColorDirection.WEST.value].getGreen(), c[ColorDirection.WEST.value].getBlue(), c[ColorDirection.WEST.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c[ColorDirection.WEST.value].getRed(), c[ColorDirection.WEST.value].getGreen(), c[ColorDirection.WEST.value].getBlue(), c[ColorDirection.WEST.value].getAlpha());
|
||||
// east (facing +X)
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c[ColorDirection.EAST.value].getRed(), c[ColorDirection.EAST.value].getGreen(), c[ColorDirection.EAST.value].getBlue(), c[ColorDirection.EAST.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c[ColorDirection.EAST.value].getRed(), c[ColorDirection.EAST.value].getGreen(), c[ColorDirection.EAST.value].getBlue(), c[ColorDirection.EAST.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c[ColorDirection.EAST.value].getRed(), c[ColorDirection.EAST.value].getGreen(), c[ColorDirection.EAST.value].getBlue(), c[ColorDirection.EAST.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c[ColorDirection.EAST.value].getRed(), c[ColorDirection.EAST.value].getGreen(), c[ColorDirection.EAST.value].getBlue(), c[ColorDirection.EAST.value].getAlpha());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail)
|
||||
{
|
||||
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints))) * howManyPointsPerLodChunk
|
||||
return (6 * 4 * (3 + 4)) * detail.dataPointLengthCount * detail.dataPointLengthCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of 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.builders.lodTemplates;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
* TODO DynamicLodTemplate
|
||||
* Chunks smoothly transition between
|
||||
* each other, unless a neighboring chunk
|
||||
* is at a significantly different height.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class DynamicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail) {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of 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.builders.lodTemplates;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
* TODO #21 TriangularLodTemplate
|
||||
* Builds each LOD chunk as a singular rectangular prism.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class TriangularLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail) {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
* This file is part of 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.builders.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.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodBuilderConfig;
|
||||
import com.seibel.lod.builders.LodChunkBuilder;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.WeightedList.Entry;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.palette.UpgradeData;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeContainer;
|
||||
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.blockstateprovider.WeightedBlockStateProvider;
|
||||
import net.minecraft.world.gen.feature.BlockClusterFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.ConfiguredFeature;
|
||||
import net.minecraft.world.gen.feature.DecoratedFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.FeatureSpread;
|
||||
import net.minecraft.world.gen.feature.FeatureSpreadConfig;
|
||||
import net.minecraft.world.gen.feature.IceAndSnowFeature;
|
||||
import net.minecraft.world.gen.feature.NoFeatureConfig;
|
||||
import net.minecraft.world.gen.placement.ConfiguredPlacement;
|
||||
import net.minecraft.world.gen.placement.DecoratedPlacementConfig;
|
||||
import net.minecraft.world.gen.placement.IPlacementConfig;
|
||||
import net.minecraft.world.gen.placement.NoiseDependant;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraft.world.server.ServerWorldLightManager;
|
||||
import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-9-2021
|
||||
*/
|
||||
public class LodChunkGenWorker implements IWorker
|
||||
{
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get());
|
||||
|
||||
private boolean threadStarted = false;
|
||||
private LodChunkGenThread thread;
|
||||
|
||||
/** If a configured feature fails for whatever reason,
|
||||
* add it to this list, this is to hopefully remove any
|
||||
* features that could cause issues down the line. */
|
||||
private static ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> configuredFeaturesToAvoid = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer,
|
||||
LodChunkBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld,
|
||||
BiomeContainer newBiomeContainer)
|
||||
{
|
||||
if (newServerWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ServerWorld");
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newLodRenderer,
|
||||
newLodBuilder, newLodBufferBuilder,
|
||||
newLodDimension, newServerWorld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
if (!threadStarted)
|
||||
{
|
||||
thread.lodBufferBuilder.numberOfChunksWaitingToGenerate.addAndGet(-1);
|
||||
|
||||
if (LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.SERVER)
|
||||
{
|
||||
// if we are using SERVER generation that has to be done
|
||||
// synchronously to prevent crashing and harmful
|
||||
// interactions with the normal world generator
|
||||
thread.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
threadStarted = true;
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWork()
|
||||
{
|
||||
return !threadStarted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private class LodChunkGenThread implements Runnable
|
||||
{
|
||||
public final ServerWorld serverWorld;
|
||||
public final LodDimension lodDim;
|
||||
public final LodChunkBuilder lodChunkBuilder;
|
||||
public final LodRenderer lodRenderer;
|
||||
private LodBufferBuilder lodBufferBuilder;
|
||||
|
||||
private ChunkPos pos;
|
||||
|
||||
public LodChunkGenThread(ChunkPos newPos, LodRenderer newLodRenderer,
|
||||
LodChunkBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
pos = newPos;
|
||||
lodRenderer = newLodRenderer;
|
||||
lodChunkBuilder = newLodBuilder;
|
||||
lodBufferBuilder = newLodBufferBuilder;
|
||||
lodDim = newLodDimension;
|
||||
serverWorld = newServerWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
if (lodDim.regionIsInRange(pos.x / LodRegion.SIZE, pos.z / LodRegion.SIZE))
|
||||
{
|
||||
// long startTime = System.currentTimeMillis();
|
||||
|
||||
switch(LodConfig.CLIENT.distanceGenerationMode.get())
|
||||
{
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
generateUsingBiomesOnly();
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
generateUsingSurface();
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
generateUsingFeatures();
|
||||
break;
|
||||
case SERVER:
|
||||
// very slow
|
||||
generateWithServer();
|
||||
break;
|
||||
}
|
||||
|
||||
lodRenderer.regenerateLODsNextFrame();
|
||||
|
||||
|
||||
// if (lodDim.getLodFromCoordinates(pos.x, pos.z) != null)
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(endTime - startTime);
|
||||
|
||||
}// if in range
|
||||
|
||||
}// run
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* takes about 2-5 ms
|
||||
*/
|
||||
private void generateUsingBiomesOnly()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, 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);
|
||||
ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
|
||||
|
||||
// generate fake height data for this LOD
|
||||
int seaLevel = serverWorld.getSeaLevel();
|
||||
|
||||
boolean simulateHeight = LodConfig.CLIENT.distanceGenerationMode.get() == 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, LodChunk.DEFAULT_HEIGHTMAP);
|
||||
for(int x = 0; x < LodChunk.WIDTH && !inTheEnd; x++)
|
||||
{
|
||||
for(int z = 0; z < LodChunk.WIDTH && !inTheEnd; z++)
|
||||
{
|
||||
if (simulateHeight)
|
||||
{
|
||||
// TODO use the biomes around each block to smooth out the transition
|
||||
|
||||
// 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, seaLevel, z).getBiomeCategory())
|
||||
{
|
||||
case NETHER:
|
||||
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
|
||||
break;
|
||||
|
||||
case EXTREME_HILLS:
|
||||
heightmap.setHeight(x, z, seaLevel + 30);
|
||||
break;
|
||||
case MESA:
|
||||
heightmap.setHeight(x, z, seaLevel + 20);
|
||||
break;
|
||||
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(LodChunk.DEFAULT_HEIGHTMAP, heightmap.getRawData());
|
||||
|
||||
|
||||
LodChunk lod;
|
||||
if (!inTheEnd)
|
||||
{
|
||||
lod = lodChunkBuilder.generateLodFromChunk(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 really bad.
|
||||
lod = new LodChunk(chunk.getPos().x, chunk.getPos().z);
|
||||
}
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* takes about 10 - 20 ms
|
||||
*/
|
||||
private void generateUsingSurface()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, 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);
|
||||
ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
|
||||
// this feature has proved 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);
|
||||
|
||||
|
||||
LodChunk lod = lodChunkBuilder.generateLodFromChunk(chunk);
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* takes about 15 - 20 ms
|
||||
*
|
||||
* Causes concurrentModification Exceptions,
|
||||
* which could cause instability or world generation bugs
|
||||
*/
|
||||
private void generateUsingFeatures()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, 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);
|
||||
ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
|
||||
|
||||
// get all the biomes in the chunk
|
||||
HashSet<Biome> biomes = new HashSet<>();
|
||||
for (int x = 0; x < LodChunk.WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodChunk.WIDTH; z++)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, serverWorld.getSeaLevel(), z);
|
||||
|
||||
// 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 = LodConfig.CLIENT.allowUnstableFeatureGeneration.get();
|
||||
|
||||
// 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(int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++)
|
||||
{
|
||||
for(Supplier<ConfiguredFeature<?, ?>> featureSupplier : featuresForState.get(featureStateToGenerate))
|
||||
{
|
||||
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
|
||||
|
||||
if (!allowUnstableFeatures &&
|
||||
configuredFeaturesToAvoid.containsKey(configuredFeature.hashCode()))
|
||||
continue;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
|
||||
}
|
||||
catch(ConcurrentModificationException e)
|
||||
{
|
||||
// This will happen. I'm not sure what to do about it
|
||||
// except pray that it doesn't effect 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)
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
catch(UnsupportedOperationException e)
|
||||
{
|
||||
// This will happen when the LodServerWorld
|
||||
// isn't able to return something that a feature
|
||||
// generator needs
|
||||
|
||||
if (!allowUnstableFeatures)
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// I'm not sure what happened, print to the log
|
||||
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
e.printStackTrace();
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
|
||||
if (!allowUnstableFeatures)
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate a Lod like normal
|
||||
LodChunk lod = lodChunkBuilder.generateLodFromChunk(chunk);
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* on pre generated chunks 0 - 1 ms
|
||||
* on un generated chunks 0 - 50 ms
|
||||
* with the median seeming to hover around 15 - 30 ms
|
||||
* and outliers in the 100 - 200 ms range
|
||||
*
|
||||
* Note this should not be multithreaded and does cause server/simulation lag
|
||||
* (Higher lag for generating than loading)
|
||||
*/
|
||||
private void generateWithServer()
|
||||
{
|
||||
lodChunkBuilder.generateLodChunkAsync(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), ClientProxy.getLodWorld(), serverWorld);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// Unused methods //
|
||||
//================//
|
||||
|
||||
// Sadly I wasn't able to get these to work,
|
||||
// they are here for documentation purposes
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
|
||||
private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config)
|
||||
{
|
||||
IPlacementConfig placementConfig = null;
|
||||
|
||||
Class oldConfigClass = config.decorator.config().getClass();
|
||||
|
||||
if (oldConfigClass == FeatureSpreadConfig.class)
|
||||
{
|
||||
FeatureSpreadConfig oldPlacementConfig = (FeatureSpreadConfig) config.decorator.config();
|
||||
FeatureSpread oldSpread = oldPlacementConfig.count();
|
||||
|
||||
placementConfig = new FeatureSpreadConfig(oldSpread);
|
||||
}
|
||||
else if(oldConfigClass == DecoratedPlacementConfig.class)
|
||||
{
|
||||
DecoratedPlacementConfig oldPlacementConfig = (DecoratedPlacementConfig) config.decorator.config();
|
||||
placementConfig = new DecoratedPlacementConfig(oldPlacementConfig.inner(), oldPlacementConfig.outer());
|
||||
}
|
||||
else if(oldConfigClass == NoiseDependant.class)
|
||||
{
|
||||
NoiseDependant oldPlacementConfig = (NoiseDependant) config.decorator.config();
|
||||
placementConfig = new NoiseDependant(oldPlacementConfig.noiseLevel, oldPlacementConfig.belowNoise, oldPlacementConfig.aboveNoise);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ClientProxy.LOGGER.debug("unkown decorated placement config: \"" + config.decorator.config().getClass() + "\"");
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
ConfiguredPlacement<?> newPlacement = new ConfiguredPlacement(config.decorator.decorator, placementConfig);
|
||||
return new DecoratedFeatureConfig(config.feature, newPlacement);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config)
|
||||
{
|
||||
WeightedBlockStateProvider provider = new WeightedBlockStateProvider();
|
||||
for(Entry<BlockState> state : ((WeightedBlockStateProvider) config.stateProvider).weightedList.entries)
|
||||
provider.weightedList.entries.add(state);
|
||||
|
||||
HashSet<Block> whitelist = new HashSet<>();
|
||||
for(Block block : config.whitelist)
|
||||
whitelist.add(block);
|
||||
|
||||
HashSet<BlockState> blacklist = new HashSet<>();
|
||||
for(BlockState state : config.blacklist)
|
||||
blacklist.add(state);
|
||||
|
||||
|
||||
BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer);
|
||||
builder.whitelist(whitelist);
|
||||
builder.blacklist(blacklist);
|
||||
builder.xspread(config.xspread);
|
||||
builder.yspread(config.yspread);
|
||||
builder.zspread(config.zspread);
|
||||
if(config.canReplace) { builder.canReplace(); }
|
||||
if(config.needWater) { builder.needWater(); }
|
||||
if(config.project) { builder.noProjection(); }
|
||||
builder.tries(config.tries);
|
||||
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stops the current genThreads if they are running
|
||||
* and then recreates the Executer service. <br><br>
|
||||
*
|
||||
* This is done to clear any outstanding tasks
|
||||
* that may exist after the player leaves their current world.
|
||||
* If this isn't done unfinished tasks may be left in the queue
|
||||
* preventing new LodChunks form being generated.
|
||||
*/
|
||||
public static void restartExecuterService()
|
||||
{
|
||||
if (genThreads != null && !genThreads.isShutdown())
|
||||
{
|
||||
genThreads.shutdownNow();
|
||||
}
|
||||
genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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 is needed to make biomes distinct)
|
||||
|
||||
Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* This file is part of 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.builders.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.objects.LodChunk;
|
||||
|
||||
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.Heightmap.Type;
|
||||
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.
|
||||
* This allows us to keep each LodChunk generation independent
|
||||
* of the actual ServerWorld, allowing us
|
||||
* to multithread generation.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-4-2021
|
||||
*/
|
||||
public class LodServerWorld implements ISeedReader {
|
||||
|
||||
public HashMap<Heightmap.Type, Heightmap> heightmaps = new HashMap<>();
|
||||
|
||||
public IChunk chunk;
|
||||
|
||||
public ServerWorld serverWorld;
|
||||
|
||||
public LodServerWorld(ServerWorld newServerWorld, IChunk newChunk)
|
||||
{
|
||||
chunk = newChunk;
|
||||
serverWorld = newServerWorld;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getHeight(Type heightmapType, int x, int z)
|
||||
{
|
||||
// make sure the block position is set relative to the chunk
|
||||
x = x % LodChunk.WIDTH;
|
||||
x = (x < 0) ? x + 16 : x;
|
||||
|
||||
z = z % LodChunk.WIDTH;
|
||||
z = (z < 0) ? z + 16 : z;
|
||||
|
||||
return chunk.getOrCreateHeightmapUnprimed(LodChunk.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getBiome(BlockPos pos)
|
||||
{
|
||||
return chunk.getBiomes().getNoiseBiome(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 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 DynamicRegistries registryAccess() {
|
||||
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 long getSeed() {
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
/**
|
||||
* TOP, NORTH, SOUTH, EAST, WEST, BOTTOM
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-17-2020
|
||||
*/
|
||||
public enum ColorDirection
|
||||
{
|
||||
// used for colors
|
||||
/** +Y */
|
||||
TOP(0),
|
||||
|
||||
/** -Z */
|
||||
NORTH(1),
|
||||
/** +Z */
|
||||
SOUTH(2),
|
||||
|
||||
/** +X */
|
||||
EAST(3),
|
||||
/** -X */
|
||||
WEST(4),
|
||||
|
||||
/** -Y */
|
||||
BOTTOM(5);
|
||||
|
||||
public final int value;
|
||||
|
||||
private ColorDirection(int newValue)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
/**
|
||||
* BIOME_ONLY <br>
|
||||
* BIOME_ONLY_SIMULATE_HEIGHT <br>
|
||||
* SURFACE <br>
|
||||
* FEATURES <br>
|
||||
* SERVER <br><br>
|
||||
*
|
||||
* In order of fastest to slowest.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public enum DistanceGenerationMode
|
||||
{
|
||||
/** Only generate the biomes and use biome
|
||||
* grass/foliage color, water color, or ice color
|
||||
* to generate the color.
|
||||
* Doesn't generate height, everything is shown at sea level.
|
||||
* Multithreaded - Fastest (2-5 ms) */
|
||||
BIOME_ONLY,
|
||||
|
||||
/**
|
||||
* Same as BIOME_ONLY, except instead
|
||||
* of always using sea level as the LOD height
|
||||
* different biome types (mountain, ocean, forest, etc.)
|
||||
* use predetermined heights to simulate having height data.
|
||||
*/
|
||||
BIOME_ONLY_SIMULATE_HEIGHT,
|
||||
|
||||
/** Generate the world surface,
|
||||
* this does NOT include caves, trees,
|
||||
* or structures.
|
||||
* Multithreaded - Faster (10-20 ms) */
|
||||
SURFACE,
|
||||
|
||||
/** Generate everything except structures.
|
||||
* NOTE: This may cause world generation bugs or instability,
|
||||
* since some features cause concurrentModification exceptions.
|
||||
* Multithreaded - Fast (15-20 ms) */
|
||||
FEATURES,
|
||||
|
||||
/** Ask the server to generate/load each chunk.
|
||||
* This is the most compatible, but causes server/simulation lag.
|
||||
* This will also show player made structures if you
|
||||
* are adding the mod to a pre-existing world.
|
||||
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) */
|
||||
SERVER;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
/**
|
||||
* NEAR, FAR, or NEAR_AND_FAR.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-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;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
/**
|
||||
* USE_OPTIFINE_FOG_SETTING, <br>
|
||||
* NEVER_DRAW_FOG, <br>
|
||||
* ALWAYS_DRAW_FOG_FAST, <br>
|
||||
* ALWAYS_DRAW_FOG_FANCY <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-03-2021
|
||||
*/
|
||||
public enum FogDrawOverride
|
||||
{
|
||||
/** Use whatever Fog setting optifine is using.
|
||||
* If optifine isn't installed this defaults to ALWAYS_DRAW_FOG. */
|
||||
USE_OPTIFINE_FOG_SETTING,
|
||||
|
||||
/** Never draw fog on the LODs */
|
||||
NEVER_DRAW_FOG,
|
||||
|
||||
/** Always draw fog on the LODs */
|
||||
ALWAYS_DRAW_FOG_FAST,
|
||||
|
||||
/** Always draw fog on the LODs */
|
||||
ALWAYS_DRAW_FOG_FANCY;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
/**
|
||||
* fast, fancy, or off
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
*/
|
||||
public enum FogQuality
|
||||
{
|
||||
FAST,
|
||||
FANCY,
|
||||
OFF;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
/**
|
||||
* NE, SE, SW, NW
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 1-20-2020
|
||||
*/
|
||||
public enum LodCorner
|
||||
{
|
||||
/** -Z, +X */
|
||||
NE(0),
|
||||
/** +Z, +X */
|
||||
SE(1),
|
||||
/** +Z, -X */
|
||||
SW(2),
|
||||
/** -Z, -X */
|
||||
NW(3);
|
||||
|
||||
public final int value;
|
||||
|
||||
private LodCorner(int newValue)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
|
||||
/**
|
||||
* single, double, quad, half, full
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-13-2021
|
||||
*/
|
||||
public enum LodDetail
|
||||
{
|
||||
/** render 1 LOD for each chunk */
|
||||
SINGLE(1),
|
||||
|
||||
/** render 4 LODs for each chunk */
|
||||
DOUBLE(2),
|
||||
|
||||
/** render 16 LODs for each chunk */
|
||||
QUAD(4),
|
||||
|
||||
/** render 64 LODs for each chunk */
|
||||
HALF(8),
|
||||
|
||||
/** render 256 LODs for each chunk */
|
||||
FULL(16);
|
||||
|
||||
|
||||
/** How many DataPoints should
|
||||
* be drawn per side per LodChunk */
|
||||
public final int dataPointLengthCount;
|
||||
/** How wide each LOD DataPoint is */
|
||||
public final int dataPointWidth;
|
||||
|
||||
/* Start/End X/Z give the block positions
|
||||
* for each individual dataPoint in a LodChunk */
|
||||
public final int[] startX;
|
||||
public final int[] startZ;
|
||||
|
||||
public final int[] endX;
|
||||
public final int[] endZ;
|
||||
|
||||
/** This is how many pieces of data should be expected
|
||||
* when creating a LodChunk with this detail level */
|
||||
public final int lodChunkStringDelimiterCount;
|
||||
|
||||
|
||||
|
||||
|
||||
private LodDetail(int newLengthCount)
|
||||
{
|
||||
dataPointLengthCount = newLengthCount;
|
||||
dataPointWidth = 16 / dataPointLengthCount;
|
||||
|
||||
if(newLengthCount == LodChunk.WIDTH)
|
||||
{
|
||||
// this is to prevent overflow
|
||||
newLengthCount = LodChunk.WIDTH - 1;
|
||||
}
|
||||
|
||||
startX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
endX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
|
||||
startZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
endZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
|
||||
|
||||
int index = 0;
|
||||
for(int x = 0; x < newLengthCount; x++)
|
||||
{
|
||||
for(int z = 0; z < newLengthCount; z++)
|
||||
{
|
||||
startX[index] = x * dataPointWidth;
|
||||
startZ[index] = z * dataPointWidth;
|
||||
|
||||
// special case for FULL
|
||||
if(dataPointWidth != 1)
|
||||
{
|
||||
endX[index] = (x*dataPointWidth) + dataPointWidth - 1;
|
||||
endZ[index] = (z*dataPointWidth) + dataPointWidth - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
endX[index] = (x*dataPointWidth) + dataPointWidth;
|
||||
endZ[index] = (z*dataPointWidth) + dataPointWidth;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lodChunkStringDelimiterCount = 2 + (dataPointLengthCount * dataPointLengthCount * LodDataPoint.NUMBER_OF_DELIMITERS);
|
||||
|
||||
}// constructor
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
import com.seibel.lod.builders.lodTemplates.AbstractLodTemplate;
|
||||
import com.seibel.lod.builders.lodTemplates.CubicLodTemplate;
|
||||
import com.seibel.lod.builders.lodTemplates.DynamicLodTemplate;
|
||||
import com.seibel.lod.builders.lodTemplates.TriangularLodTemplate;
|
||||
|
||||
/**
|
||||
* Cubic, Triangular, Dynamic
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public enum LodTemplate
|
||||
{
|
||||
// used for position
|
||||
|
||||
/** Chunks are rendered as
|
||||
* rectangular prisms. */
|
||||
CUBIC(new CubicLodTemplate()),
|
||||
|
||||
/** Chunks smoothly transition between
|
||||
* each other. */
|
||||
TRIANGULAR(new TriangularLodTemplate()),
|
||||
|
||||
/** Chunks smoothly transition between
|
||||
* each other, unless a neighboring chunk
|
||||
* is at a significantly different height. */
|
||||
DYNAMIC(new DynamicLodTemplate());
|
||||
|
||||
|
||||
public final AbstractLodTemplate template;
|
||||
|
||||
private LodTemplate(AbstractLodTemplate newTemplate)
|
||||
{
|
||||
template = newTemplate;
|
||||
}
|
||||
|
||||
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail)
|
||||
{
|
||||
return template.getBufferMemoryForSingleLod(detail);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of 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.enums;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* NE, SE, SW, NW <br>
|
||||
* NORTH, SOUTH, EAST, WEST, <br>
|
||||
* CENTER
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-30-2021
|
||||
*/
|
||||
public enum RelativeChunkPos
|
||||
{
|
||||
/** +X, -Z */
|
||||
NE(0, 1,-1),
|
||||
/** +X, +Z */
|
||||
SE(1, 1,1),
|
||||
/** -X, +Z */
|
||||
SW(2, -1,1),
|
||||
/** -X, -Z */
|
||||
NW(3, -1,-1),
|
||||
|
||||
/** -Z */
|
||||
NORTH(4, 0,-1),
|
||||
/** +Z */
|
||||
SOUTH(5, 0,1),
|
||||
|
||||
/** +X */
|
||||
EAST(6, 1,0),
|
||||
/** -X */
|
||||
WEST(7, -1,0),
|
||||
|
||||
CENTER(8, 0,0);
|
||||
|
||||
/** index used when referencing objects in an array */
|
||||
public final int index;
|
||||
|
||||
/** position relative to X CENTER */
|
||||
public final int x;
|
||||
/** position relative to Z CENTER */
|
||||
public final int z;
|
||||
|
||||
/** NORTH, SOUTH, EAST, WEST */
|
||||
public static EnumSet<RelativeChunkPos> ADJACENT = EnumSet.of(NORTH, SOUTH, EAST, WEST);
|
||||
/** NE, NW, SE, SW */
|
||||
public static EnumSet<RelativeChunkPos> DIAGONAL = EnumSet.of(NE, NW, SE, SW);
|
||||
|
||||
|
||||
public static EnumSet<RelativeChunkPos> NW_CORNER = EnumSet.of(WEST, NW, NORTH);
|
||||
public static EnumSet<RelativeChunkPos> NE_CORNER = EnumSet.of(EAST, NE, NORTH);
|
||||
public static EnumSet<RelativeChunkPos> SW_CORNER = EnumSet.of(WEST, SW, SOUTH);
|
||||
public static EnumSet<RelativeChunkPos> SE_CORNER = EnumSet.of(EAST, SE, SOUTH);
|
||||
|
||||
/** NW_CORNER, NE_CORNER, SW_CORNER, SE_CORNER <br>
|
||||
* Contains the 3 points surrounding a corner. */
|
||||
public static ArrayList<EnumSet<RelativeChunkPos>> CORNERS = new ArrayList<EnumSet<RelativeChunkPos>>( Arrays.asList(NW_CORNER, NE_CORNER, SW_CORNER, SE_CORNER) );
|
||||
|
||||
private RelativeChunkPos(int newIndex, int newX, int newZ)
|
||||
{
|
||||
index = newIndex;
|
||||
x = newX;
|
||||
z = newZ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* This file is part of 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.handlers;
|
||||
|
||||
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.ModInfo;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.FogDrawOverride;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-5-2021
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class LodConfig
|
||||
{
|
||||
public static class Client
|
||||
{
|
||||
public ForgeConfigSpec.BooleanValue drawLODs;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
|
||||
|
||||
public ForgeConfigSpec.BooleanValue debugMode;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> lodDetail;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
|
||||
|
||||
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
|
||||
|
||||
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
|
||||
|
||||
/** this is multiplied by the default view distance
|
||||
* to determine how far out to generate/render LODs */
|
||||
public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier;
|
||||
|
||||
|
||||
|
||||
Client(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
|
||||
|
||||
drawLODs = builder
|
||||
.comment("\n\n"
|
||||
+ " If false LODs will not be drawn, \n"
|
||||
+ " however they will still be generated \n"
|
||||
+ " and saved to file for later use. \n")
|
||||
.define("drawLODs", true);
|
||||
|
||||
fogDistance = builder
|
||||
.comment("\n\n"
|
||||
+ " At what distance should Fog be drawn on the LODs? \n"
|
||||
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
|
||||
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
|
||||
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
|
||||
|
||||
fogDrawOverride = builder
|
||||
.comment("\n\n"
|
||||
+ " When should fog be drawn? \n"
|
||||
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
|
||||
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
|
||||
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
|
||||
|
||||
debugMode = builder
|
||||
.comment("\n\n"
|
||||
+ " If false the LODs will draw with their normal world colors. \n"
|
||||
+ " If true they will draw as a black and white checkerboard. \n"
|
||||
+ " This can be used for debugging or imagining you are playing a \n"
|
||||
+ " giant game of chess ;) \n")
|
||||
.define("drawCheckerBoard", false);
|
||||
|
||||
lodTemplate = builder
|
||||
.comment("\n\n"
|
||||
+ " How should the LODs be drawn? \n"
|
||||
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
|
||||
+ " \n"
|
||||
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
|
||||
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
|
||||
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
|
||||
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
|
||||
.defineEnum("lodTemplate", LodTemplate.CUBIC);
|
||||
|
||||
lodDetail = builder
|
||||
.comment("\n\n"
|
||||
+ " How detailed should the LODs be? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n")
|
||||
.defineEnum("lodGeometryQuality", LodDetail.DOUBLE);
|
||||
|
||||
lodChunkRadiusMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " This is multiplied by the default view distance \n"
|
||||
+ " to determine how far out to generate/render LODs. \n"
|
||||
+ " A value of 2 means that there is 1 render distance worth \n"
|
||||
+ " of LODs in each cardinal direction. \n")
|
||||
.defineInRange("lodChunkRadiusMultiplier", 8, 2, 16);
|
||||
|
||||
distanceGenerationMode = builder
|
||||
.comment("\n\n"
|
||||
+ " Note: The times listed here are the amount of time it took \n"
|
||||
+ " 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"
|
||||
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
|
||||
+ " Only generate the biomes and use biome \n"
|
||||
+ " grass/foliage color, water color, or snow color \n"
|
||||
+ " to generate the color. \n"
|
||||
+ " Doesn't generate height, everything is shown at sea level. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
|
||||
+ " Same as 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"
|
||||
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
|
||||
+ " Generate the world surface, \n"
|
||||
+ " this does NOT include caves, trees, \n"
|
||||
+ " or structures. \n"
|
||||
+ " Multithreaded - Faster (10-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
|
||||
+ " Generate everything except structures. \n"
|
||||
+ " WARNING: This may cause world generation bugs or instability! \n"
|
||||
+ " Multithreaded - Fast (15-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
|
||||
+ " Ask the server to generate/load each chunk. \n"
|
||||
+ " This is the most compatible, but causes server/simulation lag. \n"
|
||||
+ " This will also show player made structures if you \n"
|
||||
+ " are adding the mod to a pre-existing world. \n"
|
||||
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
|
||||
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
|
||||
|
||||
allowUnstableFeatureGeneration = builder
|
||||
.comment("\n\n"
|
||||
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + "generation mode \n"
|
||||
+ " some features may not be thread safe, which could \n"
|
||||
+ " cause instability and crashes. \n"
|
||||
+ " By default (false) those features are skipped, \n"
|
||||
+ " improving stability, but decreasing how many features are \n"
|
||||
+ " actually generated. \n"
|
||||
+ " (for example: some tree generation is unstable, \n"
|
||||
+ " 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"
|
||||
+ " 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")
|
||||
.define("allowUnstableFeatureGeneration", false);
|
||||
|
||||
numberOfWorldGenerationThreads = builder
|
||||
.comment("\n\n"
|
||||
+ " This is how many threads are used when generating terrain. \n"
|
||||
+ " If you experience stuttering when generating terrain, decrease \n"
|
||||
+ " this number. If you want to increase LOD generation speed, \n"
|
||||
+ " increase the number. \n"
|
||||
+ " The max is the number of processors on your CPU. \n"
|
||||
+ "\n"
|
||||
+ " Requires a restart to take effect. \n"
|
||||
)
|
||||
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@link Path} to the configuration file of this mod
|
||||
*/
|
||||
private static final Path CONFIG_PATH =
|
||||
Paths.get("config", ModInfo.MODID + ".toml");
|
||||
|
||||
public static final ForgeConfigSpec clientSpec;
|
||||
public static final Client CLIENT;
|
||||
static {
|
||||
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
|
||||
clientSpec = specPair.getRight();
|
||||
CLIENT = specPair.getLeft();
|
||||
|
||||
// setup the config file
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH)
|
||||
.writingMode(WritingMode.REPLACE)
|
||||
.build();
|
||||
config.load();
|
||||
config.save();
|
||||
clientSpec.setConfig(config);
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLoad(final ModConfig.Loading configEvent)
|
||||
{
|
||||
LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onFileChange(final ModConfig.Reloading configEvent)
|
||||
{
|
||||
LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* This file is part of 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.handlers;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
|
||||
/**
|
||||
* This object handles creating LodRegions
|
||||
* from files and saving LodRegion objects
|
||||
* to file.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodDimensionFileHandler
|
||||
{
|
||||
/** This is what separates each piece of data */
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
|
||||
|
||||
private LodDimension loadedDimension = null;
|
||||
public long regionLastWriteTime[][];
|
||||
|
||||
private File dimensionDataSaveFolder;
|
||||
|
||||
/** lod */
|
||||
private final String FILE_NAME_PREFIX = "lod";
|
||||
/** .txt */
|
||||
private final String FILE_EXTENSION = ".txt";
|
||||
|
||||
/** This is the file version currently accepted by this
|
||||
* 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 = 2;
|
||||
|
||||
/** This is the string written before the file version */
|
||||
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
|
||||
|
||||
/** Allow saving asynchronously, but never try to save multiple regions
|
||||
* at a time */
|
||||
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor();
|
||||
|
||||
|
||||
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
|
||||
{
|
||||
if (newSaveFolder == null)
|
||||
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
|
||||
|
||||
dimensionDataSaveFolder = newSaveFolder;
|
||||
|
||||
loadedDimension = newLoadedDimension;
|
||||
// these two variable are used in sync with the LodDimension
|
||||
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
|
||||
for(int i = 0; i < loadedDimension.getWidth(); i++)
|
||||
for(int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
regionLastWriteTime[i][j] = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// read from file //
|
||||
//================//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the LodRegion at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(int regionX, int regionZ)
|
||||
{
|
||||
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, LodConfig.CLIENT.lodDetail.get());
|
||||
|
||||
File f = new File(fileName);
|
||||
|
||||
if (!f.exists())
|
||||
{
|
||||
// there wasn't a file, don't
|
||||
// return anything
|
||||
return null;
|
||||
}
|
||||
|
||||
LodRegion region = new LodRegion(regionX, regionZ);
|
||||
|
||||
try
|
||||
{
|
||||
BufferedReader br = new BufferedReader(new FileReader(f));
|
||||
String s = br.readLine();
|
||||
int fileVersion = -1;
|
||||
|
||||
if(s != null && !s.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
|
||||
}
|
||||
catch(NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a version
|
||||
// keep the version as -1
|
||||
fileVersion = -1;
|
||||
}
|
||||
|
||||
// check if this file can be read by this file handler
|
||||
if(fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
br.close();
|
||||
f.delete();
|
||||
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" File was been deleted.");
|
||||
|
||||
return null;
|
||||
}
|
||||
else if(fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// close the reader and ignore the file, we don't
|
||||
// want to accidently delete anything the user may want.
|
||||
br.close();
|
||||
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" this region will not be written to in order to protect the newer file.");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is no data in this file
|
||||
br.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// this file is a readable version, begin reading the file
|
||||
s = br.readLine();
|
||||
|
||||
while(s != null && !s.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
// convert each line into an LOD object and add it to the region
|
||||
LodChunk lod = new LodChunk(s, LodConfig.CLIENT.lodDetail.get());
|
||||
|
||||
region.addLod(lod);
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
// we were unable to create this chunk
|
||||
// for whatever reason.
|
||||
// skip to the next chunk
|
||||
ClientProxy.LOGGER.warn(e.getMessage());
|
||||
}
|
||||
|
||||
s = br.readLine();
|
||||
}
|
||||
|
||||
br.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// the buffered reader encountered a
|
||||
// problem reading the file
|
||||
return null;
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// Save to File //
|
||||
//==============//
|
||||
|
||||
/**
|
||||
* Save all dirty regions in this LodDimension to file.
|
||||
*/
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||
}
|
||||
|
||||
private Thread saveDirtyRegionsThread = new Thread(() ->
|
||||
{
|
||||
for(int i = 0; i < loadedDimension.getWidth(); i++)
|
||||
{
|
||||
for(int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
{
|
||||
if(loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null)
|
||||
{
|
||||
saveRegionToDisk(loadedDimension.regions[i][j]);
|
||||
loadedDimension.isRegionDirty[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a specific region to disk.<br>
|
||||
* Note: <br>
|
||||
* 1. If a file already exists for a newer version
|
||||
* the file won't be written.<br>
|
||||
* 2. This will save to the LodDimension that this
|
||||
* handler is associated with.
|
||||
*/
|
||||
private void saveRegionToDisk(LodRegion region)
|
||||
{
|
||||
// convert chunk coordinates to region
|
||||
// coordinates
|
||||
int x = region.x;
|
||||
int z = region.z;
|
||||
|
||||
File f = new File(getFileNameAndPathForRegion(x, z, LodConfig.CLIENT.lodDetail.get()));
|
||||
|
||||
try
|
||||
{
|
||||
// make sure the file and folder exists
|
||||
if (!f.exists())
|
||||
{
|
||||
// the file doesn't exist,
|
||||
// create it and the folder if need be
|
||||
if(!f.getParentFile().exists())
|
||||
f.getParentFile().mkdirs();
|
||||
f.createNewFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
// the file exists, make sure it
|
||||
// is the correct version.
|
||||
// (to make sure we don't overwrite a newer
|
||||
// version file if it exists)
|
||||
|
||||
BufferedReader br = new BufferedReader(new FileReader(f));
|
||||
String s = br.readLine();
|
||||
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||
|
||||
if(s != null && !s.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
|
||||
}
|
||||
catch(NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a correctly formated version
|
||||
// just overwrite the file
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
|
||||
// check if this file can be written to by the file handler
|
||||
if(fileVersion <= LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// we are good to continue and overwrite the old file
|
||||
}
|
||||
else //if(fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// don't write anything, we don't want to accidently
|
||||
// delete anything the user may want.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter fw = new FileWriter(f);
|
||||
|
||||
// add the version of this file
|
||||
fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n");
|
||||
|
||||
// add each LodChunk to the file
|
||||
for(LodChunk[] chunkArray : region.getAllLods())
|
||||
for(LodChunk chunk : chunkArray)
|
||||
if(chunk != null && !chunk.isPlaceholder())
|
||||
fw.write(chunk.toData() + "\n");
|
||||
|
||||
fw.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
ClientProxy.LOGGER.error("LOD file write error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't ready to read and write. <br><br>
|
||||
*
|
||||
* example: "lod.FULL.0.0.txt"
|
||||
*/
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ, LodDetail detail)
|
||||
{
|
||||
try
|
||||
{
|
||||
// saveFolder is something like
|
||||
// ".\Super Flat\DIM-1\data"
|
||||
// or
|
||||
// ".\Super Flat\data"
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + detail.toString() + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of 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.handlers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.seibel.lod.enums.FogQuality;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* This object is used to get variables from methods
|
||||
* where they are private. Specifically the fog setting
|
||||
* in Optifine.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-03-2021
|
||||
*/
|
||||
public class ReflectionHandler
|
||||
{
|
||||
private Minecraft mc = Minecraft.getInstance();
|
||||
public Field ofFogField = null;
|
||||
|
||||
public ReflectionHandler()
|
||||
{
|
||||
setupFogField();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Similar to setupFovMethod.
|
||||
*/
|
||||
private void setupFogField()
|
||||
{
|
||||
// get every variable from the entity renderer
|
||||
Field[] optionFields = mc.options.getClass().getDeclaredFields();
|
||||
|
||||
// try and find the ofFogType variable in gameSettings
|
||||
for(Field field : optionFields)
|
||||
{
|
||||
if(field.getName().equals("ofFogType"))
|
||||
{
|
||||
ofFogField = field;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find the field,
|
||||
// either optifine isn't installed, or
|
||||
// optifine changed the name of the variable
|
||||
ofFogField = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get what type of fog optifine is currently set to render.
|
||||
*/
|
||||
public FogQuality getFogQuality()
|
||||
{
|
||||
if (ofFogField == null)
|
||||
{
|
||||
// either optifine isn't installed,
|
||||
// the variable name was changed, or
|
||||
// the setup method wasn't called yet.
|
||||
return FogQuality.FANCY;
|
||||
}
|
||||
|
||||
int returnNum = 0;
|
||||
|
||||
try
|
||||
{
|
||||
returnNum = (int)ofFogField.get(mc.options);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
switch (returnNum)
|
||||
{
|
||||
// optifine's "default" option,
|
||||
// it should never be called in this case
|
||||
case 0:
|
||||
return FogQuality.FAST;
|
||||
|
||||
// normal options
|
||||
case 1:
|
||||
return FogQuality.FAST;
|
||||
case 2:
|
||||
return FogQuality.FANCY;
|
||||
case 3:
|
||||
return FogQuality.OFF;
|
||||
|
||||
default:
|
||||
return FogQuality.FAST;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of 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.mixin;
|
||||
|
||||
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.LodMain;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
|
||||
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 the LODs would render on top
|
||||
* of the normal terrain.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-29-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", cancellable = false)
|
||||
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", cancellable = false)
|
||||
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 (LodConfig.CLIENT.drawLODs.get() && renderType.equals(RenderType.solid()))
|
||||
LodMain.client_proxy.renderLods(previousPartialTicks);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
|
||||
/**
|
||||
* This object contains position
|
||||
* and color data for an LOD object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodChunk
|
||||
{
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
|
||||
|
||||
/** Width of a Minecraft Chunk */
|
||||
public static final int WIDTH = 16;
|
||||
|
||||
/** alpha used when drawing chunks in debug mode */
|
||||
private static final int DEBUG_ALPHA = 255; // 0 - 255
|
||||
private static final Color DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA);
|
||||
private static final Color DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA);
|
||||
private static final Color INVISIBLE = new Color(0,0,0,0);
|
||||
|
||||
/** If we ever have to use a heightmap for any reason, use this one. */
|
||||
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
|
||||
|
||||
|
||||
public LodDetail detail = LodDetail.SINGLE;
|
||||
|
||||
/** If this is set to true then toData will return
|
||||
* the empty string */
|
||||
public boolean dontSave = false;
|
||||
|
||||
// TODO store the DistanceGenerationMethod used for this chunk (so we can upgrade old chunks if we want to)
|
||||
|
||||
|
||||
/** The x coordinate of the chunk. */
|
||||
public int x = 0;
|
||||
/** The z coordinate of the chunk. */
|
||||
public int z = 0;
|
||||
|
||||
|
||||
/** This stores the height and color for each data point in the LodChunk */
|
||||
public LodDataPoint dataPoints[][];
|
||||
|
||||
/** If true then this LodChunk contains no data */
|
||||
private boolean empty = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create an empty, invisible, non-saving LodChunk at (0,0)
|
||||
*/
|
||||
public LodChunk()
|
||||
{
|
||||
dontSave = true;
|
||||
empty = true;
|
||||
|
||||
x = 0;
|
||||
z = 0;
|
||||
|
||||
detail = LodDetail.SINGLE;
|
||||
|
||||
dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount];
|
||||
|
||||
// fill with dummy data, to prevent null pointers
|
||||
for(int i = 0; i < detail.dataPointLengthCount; i++)
|
||||
for(int j = 0; j < detail.dataPointLengthCount; j++)
|
||||
dataPoints[i][j] = new LodDataPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty, invisible, non-saving LodChunk at the given ChunkPos
|
||||
*/
|
||||
public LodChunk(ChunkPos pos)
|
||||
{
|
||||
this();
|
||||
|
||||
x = pos.x;
|
||||
z = pos.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty, invisible, non-saving LodChunk at the given ChunkPos
|
||||
*/
|
||||
public LodChunk(int newX, int newZ)
|
||||
{
|
||||
this();
|
||||
|
||||
x = newX;
|
||||
z = newZ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an LodChunk from the string
|
||||
* generated by the toData method.
|
||||
*
|
||||
* @throws IllegalArgumentException if the data isn't valid to create a LodChunk
|
||||
* @throws NumberFormatException if any piece of data can't be converted at any point
|
||||
*/
|
||||
public LodChunk(String data, LodDetail newDetail) throws IllegalArgumentException, NumberFormatException
|
||||
{
|
||||
/*
|
||||
* data format:
|
||||
* x, z, dataPoint[0][0], dataPoint[0][1], ...
|
||||
* x, z, height, depth, rgb color data, height, depth, rgb....
|
||||
*
|
||||
* example:
|
||||
* 5,8, 4, 0, 255,255,255, 4, 0, 255, 255, 255, ...
|
||||
*/
|
||||
|
||||
dontSave = false;
|
||||
|
||||
// make sure there are the correct number of entries
|
||||
// in the data string
|
||||
int count = 0;
|
||||
detail = newDetail;
|
||||
|
||||
for(int i = 0; i < data.length(); i++)
|
||||
if(data.charAt(i) == DATA_DELIMITER)
|
||||
count++;
|
||||
|
||||
if(count != detail.lodChunkStringDelimiterCount)
|
||||
throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + detail.lodChunkStringDelimiterCount + ".");
|
||||
|
||||
|
||||
dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount];
|
||||
|
||||
// index we will use when going through the String
|
||||
int index = 0;
|
||||
int lastIndex = 0;
|
||||
|
||||
|
||||
|
||||
// x and z position
|
||||
index = data.indexOf(DATA_DELIMITER, 0);
|
||||
this.x = Integer.parseInt(data.substring(0,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.z = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
|
||||
// LodDataPoints
|
||||
for(int blockX = 0; blockX < detail.dataPointLengthCount; blockX++)
|
||||
{
|
||||
for(int blockZ = 0; blockZ < detail.dataPointLengthCount; blockZ++)
|
||||
{
|
||||
// height
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int height = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
|
||||
// depth
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int depth = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
|
||||
|
||||
// color
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
// get r,g,b
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
|
||||
String raw = "";
|
||||
switch(i)
|
||||
{
|
||||
case 0:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
red = Short.parseShort(raw);
|
||||
break;
|
||||
case 1:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
green = Short.parseShort(raw);
|
||||
break;
|
||||
case 2:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
blue = Short.parseShort(raw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dataPoints[blockX][blockZ] = new LodDataPoint((short)height, (short)depth, new Color(red, green, blue));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
empty = determineIfEmtpy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LodChunk from the given values.
|
||||
*/
|
||||
public LodChunk(ChunkPos pos, LodDataPoint[][] newDataPoints, LodDetail newDetail)
|
||||
{
|
||||
x = pos.x;
|
||||
z = pos.z;
|
||||
|
||||
dataPoints = newDataPoints;
|
||||
|
||||
dontSave = false;
|
||||
detail = newDetail;
|
||||
|
||||
empty = determineIfEmtpy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// misc functions //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* Returns true if this LodChunk is an emptyPlaceholder
|
||||
*/
|
||||
public boolean isPlaceholder()
|
||||
{
|
||||
return empty;
|
||||
}
|
||||
|
||||
public boolean isLodEmpty()
|
||||
{
|
||||
return empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this LOD is either invisible
|
||||
* from every direction or doesn't have a valid height.
|
||||
*/
|
||||
private boolean determineIfEmtpy()
|
||||
{
|
||||
for(LodDataPoint[] dataPointArray : dataPoints)
|
||||
{
|
||||
for(LodDataPoint dataPoint : dataPointArray)
|
||||
{
|
||||
if (dataPoint == null)
|
||||
continue;
|
||||
|
||||
// we don't check the depth since the
|
||||
// height should always be greater than or equal
|
||||
// to the depth
|
||||
if(dataPoint.height >= 0)
|
||||
{
|
||||
// the height is valid,
|
||||
|
||||
if (dataPoint.color == INVISIBLE)
|
||||
continue;
|
||||
|
||||
// the color and height are valid
|
||||
// this LodChunk isn't empty
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we checked everywhere, this LodChunk is empty
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// output //
|
||||
//========//
|
||||
|
||||
/**
|
||||
* Returns the data point for the given relative block position.
|
||||
*/
|
||||
public LodDataPoint getDataPointForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return dataPoints[blockX / detail.dataPointWidth][blockZ / detail.dataPointWidth];
|
||||
}
|
||||
|
||||
public Color getColorForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return getDataPointForBlockPos(blockX, blockZ).color;
|
||||
}
|
||||
|
||||
public short getHeightForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return getDataPointForBlockPos(blockX, blockZ).height;
|
||||
}
|
||||
|
||||
public short getDepthForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return getDataPointForBlockPos(blockX, blockZ).depth;
|
||||
}
|
||||
|
||||
|
||||
public Color getColor(int xIndex, int zIndex)
|
||||
{
|
||||
return dataPoints[xIndex][zIndex].color;
|
||||
}
|
||||
|
||||
public short getHeight(int xIndex, int zIndex)
|
||||
{
|
||||
return dataPoints[xIndex][zIndex].height;
|
||||
}
|
||||
|
||||
public short getDepth(int xIndex, int zIndex)
|
||||
{
|
||||
return dataPoints[xIndex][zIndex].depth;
|
||||
}
|
||||
|
||||
public short calculateHighestPoint()
|
||||
{
|
||||
short highest = 0;
|
||||
|
||||
for(int x = 0; x < detail.dataPointLengthCount; x++)
|
||||
{
|
||||
for(int z = 0; z < detail.dataPointLengthCount; z++)
|
||||
{
|
||||
if (getHeight(x,z) > highest)
|
||||
highest = getHeight(x,z);
|
||||
}
|
||||
}
|
||||
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
* @return
|
||||
*/
|
||||
public short getAverageHeightOverArea(int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
if (startX == endX || startZ == endZ)
|
||||
// we were given an area with 0 blocks in it
|
||||
return getHeightForBlockPos(startX,startZ);
|
||||
|
||||
int average = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
average += getHeightForBlockPos(x,z);
|
||||
|
||||
return (short) (average / ((endX - startX) * (endZ - startZ)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
* @return
|
||||
*/
|
||||
public short getAverageDepthOverArea(int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
if (startX == endX || startZ == endZ)
|
||||
// we were given an area with 0 blocks in it
|
||||
return getDepthForBlockPos(startX,startZ);
|
||||
|
||||
int average = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
average += getDepthForBlockPos(x,z);
|
||||
|
||||
return (short) (average / ((endX - startX) * (endZ - startZ)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the color of this LOD.
|
||||
*/
|
||||
public Color getAverageColorOverArea(int startX, int startZ, int endX, int endZ, boolean debugging)
|
||||
{
|
||||
if (startX == endX || startZ == endZ)
|
||||
// we were given an area with 0 blocks in it
|
||||
return getColorForBlockPos(startX,startZ);
|
||||
|
||||
|
||||
|
||||
int[] colorComponents = new int[3];
|
||||
|
||||
|
||||
if (debugging)
|
||||
{
|
||||
// draw the squares as a black and white,
|
||||
// like a checker board
|
||||
|
||||
// only return 1 color to prevent
|
||||
// getting back gray
|
||||
if ((startX + startZ) % 2 == 0)
|
||||
return DEBUG_WHITE;
|
||||
else
|
||||
return DEBUG_BLACK;
|
||||
}
|
||||
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
colorComponents = addColorToColorAverages(colorComponents, getColorForBlockPos(x,z));
|
||||
}
|
||||
}
|
||||
|
||||
int numbPoints = ((endX - startX) * (endZ - startZ));
|
||||
|
||||
return new Color(colorComponents[0]/numbPoints, colorComponents[1]/numbPoints, colorComponents[2]/numbPoints);
|
||||
}
|
||||
|
||||
|
||||
private int[] addColorToColorAverages(int[] colorAverages, Color colorToAdd)
|
||||
{
|
||||
// convert the colorToAdd to an int array
|
||||
float[] colorCompoments = new float[4];
|
||||
colorCompoments = colorToAdd.getColorComponents(colorCompoments);
|
||||
|
||||
// add each color component to the array
|
||||
for(int rgbIndex = 0; rgbIndex < 3; rgbIndex++)
|
||||
{
|
||||
// * 255 + 0.5 taken from the Color java class
|
||||
colorAverages[rgbIndex] += (int) (colorCompoments[rgbIndex] * 255 + 0.5);
|
||||
}
|
||||
|
||||
return colorAverages;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Outputs all data in a csv format
|
||||
* with the given delimiter, if
|
||||
* dontSave is false. <br><br>
|
||||
*
|
||||
* data format: <br>
|
||||
* x, z, dataPoint[0][0], dataPoint[0][1], ... <br>
|
||||
* x, z, height, depth, rgb color data, height, depth, rgb.... <br><br>
|
||||
*
|
||||
* example: <br>
|
||||
* 5,8, 4, 0, 255,255,255, 4, 0, 255, 255, 255, ...
|
||||
*/
|
||||
public String toData()
|
||||
{
|
||||
if (dontSave)
|
||||
return "";
|
||||
|
||||
String s = "";
|
||||
s += Integer.toString(x) + DATA_DELIMITER + Integer.toString(z) + DATA_DELIMITER;
|
||||
|
||||
for (int i = 0; i < dataPoints.length; i++)
|
||||
for (int j = 0; j < dataPoints[i].length; j++)
|
||||
s += dataPoints[i][j].toData();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "";
|
||||
|
||||
s += "x: " + x + " z: " + z + "\t";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
|
||||
/**
|
||||
* This stores the height and color
|
||||
* for a specific area in a LodChunk.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-19-2021
|
||||
*/
|
||||
public class LodDataPoint
|
||||
{
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
|
||||
|
||||
/** this is how many pieces of data are exported when toData is called */
|
||||
public static final int NUMBER_OF_DELIMITERS = 5;
|
||||
|
||||
private static final Color INVISIBLE = new Color(0,0,0,0);
|
||||
|
||||
/** highest point */
|
||||
public short height;
|
||||
|
||||
/** lowest point */
|
||||
public short depth;
|
||||
|
||||
/** The average color for the 6 cardinal directions */
|
||||
public Color color;
|
||||
|
||||
|
||||
/**
|
||||
* Creates and empty LodDataPoint
|
||||
*/
|
||||
public LodDataPoint()
|
||||
{
|
||||
height = -1;
|
||||
depth = -1;
|
||||
color = INVISIBLE;
|
||||
}
|
||||
|
||||
|
||||
public LodDataPoint(short newHeight, short newDepth, Color newColor)
|
||||
{
|
||||
height = newHeight;
|
||||
depth = newDepth;
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
public LodDataPoint(int newHeight, int newDepth, Color newColor)
|
||||
{
|
||||
height = (short) newHeight;
|
||||
depth = (short) newDepth;
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs all data in a csv format
|
||||
* with the given delimiter.
|
||||
* <br>
|
||||
* Exports data in the form:
|
||||
* <br>
|
||||
* height, depth, rgb color data
|
||||
*
|
||||
* <br>
|
||||
* example output:
|
||||
* <br>
|
||||
* 4, 0, 255,255,255,
|
||||
*/
|
||||
public String toData()
|
||||
{
|
||||
String s = Short.toString(height) + DATA_DELIMITER;
|
||||
|
||||
s += Short.toString(depth) + DATA_DELIMITER;
|
||||
|
||||
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = Short.toString(height) + DATA_DELIMITER;
|
||||
|
||||
s += Short.toString(depth) + DATA_DELIMITER;
|
||||
|
||||
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
+173
-68
@@ -1,39 +1,93 @@
|
||||
package com.backsun.lod.objects;
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
import com.backsun.lod.util.LodFileHandler;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
||||
/**
|
||||
* This object holds all loaded LOD regions
|
||||
* for a given dimension.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 01-31-2021
|
||||
* @version 06-27-2021
|
||||
*/
|
||||
public class LodDimension
|
||||
{
|
||||
public final DimensionType dimension;
|
||||
|
||||
private volatile int width; // if this ever changes make sure to update the halfWidth too
|
||||
private volatile int width;
|
||||
private volatile int halfWidth;
|
||||
|
||||
public LodRegion regions[][];
|
||||
public boolean isRegionDirty[][];
|
||||
public volatile LodRegion regions[][];
|
||||
public volatile boolean isRegionDirty[][];
|
||||
|
||||
private int centerX;
|
||||
private int centerZ;
|
||||
|
||||
private LodFileHandler rfHandler;
|
||||
private LodDimensionFileHandler fileHandler;
|
||||
|
||||
public LodDimension(DimensionType newDimension, int newMaxWidth)
|
||||
|
||||
public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newMaxWidth)
|
||||
{
|
||||
dimension = newDimension;
|
||||
width = newMaxWidth;
|
||||
|
||||
// dimension 0 works here since we are just looking for the save handler anyway
|
||||
rfHandler = new LodFileHandler(Minecraft.getMinecraft().getIntegratedServer().getWorld(0).getSaveHandler(), this);
|
||||
try
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
File saveDir;
|
||||
if(mc.hasSingleplayerServer())
|
||||
{
|
||||
// local world
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
|
||||
// provider needs a separate variable to prevent
|
||||
// the compiler from complaining
|
||||
ServerChunkProvider provider = serverWorld.getChunkSource();
|
||||
saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod");
|
||||
}
|
||||
else
|
||||
{
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level));
|
||||
}
|
||||
|
||||
fileHandler = new LodDimensionFileHandler(saveDir, this);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
// the file handler wasn't able to be created
|
||||
// we won't be able to read or write any files
|
||||
}
|
||||
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
@@ -50,7 +104,10 @@ public class LodDimension
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Move the center of this LodDimension and move all owned
|
||||
* regions over by the given x and z offset.
|
||||
*/
|
||||
public void move(int xOffset, int zOffset)
|
||||
{
|
||||
// if the x or z offset is equal to or greater than
|
||||
@@ -144,22 +201,16 @@ public class LodDimension
|
||||
}
|
||||
|
||||
|
||||
public int getCenterX()
|
||||
{
|
||||
return centerX;
|
||||
}
|
||||
|
||||
public int getCenterZ()
|
||||
{
|
||||
return centerZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the region at the given X and Z
|
||||
* <br>
|
||||
* Returns null if the region doesn't exist
|
||||
* or is outside the loaded area.
|
||||
*/
|
||||
public LodRegion getRegion(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - centerX) + halfWidth;
|
||||
@@ -201,69 +252,56 @@ public class LodDimension
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add the given LOD to this dimension at the coordinate
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinates it will be overwritten.
|
||||
*/
|
||||
public void addLod(LodChunk lod)
|
||||
{
|
||||
int regionX = (lod.x + centerX) / LodRegion.SIZE;
|
||||
int regionZ = (lod.z + centerZ) / LodRegion.SIZE;
|
||||
|
||||
// prevent issues if X/Z is negative and less than 16
|
||||
if (lod.x < 0)
|
||||
{
|
||||
regionX = (Math.abs(regionX) * -1) - 1;
|
||||
}
|
||||
if (lod.z < 0)
|
||||
{
|
||||
regionZ = (Math.abs(regionZ) * -1) - 1;
|
||||
}
|
||||
RegionPos pos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(lod.x, lod.z));
|
||||
|
||||
// don't continue if the region can't be saved
|
||||
if (!regionIsInRange(regionX, regionZ))
|
||||
if (!regionIsInRange(pos.x, pos.z))
|
||||
{
|
||||
return;
|
||||
|
||||
LodRegion region = getRegion(regionX, regionZ);
|
||||
}
|
||||
|
||||
LodRegion region = getRegion(pos.x, pos.z);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
// if no region exists, create it
|
||||
region = new LodRegion(regionX, regionZ);
|
||||
region = new LodRegion(pos.x, pos.z);
|
||||
setRegion(region);
|
||||
}
|
||||
|
||||
region.addLod(lod);
|
||||
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (regionX - centerX) + halfWidth;
|
||||
int zIndex = (regionZ - centerZ) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
|
||||
|
||||
|
||||
rfHandler.saveDirtyRegionsToFile();
|
||||
// don't save empty place holders to disk
|
||||
if (!lod.isPlaceholder() && fileHandler != null)
|
||||
{
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (pos.x - centerX) + halfWidth;
|
||||
int zIndex = (pos.z - centerZ) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
fileHandler.saveDirtyRegionsToFileAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if the LodChunk isn't loaded
|
||||
* Get the LodChunk 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 LodChunk getLodFromCoordinates(int chunkX, int chunkZ)
|
||||
{
|
||||
// (chunkX + centerX) % width
|
||||
int regionX = (chunkX + centerX) / LodRegion.SIZE;
|
||||
int regionZ = (chunkZ + centerZ) / LodRegion.SIZE;
|
||||
RegionPos pos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(chunkX, chunkZ));
|
||||
|
||||
// prevent issues if chunkX/Z is negative and less than width
|
||||
if (chunkX < 0)
|
||||
{
|
||||
regionX = (Math.abs(regionX) * -1) - 1;
|
||||
}
|
||||
if (chunkZ < 0)
|
||||
{
|
||||
regionZ = (Math.abs(regionZ) * -1) - 1;
|
||||
}
|
||||
LodRegion region = getRegion(pos.x, pos.z);
|
||||
|
||||
LodRegion region = getRegion(regionX, regionZ);
|
||||
|
||||
// TODO fix small render distances sometimes not having all regions loaded
|
||||
if(region == null)
|
||||
return null;
|
||||
|
||||
@@ -271,11 +309,16 @@ public class LodDimension
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the region at the given X and Z coordinates from the
|
||||
* RegionFileHandler.
|
||||
*/
|
||||
public LodRegion getRegionFromFile(int regionX, int regionZ)
|
||||
{
|
||||
return rfHandler.loadRegionFromFile(regionX, regionZ);
|
||||
if (fileHandler != null)
|
||||
return fileHandler.loadRegionFromFile(regionX, regionZ);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +326,7 @@ public class LodDimension
|
||||
* Returns whether the region at the given X and Z coordinates
|
||||
* is within the loaded range.
|
||||
*/
|
||||
private boolean regionIsInRange(int regionX, int regionZ)
|
||||
public boolean regionIsInRange(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - centerX) + halfWidth;
|
||||
int zIndex = (regionZ - centerZ) + halfWidth;
|
||||
@@ -293,6 +336,56 @@ public class LodDimension
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public int getCenterX()
|
||||
{
|
||||
return centerX;
|
||||
}
|
||||
|
||||
public int getCenterZ()
|
||||
{
|
||||
return centerZ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns how many non-null LodChunks
|
||||
* are stored in this LodDimension.
|
||||
*/
|
||||
public int getNumberOfLods()
|
||||
{
|
||||
int numbLods = 0;
|
||||
for (LodRegion[] regions : regions)
|
||||
{
|
||||
if(regions == null)
|
||||
continue;
|
||||
|
||||
for (LodRegion region : regions)
|
||||
{
|
||||
if(region == null)
|
||||
continue;
|
||||
|
||||
for(LodChunk[] lods : region.getAllLods())
|
||||
{
|
||||
if(lods == null)
|
||||
continue;
|
||||
|
||||
for(LodChunk lod : lods)
|
||||
{
|
||||
if (lod != null)
|
||||
numbLods++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numbLods;
|
||||
}
|
||||
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
@@ -311,6 +404,18 @@ public class LodDimension
|
||||
for(int j = 0; j < width; j++)
|
||||
isRegionDirty[i][j] = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "";
|
||||
|
||||
s += "dim: " + dimension.toString() + "\t";
|
||||
s += "(" + centerX + "," + centerZ + ")";
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-6
@@ -1,4 +1,21 @@
|
||||
package com.backsun.lod.objects;
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
/**
|
||||
* A LodRegion is the a 32x32
|
||||
@@ -7,7 +24,7 @@ package com.backsun.lod.objects;
|
||||
* one file in the file system.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 1-20-2021
|
||||
* @version 6-12-2021
|
||||
*/
|
||||
public class LodRegion
|
||||
{
|
||||
@@ -31,6 +48,11 @@ public class LodRegion
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the given LOD to this region at the coordinate
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinates it will be overwritten.
|
||||
*/
|
||||
public void addLod(LodChunk lod)
|
||||
{
|
||||
// we use ABS since LODs can be negative, but if they are
|
||||
@@ -43,20 +65,26 @@ public class LodRegion
|
||||
chunks[xIndex][zIndex] = lod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LodChunk at the given X and Z coordinates
|
||||
* in this region.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public LodChunk getLod(int chunkX, int chunkZ)
|
||||
{
|
||||
// since we add LOD's with ABS, we get them the same way
|
||||
int arrayX = Math.abs(chunkX % SIZE);
|
||||
int arrayZ = Math.abs(chunkZ % SIZE);
|
||||
|
||||
if(arrayX >= SIZE || arrayZ >= SIZE)
|
||||
return null;
|
||||
|
||||
return chunks[arrayX][arrayZ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns all LodChunks in this region
|
||||
*/
|
||||
public LodChunk[][] getAllLods()
|
||||
{
|
||||
return chunks;
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.world.DimensionType;
|
||||
|
||||
/**
|
||||
* This stores all LODs for a given world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 04-01-2021
|
||||
*/
|
||||
public class LodWorld
|
||||
{
|
||||
private String worldName;
|
||||
|
||||
private Map<DimensionType, LodDimension> lodDimensions;
|
||||
/** If true then the LOD world is setup and ready to use */
|
||||
private boolean isWorldLoaded = false;
|
||||
|
||||
public static final String NO_WORLD_LOADED = "No world loaded";
|
||||
|
||||
public LodWorld()
|
||||
{
|
||||
worldName = NO_WORLD_LOADED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the LodWorld with the given newWorldName. <br>
|
||||
* This should be done whenever loading a new world.
|
||||
* @param newWorldName
|
||||
*/
|
||||
public void selectWorld(String newWorldName)
|
||||
{
|
||||
if(newWorldName.isEmpty())
|
||||
{
|
||||
deselectWorld();
|
||||
return;
|
||||
}
|
||||
|
||||
if (worldName.equals(newWorldName))
|
||||
// don't recreate everything if we
|
||||
// didn't actually change worlds
|
||||
return;
|
||||
|
||||
worldName = newWorldName;
|
||||
lodDimensions = new Hashtable<DimensionType, LodDimension>();
|
||||
isWorldLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the worldName to "No world loaded"
|
||||
* and clear the lodDimensions Map. <br>
|
||||
* This should be done whenever unloaded a world.
|
||||
*/
|
||||
public void deselectWorld()
|
||||
{
|
||||
worldName = NO_WORLD_LOADED;
|
||||
lodDimensions = null;
|
||||
isWorldLoaded = false;
|
||||
}
|
||||
|
||||
|
||||
public void addLodDimension(LodDimension newStorage)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
|
||||
|
||||
lodDimensions.put(newStorage.dimension, newStorage);
|
||||
}
|
||||
|
||||
public LodDimension getLodDimension(DimensionType dimension)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
|
||||
|
||||
return lodDimensions.get(dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the max width in regions that each LodDimension
|
||||
* should use.
|
||||
*/
|
||||
public void resizeDimensionRegionWidth(int newWidth)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
|
||||
|
||||
for(DimensionType key : lodDimensions.keySet())
|
||||
lodDimensions.get(key).setRegionWidth(newWidth);
|
||||
}
|
||||
|
||||
|
||||
public boolean getIsWorldLoaded()
|
||||
{
|
||||
return isWorldLoaded;
|
||||
}
|
||||
|
||||
public String getWorldName()
|
||||
{
|
||||
return worldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "World name: " + worldName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
* This object is just a replacement for an array
|
||||
* to make things easier to understand in the LodRenderer
|
||||
* and BuildBufferThread.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 03-25-2021
|
||||
*/
|
||||
public class NearFarBuffer
|
||||
{
|
||||
public BufferBuilder nearBuffer;
|
||||
|
||||
public BufferBuilder farBuffer;
|
||||
|
||||
/**
|
||||
* @param newNearBuffer
|
||||
* @param newFarBuffer
|
||||
*/
|
||||
public NearFarBuffer(BufferBuilder newNearBuffer, BufferBuilder newFarBuffer)
|
||||
{
|
||||
nearBuffer = newNearBuffer;
|
||||
farBuffer = newFarBuffer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.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 NearOrFarSetting near = new NearOrFarSetting(FogDistance.NEAR);
|
||||
public NearOrFarSetting far = new NearOrFarSetting(FogDistance.FAR);
|
||||
|
||||
/** If true that means Minecraft is
|
||||
* rendering fog along side us */
|
||||
public boolean vanillaIsRenderingFog = true;
|
||||
|
||||
public NearFarFogSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This holds all relevant data to rendering fog at either
|
||||
* near or far distances.
|
||||
*/
|
||||
public class NearOrFarSetting
|
||||
{
|
||||
public FogQuality quality = FogQuality.FANCY;
|
||||
public FogDistance distance = FogDistance.FAR;
|
||||
|
||||
/** If true this section should render with fog */
|
||||
public boolean enabled = true;
|
||||
|
||||
public NearOrFarSetting(FogDistance newFogDistance)
|
||||
{
|
||||
distance = newFogDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of 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.objects;
|
||||
|
||||
/**
|
||||
* This object is similar to ChunkPos or BlockPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class RegionPos
|
||||
{
|
||||
public int x;
|
||||
public int z;
|
||||
|
||||
|
||||
/**
|
||||
* Default Constructor <br>
|
||||
*
|
||||
* Sets x and z to 0
|
||||
*/
|
||||
public RegionPos()
|
||||
{
|
||||
x = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
public RegionPos(int newX, int newZ)
|
||||
{
|
||||
x = newX;
|
||||
z = newZ;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* This file is part of 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.proxy;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodChunkBuilder;
|
||||
import com.seibel.lod.builders.worldGeneration.LodChunkGenWorker;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
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 this program.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 7-9-2021
|
||||
*/
|
||||
public class ClientProxy
|
||||
{
|
||||
public static final Logger LOGGER = LogManager.getLogger("LOD");
|
||||
|
||||
private static LodWorld lodWorld = new LodWorld();
|
||||
private static LodChunkBuilder lodChunkBuilder = new LodChunkBuilder();
|
||||
private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(lodChunkBuilder);
|
||||
private static LodRenderer renderer = new LodRenderer(lodBufferBuilder);
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
|
||||
public ClientProxy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// render event //
|
||||
//==============//
|
||||
|
||||
/**
|
||||
* Do any setup that is required to draw LODs
|
||||
* and then tell the LodRenderer to draw.
|
||||
*/
|
||||
public void renderLods(float partialTicks)
|
||||
{
|
||||
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
// update each regions' width to match the new render distance
|
||||
int newWidth = Math.max(4,
|
||||
// TODO is this logic good?
|
||||
(mc.options.renderDistance * LodChunk.WIDTH * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()) / LodRegion.SIZE
|
||||
);
|
||||
if (lodChunkBuilder.regionWidth != newWidth)
|
||||
{
|
||||
lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
lodChunkBuilder.regionWidth = newWidth;
|
||||
|
||||
// skip this frame, hopefully the lodWorld
|
||||
// should have everything set up by then
|
||||
return;
|
||||
}
|
||||
|
||||
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
|
||||
// offset the regions
|
||||
double playerX = mc.player.getX();
|
||||
double playerZ = mc.player.getZ();
|
||||
|
||||
int xOffset = ((int)playerX / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterX();
|
||||
int zOffset = ((int)playerZ / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterZ();
|
||||
|
||||
if (xOffset != 0 || zOffset != 0)
|
||||
{
|
||||
lodDim.move(xOffset, zOffset);
|
||||
}
|
||||
|
||||
|
||||
// for testing
|
||||
// LodConfig.CLIENT.drawLODs.set(true);
|
||||
// LodConfig.CLIENT.debugMode.set(false);
|
||||
|
||||
// LodConfig.CLIENT.lodDetail.set(LodDetail.DOUBLE);
|
||||
// LodConfig.CLIENT.lodChunkRadiusMultiplier.set(12);
|
||||
// LodConfig.CLIENT.fogDistance.set(FogDistance.FAR);
|
||||
// LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
|
||||
|
||||
// LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES);
|
||||
// LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
|
||||
// LOGGER.info(lodBufferBuilder.numberOfChunksWaitingToGenerate.get());
|
||||
|
||||
|
||||
// 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 profile to measure
|
||||
IProfiler profiler = mc.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
|
||||
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // restart terrain
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// forge events //
|
||||
//==============//
|
||||
|
||||
@SubscribeEvent
|
||||
public void chunkLoadEvent(ChunkEvent.Load event)
|
||||
{
|
||||
lodChunkBuilder.generateLodChunkAsync(event.getChunk(), lodWorld, event.getWorld());
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldLoadEvent(WorldEvent.Load event)
|
||||
{
|
||||
// the player just loaded a new world/dimension
|
||||
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
|
||||
// make sure the correct LODs are being rendered
|
||||
// (if this isn't done the previous world's LODs may be drawn)
|
||||
renderer.regenerateLODsNextFrame();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldUnloadEvent(WorldEvent.Unload event)
|
||||
{
|
||||
// the player just unloaded a world/dimension
|
||||
|
||||
if(mc.getConnection().getLevel() == null)
|
||||
{
|
||||
// if this isn't done unfinished tasks may be left in the queue
|
||||
// preventing new LodChunks form being generated
|
||||
LodChunkGenWorker.restartExecuterService();
|
||||
|
||||
lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0);
|
||||
// the player has disconnected from a server
|
||||
lodWorld.deselectWorld();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void blockChangeEvent(BlockEvent event)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// recreate the LOD where the blocks were changed
|
||||
lodChunkBuilder.generateLodChunkAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// public getters //
|
||||
//================//
|
||||
|
||||
public static LodWorld getLodWorld()
|
||||
{
|
||||
return lodWorld;
|
||||
}
|
||||
|
||||
public static LodChunkBuilder getLodBuilder()
|
||||
{
|
||||
return lodChunkBuilder;
|
||||
}
|
||||
|
||||
public static LodRenderer getRenderer()
|
||||
{
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
* This file is part of 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.render;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.NVFogDistance;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.FogDrawOverride;
|
||||
import com.seibel.lod.enums.FogQuality;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.handlers.ReflectionHandler;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.NearFarBuffer;
|
||||
import com.seibel.lod.objects.NearFarFogSettings;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.player.ClientPlayerEntity;
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.potion.Effects;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
|
||||
|
||||
/**
|
||||
* This is where all the magic happens. <br>
|
||||
* This is where LODs are draw to the world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 07-4-2021
|
||||
*/
|
||||
public class LodRenderer
|
||||
{
|
||||
/** this is the light used when rendering the LODs,
|
||||
* it should be something different than what is used by Minecraft */
|
||||
private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2;
|
||||
|
||||
/**
|
||||
* 64 MB by default is the maximum amount of memory that
|
||||
* can be directly allocated. <br><br>
|
||||
*
|
||||
* I know there are commands to change that amount
|
||||
* (specifically "-XX:MaxDirectMemorySize"), but
|
||||
* I have no idea how to access that amount. <br>
|
||||
* So I guess this will be the hard limit for now. <br><br>
|
||||
*
|
||||
* https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx
|
||||
*/
|
||||
public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
|
||||
|
||||
/** Does this computer's GPU support fancy fog? */
|
||||
public static boolean fancyFogAvailable = false;
|
||||
|
||||
|
||||
|
||||
/** If true the LODs colors will be replaced with
|
||||
* a checkerboard, this can be used for debugging. */
|
||||
public boolean debugging = false;
|
||||
|
||||
private Minecraft mc;
|
||||
private GameRenderer gameRender;
|
||||
private IProfiler profiler;
|
||||
private float farPlaneDistance;
|
||||
private ReflectionHandler reflectionHandler;
|
||||
|
||||
|
||||
/** This is used to generate the buildable buffers */
|
||||
private LodBufferBuilder lodBufferBuilder;
|
||||
|
||||
/** The buffers that are used to draw LODs using near fog */
|
||||
private volatile BufferBuilder drawableNearBuffer;
|
||||
/** The buffers that are used to draw LODs using far fog */
|
||||
private volatile BufferBuilder drawableFarBuffer;
|
||||
|
||||
/** This is the VertexBuffer used to draw any LODs that use near fog */
|
||||
private volatile VertexBuffer nearVbo;
|
||||
/** This is the VertexBuffer used to draw any LODs that use far fog */
|
||||
private volatile VertexBuffer farVbo;
|
||||
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
|
||||
|
||||
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int previousChunkRenderDistance = 0;
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int prevChunkX = 0;
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int prevChunkZ = 0;
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR;
|
||||
|
||||
/** if this is true the LOD buffers should be regenerated,
|
||||
* provided they aren't already being regenerated. */
|
||||
private volatile boolean regen = false;
|
||||
|
||||
/** This HashSet contains every chunk that Vanilla Minecraft
|
||||
* is going to render */
|
||||
public HashSet<ChunkPos> vanillaRenderedChunks = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
public LodRenderer(LodBufferBuilder newLodBufferBuilder)
|
||||
{
|
||||
mc = Minecraft.getInstance();
|
||||
gameRender = mc.gameRenderer;
|
||||
|
||||
reflectionHandler = new ReflectionHandler();
|
||||
lodBufferBuilder = newLodBufferBuilder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Besides drawing the LODs this method also starts
|
||||
* the async process of generating the Buffers that hold those LODs.
|
||||
*
|
||||
* @param newDimension The dimension to draw, if null doesn't replace the current dimension.
|
||||
* @param partialTicks how far into the current tick this method was called.
|
||||
*/
|
||||
public void drawLODs(LodDimension lodDim, float partialTicks, IProfiler newProfiler)
|
||||
{
|
||||
if (lodDim == null)
|
||||
{
|
||||
// if there aren't any loaded LodChunks
|
||||
// don't try drawing anything
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// initial setup //
|
||||
//===============//
|
||||
|
||||
profiler = newProfiler;
|
||||
profiler.push("LOD setup");
|
||||
|
||||
ClientPlayerEntity player = mc.player;
|
||||
|
||||
// should LODs be regenerated?
|
||||
if ((int)player.getX() / LodChunk.WIDTH != prevChunkX ||
|
||||
(int)player.getZ() / LodChunk.WIDTH != prevChunkZ ||
|
||||
previousChunkRenderDistance != mc.options.renderDistance ||
|
||||
prevFogDistance != LodConfig.CLIENT.fogDistance.get())
|
||||
{
|
||||
// yes
|
||||
regen = true;
|
||||
|
||||
prevChunkX = (int)player.getX() / LodChunk.WIDTH;
|
||||
prevChunkZ = (int)player.getZ() / LodChunk.WIDTH;
|
||||
prevFogDistance = LodConfig.CLIENT.fogDistance.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// nope, the player hasn't moved, the
|
||||
// render distance hasn't changed, and
|
||||
// the dimension is the same
|
||||
}
|
||||
|
||||
// did the user change the debug setting?
|
||||
if (LodConfig.CLIENT.debugMode.get() != debugging)
|
||||
{
|
||||
debugging = LodConfig.CLIENT.debugMode.get();
|
||||
regen = true;
|
||||
}
|
||||
|
||||
|
||||
// determine how far the game's render distance is currently set
|
||||
int renderDistWidth = mc.options.renderDistance;
|
||||
farPlaneDistance = renderDistWidth * LodChunk.WIDTH;
|
||||
|
||||
// set how big the LODs will be and how far they will go
|
||||
int totalLength = (int) farPlaneDistance * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() * 2;
|
||||
int numbChunksWide = (totalLength / LodChunk.WIDTH);
|
||||
|
||||
// determine which LODs should not be rendered close to the player
|
||||
HashSet<ChunkPos> chunkPosToSkip = getNearbyLodChunkPosToSkip(lodDim, player.blockPosition());
|
||||
|
||||
// see if the chunks Minecraft is going to render are the
|
||||
// same as last time
|
||||
if (!vanillaRenderedChunks.containsAll(chunkPosToSkip))
|
||||
{
|
||||
regen = true;
|
||||
vanillaRenderedChunks = chunkPosToSkip;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// create the LODs //
|
||||
//=================//
|
||||
|
||||
// only regenerate the LODs if:
|
||||
// 1. we want to regenerate LODs
|
||||
// 2. we aren't already regenerating the LODs
|
||||
// 3. we aren't waiting for the build and draw buffers to swap
|
||||
// (this is to prevent thread conflicts)
|
||||
if (regen && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvaliable())
|
||||
{
|
||||
// this will mainly happen when the view distance is changed
|
||||
if (drawableNearBuffer == null || drawableFarBuffer == null ||
|
||||
previousChunkRenderDistance != mc.options.renderDistance)
|
||||
setupBuffers(numbChunksWide);
|
||||
|
||||
// generate the LODs on a separate thread to prevent stuttering or freezing
|
||||
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, player.getX(), player.getZ(), numbChunksWide);
|
||||
|
||||
// the regen process has been started,
|
||||
// it will be done when lodBufferBuilder.newBuffersAvaliable
|
||||
// is true
|
||||
regen = false;
|
||||
}
|
||||
|
||||
// replace the buffers used to draw and build,
|
||||
// this is only done when the createLodBufferGenerationThread
|
||||
// has finished executing on a parallel thread.
|
||||
if (lodBufferBuilder.newBuffersAvaliable())
|
||||
{
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===========================//
|
||||
// GL settings for rendering //
|
||||
//===========================//
|
||||
|
||||
// set the required open GL settings
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glEnable(GL11.GL_CULL_FACE);
|
||||
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
|
||||
// disable the lights Minecraft uses
|
||||
GL11.glDisable(GL11.GL_LIGHT0);
|
||||
GL11.glDisable(GL11.GL_LIGHT1);
|
||||
|
||||
// get the default projection matrix so we can
|
||||
// reset it after drawing the LODs
|
||||
float[] defaultProjMatrix = new float[16];
|
||||
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, defaultProjMatrix);
|
||||
|
||||
Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks);
|
||||
|
||||
setupProjectionMatrix(partialTicks);
|
||||
setupLighting(lodDim, partialTicks);
|
||||
|
||||
NearFarFogSettings fogSettings = determineFogSettings();
|
||||
|
||||
// determine the current fog settings so they can be
|
||||
// reset after drawing the LODs
|
||||
float defaultFogStartDist = GL11.glGetFloat(GL11.GL_FOG_START);
|
||||
float defaultFogEndDist = GL11.glGetFloat(GL11.GL_FOG_END);
|
||||
int defaultFogMode = GL11.glGetInteger(GL11.GL_FOG_MODE);
|
||||
int defaultFogDistance = GL11.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// rendering //
|
||||
//===========//
|
||||
profiler.popPush("LOD draw");
|
||||
|
||||
setupFog(fogSettings.near.distance, fogSettings.near.quality);
|
||||
sendLodsToGpuAndDraw(nearVbo, modelViewMatrix);
|
||||
|
||||
setupFog(fogSettings.far.distance, fogSettings.far.quality);
|
||||
sendLodsToGpuAndDraw(farVbo, modelViewMatrix);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// cleanup //
|
||||
//=========//
|
||||
|
||||
profiler.popPush("LOD cleanup");
|
||||
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glDisable(LOD_GL_LIGHT_NUMBER);
|
||||
// re-enable the lights Minecraft uses
|
||||
GL11.glEnable(GL11.GL_LIGHT0);
|
||||
GL11.glEnable(GL11.GL_LIGHT1);
|
||||
RenderSystem.disableLighting();
|
||||
|
||||
// this can't be called until after the buffers are built
|
||||
// because otherwise the buffers may be set to the wrong size
|
||||
previousChunkRenderDistance = mc.options.renderDistance;
|
||||
|
||||
// reset the fog settings so the normal chunks
|
||||
// will be drawn correctly
|
||||
cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance);
|
||||
|
||||
// reset the projection matrix so anything drawn after
|
||||
// the LODs will use the correct projection matrix
|
||||
Matrix4f mvm = new Matrix4f(defaultProjMatrix);
|
||||
mvm.transpose();
|
||||
gameRender.resetProjectionMatrix(mvm);
|
||||
|
||||
// clear the depth buffer so anything drawn is drawn
|
||||
// over the LODs
|
||||
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
// end of internal LOD profiling
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is where the actual drawing happens.
|
||||
*
|
||||
* @param buffers the buffers sent to the GPU to draw
|
||||
*/
|
||||
private void sendLodsToGpuAndDraw(VertexBuffer vbo, Matrix4f modelViewMatrix)
|
||||
{
|
||||
if (vbo == null)
|
||||
return;
|
||||
|
||||
vbo.bind();
|
||||
// 0L is the starting pointer
|
||||
LOD_VERTEX_FORMAT.setupBufferState(0L);
|
||||
|
||||
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
|
||||
|
||||
VertexBuffer.unbind();
|
||||
LOD_VERTEX_FORMAT.clearBufferState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// Setup Functions //
|
||||
//=================//
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
|
||||
{
|
||||
if(fogQuality == FogQuality.OFF)
|
||||
{
|
||||
FogRenderer.setupNoFog();
|
||||
RenderSystem.disableFog();
|
||||
return;
|
||||
}
|
||||
|
||||
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 = NVFogDistance.GL_EYE_RADIAL_NV;
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
{
|
||||
// fancy fog (fragment distance based fog)
|
||||
glFogDistanceMode = NVFogDistance.GL_EYE_RADIAL_NV;
|
||||
}
|
||||
else
|
||||
{
|
||||
// fast fog (frustum distance based fog)
|
||||
glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV;
|
||||
}
|
||||
|
||||
|
||||
// the multipliers are percentages
|
||||
// of the regular view distance.
|
||||
if(fogDistance == FogDistance.NEAR)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
RenderSystem.fogEnd(farPlaneDistance * 1.75f);
|
||||
RenderSystem.fogStart(farPlaneDistance * 1.95f);
|
||||
}
|
||||
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
|
||||
|
||||
RenderSystem.fogEnd(farPlaneDistance * 1.5f);
|
||||
RenderSystem.fogStart(farPlaneDistance * 2.0f);
|
||||
}
|
||||
}
|
||||
else if(fogDistance == FogDistance.FAR)
|
||||
{
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
{
|
||||
RenderSystem.fogStart(farPlaneDistance * 0.85f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get());
|
||||
RenderSystem.fogEnd(farPlaneDistance * 1.0f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get());
|
||||
}
|
||||
else if(fogQuality == FogQuality.FAST)
|
||||
{
|
||||
RenderSystem.fogStart(farPlaneDistance * 0.5f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get());
|
||||
RenderSystem.fogEnd(farPlaneDistance * 0.75f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GL11.glEnable(GL11.GL_FOG);
|
||||
RenderSystem.enableFog();
|
||||
RenderSystem.setupNvFogDistance();
|
||||
RenderSystem.fogMode(GlStateManager.FogMode.LINEAR);
|
||||
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert any changes that were made to the fog.
|
||||
*/
|
||||
private void cleanupFog(NearFarFogSettings fogSettings,
|
||||
float defaultFogStartDist, float defaultFogEndDist,
|
||||
int defaultFogMode, int defaultFogDistance)
|
||||
{
|
||||
RenderSystem.fogStart(defaultFogStartDist);
|
||||
RenderSystem.fogEnd(defaultFogEndDist);
|
||||
RenderSystem.fogMode(defaultFogMode);
|
||||
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, defaultFogDistance);
|
||||
|
||||
// disable fog if Minecraft wasn't rendering fog
|
||||
// but we were
|
||||
if(!fogSettings.vanillaIsRenderingFog &&
|
||||
(fogSettings.near.quality != FogQuality.OFF ||
|
||||
fogSettings.far.quality != FogQuality.OFF))
|
||||
{
|
||||
GL11.glDisable(GL11.GL_FOG);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the model view matrix to move the LODs
|
||||
* from object space into world space.
|
||||
*/
|
||||
private Matrix4f generateModelViewMatrix(float partialTicks)
|
||||
{
|
||||
// get all relevant camera info
|
||||
ActiveRenderInfo renderInfo = mc.gameRenderer.getMainCamera();
|
||||
Vector3d projectedView = renderInfo.getPosition();
|
||||
|
||||
|
||||
// generate the model view matrix
|
||||
MatrixStack matrixStack = new MatrixStack();
|
||||
matrixStack.pushPose();
|
||||
// translate and rotate to the current camera location
|
||||
matrixStack.mulPose(Vector3f.XP.rotationDegrees(renderInfo.getXRot()));
|
||||
matrixStack.mulPose(Vector3f.YP.rotationDegrees(renderInfo.getYRot() + 180));
|
||||
matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z);
|
||||
|
||||
return matrixStack.last().pose();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create a new projection matrix and send it over to the GPU
|
||||
* <br><br>
|
||||
* A lot of this code is copied from renderLevel (line 567)
|
||||
* in the GameRender class. The code copied is anything with
|
||||
* a matrixStack and is responsible for making sure the LOD
|
||||
* objects distort correctly relative to the rest of the world.
|
||||
* Distortions are caused by: standing in a nether portal,
|
||||
* nausea potion effect, walking bobbing.
|
||||
*
|
||||
* @param partialTicks how many ticks into the frame we are
|
||||
*/
|
||||
private void setupProjectionMatrix(float partialTicks)
|
||||
{
|
||||
// Note: if the LOD objects don't distort correctly
|
||||
// compared to regular minecraft terrain, make sure
|
||||
// all the transformations in renderWorld are here too
|
||||
|
||||
MatrixStack matrixStack = new MatrixStack();
|
||||
matrixStack.pushPose();
|
||||
|
||||
gameRender.bobHurt(matrixStack, partialTicks);
|
||||
if (this.mc.options.bobView) {
|
||||
gameRender.bobView(matrixStack, partialTicks);
|
||||
}
|
||||
|
||||
// potion and nausea effects
|
||||
float f = MathHelper.lerp(partialTicks, this.mc.player.oPortalTime, this.mc.player.portalTime) * this.mc.options.screenEffectScale * this.mc.options.screenEffectScale;
|
||||
if (f > 0.0F) {
|
||||
int i = this.mc.player.hasEffect(Effects.CONFUSION) ? 7 : 20;
|
||||
float f1 = 5.0F / (f * f + 5.0F) - f * 0.04F;
|
||||
f1 = f1 * f1;
|
||||
Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_OF_TWO / 2.0F, MathHelper.SQRT_OF_TWO / 2.0F);
|
||||
matrixStack.mulPose(vector3f.rotationDegrees((gameRender.tick + partialTicks) * i));
|
||||
matrixStack.scale(1.0F / f1, 1.0F, 1.0F);
|
||||
float f2 = -(gameRender.tick + partialTicks) * i;
|
||||
matrixStack.mulPose(vector3f.rotationDegrees(f2));
|
||||
}
|
||||
|
||||
// this projection matrix allows us to see past the normal
|
||||
// world render distance
|
||||
Matrix4f projectionMatrix =
|
||||
Matrix4f.perspective(
|
||||
getFov(partialTicks, true),
|
||||
(float)this.mc.getWindow().getScreenWidth() / (float)this.mc.getWindow().getScreenHeight(),
|
||||
// it is possible to see the near clip plane, but
|
||||
// you have to be flying quickly in spectator mode through ungenerated
|
||||
// terrain, so I don't think it is much of an issue.
|
||||
LodConfig.CLIENT.lodChunkRadiusMultiplier.get(),
|
||||
this.farPlaneDistance * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() * 2);
|
||||
|
||||
// add the screen space distortions
|
||||
projectionMatrix.multiply(matrixStack.last().pose());
|
||||
gameRender.resetProjectionMatrix(projectionMatrix);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* setup the lighting to be used for the LODs
|
||||
*/
|
||||
private void setupLighting(LodDimension lodDimension, float partialTicks)
|
||||
{
|
||||
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.level.getSkyDarken(partialTicks) : 0.2f;
|
||||
float gammaMultiplyer = (float)mc.options.gamma - 0.5f;
|
||||
float lightStrength = ((sunBrightness / 2f) - 0.2f) + (gammaMultiplyer * 0.2f);
|
||||
|
||||
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
|
||||
|
||||
// can be used for debugging
|
||||
// if (partialTicks < 0.005)
|
||||
// ClientProxy.LOGGER.debug(lightStrength);
|
||||
|
||||
ByteBuffer temp = ByteBuffer.allocateDirect(16);
|
||||
temp.order(ByteOrder.nativeOrder());
|
||||
GL11.glLightfv(LOD_GL_LIGHT_NUMBER, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
|
||||
GL11.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
|
||||
|
||||
RenderSystem.enableLighting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all buffers that will be used.
|
||||
*/
|
||||
private void setupBuffers(int numbChunksWide)
|
||||
{
|
||||
// calculate the max amount of memory needed (in bytes)
|
||||
int bufferMemory = RenderUtil.getBufferMemoryForRadiusMultiplier(LodConfig.CLIENT.lodChunkRadiusMultiplier.get());
|
||||
|
||||
// if the required memory is greater than the
|
||||
// MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier
|
||||
// to fit.
|
||||
if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY)
|
||||
{
|
||||
int maxRadiusMultiplier = RenderUtil.getMaxRadiusMultiplierWithAvaliableMemory(LodConfig.CLIENT.lodTemplate.get(), LodConfig.CLIENT.lodDetail.get());
|
||||
|
||||
ClientProxy.LOGGER.warn("The lodChunkRadiusMultiplier was set too high "
|
||||
+ "and had to be lowered to fit memory constraints "
|
||||
+ "from " + LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + " "
|
||||
+ "to " + maxRadiusMultiplier);
|
||||
|
||||
LodConfig.CLIENT.lodChunkRadiusMultiplier.set(
|
||||
maxRadiusMultiplier);
|
||||
|
||||
bufferMemory = RenderUtil.getBufferMemoryForRadiusMultiplier(maxRadiusMultiplier);
|
||||
}
|
||||
|
||||
drawableNearBuffer = new BufferBuilder(bufferMemory);
|
||||
drawableFarBuffer = new BufferBuilder(bufferMemory);
|
||||
|
||||
lodBufferBuilder.setupBuffers(bufferMemory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//======================//
|
||||
// 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.
|
||||
*/
|
||||
public void regenerateLODsNextFrame()
|
||||
{
|
||||
regen = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace the current drawable buffers with the newly
|
||||
* created buffers from the lodBufferBuilder.
|
||||
*/
|
||||
private void swapBuffers()
|
||||
{
|
||||
// replace the drawable buffers with
|
||||
// the newly created buffers from the lodBufferBuilder
|
||||
NearFarBuffer newBuffers = lodBufferBuilder.swapBuffers(drawableNearBuffer, drawableFarBuffer);
|
||||
drawableNearBuffer = newBuffers.nearBuffer;
|
||||
drawableFarBuffer = newBuffers.farBuffer;
|
||||
|
||||
|
||||
// bind the buffers with their respective VBOs
|
||||
if (nearVbo != null)
|
||||
nearVbo.close();
|
||||
|
||||
nearVbo = new VertexBuffer(LOD_VERTEX_FORMAT);
|
||||
nearVbo.upload(drawableNearBuffer);
|
||||
|
||||
|
||||
if (farVbo != null)
|
||||
farVbo.close();
|
||||
|
||||
farVbo = new VertexBuffer(LOD_VERTEX_FORMAT);
|
||||
farVbo.upload(drawableFarBuffer);
|
||||
}
|
||||
|
||||
|
||||
private double getFov(float partialTicks, boolean useFovSetting)
|
||||
{
|
||||
return mc.gameRenderer.getFov(mc.gameRenderer.getMainCamera(), partialTicks, useFovSetting);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return what fog settings should be used when rendering.
|
||||
*/
|
||||
private NearFarFogSettings determineFogSettings()
|
||||
{
|
||||
NearFarFogSettings fogSettings = new NearFarFogSettings();
|
||||
|
||||
|
||||
FogQuality quality = reflectionHandler.getFogQuality();
|
||||
FogDrawOverride override = LodConfig.CLIENT.fogDrawOverride.get();
|
||||
|
||||
|
||||
if (quality == FogQuality.OFF)
|
||||
fogSettings.vanillaIsRenderingFog = false;
|
||||
else
|
||||
fogSettings.vanillaIsRenderingFog = true;
|
||||
|
||||
|
||||
// use any fog overrides the user may have set
|
||||
switch(override)
|
||||
{
|
||||
case ALWAYS_DRAW_FOG_FANCY:
|
||||
quality = FogQuality.FANCY;
|
||||
break;
|
||||
|
||||
case NEVER_DRAW_FOG:
|
||||
quality = FogQuality.OFF;
|
||||
break;
|
||||
|
||||
case ALWAYS_DRAW_FOG_FAST:
|
||||
quality = FogQuality.FAST;
|
||||
break;
|
||||
|
||||
case USE_OPTIFINE_FOG_SETTING:
|
||||
// don't override anything
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// only use fancy fog if the user's GPU can deliver
|
||||
if (!fancyFogAvailable && quality == FogQuality.FANCY)
|
||||
{
|
||||
quality = FogQuality.FAST;
|
||||
}
|
||||
|
||||
|
||||
// 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(LodConfig.CLIENT.fogDistance.get())
|
||||
{
|
||||
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(LodConfig.CLIENT.fogDistance.get())
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
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 OFF:
|
||||
|
||||
fogSettings.near.quality = FogQuality.OFF;
|
||||
fogSettings.far.quality = FogQuality.OFF;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return fogSettings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a HashSet of all ChunkPos within the normal render distance
|
||||
* that should not be rendered.
|
||||
*/
|
||||
private HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
|
||||
{
|
||||
int chunkRenderDist = mc.options.renderDistance;
|
||||
int blockRenderDist = chunkRenderDist * 16;
|
||||
ChunkPos centerChunk = new ChunkPos(playerPos);
|
||||
|
||||
// skip chunks that are already going to be rendered by Minecraft
|
||||
HashSet<ChunkPos> posToSkip = getRenderedChunks();
|
||||
|
||||
|
||||
// go through each chunk within the normal view distance
|
||||
for(int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++)
|
||||
{
|
||||
for(int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
|
||||
{
|
||||
LodChunk lod = lodDim.getLodFromCoordinates(x, z);
|
||||
if (lod != null)
|
||||
{
|
||||
short lodHighestPoint = lod.calculateHighestPoint();
|
||||
|
||||
if (playerPos.getY() < lodHighestPoint)
|
||||
{
|
||||
// don't draw Lod's that are taller than the player
|
||||
// to prevent LODs being drawn on top of the player
|
||||
posToSkip.add(new ChunkPos(x, z));
|
||||
}
|
||||
else if (blockRenderDist < Math.abs(playerPos.getY() - lodHighestPoint))
|
||||
{
|
||||
// draw Lod's that are lower than the player's view range
|
||||
posToSkip.remove(new ChunkPos(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return posToSkip;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of all chunks that Minecraft
|
||||
* is going to render this frame. <br><br>
|
||||
*
|
||||
* 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).
|
||||
*/
|
||||
public static HashSet<ChunkPos> getRenderedChunks()
|
||||
{
|
||||
HashSet<ChunkPos> loadedPos = new HashSet<>();
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
// Wow those are some long names!
|
||||
|
||||
// go through every RenderInfo to get the compiled chunks
|
||||
for(WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : mc.levelRenderer.renderChunks)
|
||||
{
|
||||
if (!worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk().hasNoRenderableLayers())
|
||||
{
|
||||
// add the ChunkPos for every empty compiled chunk
|
||||
BlockPos bpos = worldrenderer$localrenderinformationcontainer.chunk.getOrigin();
|
||||
|
||||
loadedPos.add(new ChunkPos(bpos.getX() / 16, bpos.getZ() / 16));
|
||||
}
|
||||
}
|
||||
|
||||
return loadedPos;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* This file is part of 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.render;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
/**
|
||||
* This holds miscellaneous helper code
|
||||
* to be used in the rendering process.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-17-2021
|
||||
*/
|
||||
public class RenderUtil
|
||||
{
|
||||
private static final Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
|
||||
/**
|
||||
* Returns if the given ChunkPos is in the loaded area of the world.
|
||||
* @param centerCoordinate the center of the loaded world (probably the player's ChunkPos)
|
||||
*/
|
||||
public static boolean isChunkPosInLoadedArea(ChunkPos pos, ChunkPos center)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
return (pos.x >= center.x - mc.options.renderDistance
|
||||
&& pos.x <= center.x + mc.options.renderDistance)
|
||||
&&
|
||||
(pos.z >= center.z - mc.options.renderDistance
|
||||
&& pos.z <= center.z + mc.options.renderDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given coordinate is in the loaded area of the world.
|
||||
* @param centerCoordinate the center of the loaded world
|
||||
*/
|
||||
public static boolean isCoordinateInLoadedArea(int i, int j, int centerCoordinate)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
return (i >= centerCoordinate - mc.options.renderDistance
|
||||
&& i <= centerCoordinate + mc.options.renderDistance)
|
||||
&&
|
||||
(j >= centerCoordinate - mc.options.renderDistance
|
||||
&& j <= centerCoordinate + mc.options.renderDistance);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the coordinates that are in the center half of the given
|
||||
* 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius).
|
||||
*/
|
||||
public static boolean isCoordinateInNearFogArea(int i, int j, int lodRadius)
|
||||
{
|
||||
int halfRadius = lodRadius / 2;
|
||||
|
||||
return (i >= lodRadius - halfRadius
|
||||
&& i <= lodRadius + halfRadius)
|
||||
&&
|
||||
(j >= lodRadius - halfRadius
|
||||
&& j <= lodRadius + halfRadius);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get how much buffer memory would be required for the given radius multiplier
|
||||
*/
|
||||
public static int getBufferMemoryForRadiusMultiplier(int radiusMultiplier)
|
||||
{
|
||||
int numbChunksWide = mc.options.renderDistance *
|
||||
radiusMultiplier * 2;
|
||||
|
||||
// calculate the max amount of buffer memory needed (in bytes)
|
||||
return numbChunksWide * numbChunksWide *
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
getBufferMemoryForSingleLod(LodConfig.CLIENT.lodDetail.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maxViewDistanceMultiplier for the given LodTemplate
|
||||
* at the given LodDetail level.
|
||||
*/
|
||||
public static int getMaxRadiusMultiplierWithAvaliableMemory(LodTemplate lodTemplate, LodDetail lodDetail)
|
||||
{
|
||||
int maxNumberOfLods = LodRenderer.MAX_ALOCATEABLE_DIRECT_MEMORY / lodTemplate.getBufferMemoryForSingleLod(lodDetail);
|
||||
int numbLodsWide = (int) Math.sqrt(maxNumberOfLods);
|
||||
|
||||
return numbLodsWide / (2 * mc.options.renderDistance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* This file is part of 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.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.chunk.ChunkSection;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
||||
/**
|
||||
* This class holds methods that may be used in multiple places.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 06-27-2021
|
||||
*/
|
||||
public class LodUtil
|
||||
{
|
||||
private static Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the first valid ServerWorld.
|
||||
*
|
||||
* @return null if there are no ServerWorlds
|
||||
*/
|
||||
public static ServerWorld getFirstValidServerWorld()
|
||||
{
|
||||
if (mc.hasSingleplayerServer())
|
||||
return null;
|
||||
|
||||
Iterable<ServerWorld> worlds = mc.getSingleplayerServer().getAllLevels();
|
||||
|
||||
for (ServerWorld world : worlds)
|
||||
return world;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ServerWorld for the relevant dimension.
|
||||
*
|
||||
* @return null if there is no ServerWorld for the given dimension
|
||||
*/
|
||||
public static ServerWorld getServerWorldFromDimension(DimensionType dimension)
|
||||
{
|
||||
IntegratedServer server = mc.getSingleplayerServer();
|
||||
if (server == null)
|
||||
return null;
|
||||
|
||||
Iterable<ServerWorld> worlds = server.getAllLevels();
|
||||
ServerWorld returnWorld = null;
|
||||
|
||||
for (ServerWorld world : worlds)
|
||||
{
|
||||
if(world.dimensionType() == dimension)
|
||||
{
|
||||
returnWorld = world;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnWorld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given ChunkPos into a RegionPos.
|
||||
*/
|
||||
public static RegionPos convertChunkPosToRegionPos(ChunkPos pos)
|
||||
{
|
||||
RegionPos rPos = new RegionPos();
|
||||
rPos.x = pos.x / LodRegion.SIZE;
|
||||
rPos.z = pos.z / LodRegion.SIZE;
|
||||
|
||||
// prevent issues if X/Z is negative and less than 16
|
||||
if (pos.x < 0)
|
||||
{
|
||||
rPos.x = (Math.abs(rPos.x) * -1) - 1;
|
||||
}
|
||||
if (pos.z < 0)
|
||||
{
|
||||
rPos.z = (Math.abs(rPos.z) * -1) - 1;
|
||||
}
|
||||
|
||||
return rPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given chunk
|
||||
* has any data in it.
|
||||
*/
|
||||
public static boolean chunkHasBlockData(IChunk chunk)
|
||||
{
|
||||
ChunkSection[] blockStorage = chunk.getSections();
|
||||
|
||||
for(ChunkSection section : blockStorage)
|
||||
{
|
||||
if(section != null && !section.isEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String getCurrentDimensionID()
|
||||
{
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
if(mc.hasSingleplayerServer())
|
||||
{
|
||||
// this will return the world save location
|
||||
// and the dimension folder
|
||||
|
||||
if(mc.level == null)
|
||||
return "";
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(mc.level.dimensionType());
|
||||
if(serverWorld == null)
|
||||
return "";
|
||||
|
||||
ServerChunkProvider provider = serverWorld.getChunkSource();
|
||||
if(provider == null)
|
||||
return "";
|
||||
|
||||
return provider.dataStorage.dataFolder.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = mc.getCurrentServer();
|
||||
return server.name + ", IP " +
|
||||
server.ip + ", GameVersion " +
|
||||
server.version.getString() + File.separatorChar
|
||||
+ "dim_" + mc.level.dimensionType().effectsLocation().getPath() + File.separatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If on single player this will return the name of the user's
|
||||
* world and the dimensional save folder, if in multiplayer
|
||||
* it will return the server name, game version, and dimension.<br>
|
||||
* <br>
|
||||
* This can be used to determine where to save files for a given
|
||||
* dimension.
|
||||
*/
|
||||
public static String getDimensionIDFromWorld(IWorld world)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
if(mc.hasSingleplayerServer())
|
||||
{
|
||||
// this will return the world save location
|
||||
// and the dimension folder
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(world.dimensionType());
|
||||
if(serverWorld == null)
|
||||
throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerWorld for the dimension " + world.dimensionType().effectsLocation().getPath());
|
||||
|
||||
ServerChunkProvider provider = serverWorld.getChunkSource();
|
||||
if(provider == null)
|
||||
throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerChunkProvider for the dimension " + world.dimensionType().effectsLocation().getPath());
|
||||
|
||||
return provider.dataStorage.dataFolder.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = mc.getCurrentServer();
|
||||
return server.name + ", IP " +
|
||||
server.ip + ", GameVersion " +
|
||||
server.version.getString() + File.separatorChar
|
||||
+ "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If on single player this will return the name of the user's
|
||||
* world, if in multiplayer it will return the server name
|
||||
* and game version.
|
||||
*/
|
||||
public static String getWorldID(IWorld world)
|
||||
{
|
||||
if(mc.hasSingleplayerServer())
|
||||
{
|
||||
// chop off the dimension ID as it is not needed/wanted
|
||||
String dimId = getDimensionIDFromWorld(world);
|
||||
|
||||
// get the world name
|
||||
int saveIndex = dimId.indexOf("saves") + 1 + "saves".length();
|
||||
int slashIndex = dimId.indexOf(File.separatorChar, saveIndex);
|
||||
dimId = dimId.substring(saveIndex, slashIndex);
|
||||
return dimId;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = mc.getCurrentServer();
|
||||
return server.name + ", IP " +
|
||||
server.ip + ", GameVersion " +
|
||||
server.version.getString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a BlockColors int into a Color object.
|
||||
*/
|
||||
public static Color intToColor(int num)
|
||||
{
|
||||
int filter = 0b11111111;
|
||||
|
||||
int red = (num >> 16 ) & filter;
|
||||
int green = (num >> 8 ) & filter;
|
||||
int blue = num & filter;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Color into a BlockColors object.
|
||||
*/
|
||||
public static int colorToInt(Color color)
|
||||
{
|
||||
return color.getRGB();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of 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.util;
|
||||
|
||||
/**
|
||||
* This holds meta information about the mod.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 04-16-2020
|
||||
*/
|
||||
public class Reference
|
||||
{
|
||||
/** the mod's identifier */
|
||||
public static final String MOD_ID = "lod";
|
||||
/** the mod's name */
|
||||
public static final String NAME = "LOD Mod";
|
||||
/** the mod's version */
|
||||
public static final String VERSION = "1.0";
|
||||
/** the version of minecraft this mod is built for */
|
||||
public static final String ACCEPTED_VERSIONS = "[1.16.4]";
|
||||
|
||||
/** where the client proxy class is */
|
||||
public static final String CLIENT_PROXY_CLASS = "com.backsun.lod.proxy.ClientProxy";
|
||||
/** where the common proxy class is*/
|
||||
public static final String COMMON_PROXY_CLASS = "com.backsun.lod.proxy.CommonProxy";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
#=====================#
|
||||
# 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_
|
||||
@@ -0,0 +1,50 @@
|
||||
#// 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="All rights reserved"
|
||||
|
||||
#// A URL to refer people to when problems occur with this mod
|
||||
#//issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #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.3.1" #mandatory
|
||||
|
||||
#// A display name for the mod
|
||||
displayName="Level of Detail" #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://change.me.to.your.mods.homepage.example.invalid/" #optional
|
||||
|
||||
#// A file name (in the root of the mod JAR) containing a logo for display
|
||||
#//logoFile="examplemod.png" #optional
|
||||
|
||||
#// 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" #optional
|
||||
|
||||
#// The description text for the mod (multi line!) (#mandatory)
|
||||
description='''
|
||||
This mod generates and renders simplified chunks beyond the normal view distance, at a low performance cost.
|
||||
'''
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "com.seibel.lod.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"refmap": "lod.refmap.json",
|
||||
"mixins": [
|
||||
"MixinWorldRenderer"
|
||||
],
|
||||
"minVersion": "0.8"
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
[
|
||||
{
|
||||
"modid": "lod",
|
||||
"name": "Level Of Details",
|
||||
"name": "Level Of Detail",
|
||||
"description": "Generates and renders simplified chunks beyond the normal view distance, at a low performance cost.",
|
||||
"version": "0.1",
|
||||
"mcversion": "1.12.2",
|
||||
"version": "a1.3.1",
|
||||
"mcversion": "1.16.4",
|
||||
"url": "",
|
||||
"updateUrl": "",
|
||||
"authorList": ["James Seibel"],
|
||||
"credits": "TechnoVision and Vike for their modding tutorials.",
|
||||
"credits": "TechnoVision, Vike, and Darkhax for their modding tutorials.",
|
||||
"logoFile": "",
|
||||
"screenshots": [],
|
||||
"dependencies": ["lodcore"]
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"pack": {
|
||||
"description": "examplemod resources",
|
||||
"pack_format": 3,
|
||||
"_comment": "A pack_format of 3 should be used starting with Minecraft 1.11. All resources, including language files, should be lowercase (eg: en_us.lang). A pack_format of 2 will load your mod resources with LegacyV2Adapter, which requires language files to have uppercase letters (eg: en_US.lang)."
|
||||
"pack_format": 6,
|
||||
"_comment": "A pack_format of 6 requires json lang files and some texture changes from 1.16.2. Note: we require v6 pack meta for all mods."
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user