Compare commits
178 Commits
blazeRender
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b802ca362 | |||
| ea4375b215 | |||
| a6e94c39a7 | |||
| 8bd6b4837c | |||
| 679f871788 | |||
| b46f10755e | |||
| 3cf799a10f | |||
| 4e99594a3f | |||
| 8a4f991906 | |||
| ab634b3204 | |||
| b836f675e1 | |||
| a4e2003e0e | |||
| 28a8bc39f2 | |||
| 4f0a3afd93 | |||
| 4ac56774fb | |||
| 18b0582152 | |||
| b85c504995 | |||
| cbff0cd9e9 | |||
| c28cf643b3 | |||
| 44aed79e78 | |||
| e192abe666 | |||
| 9754943752 | |||
| 299ab0f856 | |||
| b97c35387c | |||
| f69ad051d1 | |||
| 9231a48998 | |||
| e8f27f7da8 | |||
| db47a9e99f | |||
| 16f72066a8 | |||
| ca4d6f158a | |||
| ad9092c45c | |||
| 95e4db2998 | |||
| 9fe5dcc16e | |||
| 8cd926f4ac | |||
| 7239b51073 | |||
| 262dcae36b | |||
| 062f86c036 | |||
| 5b4029f0ad | |||
| faa4fa3782 | |||
| 9db045d614 | |||
| c81dc83bb1 | |||
| b14701ef6b | |||
| 78f84f17cd | |||
| 7739e1cafd | |||
| d39c04d9cf | |||
| 492afa7328 | |||
| 9465512491 | |||
| 51b52a7d2a | |||
| f106867091 | |||
| 1c4908bbc5 | |||
| baa9b94a5d | |||
| 21136ba1ef | |||
| ec6f8255b5 | |||
| 2f9504b167 | |||
| f67d9e4e04 | |||
| c1644ad419 | |||
| e07557e6e3 | |||
| 52b8a91dc5 | |||
| 64fe804c40 | |||
| 2dd5b82be3 | |||
| d1f0325f87 | |||
| b10a367ce6 | |||
| 4dec387ca1 | |||
| fd3a8f7ddf | |||
| e3f586da56 | |||
| 775984f651 | |||
| 269f2c30fd | |||
| b674f49600 | |||
| b592012ba8 | |||
| 5d1e8a44fd | |||
| 40b27335ea | |||
| d0b07a5d2f | |||
| cb0fee9780 | |||
| 895e9276cd | |||
| 9ee0af8b01 | |||
| 69941fb7f8 | |||
| 36862a968f | |||
| 27204336b2 | |||
| 4846cf5019 | |||
| f7f3c1146f | |||
| aaa5e958f0 | |||
| 726da953bd | |||
| c4f4935fdd | |||
| 3ef8bd7e20 | |||
| ec72762067 | |||
| 4d0ed2a6dc | |||
| 7b252b173b | |||
| 7b0c66e3ae | |||
| 1b066327a8 | |||
| 43d0a971f7 | |||
| 9e60c698de | |||
| bf2affa6d1 | |||
| 98f6cea86a | |||
| 9ae01dc1f8 | |||
| 40efc5cbf3 | |||
| 66bba1c80a | |||
| d9f3b31cc5 | |||
| e465ef5325 | |||
| 225385a43f | |||
| 7d7d07416b | |||
| 5ef308cbee | |||
| d61b601c14 | |||
| 246c679a97 | |||
| 4b317a8e00 | |||
| 1debd4b875 | |||
| 5dcda31990 | |||
| ae16ed2341 | |||
| 2c266d2495 | |||
| 7e40546bc5 | |||
| 5d391c83ea | |||
| 0895bf53e3 | |||
| a7203f8f33 | |||
| 22efbb211a | |||
| 95c4459d8a | |||
| 0ef11caaf2 | |||
| 2e3dfab6c3 | |||
| 42be139e94 | |||
| f866e7f8e3 | |||
| 53fcce9d7c | |||
| 7b8b22fd5a | |||
| 6f54cfacb5 | |||
| 61eaf43ba0 | |||
| 1d368e3adc | |||
| 9e65e2dd4c | |||
| 2d878338cb | |||
| d62d21776d | |||
| a44c5d562d | |||
| 1d9bffe64e | |||
| b4e0687e2a | |||
| 89804f1ba1 | |||
| 9dbc5ef525 | |||
| e5dcb0999d | |||
| 50e0e940d1 | |||
| cb3e42fac4 | |||
| 34cdaf02eb | |||
| 71e6b9b58e | |||
| 1ec342928f | |||
| d45a1379bd | |||
| ecb3dce963 | |||
| 2b8cddd424 | |||
| 13895fec51 | |||
| 88c7245be6 | |||
| 6aad156a32 | |||
| 94535a213e | |||
| 63d6d42356 | |||
| acecbede8e | |||
| e2a8953e4c | |||
| 32eae23963 | |||
| 480c3b3ec5 | |||
| 6c0736a2a0 | |||
| c89abd414b | |||
| da2454b249 | |||
| 5933ef8245 | |||
| b9984c7723 | |||
| 2de50475f1 | |||
| bb838328a7 | |||
| 1980e64b2f | |||
| c3df26f5cb | |||
| 17df533fa6 | |||
| 2393bdd8e8 | |||
| 082b364f52 | |||
| 8a39610b8c | |||
| aa53835772 | |||
| cd5a3ce52b | |||
| 38e104a9fc | |||
| e27cee1f71 | |||
| 15d1d78954 | |||
| e2421c97ed | |||
| 8b72920652 | |||
| 212031a05f | |||
| 804293e291 | |||
| cfdd464f30 | |||
| 4c9d703e15 | |||
| 0eba376e70 | |||
| 89e6355d73 | |||
| 40149bc1d1 | |||
| b0e7c31964 | |||
| 2e906b57c4 |
+59
-23
@@ -2,48 +2,63 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
|
id "com.gradleup.shadow"
|
||||||
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
shadowJar {
|
mavenCentral()
|
||||||
// required for basic shadowJar setup
|
|
||||||
configurations = [project.configurations.shadow]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task addSourcesToCompiledJar(type: ShadowJar) {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
options.release = 8
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
def sourceJarPath = "build/libs/DistantHorizons-api-${rootProject.versionStr}-sources.jar"
|
configurations {
|
||||||
def secondJarFile = file(sourceJarPath)
|
testImplementation.extendsFrom compileOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly "org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}"
|
||||||
|
testImplementation "junit:junit:4.13"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
task createReleaseApiJar(type: ShadowJar) {
|
||||||
|
|
||||||
|
mustRunAfter sourcesJar
|
||||||
|
dependsOn shadowJar
|
||||||
|
|
||||||
|
// the compiled "-all" jar is used since it's available at the time this task is run
|
||||||
|
def compiledJarPath = "build/libs/DistantHorizonsApi-${rootProject.api_version}-all.jar"
|
||||||
|
def compiledJarFile = file(compiledJarPath)
|
||||||
|
|
||||||
// doFirst is so these only run when the task is actually executed
|
// doFirst is so these only run when the task is actually executed
|
||||||
doFirst {
|
doFirst {
|
||||||
System.out.println("Adding source files from: \n" +
|
System.out.println("Adding class files from: \n" +
|
||||||
"[" + sourceJarPath + "] to compiled API jar: \n" +
|
"[" + compiledJarPath + "] to source API jar: \n" +
|
||||||
"[" + shadowJar.archivePath + "]")
|
"[" + shadowJar.archiveFile.get().asFile + "]")
|
||||||
|
|
||||||
// Validate the input JAR file
|
// Validate the input JAR file
|
||||||
if (!secondJarFile.exists()) {
|
if (!compiledJarFile.exists()) {
|
||||||
throw new GradleException("Second JAR file not found: [${secondJarFile}]")
|
throw new GradleException("Compiled JAR file not found: [${compiledJarFile}]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the name of the combined JAR file
|
archiveFileName.set("DistantHorizonsApi-${rootProject.api_version}-combined.jar") // jar name
|
||||||
archiveFileName.set("DistantHorizonsApi-${rootProject.api_version}.jar")
|
destinationDirectory = file('build/libs/') // jar location
|
||||||
|
|
||||||
// Set the destination directory for the combined JAR file
|
|
||||||
destinationDirectory = file('build/libs/merged/')
|
|
||||||
|
|
||||||
// Set the input JAR files to be combined
|
// Set the input JAR files to be combined
|
||||||
from sourceSets.main.allJava
|
from sourceSets.main.allJava
|
||||||
from {
|
from {
|
||||||
configurations.shadow.collect { it.isDirectory() ? it : zipTree(it) }
|
project.configurations.shadow.collect { it.isDirectory() ? it : zipTree(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the jars to merge
|
// add the class files
|
||||||
from shadowJar.archivePath
|
from zipTree(compiledJarFile)
|
||||||
from secondJarFile
|
|
||||||
|
|
||||||
// alternative method to Include the source files in the combined JAR
|
// alternative method to Include the source files in the combined JAR
|
||||||
// and/or see which files are being included
|
// and/or see which files are being included
|
||||||
@@ -77,6 +92,13 @@ task addSourcesToCompiledJar(type: ShadowJar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// always create a combined jar for easy deployment
|
||||||
|
assemble.dependsOn(createReleaseApiJar)
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
// required for basic shadowJar setup
|
||||||
|
configurations = [project.configurations.shadow]
|
||||||
|
}
|
||||||
|
|
||||||
javadoc {
|
javadoc {
|
||||||
options {
|
options {
|
||||||
@@ -87,3 +109,17 @@ javadoc {
|
|||||||
addStringOption('Xdoclint:all,-missing', '-quiet')
|
addStringOption('Xdoclint:all,-missing', '-quiet')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set the jar name
|
||||||
|
def configureJar = { task ->
|
||||||
|
// outputs in the format:
|
||||||
|
// "DistantHorizonsApi-6.0.0.jar"
|
||||||
|
// "DistantHorizonsApi-6.0.0-sources.jar"
|
||||||
|
task.archiveBaseName = rootProject.api_name
|
||||||
|
task.archiveVersion = rootProject.api_version
|
||||||
|
}
|
||||||
|
configureJar(tasks.named("jar").get())
|
||||||
|
configureJar(tasks.named("sourcesJar").get())
|
||||||
|
configureJar(tasks.named("shadowJar").get())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.seibel.distanthorizons.api.enums.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VULKAN, <br>
|
||||||
|
* OPEN_GL, <br>
|
||||||
|
*
|
||||||
|
* @see EDhApiRenderingEngine
|
||||||
|
*
|
||||||
|
* @since API 7.0.0
|
||||||
|
* @version 2026-3-10
|
||||||
|
*/
|
||||||
|
public enum EDhApiRenderingApi
|
||||||
|
{
|
||||||
|
VULKAN,
|
||||||
|
OPEN_GL;
|
||||||
|
|
||||||
|
}
|
||||||
+4
-4
@@ -1,16 +1,16 @@
|
|||||||
package com.seibel.distanthorizons.api.enums.config;
|
package com.seibel.distanthorizons.api.enums.config;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AUTO, <br>
|
* AUTO, <br>
|
||||||
* OPEN_GL, <br>
|
* OPEN_GL, <br>
|
||||||
* BLAZE_3D, <br><br>
|
* BLAZE_3D, <br><br>
|
||||||
*
|
*
|
||||||
* @since API 6.0.0
|
* @see EDhApiRenderingApi
|
||||||
|
*
|
||||||
|
* @since API 7.0.0
|
||||||
* @version 2026-3-10
|
* @version 2026-3-10
|
||||||
*/
|
*/
|
||||||
public enum EDhApiRenderApi
|
public enum EDhApiRenderingEngine
|
||||||
{
|
{
|
||||||
AUTO,
|
AUTO,
|
||||||
OPEN_GL,
|
OPEN_GL,
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.api.enums.config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NEVER, <br>
|
|
||||||
* DYNAMIC, <br>
|
|
||||||
* ALWAYS <br> <br>
|
|
||||||
*
|
|
||||||
* This represents how far the LODs should overlap with
|
|
||||||
* the vanilla Minecraft terrain.
|
|
||||||
*
|
|
||||||
* @author James Seibel
|
|
||||||
* @since API 2.0.0
|
|
||||||
* @version 2024-4-6
|
|
||||||
*/
|
|
||||||
@Deprecated // not currently in use, if the config this enum represents is re-implemented, the deprecated flag can be removed
|
|
||||||
public enum EDhApiVanillaOverdraw
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Don't draw LODs where a minecraft chunk could be.
|
|
||||||
* Use Overdraw Offset to tweak the border thickness.
|
|
||||||
*/
|
|
||||||
NEVER,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw LODs over the farther minecraft chunks.
|
|
||||||
* Dynamically decides the border thickness
|
|
||||||
*/
|
|
||||||
DYNAMIC,
|
|
||||||
|
|
||||||
/** Draw LODs over all minecraft chunks. */
|
|
||||||
ALWAYS,
|
|
||||||
}
|
|
||||||
+9
-9
@@ -20,17 +20,17 @@
|
|||||||
package com.seibel.distanthorizons.api.enums.rendering;
|
package com.seibel.distanthorizons.api.enums.rendering;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default <br>
|
* DEFAULT <br>
|
||||||
* Debug <br>
|
* DEBUG_TRIANGLE <br>
|
||||||
* Disabled <br>
|
* DISABLED <br>
|
||||||
*
|
*
|
||||||
* @since API 2.0.0
|
* @since API 2.0.0
|
||||||
* @version 2024-4-6
|
* @version 2026-03-23
|
||||||
*/
|
*/
|
||||||
public enum EDhApiRendererMode
|
public enum EDhApiRendererMode
|
||||||
{
|
{
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
DEBUG,
|
DEBUG_TRIANGLE,
|
||||||
DISABLED;
|
DISABLED;
|
||||||
|
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ public enum EDhApiRendererMode
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case DEFAULT:
|
case DEFAULT:
|
||||||
return DEBUG;
|
return DEBUG_TRIANGLE;
|
||||||
case DEBUG:
|
case DEBUG_TRIANGLE:
|
||||||
return DISABLED;
|
return DISABLED;
|
||||||
default:
|
default:
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
@@ -55,10 +55,10 @@ public enum EDhApiRendererMode
|
|||||||
{
|
{
|
||||||
case DEFAULT:
|
case DEFAULT:
|
||||||
return DISABLED;
|
return DISABLED;
|
||||||
case DEBUG:
|
case DEBUG_TRIANGLE:
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
default:
|
default:
|
||||||
return DEBUG;
|
return DEBUG_TRIANGLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-15
@@ -21,25 +21,13 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* DISABLED, <br>
|
* DISABLED, <br>
|
||||||
* FAKE, <br>
|
|
||||||
* COMPLETE, <br>
|
* COMPLETE, <br>
|
||||||
*
|
*
|
||||||
* @since API 2.0.0
|
* @since API 2.0.0
|
||||||
* @version 2024-4-6
|
* @version 2026-05-19
|
||||||
*/
|
*/
|
||||||
public enum EDhApiTransparency
|
public enum EDhApiTransparency
|
||||||
{
|
{
|
||||||
DISABLED(false, false),
|
DISABLED,
|
||||||
FAKE(true, true),
|
COMPLETE;
|
||||||
COMPLETE(true, false);
|
|
||||||
|
|
||||||
public final boolean transparencyEnabled;
|
|
||||||
public final boolean fakeTransparencyEnabled;
|
|
||||||
|
|
||||||
EDhApiTransparency(boolean transparencyEnabled, boolean fakeTransparencyEnabled)
|
|
||||||
{
|
|
||||||
this.transparencyEnabled = transparencyEnabled;
|
|
||||||
this.fakeTransparencyEnabled = fakeTransparencyEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.api.interfaces;
|
package com.seibel.distanthorizons.api.interfaces;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented by wrappers so developers can
|
* Implemented by wrappers so developers can
|
||||||
* access the underlying Minecraft object(s).
|
* access the underlying Minecraft object(s).
|
||||||
@@ -38,7 +40,11 @@ public interface IDhApiUnsafeWrapper
|
|||||||
* In order to cast this object to something usable, you may want
|
* In order to cast this object to something usable, you may want
|
||||||
* to use <code>obj.getClass()</code> when in your IDE
|
* to use <code>obj.getClass()</code> when in your IDE
|
||||||
* in order to determine what object this method returns for
|
* in order to determine what object this method returns for
|
||||||
* the specific version of Minecraft you are developing for.
|
* the specific version of Minecraft you are developing for. <br><br>
|
||||||
|
*
|
||||||
|
* Note:<br>
|
||||||
|
* This method may return null in some cases, IE {@link IDhApiBlockStateWrapper}
|
||||||
|
* when it is wrapping air.
|
||||||
*/
|
*/
|
||||||
Object getWrappedMcObject();
|
Object getWrappedMcObject();
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -23,7 +23,10 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
|||||||
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
|
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Minecraft version independent way of handling Blocks.
|
* A Minecraft version independent way of handling Blocks. <br><br>
|
||||||
|
*
|
||||||
|
* Note: the wrapped object (IE the object returned by {@link IDhApiUnsafeWrapper#getWrappedMcObject})
|
||||||
|
* will be null if this object is wrapping air.
|
||||||
*
|
*
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2023-6-11
|
* @version 2023-6-11
|
||||||
@@ -39,6 +42,12 @@ public interface IDhApiBlockStateWrapper extends IDhApiUnsafeWrapper
|
|||||||
/** @since API 1.0.0 */
|
/** @since API 1.0.0 */
|
||||||
boolean isLiquid();
|
boolean isLiquid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value between 0 (fully transparent) and 16 (fully opaque).
|
||||||
|
* @since 6.1.0
|
||||||
|
*/
|
||||||
|
int getOpacity();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the full serialized form of the given block
|
* Returns the full serialized form of the given block
|
||||||
* as defined by DH's serialization methods.
|
* as defined by DH's serialization methods.
|
||||||
|
|||||||
+12
@@ -85,6 +85,18 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
|
|||||||
/** Modifies the quadratic function fake chunks use for horizontal quality drop-off. */
|
/** Modifies the quadratic function fake chunks use for horizontal quality drop-off. */
|
||||||
IDhApiConfigValue<EDhApiHorizontalQuality> horizontalQuality();
|
IDhApiConfigValue<EDhApiHorizontalQuality> horizontalQuality();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true DH will try to use the camera position when
|
||||||
|
* determining LOD quality drop-off. <br>
|
||||||
|
* If false DH will use the player's position.
|
||||||
|
* <br><br>
|
||||||
|
* Enabling helps free-cam mods render correctly. <br>
|
||||||
|
* Disabling helps multi-camera mods render correctly (ie Immersive Portals or camera mods).
|
||||||
|
*
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
IDhApiConfigValue<Boolean> useCameraPositionForQualityDropOff();
|
||||||
|
|
||||||
IDhApiConfigValue<EDhApiTransparency> transparency();
|
IDhApiConfigValue<EDhApiTransparency> transparency();
|
||||||
|
|
||||||
/** Defines what blocks won't be rendered as LODs. */
|
/** Defines what blocks won't be rendered as LODs. */
|
||||||
|
|||||||
+6
@@ -24,4 +24,10 @@ public interface IDhApiTerrainDataCache extends AutoCloseable
|
|||||||
*/
|
*/
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
// override without an exception
|
||||||
|
@Override
|
||||||
|
void close();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -19,9 +19,21 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.api.interfaces.override.rendering;
|
package com.seibel.distanthorizons.api.interfaces.override.rendering;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderingEngine;
|
||||||
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
|
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <b>Note: </b><br>
|
||||||
|
* This is only used if Distant Horizons'
|
||||||
|
* {@link IDhApiRenderProxy#getRenderingApi()} returns {@link EDhApiRenderingEngine#OPEN_GL}
|
||||||
|
* and {@link IDhApiRenderProxy#isNativeRenderer()} returns true.
|
||||||
|
* ie this is only used when DH is doing native OpenGL rendering,
|
||||||
|
* if DH is using Blaze3D this interface will be ignored.
|
||||||
|
*
|
||||||
|
* @see IDhApiRenderProxy#getRenderingApi()
|
||||||
|
* @see IDhApiRenderProxy#isNativeRenderer()
|
||||||
|
*
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2024-1-24
|
* @version 2024-1-24
|
||||||
* @since API 2.0.0
|
* @since API 2.0.0
|
||||||
|
|||||||
+11
@@ -19,7 +19,9 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.api.interfaces.override.rendering;
|
package com.seibel.distanthorizons.api.interfaces.override.rendering;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderingEngine;
|
||||||
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
|
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
|
||||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
|
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
|
||||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
|
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
|
||||||
@@ -27,7 +29,16 @@ import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
|
|||||||
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
|
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <b>Note: </b><br>
|
||||||
|
* This is only used if Distant Horizons'
|
||||||
|
* {@link IDhApiRenderProxy#getRenderingApi()} returns {@link EDhApiRenderingEngine#OPEN_GL}
|
||||||
|
* and {@link IDhApiRenderProxy#isNativeRenderer()} returns true.
|
||||||
|
* ie this is only used when DH is doing native OpenGL rendering,
|
||||||
|
* if DH is using Blaze3D this interface will be ignored.
|
||||||
|
*
|
||||||
* @see IDhApiShaderProgram
|
* @see IDhApiShaderProgram
|
||||||
|
* @see IDhApiRenderProxy#getRenderingApi()
|
||||||
|
* @see IDhApiRenderProxy#isNativeRenderer()
|
||||||
*
|
*
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2024-7-11
|
* @version 2024-7-11
|
||||||
|
|||||||
+11
@@ -19,12 +19,23 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.api.interfaces.override.rendering;
|
package com.seibel.distanthorizons.api.interfaces.override.rendering;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderingEngine;
|
||||||
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
|
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
|
||||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
|
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <b>Note: </b><br>
|
||||||
|
* This is only used if Distant Horizons'
|
||||||
|
* {@link IDhApiRenderProxy#getRenderingApi()} returns {@link EDhApiRenderingEngine#OPEN_GL}
|
||||||
|
* and {@link IDhApiRenderProxy#isNativeRenderer()} returns true.
|
||||||
|
* ie this is only used when DH is doing native OpenGL rendering,
|
||||||
|
* if DH is using Blaze3D this interface will be ignored.
|
||||||
|
*
|
||||||
* @see IDhApiGenericObjectShaderProgram
|
* @see IDhApiGenericObjectShaderProgram
|
||||||
|
* @see IDhApiRenderProxy#getRenderingApi()
|
||||||
|
* @see IDhApiRenderProxy#isNativeRenderer()
|
||||||
*
|
*
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2024-1-24
|
* @version 2024-1-24
|
||||||
|
|||||||
+36
-4
@@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.api.interfaces.render;
|
package com.seibel.distanthorizons.api.interfaces.render;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderingApi;
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderingEngine;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
|
||||||
import com.seibel.distanthorizons.api.objects.DhApiResult;
|
import com.seibel.distanthorizons.api.objects.DhApiResult;
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +47,33 @@ public interface IDhApiRenderProxy
|
|||||||
*/
|
*/
|
||||||
DhApiResult<Boolean> clearRenderDataCache();
|
DhApiResult<Boolean> clearRenderDataCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns which specific {@link EDhApiRenderingApi}
|
||||||
|
* Distant Horizons will use for rendering. <br><br>
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no renderer has been bound yet,
|
||||||
|
* wait till after {@link DhApiAfterDhInitEvent} has been fired
|
||||||
|
*
|
||||||
|
* @see DhApiAfterDhInitEvent
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
EDhApiRenderingApi getRenderingApi() throws IllegalStateException;
|
||||||
|
/**
|
||||||
|
* Returns true if the current renderer
|
||||||
|
* is calling the base rendering API's method calls. <br>
|
||||||
|
* ie GL.drawArrays() for OpenGL. <Br><br>
|
||||||
|
*
|
||||||
|
* If DH is using a rendering interpretation layer like Blaze3D (Mojang's rendering API)
|
||||||
|
* this will return false.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no renderer has been bound yet,
|
||||||
|
* wait till after {@link DhApiAfterDhInitEvent} has been fired
|
||||||
|
*
|
||||||
|
* @see DhApiAfterDhInitEvent
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
boolean isNativeRenderer() throws IllegalStateException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======================//
|
//=======================//
|
||||||
@@ -51,14 +81,16 @@ public interface IDhApiRenderProxy
|
|||||||
//=======================//
|
//=======================//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of Distant Horizons' depth texture. <br>
|
* Returns the OpenGL name of Distant Horizons' depth texture. <br>
|
||||||
* Will return {@link DhApiResult#success} = false and {@link DhApiResult#payload} = -1 if the texture hasn't been created yet.
|
* Will return {@link DhApiResult#success} = false and {@link DhApiResult#payload} = -1 if the texture hasn't been created yet
|
||||||
|
* or a rendering API other than OpenGL is in use.
|
||||||
*/
|
*/
|
||||||
DhApiResult<Integer> getDhDepthTextureId();
|
DhApiResult<Integer> getDhDepthTextureId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of Distant Horizons' color texture. <br>
|
* Returns the OpenGL name of Distant Horizons' color texture. <br>
|
||||||
* Will return {@link DhApiResult#success} = false and {@link DhApiResult#payload} = -1 if the texture hasn't been created yet.
|
* Will return {@link DhApiResult#success} = false and {@link DhApiResult#payload} = -1 if the texture hasn't been created yet
|
||||||
|
* or a rendering API other than OpenGL is in use
|
||||||
*/
|
*/
|
||||||
DhApiResult<Integer> getDhColorTextureId();
|
DhApiResult<Integer> getDhColorTextureId();
|
||||||
|
|
||||||
|
|||||||
+26
@@ -21,8 +21,14 @@ package com.seibel.distanthorizons.api.interfaces.world;
|
|||||||
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
|
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent;
|
||||||
|
import com.seibel.distanthorizons.api.objects.DhApiResult;
|
||||||
|
import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,6 +96,26 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
|
|||||||
*/
|
*/
|
||||||
File getDhSaveFolder();
|
File getDhSaveFolder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color DH would use for the given block/biome
|
||||||
|
* pair at the given world position before any API color overrides
|
||||||
|
* are considered. <br>
|
||||||
|
* API color overrides are ignored to prevent infinite
|
||||||
|
* loops if this event is triggered inside said API override.
|
||||||
|
* <br><br>
|
||||||
|
*
|
||||||
|
* Returns {@link DhApiResult#success} = false if {@link IDhApiLevelWrapper#getLevelType()} returns a {@link EDhApiLevelType#SERVER_LEVEL}
|
||||||
|
* (server levels have no concept of textures or colors).
|
||||||
|
*
|
||||||
|
* @see DhApiBlockColorOverrideEvent
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
DhApiResult<Color> getBlockColorPreApi(
|
||||||
|
IDhApiBlockStateWrapper blockStateWrapper,
|
||||||
|
IDhApiBiomeWrapper biomeWrapper,
|
||||||
|
int blockWorldPosX, int blockWorldPosY, int blockWorldPosZ,
|
||||||
|
IDhApiFullDataSource dataSource);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-10
@@ -27,7 +27,7 @@ import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called before Distant Horizons starts rendering a buffer. <br>
|
* Called before Distant Horizons starts rendering a buffer. <br>
|
||||||
* This event cannot be cancelled, use {@link DhApiBeforeRenderEvent} if you want to cancel rendering.
|
* This event cannot be canceled, use {@link DhApiBeforeRenderEvent} if you want to cancel rendering.
|
||||||
*
|
*
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2023-1-31
|
* @version 2023-1-31
|
||||||
@@ -59,23 +59,25 @@ public abstract class DhApiBeforeBufferRenderEvent implements IDhApiEvent<DhApiB
|
|||||||
* Measured in blocks.
|
* Measured in blocks.
|
||||||
* Should be applied to the model view matrix to move the buffer into its proper place.
|
* Should be applied to the model view matrix to move the buffer into its proper place.
|
||||||
*/
|
*/
|
||||||
public final DhApiVec3f modelPos;
|
public DhApiVec3f modelPos;
|
||||||
|
|
||||||
|
|
||||||
public EventParam(DhApiRenderParam parent, DhApiVec3f modelPos)
|
|
||||||
|
public EventParam() { }
|
||||||
|
|
||||||
|
public void update(DhApiRenderParam parent, DhApiVec3f modelPos)
|
||||||
{
|
{
|
||||||
super(parent);
|
super.update(parent);
|
||||||
this.modelPos = modelPos;
|
this.modelPos = modelPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EventParam copy()
|
public boolean getCopyBeforeFire() { return false; }
|
||||||
{
|
|
||||||
return new EventParam(
|
@Override
|
||||||
this, this.modelPos.copy()
|
public EventParam copy() { return this; }
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+3
@@ -31,7 +31,10 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
|
|||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2025-6-9
|
* @version 2025-6-9
|
||||||
* @since API 4.1.0
|
* @since API 4.1.0
|
||||||
|
* @deprecated Only used for the legacy OpenGL renderer <Br>
|
||||||
|
* Using {@link DhApiAfterColorDepthTextureCreatedEvent} instead is recommended.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class DhApiBeforeColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiTextureCreatedParam>
|
public abstract class DhApiBeforeColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiTextureCreatedParam>
|
||||||
{
|
{
|
||||||
/** Fired before Distant Horizons creates. */
|
/** Fired before Distant Horizons creates. */
|
||||||
|
|||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiFogRenderParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiMutableFogRenderParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||||
|
import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired before DH renders its fog.
|
||||||
|
* Canceling this event disables fog for that frame.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 2026-05-20
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
public abstract class DhApiBeforeFogRenderEvent implements IDhApiCancelableEvent<DhApiBeforeFogRenderEvent.EventParam>
|
||||||
|
{
|
||||||
|
/** Fired before fog is generated. */
|
||||||
|
public abstract void beforeRender(DhApiCancelableEventParam<DhApiBeforeFogRenderEvent.EventParam> event);
|
||||||
|
|
||||||
|
|
||||||
|
//=========================//
|
||||||
|
// internal DH API methods //
|
||||||
|
//=========================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void fireEvent(DhApiCancelableEventParam<DhApiBeforeFogRenderEvent.EventParam> event) { this.beforeRender(event); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==================//
|
||||||
|
// parameter object //
|
||||||
|
//==================//
|
||||||
|
|
||||||
|
public static class EventParam implements IDhApiEventParam
|
||||||
|
{
|
||||||
|
private DhApiRenderParam renderParam;
|
||||||
|
private DhApiFogRenderParam originalFogRenderParam;
|
||||||
|
private DhApiMutableFogRenderParam fogRenderParam;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public EventParam() {}
|
||||||
|
|
||||||
|
public void update(DhApiRenderParam renderParam, DhApiFogRenderParam fogRenderParam)
|
||||||
|
{
|
||||||
|
this.renderParam = renderParam;
|
||||||
|
this.originalFogRenderParam = fogRenderParam;
|
||||||
|
this.fogRenderParam = new DhApiMutableFogRenderParam(fogRenderParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// getters/setters //
|
||||||
|
//=================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public DhApiRenderParam getRenderParam() { return this.renderParam; }
|
||||||
|
|
||||||
|
/** immutable, stores what DH would do without API intervention so API users have a reference point */
|
||||||
|
public DhApiFogRenderParam getOriginalFogRenderParam() { return this.originalFogRenderParam; }
|
||||||
|
/** mutable, can be modified by API users */
|
||||||
|
public DhApiMutableFogRenderParam getFogRenderParam() { return this.fogRenderParam; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==========================//
|
||||||
|
// base api event overrides //
|
||||||
|
//==========================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the same instance of this event.
|
||||||
|
* Copying this event isn't supported
|
||||||
|
* since the internal parameters must be mutated
|
||||||
|
* by API users in order to be tracked by DH's internal
|
||||||
|
* logic.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DhApiBeforeFogRenderEvent.EventParam copy() { return this; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getCopyBeforeFire() { return false; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+26
-25
@@ -54,44 +54,45 @@ public abstract class DhApiBeforeGenericObjectRenderEvent implements IDhApiCance
|
|||||||
|
|
||||||
public static class EventParam extends DhApiRenderParam implements IDhApiEventParam
|
public static class EventParam extends DhApiRenderParam implements IDhApiEventParam
|
||||||
{
|
{
|
||||||
public final long boxGroupId;
|
public long boxGroupId;
|
||||||
public final String resourceLocationNamespace;
|
public String resourceLocationNamespace;
|
||||||
public final String resourceLocationPath;
|
public String resourceLocationPath;
|
||||||
|
|
||||||
|
|
||||||
public EventParam(
|
|
||||||
DhApiRenderParam renderParam,
|
//==============//
|
||||||
IDhApiRenderableBoxGroup boxGroup
|
// constructors //
|
||||||
)
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public EventParam() { }
|
||||||
|
|
||||||
|
/** internal DH method */
|
||||||
|
public void update(DhApiRenderParam renderParam, IDhApiRenderableBoxGroup boxGroup)
|
||||||
{
|
{
|
||||||
super(renderParam);
|
super.update(renderParam);
|
||||||
|
|
||||||
this.boxGroupId = boxGroup.getId();
|
this.boxGroupId = boxGroup.getId();
|
||||||
this.resourceLocationNamespace = boxGroup.getResourceLocationNamespace();
|
this.resourceLocationNamespace = boxGroup.getResourceLocationNamespace();
|
||||||
this.resourceLocationPath = boxGroup.getResourceLocationPath();
|
this.resourceLocationPath = boxGroup.getResourceLocationPath();
|
||||||
}
|
}
|
||||||
public EventParam(
|
|
||||||
DhApiRenderParam renderParam,
|
|
||||||
long boxGroupId, String resourceLocationNamespace, String resourceLocationPath
|
|
||||||
)
|
|
||||||
{
|
|
||||||
super(renderParam);
|
|
||||||
|
|
||||||
this.boxGroupId = boxGroupId;
|
//endregion
|
||||||
this.resourceLocationNamespace = resourceLocationNamespace;
|
|
||||||
this.resourceLocationPath = resourceLocationPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EventParam copy()
|
public EventParam copy() { return this; }
|
||||||
{
|
|
||||||
return new EventParam(
|
//endregion
|
||||||
this,
|
|
||||||
this.boxGroupId, this.resourceLocationNamespace, this.resourceLocationPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+187
@@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performance note: this event will be fired millions of times on concurrent threads,
|
||||||
|
* make it thread safe and as fast as possible. <br>
|
||||||
|
* (If every LOD block goes through this event, On a 512 render distance world,
|
||||||
|
* at the medium quality preset, it will be triggered around 40,000,000 times.)
|
||||||
|
* <Br><Br>
|
||||||
|
*
|
||||||
|
* This event is fired when DH needs to convert a {@link IDhApiBlockStateWrapper}
|
||||||
|
* into a color for rendering. This event is fired after DH attempts to determine
|
||||||
|
* the color itself (using the base color, tinting, etc.). <Br>
|
||||||
|
* Using this event will override the tinting config for this block
|
||||||
|
* unless you re-implement that logic yourself.
|
||||||
|
* <Br><Br>
|
||||||
|
*
|
||||||
|
* This event will only trigger for {@link IDhApiBlockStateWrapper}s that have been registered
|
||||||
|
* via {@link DhApiBlockStateWrapperCreatedEvent.EventParam#setAllowApiColorOverride(boolean)}.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 2026-05-18
|
||||||
|
* @since API 6.0.0
|
||||||
|
* @see IDhApiBlockStateWrapper
|
||||||
|
*/
|
||||||
|
public abstract class DhApiBlockColorOverrideEvent implements IDhApiEvent<DhApiBlockColorOverrideEvent.EventParam>
|
||||||
|
{
|
||||||
|
public abstract void onBlockColorOverridden(DhApiEventParam<EventParam> event);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=========================//
|
||||||
|
// internal DH API methods //
|
||||||
|
//=========================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void fireEvent(DhApiEventParam<EventParam> event) { this.onBlockColorOverridden(event); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==================//
|
||||||
|
// parameter object //
|
||||||
|
//==================//
|
||||||
|
|
||||||
|
public static class EventParam implements IDhApiEventParam
|
||||||
|
{
|
||||||
|
private IDhApiLevelWrapper levelWrapper;
|
||||||
|
private IDhApiFullDataSource dataSource;
|
||||||
|
private IDhApiBlockStateWrapper blockStateWrapper = null;
|
||||||
|
private IDhApiBiomeWrapper biomeWrapper = null;
|
||||||
|
private int colorAsInt = -1;
|
||||||
|
private int blockPosX = 0, blockPosY = 0, blockPosZ = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public EventParam() {}
|
||||||
|
|
||||||
|
public void update(
|
||||||
|
IDhApiLevelWrapper levelWrapper,
|
||||||
|
IDhApiFullDataSource dataSource,
|
||||||
|
IDhApiBlockStateWrapper blockStateWrapper,
|
||||||
|
IDhApiBiomeWrapper biomeWrapper,
|
||||||
|
int colorAsInt,
|
||||||
|
int blockPosX, int blockPosY, int blockPosZ)
|
||||||
|
{
|
||||||
|
this.levelWrapper = levelWrapper;
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
this.blockStateWrapper = blockStateWrapper;
|
||||||
|
this.biomeWrapper = biomeWrapper;
|
||||||
|
this.colorAsInt = colorAsInt;
|
||||||
|
|
||||||
|
this.blockPosX = blockPosX;
|
||||||
|
this.blockPosY = blockPosY;
|
||||||
|
this.blockPosZ = blockPosZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// getters/setters //
|
||||||
|
//=================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public IDhApiBlockStateWrapper getBlockStateWrapper() { return this.blockStateWrapper; }
|
||||||
|
/** @since API 7.0.0 */
|
||||||
|
public IDhApiBiomeWrapper getBiomeWrapper() { return this.biomeWrapper; }
|
||||||
|
|
||||||
|
/** the level DH is resolving this block's color in. */
|
||||||
|
public IDhApiLevelWrapper getLevelWrapper() { return this.levelWrapper; }
|
||||||
|
/**
|
||||||
|
* The DH datasource that contains this block's position. Can be used to access adjacent
|
||||||
|
* {@link IDhApiBlockStateWrapper}'s and {@link IDhApiBiomeWrapper}'s for adjacent aware tinting.
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
public IDhApiFullDataSource getDataSource() { return this.dataSource; }
|
||||||
|
|
||||||
|
public int getColorAsInt() { return this.colorAsInt; }
|
||||||
|
public int getAlpha() { return ColorUtil.getAlpha(this.colorAsInt); }
|
||||||
|
public int getRed() { return ColorUtil.getRed(this.colorAsInt); }
|
||||||
|
public int getGreen() { return ColorUtil.getGreen(this.colorAsInt); }
|
||||||
|
public int getBlue() { return ColorUtil.getBlue(this.colorAsInt); }
|
||||||
|
public void setColor(int red, int green, int blue) throws IllegalArgumentException { this.setColor(this.getAlpha(), red, green, blue); }
|
||||||
|
/**
|
||||||
|
* Note: when if you set a partially transparent alpha channel the underlying {@link IDhApiBlockStateWrapper#getOpacity()}
|
||||||
|
* method should also return a non-opaque value.
|
||||||
|
* Otherwise LODs may behave incorrectly.
|
||||||
|
*/
|
||||||
|
public void setColor(int alpha, int red, int green, int blue) throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
ColorUtil.throwIfColorValueOutOfIntRange("alpha", alpha);
|
||||||
|
ColorUtil.throwIfColorValueOutOfIntRange("red", red);
|
||||||
|
ColorUtil.throwIfColorValueOutOfIntRange("green", green);
|
||||||
|
ColorUtil.throwIfColorValueOutOfIntRange("blue", blue);
|
||||||
|
|
||||||
|
this.colorAsInt = ColorUtil.argbToInt(alpha, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the block's X value in the world */
|
||||||
|
public int getBlockPosX() { return blockPosX; }
|
||||||
|
/** @return the block's Y value in the world */
|
||||||
|
public int getBlockPosY() { return blockPosY; }
|
||||||
|
/** @return the block's Z value in the world */
|
||||||
|
public int getBlockPosZ() { return blockPosZ; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==========================//
|
||||||
|
// base api event overrides //
|
||||||
|
//==========================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the same instance of this event.
|
||||||
|
* Copying this event isn't supported
|
||||||
|
* since the internal parameters must be mutated
|
||||||
|
* by API users in order to be tracked by DH's internal
|
||||||
|
* logic.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EventParam copy() { return this; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getCopyBeforeFire() { return false; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to modify {@link IDhApiBlockStateWrapper}'s as they're created.
|
||||||
|
* This can be helpful for modded blocks that are mis-categorized by DH's base logic. <br><br>
|
||||||
|
*
|
||||||
|
* Note: this is only fired once per {@link IDhApiBlockStateWrapper} that is created
|
||||||
|
* and those {@link IDhApiBlockStateWrapper} will only be created once per JVM session.
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 2026-04-14
|
||||||
|
* @since API 6.0.0
|
||||||
|
* @see IDhApiBlockStateWrapper
|
||||||
|
*/
|
||||||
|
public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<DhApiBlockStateWrapperCreatedEvent.EventParam>
|
||||||
|
{
|
||||||
|
public abstract void blockStateWrapperCreated(DhApiEventParam<EventParam> event);
|
||||||
|
|
||||||
|
|
||||||
|
//=========================//
|
||||||
|
// internal DH API methods //
|
||||||
|
//=========================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void fireEvent(DhApiEventParam<EventParam> event) { this.blockStateWrapperCreated(event); }
|
||||||
|
|
||||||
|
|
||||||
|
//==================//
|
||||||
|
// parameter object //
|
||||||
|
//==================//
|
||||||
|
|
||||||
|
public static class EventParam implements IDhApiEventParam
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A copy of the wrapper that will be created. <Br>
|
||||||
|
* Note: modifying this object won't change anything
|
||||||
|
* a new wrapper will be created after this event finishes.
|
||||||
|
*/
|
||||||
|
private final IDhApiBlockStateWrapper blockStateWrapper;
|
||||||
|
|
||||||
|
private boolean overridesSet = false;
|
||||||
|
private EDhApiBlockMaterial blockMaterial = null;
|
||||||
|
private Integer opacity = null;
|
||||||
|
private Boolean allowApiColorOverride = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
public EventParam(IDhApiBlockStateWrapper blockStateWrapper) { this.blockStateWrapper = blockStateWrapper; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// getters/setters //
|
||||||
|
//=================//
|
||||||
|
|
||||||
|
public IDhApiBlockStateWrapper getBlockStateWrapper() { return this.blockStateWrapper; }
|
||||||
|
|
||||||
|
/** if set this will override the value currently set in the given {@link IDhApiBlockStateWrapper} */
|
||||||
|
public void setBlockMaterial(EDhApiBlockMaterial blockMaterial)
|
||||||
|
{
|
||||||
|
this.blockMaterial = blockMaterial;
|
||||||
|
this.overridesSet = true;
|
||||||
|
}
|
||||||
|
public EDhApiBlockMaterial getBlockMaterial() { return this.blockMaterial; }
|
||||||
|
|
||||||
|
/** if set this will override the value currently set in the given {@link IDhApiBlockStateWrapper} */
|
||||||
|
public void setOpacity(int opacity)
|
||||||
|
{
|
||||||
|
this.opacity = opacity;
|
||||||
|
this.overridesSet = true;
|
||||||
|
}
|
||||||
|
public Integer getOpacity() { return this.opacity; }
|
||||||
|
|
||||||
|
/** if set to true this {@link IDhApiBlockStateWrapper} will trigger {@link DhApiBlockColorOverrideEvent} */
|
||||||
|
public void setAllowApiColorOverride(boolean allowApiColorOverride)
|
||||||
|
{
|
||||||
|
this.allowApiColorOverride = allowApiColorOverride;
|
||||||
|
this.overridesSet = true;
|
||||||
|
}
|
||||||
|
public Boolean getAllowApiColorOverride() { return this.allowApiColorOverride; }
|
||||||
|
|
||||||
|
/** If true then one or more options for this block were set to be changed */
|
||||||
|
public boolean getOverridesSet() { return this.overridesSet; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the same instance of this event.
|
||||||
|
* Copying this event isn't supported
|
||||||
|
* since the internal parameters must be mutated
|
||||||
|
* by API users in order to be tracked by DH's internal
|
||||||
|
* logic.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EventParam copy() { return this; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getCopyBeforeFire() { return false; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
@@ -34,6 +34,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
|
|||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2023-6-23
|
* @version 2023-6-23
|
||||||
* @see IDhApiTerrainDataRepo
|
* @see IDhApiTerrainDataRepo
|
||||||
|
* @see DhApiChunkProcessingEvent
|
||||||
* @since API 1.0.0
|
* @since API 1.0.0
|
||||||
*/
|
*/
|
||||||
public abstract class DhApiChunkModifiedEvent implements IDhApiEvent<DhApiChunkModifiedEvent.EventParam>
|
public abstract class DhApiChunkModifiedEvent implements IDhApiEvent<DhApiChunkModifiedEvent.EventParam>
|
||||||
|
|||||||
+1
@@ -51,6 +51,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
|
|||||||
*
|
*
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2025-09-29
|
* @version 2025-09-29
|
||||||
|
* @see DhApiChunkModifiedEvent
|
||||||
* @since API 4.1.0
|
* @since API 4.1.0
|
||||||
*/
|
*/
|
||||||
public abstract class DhApiChunkProcessingEvent implements IDhApiEvent<DhApiChunkProcessingEvent.EventParam>
|
public abstract class DhApiChunkProcessingEvent implements IDhApiEvent<DhApiChunkProcessingEvent.EventParam>
|
||||||
|
|||||||
+3
-1
@@ -31,7 +31,9 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
|
|||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2024-3-2
|
* @version 2024-3-2
|
||||||
* @since API 2.0.0
|
* @since API 2.0.0
|
||||||
* @deprecated Replaced by {@link DhApiBeforeColorDepthTextureCreatedEvent} since this event's name isn't obvious when it fires.
|
* @deprecated Only used for the legacy OpenGL renderer <Br>
|
||||||
|
* Replaced by {@link DhApiBeforeColorDepthTextureCreatedEvent} since this event's name isn't obvious when it fires.
|
||||||
|
* Using {@link DhApiAfterColorDepthTextureCreatedEvent} instead is recommended
|
||||||
*/
|
*/
|
||||||
@Deprecated // internal notes: this method must be kept around due to Iris using it and we don't want to break old Iris support
|
@Deprecated // internal notes: this method must be kept around due to Iris using it and we don't want to break old Iris support
|
||||||
public abstract class DhApiColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiColorDepthTextureCreatedEvent.EventParam>
|
public abstract class DhApiColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiColorDepthTextureCreatedEvent.EventParam>
|
||||||
|
|||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.api.methods.events.sharedParameterObjects;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection;
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFarFogConfig;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiHeightFogConfig;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains all the information needed to render Distant Horizons' fog.
|
||||||
|
*
|
||||||
|
* @see IDhApiFogConfig
|
||||||
|
* @see IDhApiFarFogConfig
|
||||||
|
* @see IDhApiHeightFogConfig
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 2026-05-20
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
public class DhApiFogRenderParam implements IDhApiEventParam
|
||||||
|
{
|
||||||
|
protected Color fogColor;
|
||||||
|
public Color getFogColor() { return this.fogColor; }
|
||||||
|
|
||||||
|
// far fog //
|
||||||
|
//region
|
||||||
|
|
||||||
|
protected EDhApiFogFalloff farFogFalloff;
|
||||||
|
/** @see IDhApiFarFogConfig#farFogFalloff() */
|
||||||
|
public EDhApiFogFalloff getFarFogFalloff() { return this.farFogFalloff; }
|
||||||
|
|
||||||
|
protected float farFogStartPercent;
|
||||||
|
/** @see IDhApiFarFogConfig#farFogStartDistance() */
|
||||||
|
public float getFarFogStartPercent() { return this.farFogStartPercent; }
|
||||||
|
|
||||||
|
protected float farFogEndPercent;
|
||||||
|
/** @see IDhApiFarFogConfig#farFogEndDistance() */
|
||||||
|
public float getFarFogEndPercent() { return this.farFogEndPercent; }
|
||||||
|
|
||||||
|
protected float farFogMinThickness;
|
||||||
|
/** @see IDhApiFarFogConfig#farFogMinThickness() */
|
||||||
|
public float getFarFogMinThickness() { return this.farFogMinThickness; }
|
||||||
|
|
||||||
|
protected float farFogMaxThickness;
|
||||||
|
/** @see IDhApiFarFogConfig#farFogMaxThickness() */
|
||||||
|
public float getFarFogMaxThickness() { return this.farFogMaxThickness; }
|
||||||
|
|
||||||
|
protected float farFogDensity;
|
||||||
|
/** @see IDhApiFarFogConfig#farFogDensity() */
|
||||||
|
public float getFarFogDensity() { return this.farFogDensity; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
// height fog //
|
||||||
|
//region
|
||||||
|
|
||||||
|
protected EDhApiFogFalloff heightFogFalloff;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogFalloff() */
|
||||||
|
public EDhApiFogFalloff getHeightFogFalloff() { return this.heightFogFalloff; }
|
||||||
|
|
||||||
|
protected EDhApiHeightFogMixMode heightFogMixingMode;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogMixMode() */
|
||||||
|
public EDhApiHeightFogMixMode getHeightFogMixingMode() { return this.heightFogMixingMode; }
|
||||||
|
|
||||||
|
protected EDhApiHeightFogDirection heightFogDirection;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogDirection() */
|
||||||
|
public EDhApiHeightFogDirection getHeightFogDirection() { return this.heightFogDirection; }
|
||||||
|
|
||||||
|
protected float heightFogBaseHeight;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogBaseHeight() */
|
||||||
|
public float getHeightFogBaseHeight() { return this.heightFogBaseHeight; }
|
||||||
|
|
||||||
|
protected float heightFogStartPercent;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogStartingHeightPercent() */
|
||||||
|
public float getHeightFogStartPercent() { return this.heightFogStartPercent; }
|
||||||
|
|
||||||
|
protected float heightFogEndPercent;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogEndingHeightPercent() */
|
||||||
|
public float getHeightFogEndPercent() { return this.heightFogEndPercent; }
|
||||||
|
|
||||||
|
protected float heightFogMinThickness;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogMinThickness() */
|
||||||
|
public float getHeightFogMinThickness() { return this.heightFogMinThickness; }
|
||||||
|
|
||||||
|
protected float heightFogMaxThickness;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogMaxThickness() */
|
||||||
|
public float getHeightFogMaxThickness() { return this.heightFogMaxThickness; }
|
||||||
|
|
||||||
|
protected float heightFogDensity;
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogDensity() */
|
||||||
|
public float getHeightFogDensity() { return this.heightFogDensity; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// constructors //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public DhApiFogRenderParam(DhApiFogRenderParam parent)
|
||||||
|
{
|
||||||
|
this(
|
||||||
|
parent.fogColor,
|
||||||
|
|
||||||
|
// far fog
|
||||||
|
parent.farFogFalloff,
|
||||||
|
parent.farFogStartPercent, parent.farFogEndPercent,
|
||||||
|
parent.farFogMinThickness, parent.farFogEndPercent,
|
||||||
|
parent.farFogDensity,
|
||||||
|
|
||||||
|
// height fog
|
||||||
|
parent.heightFogFalloff,
|
||||||
|
parent.heightFogMixingMode, parent.heightFogDirection,
|
||||||
|
parent.heightFogBaseHeight,
|
||||||
|
parent.heightFogStartPercent, parent.heightFogEndPercent,
|
||||||
|
parent.heightFogMinThickness, parent.heightFogMaxThickness,
|
||||||
|
parent.heightFogDensity
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public DhApiFogRenderParam(
|
||||||
|
Color fogColor,
|
||||||
|
|
||||||
|
// far fog
|
||||||
|
EDhApiFogFalloff farFogFalloff,
|
||||||
|
float farFogStartPercent, float farFogEndPercent,
|
||||||
|
float farFogMinThickness, float farFogMaxThickness,
|
||||||
|
float farFogDensity,
|
||||||
|
|
||||||
|
// height fog
|
||||||
|
EDhApiFogFalloff heightFogFalloff,
|
||||||
|
EDhApiHeightFogMixMode heightFogMixingMode, EDhApiHeightFogDirection heightFogDirection,
|
||||||
|
float heightFogBaseHeight,
|
||||||
|
float heightFogStartPercent, float heightFogEndPercent,
|
||||||
|
float heightFogMinThickness, float heightFogMaxThickness,
|
||||||
|
float heightFogDensity
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.fogColor = fogColor;
|
||||||
|
|
||||||
|
// far fog
|
||||||
|
this.farFogFalloff = farFogFalloff;
|
||||||
|
this.farFogStartPercent = farFogStartPercent;
|
||||||
|
this.farFogEndPercent = farFogEndPercent;
|
||||||
|
this.farFogMinThickness = farFogMinThickness;
|
||||||
|
this.farFogMaxThickness = farFogMaxThickness;
|
||||||
|
this.farFogDensity = farFogDensity;
|
||||||
|
|
||||||
|
// height fog
|
||||||
|
this.heightFogFalloff = heightFogFalloff;
|
||||||
|
this.heightFogMixingMode = heightFogMixingMode;
|
||||||
|
this.heightFogDirection = heightFogDirection;
|
||||||
|
this.heightFogBaseHeight = heightFogBaseHeight;
|
||||||
|
this.heightFogStartPercent = heightFogStartPercent;
|
||||||
|
this.heightFogEndPercent = heightFogEndPercent;
|
||||||
|
this.heightFogMinThickness = heightFogMinThickness;
|
||||||
|
this.heightFogMaxThickness = heightFogMaxThickness;
|
||||||
|
this.heightFogDensity = heightFogDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DhApiFogRenderParam copy() { return new DhApiFogRenderParam(this); }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.api.methods.events.sharedParameterObjects;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection;
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFarFogConfig;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig;
|
||||||
|
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiHeightFogConfig;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable version of {@link DhApiFogRenderParam} to allow
|
||||||
|
* API modification of DH's fog rendering.
|
||||||
|
*
|
||||||
|
* @see IDhApiFogConfig
|
||||||
|
* @see IDhApiFarFogConfig
|
||||||
|
* @see IDhApiHeightFogConfig
|
||||||
|
* @see DhApiFogRenderParam
|
||||||
|
*
|
||||||
|
* @author James Seibel
|
||||||
|
* @version 2026-05-20
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
public class DhApiMutableFogRenderParam extends DhApiFogRenderParam
|
||||||
|
{
|
||||||
|
public void setFogColor(Color fogColor) { this.fogColor = fogColor; }
|
||||||
|
|
||||||
|
// far fog //
|
||||||
|
//region
|
||||||
|
|
||||||
|
/** @see IDhApiFarFogConfig#farFogFalloff() */
|
||||||
|
public void setFarFogFalloff(EDhApiFogFalloff farFogFalloff) { this.farFogFalloff = farFogFalloff; }
|
||||||
|
/** @see IDhApiFarFogConfig#farFogStartDistance() */
|
||||||
|
public void setFarFogStartPercent(float farFogStartPercent) { this.farFogStartPercent = farFogStartPercent; }
|
||||||
|
/** @see IDhApiFarFogConfig#farFogEndDistance() */
|
||||||
|
public void setFarFogEndPercent(float farFogEndPercent) { this.farFogEndPercent = farFogEndPercent; }
|
||||||
|
/** @see IDhApiFarFogConfig#farFogMinThickness() */
|
||||||
|
public void setFarFogMinThickness(float farFogMinThickness) { this.farFogMinThickness = farFogMinThickness; }
|
||||||
|
/** @see IDhApiFarFogConfig#farFogMaxThickness() */
|
||||||
|
public void setFarFogMaxThickness(float farFogMaxThickness) { this.farFogMaxThickness = farFogMaxThickness; }
|
||||||
|
/** @see IDhApiFarFogConfig#farFogDensity() */
|
||||||
|
public void setFarFogDensity(float farFogDensity) { this.farFogDensity = farFogDensity; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
// height fog //
|
||||||
|
//region
|
||||||
|
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogFalloff() */
|
||||||
|
public void setHeightFogFalloff(EDhApiFogFalloff heightFogFalloff) { this.heightFogFalloff = heightFogFalloff; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogMixMode() */
|
||||||
|
public void setHeightFogMixingMode(EDhApiHeightFogMixMode heightFogMixingMode) { this.heightFogMixingMode = heightFogMixingMode; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogDirection() */
|
||||||
|
public void setHeightFogDirection(EDhApiHeightFogDirection heightFogDirection) { this.heightFogDirection = heightFogDirection; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogBaseHeight() */
|
||||||
|
public void setHeightFogBaseHeight(float heightFogBaseHeight) { this.heightFogBaseHeight = heightFogBaseHeight; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogStartingHeightPercent() */
|
||||||
|
public void setHeightFogStartPercent(float heightFogStartPercent) { this.heightFogStartPercent = heightFogStartPercent; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogEndingHeightPercent() */
|
||||||
|
public void setHeightFogEndPercent(float heightFogEnd) { this.heightFogEndPercent = heightFogEnd; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogMinThickness() */
|
||||||
|
public void setHeightFogMinThickness(float heightFogMinThickness) { this.heightFogMinThickness = heightFogMinThickness; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogMaxThickness() */
|
||||||
|
public void setHeightFogMaxThickness(float heightFogMaxThickness) { this.heightFogMaxThickness = heightFogMaxThickness; }
|
||||||
|
/** @see IDhApiHeightFogConfig#heightFogDensity() */
|
||||||
|
public void setHeightFogDensity(float heightFogDensity) { this.heightFogDensity = heightFogDensity; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// constructors //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public DhApiMutableFogRenderParam(DhApiFogRenderParam parent)
|
||||||
|
{ super(parent); }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DhApiMutableFogRenderParam copy() { return new DhApiMutableFogRenderParam(this); }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+58
-29
@@ -35,62 +35,69 @@ import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
|
|||||||
public class DhApiRenderParam implements IDhApiEventParam
|
public class DhApiRenderParam implements IDhApiEventParam
|
||||||
{
|
{
|
||||||
/** Indicates what render pass DH is currently rendering */
|
/** Indicates what render pass DH is currently rendering */
|
||||||
public final EDhApiRenderPass renderPass;
|
public EDhApiRenderPass renderPass;
|
||||||
|
|
||||||
/** Indicates how far into this tick the frame is. */
|
/** Indicates how far into this tick the frame is. */
|
||||||
public final float partialTicks;
|
public float partialTicks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates DH's near clip plane, measured in blocks.
|
* Indicates DH's near clip plane, measured in blocks.
|
||||||
* Note: this may change based on time, player speed, and other factors.
|
* Note: this may change based on time, player speed, and other factors.
|
||||||
*/
|
*/
|
||||||
public final float nearClipPlane;
|
public float nearClipPlane;
|
||||||
/**
|
/**
|
||||||
* Indicates DH's far clip plane, measured in blocks.
|
* Indicates DH's far clip plane, measured in blocks.
|
||||||
* Note: this may change based on time, player speed, and other factors.
|
* Note: this may change based on time, player speed, and other factors.
|
||||||
*/
|
*/
|
||||||
public final float farClipPlane;
|
public float farClipPlane;
|
||||||
|
|
||||||
/** The projection matrix Minecraft is using to render this frame. */
|
/** The projection matrix Minecraft is using to render this frame. */
|
||||||
public final DhApiMat4f mcProjectionMatrix;
|
public final DhApiMat4f mcProjectionMatrix = new DhApiMat4f();
|
||||||
/** The model view matrix Minecraft is using to render this frame. */
|
/** The model view matrix Minecraft is using to render this frame. */
|
||||||
public final DhApiMat4f mcModelViewMatrix;
|
public final DhApiMat4f mcModelViewMatrix = new DhApiMat4f();
|
||||||
|
public final DhApiMat4f mcInverseMvmProjectionMatrix = new DhApiMat4f();
|
||||||
|
|
||||||
/** The projection matrix Distant Horizons is using to render this frame. */
|
/** The projection matrix Distant Horizons is using to render this frame. */
|
||||||
public final DhApiMat4f dhProjectionMatrix;
|
public final DhApiMat4f dhProjectionMatrix = new DhApiMat4f();
|
||||||
/** The model view matrix Distant Horizons is using to render this frame. */
|
/** The model view matrix Distant Horizons is using to render this frame. */
|
||||||
public final DhApiMat4f dhModelViewMatrix;
|
public final DhApiMat4f dhModelViewMatrix = new DhApiMat4f();
|
||||||
/** combination of the MVM and projection matrices */
|
/** combination of the MVM and projection matrices */
|
||||||
public final DhApiMat4f dhMvmProjMatrix;
|
public final DhApiMat4f dhMvmProjMatrix = new DhApiMat4f();
|
||||||
|
public final DhApiMat4f dhInverseMvmProjectionMatrix = new DhApiMat4f();
|
||||||
|
|
||||||
public final int worldYOffset;
|
public int worldYOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The level currently being rendered.
|
* The level currently being rendered.
|
||||||
*
|
*
|
||||||
* @since API 5.1.0
|
* @since API 5.1.0
|
||||||
*/
|
*/
|
||||||
public final IDhApiLevelWrapper clientLevelWrapper;
|
public IDhApiLevelWrapper clientLevelWrapper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// constructors //
|
// constructors //
|
||||||
//==============//
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
public DhApiRenderParam(DhApiRenderParam parent)
|
public DhApiRenderParam() {}
|
||||||
|
|
||||||
|
/** Internal DH method */
|
||||||
|
public void update(DhApiRenderParam param)
|
||||||
{
|
{
|
||||||
this(
|
this.update(
|
||||||
parent.renderPass,
|
param.renderPass,
|
||||||
parent.partialTicks,
|
param.partialTicks,
|
||||||
parent.nearClipPlane, parent.farClipPlane,
|
param.nearClipPlane, param.farClipPlane,
|
||||||
parent.mcProjectionMatrix.copy(), parent.mcModelViewMatrix.copy(),
|
param.mcProjectionMatrix, param.mcModelViewMatrix,
|
||||||
parent.dhProjectionMatrix.copy(), parent.dhModelViewMatrix.copy(),
|
param.dhProjectionMatrix, param.mcModelViewMatrix,
|
||||||
parent.worldYOffset,
|
param.worldYOffset,
|
||||||
parent.clientLevelWrapper
|
param.clientLevelWrapper
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public DhApiRenderParam(
|
/** Internal DH method */
|
||||||
|
public void update(
|
||||||
EDhApiRenderPass renderPass,
|
EDhApiRenderPass renderPass,
|
||||||
float newPartialTicks,
|
float newPartialTicks,
|
||||||
float nearClipPlane, float farClipPlane,
|
float nearClipPlane, float farClipPlane,
|
||||||
@@ -107,29 +114,51 @@ public class DhApiRenderParam implements IDhApiEventParam
|
|||||||
this.farClipPlane = farClipPlane;
|
this.farClipPlane = farClipPlane;
|
||||||
this.nearClipPlane = nearClipPlane;
|
this.nearClipPlane = nearClipPlane;
|
||||||
|
|
||||||
this.mcProjectionMatrix = newMcProjectionMatrix;
|
// mc matricies
|
||||||
this.mcModelViewMatrix = newMcModelViewMatrix;
|
{
|
||||||
|
this.mcProjectionMatrix.set(newMcProjectionMatrix);
|
||||||
|
this.mcModelViewMatrix.set(newMcModelViewMatrix);
|
||||||
|
|
||||||
this.dhProjectionMatrix = newDhProjectionMatrix;
|
// inverse mvm Proj
|
||||||
this.dhModelViewMatrix = newDhModelViewMatrix;
|
this.mcInverseMvmProjectionMatrix.set(newMcProjectionMatrix);
|
||||||
|
this.mcInverseMvmProjectionMatrix.invert();
|
||||||
|
}
|
||||||
|
|
||||||
DhApiMat4f combinedMatrix = new DhApiMat4f(this.dhProjectionMatrix);
|
// dh matricies
|
||||||
combinedMatrix.multiply(this.dhModelViewMatrix);
|
{
|
||||||
this.dhMvmProjMatrix = combinedMatrix;
|
this.dhProjectionMatrix.set(newDhProjectionMatrix);
|
||||||
|
this.dhModelViewMatrix.set(newDhModelViewMatrix);
|
||||||
|
|
||||||
|
// proj
|
||||||
|
this.dhMvmProjMatrix.set(this.dhProjectionMatrix);
|
||||||
|
this.dhMvmProjMatrix.multiply(this.dhModelViewMatrix);
|
||||||
|
|
||||||
|
// inverse mvm Proj
|
||||||
|
this.dhInverseMvmProjectionMatrix.set(this.dhMvmProjMatrix);
|
||||||
|
this.dhInverseMvmProjectionMatrix.invert();
|
||||||
|
}
|
||||||
|
|
||||||
this.worldYOffset = worldYOffset;
|
this.worldYOffset = worldYOffset;
|
||||||
this.clientLevelWrapper = clientLevelWrapper;
|
this.clientLevelWrapper = clientLevelWrapper;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// base overrides //
|
// base overrides //
|
||||||
//================//
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DhApiRenderParam copy() { return new DhApiRenderParam(this); }
|
public boolean getCopyBeforeFire() { return false; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DhApiRenderParam copy() { return this; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class DhApiResult<T>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// these constructors are private because the create... methods below are easier to understand
|
// these constructors are private because the create methods below are easier to understand
|
||||||
private DhApiResult(boolean success, String message) { this(success, message, null); }
|
private DhApiResult(boolean success, String message) { this(success, message, null); }
|
||||||
private DhApiResult(boolean success, String message, T payload)
|
private DhApiResult(boolean success, String message, T payload)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ package com.seibel.distanthorizons.api.objects.math;
|
|||||||
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable;
|
import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An (almost) exact copy of Minecraft's 1.16.5
|
* An (almost) exact copy of Minecraft's 1.16.5
|
||||||
* implementation of a 4x4 float matrix. <br><br>
|
* implementation of a 4x4 float matrix. <br><br>
|
||||||
@@ -33,7 +35,7 @@ import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable;
|
|||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2024-6-30
|
* @version 2026-05-22
|
||||||
*/
|
*/
|
||||||
public class DhApiMat4f implements IDhApiCopyable
|
public class DhApiMat4f implements IDhApiCopyable
|
||||||
{
|
{
|
||||||
@@ -62,6 +64,7 @@ public class DhApiMat4f implements IDhApiCopyable
|
|||||||
//==============//
|
//==============//
|
||||||
// constructors //
|
// constructors //
|
||||||
//==============//
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
public DhApiMat4f() { /* all values are 0 */ }
|
public DhApiMat4f() { /* all values are 0 */ }
|
||||||
|
|
||||||
@@ -71,14 +74,17 @@ public class DhApiMat4f implements IDhApiCopyable
|
|||||||
this.m01 = sourceMatrix.m01;
|
this.m01 = sourceMatrix.m01;
|
||||||
this.m02 = sourceMatrix.m02;
|
this.m02 = sourceMatrix.m02;
|
||||||
this.m03 = sourceMatrix.m03;
|
this.m03 = sourceMatrix.m03;
|
||||||
|
|
||||||
this.m10 = sourceMatrix.m10;
|
this.m10 = sourceMatrix.m10;
|
||||||
this.m11 = sourceMatrix.m11;
|
this.m11 = sourceMatrix.m11;
|
||||||
this.m12 = sourceMatrix.m12;
|
this.m12 = sourceMatrix.m12;
|
||||||
this.m13 = sourceMatrix.m13;
|
this.m13 = sourceMatrix.m13;
|
||||||
|
|
||||||
this.m20 = sourceMatrix.m20;
|
this.m20 = sourceMatrix.m20;
|
||||||
this.m21 = sourceMatrix.m21;
|
this.m21 = sourceMatrix.m21;
|
||||||
this.m22 = sourceMatrix.m22;
|
this.m22 = sourceMatrix.m22;
|
||||||
this.m23 = sourceMatrix.m23;
|
this.m23 = sourceMatrix.m23;
|
||||||
|
|
||||||
this.m30 = sourceMatrix.m30;
|
this.m30 = sourceMatrix.m30;
|
||||||
this.m31 = sourceMatrix.m31;
|
this.m31 = sourceMatrix.m31;
|
||||||
this.m32 = sourceMatrix.m32;
|
this.m32 = sourceMatrix.m32;
|
||||||
@@ -109,12 +115,74 @@ public class DhApiMat4f implements IDhApiCopyable
|
|||||||
this.m33 = values[15];
|
this.m33 = values[15];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
//=========//
|
||||||
// methods //
|
// methods //
|
||||||
//=========//
|
//=========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/** Returns the values of this matrix in row major order (AKA rows then columns) */
|
||||||
|
public float[] getValuesAsArray()
|
||||||
|
{
|
||||||
|
float[] array = new float[16];
|
||||||
|
this.putValuesInArray(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the values of this matrix in row major order (AKA rows then columns)
|
||||||
|
* @since API 7.0.0
|
||||||
|
*/
|
||||||
|
public void putValuesInArray(float[] array)
|
||||||
|
{
|
||||||
|
array[0] = this.m00;
|
||||||
|
array[1] = this.m01;
|
||||||
|
array[2] = this.m02;
|
||||||
|
array[3] = this.m03;
|
||||||
|
|
||||||
|
array[4] = this.m10;
|
||||||
|
array[5] = this.m11;
|
||||||
|
array[6] = this.m12;
|
||||||
|
array[7] = this.m13;
|
||||||
|
|
||||||
|
array[8] = this.m20;
|
||||||
|
array[9] = this.m21;
|
||||||
|
array[10] = this.m22;
|
||||||
|
array[11] = this.m23;
|
||||||
|
|
||||||
|
array[12] = this.m30;
|
||||||
|
array[13] = this.m31;
|
||||||
|
array[14] = this.m32;
|
||||||
|
array[15] = this.m33;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @since API 7.0.0 */
|
||||||
|
public void set(DhApiMat4f mat)
|
||||||
|
{
|
||||||
|
this.m00 = mat.m00;
|
||||||
|
this.m01 = mat.m01;
|
||||||
|
this.m02 = mat.m02;
|
||||||
|
this.m03 = mat.m03;
|
||||||
|
|
||||||
|
this.m10 = mat.m10;
|
||||||
|
this.m11 = mat.m11;
|
||||||
|
this.m12 = mat.m12;
|
||||||
|
this.m13 = mat.m13;
|
||||||
|
|
||||||
|
this.m20 = mat.m20;
|
||||||
|
this.m21 = mat.m21;
|
||||||
|
this.m22 = mat.m22;
|
||||||
|
this.m23 = mat.m23;
|
||||||
|
|
||||||
|
this.m30 = mat.m30;
|
||||||
|
this.m31 = mat.m31;
|
||||||
|
this.m32 = mat.m32;
|
||||||
|
this.m33 = mat.m33;
|
||||||
|
}
|
||||||
|
|
||||||
public void setIdentity()
|
public void setIdentity()
|
||||||
{
|
{
|
||||||
@@ -279,47 +347,14 @@ public class DhApiMat4f implements IDhApiCopyable
|
|||||||
this.m33 *= scalar;
|
this.m33 *= scalar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
|
||||||
// Distant Horizons //
|
|
||||||
// methods //
|
|
||||||
//==================//
|
|
||||||
|
|
||||||
private static int getArrayIndex(int xIndex, int zIndex) { return (zIndex * 4) + xIndex; }
|
|
||||||
|
|
||||||
/** Returns the values of this matrix in row major order (AKA rows then columns) */
|
|
||||||
public float[] getValuesAsArray()
|
|
||||||
{
|
|
||||||
return new float[]{
|
|
||||||
this.m00,
|
|
||||||
this.m01,
|
|
||||||
this.m02,
|
|
||||||
this.m03,
|
|
||||||
|
|
||||||
this.m10,
|
|
||||||
this.m11,
|
|
||||||
this.m12,
|
|
||||||
this.m13,
|
|
||||||
|
|
||||||
this.m20,
|
|
||||||
this.m21,
|
|
||||||
this.m22,
|
|
||||||
this.m23,
|
|
||||||
|
|
||||||
this.m30,
|
|
||||||
this.m31,
|
|
||||||
this.m32,
|
|
||||||
this.m33,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// base overrides //
|
// base overrides //
|
||||||
//================//
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj)
|
public boolean equals(Object obj)
|
||||||
@@ -388,4 +423,8 @@ public class DhApiMat4f implements IDhApiCopyable
|
|||||||
@Override
|
@Override
|
||||||
public DhApiMat4f copy() { return new DhApiMat4f(this); }
|
public DhApiMat4f copy() { return new DhApiMat4f(this); }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-4
@@ -35,7 +35,19 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class DependencyInjector<BindableType extends IBindable> implements IDependencyInjector<BindableType> // Note to self: Don't try adding a generic type to IDhApiEvent, the constructor won't accept it
|
public class DependencyInjector<BindableType extends IBindable> implements IDependencyInjector<BindableType> // Note to self: Don't try adding a generic type to IDhApiEvent, the constructor won't accept it
|
||||||
{
|
{
|
||||||
protected final Map<Class<? extends BindableType>, ArrayList<BindableType>> dependencies = new HashMap<>();
|
/**
|
||||||
|
* empty list is to reduce GC pressure slightly in the common path
|
||||||
|
* that {@link DependencyInjector#getInternalLogic(Class, boolean)} is called
|
||||||
|
* when nothing has been bound.
|
||||||
|
*/
|
||||||
|
private static final ArrayList<?> EMPTY_GET_ALL_LIST = new ArrayList<>();
|
||||||
|
static
|
||||||
|
{
|
||||||
|
EMPTY_GET_ALL_LIST.add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected final HashMap<Class<? extends BindableType>, ArrayList<BindableType>> dependencies = new HashMap<>();
|
||||||
|
|
||||||
/** Internal class reference to BindableType since we can't get it any other way. */
|
/** Internal class reference to BindableType since we can't get it any other way. */
|
||||||
protected final Class<? extends BindableType> bindableInterface;
|
protected final Class<? extends BindableType> bindableInterface;
|
||||||
@@ -246,9 +258,7 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
|
|||||||
|
|
||||||
|
|
||||||
// return an empty list to prevent null pointers
|
// return an empty list to prevent null pointers
|
||||||
ArrayList<T> emptyList = new ArrayList<T>();
|
return (ArrayList<T>)EMPTY_GET_ALL_LIST;
|
||||||
emptyList.add(null);
|
|
||||||
return emptyList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|||||||
@@ -31,19 +31,24 @@ public final class ModInfo
|
|||||||
public static final String DEDICATED_SERVER_INITIAL_PATH = "dedicated_server_initial";
|
public static final String DEDICATED_SERVER_INITIAL_PATH = "dedicated_server_initial";
|
||||||
|
|
||||||
/** Incremented every time any packets are added, changed or removed, with a few exceptions. */
|
/** Incremented every time any packets are added, changed or removed, with a few exceptions. */
|
||||||
public static final int PROTOCOL_VERSION = 13;
|
public static final int PROTOCOL_VERSION = 15;
|
||||||
public static final String WRAPPER_PACKET_PATH = "message";
|
|
||||||
|
/**
|
||||||
|
* The full plugin channel name (RESOURCE_NAMESPACE:WRAPPER_PACKET_PATH)
|
||||||
|
* must be 20 characters or fewer for compatibility with MC 1.13 and older.
|
||||||
|
*/
|
||||||
|
public static final String WRAPPER_PACKET_PATH = "msg";
|
||||||
|
|
||||||
/** The internal mod name */
|
/** The internal mod name */
|
||||||
public static final String NAME = "DistantHorizons";
|
public static final String NAME = "DistantHorizons";
|
||||||
/** Human-readable version of NAME */
|
/** Human-readable version of NAME */
|
||||||
public static final String READABLE_NAME = "Distant Horizons";
|
public static final String READABLE_NAME = "Distant Horizons";
|
||||||
public static final String VERSION = "2.4.6-b-dev";
|
public static final String VERSION = "3.0.4-b-dev";
|
||||||
/** Returns true if the current build is an unstable developer build, false otherwise. */
|
/** Returns true if the current build is an unstable developer build, false otherwise. */
|
||||||
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
|
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
|
||||||
|
|
||||||
/** This version should only be updated when breaking changes are introduced to the DH API */
|
/** This version should only be updated when breaking changes are introduced to the DH API */
|
||||||
public static final int API_MAJOR_VERSION = 6;
|
public static final int API_MAJOR_VERSION = 7;
|
||||||
/** This version should be updated whenever new methods are added to the DH API */
|
/** This version should be updated whenever new methods are added to the DH API */
|
||||||
public static final int API_MINOR_VERSION = 0;
|
public static final int API_MINOR_VERSION = 0;
|
||||||
/** This version should be updated whenever non-breaking fixes are added to the DH API */
|
/** This version should be updated whenever non-breaking fixes are added to the DH API */
|
||||||
|
|||||||
+11
-1
@@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.seibel.distanthorizons.core.util;
|
package com.seibel.distanthorizons.coreapi.util;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@@ -89,6 +89,16 @@ public class ColorUtil
|
|||||||
/** @param newBlue should be a value between 0 and 255 */
|
/** @param newBlue should be a value between 0 and 255 */
|
||||||
public static int setBlue(int color, int newBlue) { return (getAlpha(color) << 24) | (getRed(color) << 16) | (getGreen(color) << 8) | newBlue; }
|
public static int setBlue(int color, int newBlue) { return (getAlpha(color) << 24) | (getRed(color) << 16) | (getGreen(color) << 8) | newBlue; }
|
||||||
|
|
||||||
|
/** @throws IllegalArgumentException if the given int value is out of the range 0 - 255 (exclusive) */
|
||||||
|
public static void throwIfColorValueOutOfIntRange(String colorName, int value) throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
if (value < 0
|
||||||
|
|| value > 255)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("["+colorName+"] with the value ["+value+"] is out of the expected range 0 - 255 (exclusive).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static int applyShade(int color, int shade)
|
public static int applyShade(int color, int shade)
|
||||||
+44
-32
@@ -1,18 +1,23 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
id "com.github.johnrengelman.shadow" version '8.1.1' apply false // Set this to true if you're using the standalone Core project
|
id "com.gradleup.shadow"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: "application"
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven { url "https://repo.enonic.com/public/" }
|
||||||
|
}
|
||||||
|
|
||||||
application {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
|
options.release = 8
|
||||||
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
shadowedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
|
shadowedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
|
||||||
shade
|
shade
|
||||||
implementation.extendsFrom shade
|
implementation.extendsFrom shade
|
||||||
|
testImplementation.extendsFrom compileOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
|
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
|
||||||
@@ -20,38 +25,45 @@ OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePl
|
|||||||
// Set the OS lwjgl is using to the current os
|
// Set the OS lwjgl is using to the current os
|
||||||
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
|
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
|
||||||
|
|
||||||
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
|
dependencies {
|
||||||
// Imports most of lwjgl's libraries (well, only the ones that we need)
|
// API project dependency
|
||||||
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
implementation project(":api")
|
||||||
|
|
||||||
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
|
// MC-provided libraries (available at runtime via Minecraft)
|
||||||
implementation "org.lwjgl:lwjgl"
|
compileOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
||||||
implementation "org.lwjgl:lwjgl-assimp"
|
compileOnly "org.lwjgl:lwjgl"
|
||||||
implementation "org.lwjgl:lwjgl-glfw"
|
compileOnly "org.lwjgl:lwjgl-assimp"
|
||||||
// OpenGL is removed since DH now handles rendering in the "Common" project
|
compileOnly "org.lwjgl:lwjgl-glfw"
|
||||||
// so we can use OpenGL for old MC versions and Blaze3D (IE Vulkan) for newer ones
|
compileOnly "org.lwjgl:lwjgl-stb"
|
||||||
// implementation "org.lwjgl:lwjgl-openal"
|
compileOnly "org.lwjgl:lwjgl-tinyfd"
|
||||||
// implementation "org.lwjgl:lwjgl-opengl"
|
testRuntimeOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
||||||
implementation "org.lwjgl:lwjgl-stb"
|
testRuntimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
||||||
implementation "org.lwjgl:lwjgl-tinyfd"
|
testRuntimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
|
||||||
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
testRuntimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
||||||
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
|
testRuntimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
||||||
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
testRuntimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
||||||
// runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
|
|
||||||
// runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
|
||||||
|
|
||||||
// FIXME for some reason this line doesn't actually shade in the library
|
compileOnly("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
|
||||||
// shade "it.unimi.dsi:fastutil:${rootProject.fastutil_version}" // Add our own fastutil version
|
compileOnly("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
|
||||||
|
compileOnly("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
|
||||||
|
compileOnly("org.joml:joml:${rootProject.joml_version}")
|
||||||
|
compileOnly("io.netty:netty-buffer:${rootProject.netty_version}")
|
||||||
|
compileOnly("org.jetbrains:annotations:16.0.2")
|
||||||
|
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
|
||||||
|
compileOnly("com.google.guava:guava:31.1-jre")
|
||||||
|
|
||||||
|
// DH's bundled libraries (shadowed + relocated in loader jars)
|
||||||
|
implementation("com.github.luben:zstd-jni:${rootProject.zstd_version}")
|
||||||
|
implementation("org.lz4:lz4-java:${rootProject.lz4_version}")
|
||||||
|
implementation("org.tukaani:xz:${rootProject.xz_version}")
|
||||||
|
implementation("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
|
||||||
|
implementation("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
|
||||||
|
implementation("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
|
||||||
|
|
||||||
// Some other dependencies
|
// JUnit (core tests only)
|
||||||
implementation("org.jetbrains:annotations:16.0.2")
|
compileOnly("junit:junit:4.13")
|
||||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
compileOnly("org.junit.jupiter:junit-jupiter:5.8.2")
|
||||||
implementation("com.google.common:google-collect:0.5")
|
compileOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
|
||||||
implementation("com.google.guava:guava:31.1-jre")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class Initializer
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void init()
|
public static void preConfigInit()
|
||||||
{
|
{
|
||||||
//============================//
|
//============================//
|
||||||
// check referenced libraries //
|
// check referenced libraries //
|
||||||
@@ -177,6 +177,11 @@ public class Initializer
|
|||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** fired after DH's config has been set up */
|
||||||
|
public static void postConfigInit()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
//==============================//
|
//==============================//
|
||||||
@@ -238,8 +243,7 @@ public class Initializer
|
|||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -35,6 +35,6 @@ public class DhApiAmbientOcclusionConfig implements IDhApiAmbientOcclusionConfig
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDhApiConfigValue<Boolean> enabled()
|
public IDhApiConfigValue<Boolean> enabled()
|
||||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Ssao.enableSsao); }
|
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.enableSsao); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ public class DhApiDebuggingConfig implements IDhApiDebuggingConfig
|
|||||||
|
|
||||||
|
|
||||||
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
|
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
|
||||||
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
|
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRenderingColors); }
|
||||||
|
|
||||||
public IDhApiConfigValue<Boolean> debugKeybindings()
|
public IDhApiConfigValue<Boolean> debugKeybindings()
|
||||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
|
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
|
||||||
|
|||||||
+6
-2
@@ -85,17 +85,21 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
|
|||||||
public IDhApiConfigValue<EDhApiHorizontalQuality> horizontalQuality()
|
public IDhApiConfigValue<EDhApiHorizontalQuality> horizontalQuality()
|
||||||
{ return new DhApiConfigValue<EDhApiHorizontalQuality, EDhApiHorizontalQuality>(Config.Client.Advanced.Graphics.Quality.horizontalQuality); }
|
{ return new DhApiConfigValue<EDhApiHorizontalQuality, EDhApiHorizontalQuality>(Config.Client.Advanced.Graphics.Quality.horizontalQuality); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IDhApiConfigValue<Boolean> useCameraPositionForQualityDropOff()
|
||||||
|
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Quality.useCameraPositionForQualityDropOff); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDhApiConfigValue<EDhApiTransparency> transparency()
|
public IDhApiConfigValue<EDhApiTransparency> transparency()
|
||||||
{ return new DhApiConfigValue<EDhApiTransparency, EDhApiTransparency>(Config.Client.Advanced.Graphics.Quality.transparency); }
|
{ return new DhApiConfigValue<EDhApiTransparency, EDhApiTransparency>(Config.Client.Advanced.Graphics.Quality.transparency); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDhApiConfigValue<EDhApiBlocksToAvoid> blocksToAvoid()
|
public IDhApiConfigValue<EDhApiBlocksToAvoid> blocksToAvoid()
|
||||||
{ return new DhApiConfigValue<EDhApiBlocksToAvoid, EDhApiBlocksToAvoid>(Config.Client.Advanced.Graphics.Quality.blocksToIgnore); }
|
{ return new DhApiConfigValue<EDhApiBlocksToAvoid, EDhApiBlocksToAvoid>(Config.Client.Advanced.Graphics.Culling.blocksToIgnore); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDhApiConfigValue<Boolean> tintWithAvoidedBlocks()
|
public IDhApiConfigValue<Boolean> tintWithAvoidedBlocks()
|
||||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks); }
|
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Culling.tintWithAvoidedBlocks); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDhApiConfigValue<Integer> getBiomeBlending()
|
public IDhApiConfigValue<Integer> getBiomeBlending()
|
||||||
|
|||||||
+37
-15
@@ -39,15 +39,15 @@ import com.seibel.distanthorizons.core.util.DhApiTerrainDataPointUtil;
|
|||||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.RayCastUtil;
|
import com.seibel.distanthorizons.core.util.RayCastUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3f;
|
import com.seibel.distanthorizons.core.util.math.DhVec3f;
|
||||||
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3i;
|
import com.seibel.distanthorizons.core.util.math.DhVec3i;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -71,24 +71,28 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
// debugging values
|
// debugging values
|
||||||
private static volatile boolean debugThreadRunning = false;
|
private static volatile boolean debugThreadRunning = false;
|
||||||
private static DhApiTerrainDataCache debugDataCache = new DhApiTerrainDataCache();
|
private static DhApiTerrainDataCache debugDataCache = new DhApiTerrainDataCache();
|
||||||
private static DhApiVec3i currentDebugVec3i = new Vec3i();
|
private static DhApiVec3i currentDebugVec3i = new DhVec3i();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
private DhApiTerrainDataRepo()
|
private DhApiTerrainDataRepo()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// Getter Methods //
|
// Getter Methods //
|
||||||
//================//
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache)
|
public DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache)
|
||||||
@@ -109,9 +113,12 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache)
|
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache)
|
||||||
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, DhSectionPos.encode(detailLevel, posX, posZ), dataCache); }
|
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, DhSectionPos.encode(detailLevel, posX, posZ), dataCache); }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// private getters //
|
// private getters //
|
||||||
|
//region
|
||||||
|
|
||||||
/** Returns a single API terrain datapoint that contains the given Y block position */
|
/** Returns a single API terrain datapoint that contains the given Y block position */
|
||||||
private static DhApiResult<DhApiTerrainDataPoint> getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, long requestedColumnPos, Integer blockYPos, IDhApiTerrainDataCache dataCache)
|
private static DhApiResult<DhApiTerrainDataPoint> getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, long requestedColumnPos, Integer blockYPos, IDhApiTerrainDataCache dataCache)
|
||||||
@@ -340,11 +347,14 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//====================//
|
//====================//
|
||||||
// raycasting methods //
|
// raycasting methods //
|
||||||
//====================//
|
//====================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DhApiResult<DhApiRaycastResult> raycast(
|
public DhApiResult<DhApiRaycastResult> raycast(
|
||||||
@@ -356,8 +366,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
IDhApiTerrainDataCache dataCache)
|
IDhApiTerrainDataCache dataCache)
|
||||||
{
|
{
|
||||||
return this.raycastLodData(levelWrapper,
|
return this.raycastLodData(levelWrapper,
|
||||||
new Vec3d(rayOriginX, rayOriginY, rayOriginZ),
|
new DhVec3d(rayOriginX, rayOriginY, rayOriginZ),
|
||||||
new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ),
|
new DhVec3f(rayDirectionX, rayDirectionY, rayDirectionZ),
|
||||||
maxRayBlockLength, dataCache);
|
maxRayBlockLength, dataCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +379,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
*/
|
*/
|
||||||
private DhApiResult<DhApiRaycastResult> raycastLodData(
|
private DhApiResult<DhApiRaycastResult> raycastLodData(
|
||||||
IDhApiLevelWrapper levelWrapper,
|
IDhApiLevelWrapper levelWrapper,
|
||||||
Vec3d rayOrigin, Vec3f rayDirection,
|
DhVec3d rayOrigin, DhVec3f rayDirection,
|
||||||
int maxRayBlockLength,
|
int maxRayBlockLength,
|
||||||
@Nullable
|
@Nullable
|
||||||
IDhApiTerrainDataCache dataCache)
|
IDhApiTerrainDataCache dataCache)
|
||||||
@@ -386,9 +396,9 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
int currentLength = 0;
|
int currentLength = 0;
|
||||||
|
|
||||||
// the exact position of this step
|
// the exact position of this step
|
||||||
Vec3d exactPos = new Vec3d(rayOrigin.x, rayOrigin.y, rayOrigin.z);
|
DhVec3d exactPos = new DhVec3d(rayOrigin.x, rayOrigin.y, rayOrigin.z);
|
||||||
// the block position for this step
|
// the block position for this step
|
||||||
Vec3i blockPos = new Vec3i((int) Math.round(rayOrigin.x), (int) Math.round(rayOrigin.y), (int) Math.round(rayOrigin.z));
|
DhVec3i blockPos = new DhVec3i((int) Math.round(rayOrigin.x), (int) Math.round(rayOrigin.y), (int) Math.round(rayOrigin.z));
|
||||||
|
|
||||||
DhApiRaycastResult closetFoundDataPoint = null;
|
DhApiRaycastResult closetFoundDataPoint = null;
|
||||||
|
|
||||||
@@ -398,8 +408,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
&& currentLength <= maxRayBlockLength)
|
&& currentLength <= maxRayBlockLength)
|
||||||
{
|
{
|
||||||
// get the LOD columns around this position
|
// get the LOD columns around this position
|
||||||
ArrayList<Vec3i> columnPositions = getIntersectingColumnsAtPosition(blockPos, rayDirection);
|
ArrayList<DhVec3i> columnPositions = getIntersectingColumnsAtPosition(blockPos, rayDirection);
|
||||||
for (Vec3i columnPos : columnPositions)
|
for (DhVec3i columnPos : columnPositions)
|
||||||
{
|
{
|
||||||
// check each column
|
// check each column
|
||||||
DhApiResult<DhApiTerrainDataPoint[]> result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z, dataCache);
|
DhApiResult<DhApiTerrainDataPoint[]> result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z, dataCache);
|
||||||
@@ -416,7 +426,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
if (dataPoint.blockStateWrapper != null && !dataPoint.blockStateWrapper.isAir())
|
if (dataPoint.blockStateWrapper != null && !dataPoint.blockStateWrapper.isAir())
|
||||||
{
|
{
|
||||||
// does this LOD contain the given Y position?
|
// does this LOD contain the given Y position?
|
||||||
Vec3i dataPointPos = new Vec3i(columnPos.x, dataPoint.bottomYBlockPos, columnPos.z);
|
DhVec3i dataPointPos = new DhVec3i(columnPos.x, dataPoint.bottomYBlockPos, columnPos.z);
|
||||||
if (exactPos.y >= dataPoint.bottomYBlockPos
|
if (exactPos.y >= dataPoint.bottomYBlockPos
|
||||||
&& exactPos.y <= dataPoint.topYBlockPos)
|
&& exactPos.y <= dataPoint.topYBlockPos)
|
||||||
{
|
{
|
||||||
@@ -469,15 +479,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
*
|
*
|
||||||
* Used to make sure the raycast step doesn't accidentally walk over any adjacent data.
|
* Used to make sure the raycast step doesn't accidentally walk over any adjacent data.
|
||||||
*/
|
*/
|
||||||
private static ArrayList<Vec3i> getIntersectingColumnsAtPosition(Vec3i rayEndingPos, Vec3f rayDirection)
|
private static ArrayList<DhVec3i> getIntersectingColumnsAtPosition(DhVec3i rayEndingPos, DhVec3f rayDirection)
|
||||||
{
|
{
|
||||||
ArrayList<Vec3i> returnList = new ArrayList<>(9);
|
ArrayList<DhVec3i> returnList = new ArrayList<>(9);
|
||||||
|
|
||||||
for (int x = -1; x <= 1; x++)
|
for (int x = -1; x <= 1; x++)
|
||||||
{
|
{
|
||||||
for (int z = -1; z <= 1; z++)
|
for (int z = -1; z <= 1; z++)
|
||||||
{
|
{
|
||||||
Vec3i pos = new Vec3i(rayEndingPos.x + x, rayEndingPos.y, rayEndingPos.z + z);
|
DhVec3i pos = new DhVec3i(rayEndingPos.x + x, rayEndingPos.y, rayEndingPos.z + z);
|
||||||
|
|
||||||
// check if this column is intersected by the ray
|
// check if this column is intersected by the ray
|
||||||
if (RayCastUtil.rayIntersectsSquare(rayEndingPos.x, rayEndingPos.z, rayDirection.x, rayDirection.z, pos.x, pos.z, 1))
|
if (RayCastUtil.rayIntersectsSquare(rayEndingPos.x, rayEndingPos.z, rayDirection.x, rayDirection.z, pos.x, pos.z, 1))
|
||||||
@@ -490,11 +500,14 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
return returnList;
|
return returnList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// setter methods //
|
// setter methods //
|
||||||
//================//
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DhApiResult<Void> overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException
|
public DhApiResult<Void> overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException
|
||||||
@@ -524,20 +537,26 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
return DhApiResult.createSuccess();
|
return DhApiResult.createSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// API helpers //
|
// API helpers //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDhApiTerrainDataCache createSoftCache() { return new DhApiTerrainDataCache(); }
|
public IDhApiTerrainDataCache createSoftCache() { return new DhApiTerrainDataCache(); }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===============//
|
//===============//
|
||||||
// debug methods //
|
// debug methods //
|
||||||
//===============//
|
//===============//
|
||||||
|
//region
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is here for debugging the repo and isn't intended for normal use.
|
* This method is here for debugging the repo and isn't intended for normal use.
|
||||||
@@ -618,5 +637,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
|
|||||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
||||||
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
|
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
|
||||||
|
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
@@ -35,13 +36,14 @@ import com.seibel.distanthorizons.core.render.RenderParams;
|
|||||||
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.*;
|
import com.seibel.distanthorizons.core.render.renderer.*;
|
||||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
@@ -52,7 +54,6 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
|
|||||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
|
||||||
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
||||||
import com.seibel.distanthorizons.core.world.DhClientWorld;
|
import com.seibel.distanthorizons.core.world.DhClientWorld;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
@@ -66,6 +67,7 @@ import org.lwjgl.glfw.GLFW;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
@@ -85,6 +87,12 @@ public class ClientApi
|
|||||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
|
||||||
|
/** Delayed accessing is necessary since this object will be created before the mod accessors are bound. */
|
||||||
|
private static class DelayedAccessors
|
||||||
|
{
|
||||||
|
public static final IImmersivePortalsAccessor IMMERSIVE_PORTALS = ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
|
||||||
|
}
|
||||||
|
|
||||||
/** this includes the is dev build message and low allocated memory warning */
|
/** this includes the is dev build message and low allocated memory warning */
|
||||||
private static final int MS_BETWEEN_STATIC_STARTUP_MESSAGES = 4_000;
|
private static final int MS_BETWEEN_STATIC_STARTUP_MESSAGES = 4_000;
|
||||||
|
|
||||||
@@ -97,6 +105,11 @@ public class ClientApi
|
|||||||
* Only downside is making sure each variable is populated before rendering.
|
* Only downside is making sure each variable is populated before rendering.
|
||||||
*/
|
*/
|
||||||
public static final DhRenderState RENDER_STATE = new DhRenderState();
|
public static final DhRenderState RENDER_STATE = new DhRenderState();
|
||||||
|
/**
|
||||||
|
* static variable so we don't have to re-create it each frame,
|
||||||
|
* reducing GC pressure.
|
||||||
|
*/
|
||||||
|
private static final RenderParams RENDER_PARAMS = new RenderParams();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 50ms = 20 FPS
|
* 50ms = 20 FPS
|
||||||
@@ -117,16 +130,10 @@ public class ClientApi
|
|||||||
|
|
||||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||||
|
|
||||||
private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent);
|
|
||||||
|
|
||||||
/** Delay loading the first level to give the server some time to respond with level to actually load */
|
|
||||||
private Timer firstLevelLoadTimer;
|
|
||||||
private static final long FIRST_LEVEL_LOAD_DELAY_IN_MS = 1_000;
|
|
||||||
|
|
||||||
/** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */
|
/** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */
|
||||||
public final HashSet<IClientLevelWrapper> waitingClientLevels = new HashSet<>();
|
public final HashSet<IClientLevelWrapper> waitingClientLevels = new HashSet<>();
|
||||||
/** Holds any chunks that were loaded before the {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} was fired. */
|
/** Holds any chunks that were found before the client levels are loaded. */
|
||||||
public final HashMap<Pair<IClientLevelWrapper, DhChunkPos>, IChunkWrapper> waitingChunkByClientLevelAndPos = new HashMap<>();
|
public final Map<Pair<IClientLevelWrapper, DhChunkPos>, IChunkWrapper> waitingChunkByClientLevelAndPos = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/** publicly available so {@link F3Screen} can display the error */
|
/** publicly available so {@link F3Screen} can display the error */
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -142,10 +149,19 @@ public class ClientApi
|
|||||||
* tracked should also be to keep the ratio roughly the same.
|
* tracked should also be to keep the ratio roughly the same.
|
||||||
* @see ClientApi#MIN_MS_BETWEEN_SPEED_CHECKS
|
* @see ClientApi#MIN_MS_BETWEEN_SPEED_CHECKS
|
||||||
*/
|
*/
|
||||||
public RollingAverage cameraSpeedRollingAverage = new RollingAverage(40);
|
private final RollingAverage cameraSpeedRollingAverage = new RollingAverage(40);
|
||||||
private Vec3d lastCameraPosForSpeedCheck = new Vec3d();
|
private DhVec3d lastCameraPosForSpeedCheck = new DhVec3d();
|
||||||
private long msSinceLastSpeedCheck = 0L;
|
private long msSinceLastSpeedCheck = 0L;
|
||||||
|
public double getAvgCameraSpeed() { return cameraSpeedRollingAverage.getAverage(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keeping track of this is necessary to fix
|
||||||
|
* out-of-date LODs from rendering when the shading
|
||||||
|
* is changed by Iris, causing LODs to often
|
||||||
|
* lack the side shading, which looks pretty bad
|
||||||
|
* when shaders are disabled.
|
||||||
|
*/
|
||||||
|
private boolean irisShadersEnabledLastFrame = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -160,11 +176,11 @@ public class ClientApi
|
|||||||
//==============//
|
//==============//
|
||||||
// world events //
|
// world events //
|
||||||
//==============//
|
//==============//
|
||||||
///region
|
//region world events
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May be fired slightly before or after the associated
|
* May be fired slightly before or after the associated
|
||||||
* {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} event
|
* level is loaded
|
||||||
* depending on how the host mod loader functions. <br><br>
|
* depending on how the host mod loader functions. <br><br>
|
||||||
*
|
*
|
||||||
* Synchronized shouldn't be necessary, but is present to match {@see onClientOnlyDisconnected} and prevent any unforeseen issues.
|
* Synchronized shouldn't be necessary, but is present to match {@see onClientOnlyDisconnected} and prevent any unforeseen issues.
|
||||||
@@ -198,30 +214,12 @@ public class ClientApi
|
|||||||
|
|
||||||
DhClientWorld world = new DhClientWorld();
|
DhClientWorld world = new DhClientWorld();
|
||||||
SharedApi.setDhWorld(world);
|
SharedApi.setDhWorld(world);
|
||||||
|
|
||||||
this.pluginChannelApi.onJoinServer(world.networkState.getSession());
|
|
||||||
world.networkState.sendConfigMessage();
|
|
||||||
|
|
||||||
LOGGER.info("Loading [" + this.waitingClientLevels.size() + "] waiting client level wrappers.");
|
|
||||||
for (IClientLevelWrapper level : this.waitingClientLevels)
|
|
||||||
{
|
|
||||||
this.clientLevelLoadEvent(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.waitingClientLevels.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Synchronized to prevent a rare issue where multiple disconnect events are triggered on top of each other. */
|
/** Synchronized to prevent a rare issue where multiple disconnect events are triggered on top of each other. */
|
||||||
public synchronized void onClientOnlyDisconnected()
|
public synchronized void onClientOnlyDisconnected()
|
||||||
{
|
{
|
||||||
// clear the first time timer
|
|
||||||
if (this.firstLevelLoadTimer != null)
|
|
||||||
{
|
|
||||||
this.firstLevelLoadTimer.cancel();
|
|
||||||
this.firstLevelLoadTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||||
if (world != null)
|
if (world != null)
|
||||||
{
|
{
|
||||||
@@ -231,111 +229,20 @@ public class ClientApi
|
|||||||
SharedApi.setDhWorld(null);
|
SharedApi.setDhWorld(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pluginChannelApi.reset();
|
|
||||||
|
|
||||||
// remove any waiting items
|
// remove any waiting items
|
||||||
this.waitingChunkByClientLevelAndPos.clear();
|
this.waitingChunkByClientLevelAndPos.clear();
|
||||||
this.waitingClientLevels.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// level events //
|
// level events //
|
||||||
//==============//
|
//==============//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
public void clientLevelUnloadEvent(IClientLevelWrapper level)
|
public void loadWaitingChunksForLevel(IClientLevelWrapper level)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LOGGER.info("Unloading client level [" + level.getClass().getSimpleName() + "]-[" + level.getDhIdentifier() + "].");
|
|
||||||
|
|
||||||
if (level instanceof IServerKeyedClientLevel)
|
|
||||||
{
|
|
||||||
this.pluginChannelApi.onClientLevelUnload();
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
|
||||||
if (world != null)
|
|
||||||
{
|
|
||||||
world.unloadLevel(level);
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(level));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.waitingClientLevels.remove(level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// handle errors here to prevent blowing up a mixin or API up stream
|
|
||||||
LOGGER.error("Unexpected error in ClientApi.clientLevelUnloadEvent(), error: "+e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clientLevelLoadEvent(@Nullable IClientLevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// can happen if there was an issue during level load
|
|
||||||
if (levelWrapper == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// wait a moment before loading the level to give the server a chance to handle the client's login request
|
|
||||||
if (MC_CLIENT.clientConnectedToDedicatedServer())
|
|
||||||
{
|
|
||||||
if (this.firstLevelLoadTimer == null)
|
|
||||||
{
|
|
||||||
this.firstLevelLoadTimer = TimerUtil.CreateTimer("FirstLevelLoadTimer");
|
|
||||||
this.firstLevelLoadTimer.schedule(new TimerTask()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run() { ClientApi.this.clientLevelLoadEvent(levelWrapper); }
|
|
||||||
}, FIRST_LEVEL_LOAD_DELAY_IN_MS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.firstLevelLoadTimer.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LOGGER.info("Loading client level [" + levelWrapper + "]-[" + levelWrapper.getDhIdentifier() + "].");
|
|
||||||
|
|
||||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
|
||||||
if (world != null)
|
|
||||||
{
|
|
||||||
if (!this.pluginChannelApi.allowLevelLoading(levelWrapper))
|
|
||||||
{
|
|
||||||
LOGGER.info("Levels in this connection are managed by the server, skipping auto-load.");
|
|
||||||
|
|
||||||
// Instead of attempting to load themselves, send the config and wait for a server provided level key.
|
|
||||||
((DhClientWorld) world).networkState.sendConfigMessage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
world.getOrLoadLevel(levelWrapper);
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(levelWrapper));
|
|
||||||
|
|
||||||
this.loadWaitingChunksForLevel(levelWrapper);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.waitingClientLevels.add(levelWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// handle errors here to prevent blowing up a mixin or API up stream
|
|
||||||
LOGGER.error("Unexpected error in ClientApi.clientLevelLoadEvent(), error: "+e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void loadWaitingChunksForLevel(IClientLevelWrapper level)
|
|
||||||
{
|
{
|
||||||
HashSet<Pair<IClientLevelWrapper, DhChunkPos>> keysToRemove = new HashSet<>();
|
HashSet<Pair<IClientLevelWrapper, DhChunkPos>> keysToRemove = new HashSet<>();
|
||||||
for (Pair<IClientLevelWrapper, DhChunkPos> levelChunkPair : this.waitingChunkByClientLevelAndPos.keySet())
|
for (Pair<IClientLevelWrapper, DhChunkPos> levelChunkPair : this.waitingChunkByClientLevelAndPos.keySet())
|
||||||
@@ -357,14 +264,14 @@ public class ClientApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============//
|
//============//
|
||||||
// networking //
|
// networking //
|
||||||
//============//
|
//============//
|
||||||
///region
|
//region networking
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forwards a decoded message into the registered handlers.
|
* Forwards a decoded message into the registered handlers.
|
||||||
@@ -384,7 +291,8 @@ public class ClientApi
|
|||||||
{
|
{
|
||||||
executor.execute(() ->
|
executor.execute(() ->
|
||||||
{
|
{
|
||||||
NetworkSession networkSession = this.pluginChannelApi.networkSession;
|
DhClientWorld world = (DhClientWorld) Objects.requireNonNull(SharedApi.tryGetDhClientWorld());
|
||||||
|
NetworkSession networkSession = world.pluginChannelApi.networkSession;
|
||||||
if (networkSession != null)
|
if (networkSession != null)
|
||||||
{
|
{
|
||||||
networkSession.tryHandleMessage(message);
|
networkSession.tryHandleMessage(message);
|
||||||
@@ -397,14 +305,14 @@ public class ClientApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===============//
|
//===============//
|
||||||
// LOD rendering //
|
// LOD rendering //
|
||||||
//===============//
|
//===============//
|
||||||
///region
|
//region lod rendering
|
||||||
|
|
||||||
/** Should be called before {@link ClientApi#renderDeferredLodsForShaders} */
|
/** Should be called before {@link ClientApi#renderDeferredLodsForShaders} */
|
||||||
public void renderLods() { this.renderLodLayer(false); }
|
public void renderLods() { this.renderLodLayer(false); }
|
||||||
@@ -415,261 +323,282 @@ public class ClientApi
|
|||||||
*/
|
*/
|
||||||
public void renderDeferredLodsForShaders() { this.renderLodLayer(true); }
|
public void renderDeferredLodsForShaders() { this.renderLodLayer(true); }
|
||||||
|
|
||||||
public static long firstRenderTimeMs = 0;
|
|
||||||
|
|
||||||
private void renderLodLayer(boolean renderingDeferredLayer)
|
private void renderLodLayer(boolean renderingDeferredLayer)
|
||||||
{
|
{
|
||||||
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
|
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
|
||||||
profiler.push("DH-RenderLevel");
|
try (IProfilerWrapper.IProfileBlock dhRender_profile = profiler.push("DH-RenderLevel"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
|
||||||
// debugging //
|
|
||||||
//===========//
|
|
||||||
//region
|
|
||||||
|
|
||||||
//DhApiTerrainDataRepo.asyncDebugMethod(
|
|
||||||
// RENDER_STATE.clientLevelWrapper,
|
|
||||||
// MC_CLIENT.getPlayerBlockPos().getX(),
|
|
||||||
// MC_CLIENT.getPlayerBlockPos().getY(),
|
|
||||||
// MC_CLIENT.getPlayerBlockPos().getZ()
|
|
||||||
//);
|
|
||||||
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=====================//
|
|
||||||
// render thread tasks //
|
|
||||||
//=====================//
|
|
||||||
///region
|
|
||||||
|
|
||||||
// only run these tasks once per frame
|
|
||||||
if (!renderingDeferredLayer)
|
|
||||||
{
|
{
|
||||||
profiler.push("DH render thread tasks");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===============//
|
//===========//
|
||||||
// chat messages //
|
// debugging //
|
||||||
//===============//
|
//===========//
|
||||||
|
//region
|
||||||
|
|
||||||
this.sendQueuedChatMessages();
|
// only run these tasks once per frame
|
||||||
|
if (!renderingDeferredLayer)
|
||||||
|
|
||||||
|
|
||||||
//======================//
|
|
||||||
// GL Proxy queued jobs //
|
|
||||||
//======================//
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks
|
//DhApiTerrainDataRepo.asyncDebugMethod(
|
||||||
RenderThreadTaskHandler.INSTANCE.runRenderThreadTasks();
|
// RENDER_STATE.clientLevelWrapper,
|
||||||
}
|
// MC_CLIENT.getPlayerBlockPos().getX(),
|
||||||
catch (Exception e)
|
// MC_CLIENT.getPlayerBlockPos().getY(),
|
||||||
{
|
// MC_CLIENT.getPlayerBlockPos().getZ()
|
||||||
LOGGER.error("Unexpected issue running render thread tasks, error: [" + e.getMessage() + "].", e);
|
//);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// camera speed //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
long nowMs = System.currentTimeMillis();
|
//=====================//
|
||||||
if (this.msSinceLastSpeedCheck + MIN_MS_BETWEEN_SPEED_CHECKS < nowMs)
|
// render thread tasks //
|
||||||
|
//=====================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
// only run these tasks once per frame
|
||||||
|
if (!renderingDeferredLayer)
|
||||||
{
|
{
|
||||||
// calc time since last check
|
try (IProfilerWrapper.IProfileBlock renderTask_profile = profiler.push("DH render thread tasks"))
|
||||||
double secSinceLastCheck = (nowMs - this.msSinceLastSpeedCheck) / 1_000.0;
|
{
|
||||||
this.msSinceLastSpeedCheck = nowMs;
|
//===============//
|
||||||
|
// chat messages //
|
||||||
|
//===============//
|
||||||
|
|
||||||
// get the distance traveled since last frame
|
this.sendQueuedChatMessages();
|
||||||
Vec3d camPos = MC_RENDER.getCameraExactPosition();
|
|
||||||
double distanceInBlocks = camPos.getDistance(this.lastCameraPosForSpeedCheck);
|
|
||||||
double speed = distanceInBlocks / secSinceLastCheck;
|
|
||||||
|
|
||||||
// record new values for next check
|
|
||||||
this.cameraSpeedRollingAverage.add(speed);
|
|
||||||
this.lastCameraPosForSpeedCheck = camPos;
|
//======================//
|
||||||
|
// GL Proxy queued jobs //
|
||||||
|
//======================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks
|
||||||
|
RenderThreadTaskHandler.INSTANCE.runRenderThreadTasks();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue running render thread tasks, error: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// camera speed //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
long nowMs = System.currentTimeMillis();
|
||||||
|
if (this.msSinceLastSpeedCheck + MIN_MS_BETWEEN_SPEED_CHECKS < nowMs
|
||||||
|
// don't track camera speed for dimensions the player isn't in
|
||||||
|
&& (DelayedAccessors.IMMERSIVE_PORTALS == null
|
||||||
|
|| !DelayedAccessors.IMMERSIVE_PORTALS.isRenderingPortal()))
|
||||||
|
{
|
||||||
|
// calc time since last check
|
||||||
|
double secSinceLastCheck = (nowMs - this.msSinceLastSpeedCheck) / 1_000.0;
|
||||||
|
this.msSinceLastSpeedCheck = nowMs;
|
||||||
|
|
||||||
|
// get the distance traveled since last frame
|
||||||
|
DhVec3d camPos = MC_RENDER.getCameraExactPosition();
|
||||||
|
double distanceInBlocks = camPos.getDistance(this.lastCameraPosForSpeedCheck);
|
||||||
|
double speed = distanceInBlocks / secSinceLastCheck;
|
||||||
|
|
||||||
|
// record new values for next check
|
||||||
|
this.cameraSpeedRollingAverage.add(speed);
|
||||||
|
this.lastCameraPosForSpeedCheck = camPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//====================//
|
||||||
|
// Iris data re-build //
|
||||||
|
//====================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
// delayed getter since ClientApi is created before this accessor is bound
|
||||||
|
IIrisAccessor irisAccessor = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
|
||||||
|
if (irisAccessor != null)
|
||||||
|
{
|
||||||
|
boolean shadersActive = irisAccessor.isShaderPackInUse();
|
||||||
|
if (this.irisShadersEnabledLastFrame != shadersActive)
|
||||||
|
{
|
||||||
|
this.irisShadersEnabledLastFrame = shadersActive;
|
||||||
|
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
profiler.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
///endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//=================//
|
||||||
// parameter setup //
|
// parameter setup //
|
||||||
//=================//
|
//=================//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
EDhApiRenderPass renderPass;
|
EDhApiRenderPass renderPass;
|
||||||
if (DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
|
if (DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
|
||||||
{
|
|
||||||
if (renderingDeferredLayer)
|
|
||||||
{
|
{
|
||||||
renderPass = EDhApiRenderPass.TRANSPARENT;
|
if (renderingDeferredLayer)
|
||||||
|
{
|
||||||
|
renderPass = EDhApiRenderPass.TRANSPARENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderPass = EDhApiRenderPass.OPAQUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
renderPass = EDhApiRenderPass.OPAQUE;
|
renderPass = EDhApiRenderPass.OPAQUE_AND_TRANSPARENT;
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
renderPass = EDhApiRenderPass.OPAQUE_AND_TRANSPARENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A global render state variable is used since MC has split up their
|
|
||||||
// render prep and actual rendering into different threads/methods
|
|
||||||
// this is annoying since it's possible to start a render with only
|
|
||||||
// partially complete info, but there isn't a better option at the moment
|
|
||||||
RenderParams renderParams = new RenderParams(renderPass, RENDER_STATE);
|
|
||||||
|
|
||||||
///endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============//
|
|
||||||
// validation //
|
|
||||||
//============//
|
|
||||||
///region
|
|
||||||
|
|
||||||
if (firstRenderTimeMs == 0)
|
|
||||||
{
|
|
||||||
firstRenderTimeMs = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
String validationMessage = renderParams.getValidationErrorMessage(firstRenderTimeMs);
|
|
||||||
if (validationMessage != null)
|
|
||||||
{
|
|
||||||
// store the error message so it can be seen on the F3 screen
|
|
||||||
this.lastRenderParamValidationMessage = validationMessage;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.lastRenderParamValidationMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.rendererDisabledBecauseOfExceptions)
|
|
||||||
{
|
|
||||||
// re-enable rendering if the user toggles DH rendering
|
|
||||||
if (!Config.Client.quickEnableRendering.get())
|
|
||||||
{
|
|
||||||
LOGGER.info("DH Renderer re-enabled after exception. Some rendering issues may occur. Please reboot Minecraft if you see any rendering issues.");
|
|
||||||
this.rendererDisabledBecauseOfExceptions = false;
|
|
||||||
Config.Client.quickEnableRendering.set(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
// A global render state variable is used since MC has split up their
|
||||||
}
|
// render prep and actual rendering into different threads/methods
|
||||||
|
// this is annoying since it's possible to start a render with only
|
||||||
|
// partially complete info, but there isn't a better option at the moment
|
||||||
|
RENDER_PARAMS.update(renderPass, RENDER_STATE);
|
||||||
|
|
||||||
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DISABLED)
|
//endregion
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
///endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
//============//
|
||||||
// rendering //
|
// validation //
|
||||||
//===========//
|
//============//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
try
|
String validationMessage = RENDER_PARAMS.getValidationErrorMessage();
|
||||||
{
|
if (validationMessage != null)
|
||||||
// render pass //
|
|
||||||
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
|
|
||||||
{
|
{
|
||||||
if (!renderingDeferredLayer)
|
// store the error message so it can be seen on the F3 screen
|
||||||
|
this.lastRenderParamValidationMessage = validationMessage;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.lastRenderParamValidationMessage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.rendererDisabledBecauseOfExceptions)
|
||||||
|
{
|
||||||
|
// re-enable rendering if the user toggles DH rendering
|
||||||
|
if (!Config.Client.quickEnableRendering.get())
|
||||||
{
|
{
|
||||||
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, renderParams);
|
LOGGER.info("DH Renderer re-enabled after exception. Some rendering issues may occur. Please reboot Minecraft if you see any rendering issues.");
|
||||||
if (!renderingCancelled)
|
this.rendererDisabledBecauseOfExceptions = false;
|
||||||
{
|
Config.Client.quickEnableRendering.set(true);
|
||||||
LodRenderer.INSTANCE.render(renderParams, profiler);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering())
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DISABLED)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===========//
|
||||||
|
// rendering //
|
||||||
|
//===========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// render pass //
|
||||||
|
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
|
||||||
|
{
|
||||||
|
if (!renderingDeferredLayer)
|
||||||
{
|
{
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
|
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, RENDER_PARAMS);
|
||||||
|
if (!renderingCancelled)
|
||||||
|
{
|
||||||
|
LodRenderer.INSTANCE.render(RENDER_PARAMS, profiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering())
|
||||||
|
{
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDeferredRenderEvent.class, RENDER_PARAMS);
|
||||||
|
if (!renderingCancelled)
|
||||||
|
{
|
||||||
|
LodRenderer.INSTANCE.renderDeferred(RENDER_PARAMS, profiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (DhApi.Delayed.renderProxy.getDeferTransparentRendering())
|
||||||
|
{
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDeferredRenderEvent.class, renderParams);
|
if (!renderingDeferredLayer)
|
||||||
if (!renderingCancelled)
|
|
||||||
{
|
{
|
||||||
LodRenderer.INSTANCE.renderDeferred(renderParams, profiler);
|
IDhMetaRenderer metaRenderer = SingletonInjector.INSTANCE.get(IDhMetaRenderer.class);
|
||||||
}
|
IDhTestTriangleRenderer testRenderer = SingletonInjector.INSTANCE.get(IDhTestTriangleRenderer.class);
|
||||||
|
if (testRenderer != null
|
||||||
|
&& metaRenderer != null)
|
||||||
|
{
|
||||||
|
// meta renderer needed for render state/texture
|
||||||
|
// for setup on some APIs (IE openGL)
|
||||||
|
metaRenderer.runRenderPassSetup(RENDER_PARAMS);
|
||||||
|
|
||||||
|
testRenderer.render(RENDER_PARAMS);
|
||||||
|
|
||||||
if (DhApi.Delayed.renderProxy.getDeferTransparentRendering())
|
metaRenderer.runRenderPassCleanup(RENDER_PARAMS);
|
||||||
{
|
}
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
|
else
|
||||||
|
{
|
||||||
|
RATE_LIMITED_LOGGER.warn("Unable to find singleton [" + IDhTestTriangleRenderer.class.getSimpleName() + "]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (!renderingDeferredLayer)
|
this.rendererDisabledBecauseOfExceptions = true;
|
||||||
{
|
LOGGER.error("Unexpected Renderer error in render pass [" + renderPass + "]. Error: " + e.getMessage(), e);
|
||||||
IDhMetaRenderer metaRenderer = SingletonInjector.INSTANCE.get(IDhMetaRenderer.class);
|
|
||||||
IDhTestTriangleRenderer testRenderer = SingletonInjector.INSTANCE.get(IDhTestTriangleRenderer.class);
|
|
||||||
if (testRenderer != null
|
|
||||||
&& metaRenderer != null)
|
|
||||||
{
|
|
||||||
// meta renderer needed for render state/texture
|
|
||||||
// for setup on some APIs (IE openGL)
|
|
||||||
metaRenderer.runRenderPassSetup(renderParams);
|
|
||||||
|
|
||||||
testRenderer.render(renderParams);
|
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "" + MinecraftTextFormat.BOLD + "ERROR: Distant Horizons renderer has encountered an exception!" + MinecraftTextFormat.CLEAR_FORMATTING);
|
||||||
|
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Renderer disabled to try preventing GL state corruption." + MinecraftTextFormat.CLEAR_FORMATTING);
|
||||||
metaRenderer.runRenderPassCleanup(renderParams);
|
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Toggle DH rendering via the config UI to re-activate DH rendering." + MinecraftTextFormat.CLEAR_FORMATTING);
|
||||||
}
|
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Error: " + MinecraftTextFormat.CLEAR_FORMATTING + e);
|
||||||
else
|
|
||||||
{
|
|
||||||
RATE_LIMITED_LOGGER.warn("Unable to find singleton [" + IDhTestTriangleRenderer.class.getSimpleName() + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
this.rendererDisabledBecauseOfExceptions = true;
|
|
||||||
LOGGER.error("Unexpected Renderer error in render pass [" + renderPass + "]. Error: " + e.getMessage(), e);
|
|
||||||
|
|
||||||
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "" + MinecraftTextFormat.BOLD + "ERROR: Distant Horizons renderer has encountered an exception!" + MinecraftTextFormat.CLEAR_FORMATTING);
|
|
||||||
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Renderer disabled to try preventing GL state corruption." + MinecraftTextFormat.CLEAR_FORMATTING);
|
|
||||||
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Toggle DH rendering via the config UI to re-activate DH rendering." + MinecraftTextFormat.CLEAR_FORMATTING);
|
|
||||||
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Error: " + MinecraftTextFormat.CLEAR_FORMATTING + e);
|
|
||||||
}
|
|
||||||
|
|
||||||
///endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
profiler.pop(); // end LOD
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// fade rendering //
|
// fade rendering //
|
||||||
//================//
|
//================//
|
||||||
///region
|
//region fade rendering
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first fade pass.
|
* The first fade pass.
|
||||||
@@ -692,11 +621,10 @@ public class ClientApi
|
|||||||
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
||||||
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
||||||
)
|
)
|
||||||
// don't fade when Iris shaders are active, otherwise the rendering can get weird
|
&& shouldRenderFade())
|
||||||
&& !DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
|
|
||||||
{
|
{
|
||||||
RenderParams renderParams = new RenderParams(EDhApiRenderPass.OPAQUE, RENDER_STATE);
|
RENDER_PARAMS.update(EDhApiRenderPass.OPAQUE, RENDER_STATE);
|
||||||
fadeRenderer.render(renderParams);
|
fadeRenderer.render(RENDER_PARAMS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -722,24 +650,42 @@ public class ClientApi
|
|||||||
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
||||||
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
||||||
)
|
)
|
||||||
// don't fade when Iris shaders are active, otherwise the rendering can get weird
|
&& shouldRenderFade();
|
||||||
&& !DhApiRenderProxy.INSTANCE.getDeferTransparentRendering();
|
|
||||||
if (renderFade)
|
if (renderFade)
|
||||||
{
|
{
|
||||||
RenderParams renderParams = new RenderParams(EDhApiRenderPass.TRANSPARENT, RENDER_STATE);
|
RENDER_PARAMS.update(EDhApiRenderPass.TRANSPARENT, RENDER_STATE);
|
||||||
fadeRenderer.render(renderParams);
|
fadeRenderer.render(RENDER_PARAMS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
private static boolean shouldRenderFade()
|
||||||
|
{
|
||||||
|
// don't fade when Iris shaders are active, otherwise the rendering can get weird
|
||||||
|
if (DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't render fade through immersive portals, this causes the fade to apply incorrectly
|
||||||
|
IImmersivePortalsAccessor immersivePortals = ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
|
||||||
|
if (immersivePortals != null
|
||||||
|
&& immersivePortals.isRenderingPortal())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========//
|
//==========//
|
||||||
// keyboard //
|
// keyboard //
|
||||||
//==========//
|
//==========//
|
||||||
///region
|
//region keyboard
|
||||||
|
|
||||||
/** Trigger once on key press, with CLIENT PLAYER. */
|
/** Trigger once on key press, with CLIENT PLAYER. */
|
||||||
public void keyPressedEvent(int glfwKey)
|
public void keyPressedEvent(int glfwKey)
|
||||||
@@ -763,19 +709,19 @@ public class ClientApi
|
|||||||
}
|
}
|
||||||
else if (glfwKey == GLFW.GLFW_KEY_F8)
|
else if (glfwKey == GLFW.GLFW_KEY_F8)
|
||||||
{
|
{
|
||||||
Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
|
Config.Client.Advanced.Debugging.debugRenderingColors.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRenderingColors.get()));
|
||||||
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRendering.get());
|
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRenderingColors.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//======//
|
//======//
|
||||||
// chat //
|
// chat //
|
||||||
//======//
|
//======//
|
||||||
///region
|
//region chat
|
||||||
|
|
||||||
private void sendQueuedChatMessages()
|
private void sendQueuedChatMessages()
|
||||||
{
|
{
|
||||||
@@ -909,7 +855,7 @@ public class ClientApi
|
|||||||
*/
|
*/
|
||||||
public void showOverlayMessageNextFrame(String message) { this.overlayMessageQueueForNextFrame.add(message); }
|
public void showOverlayMessageNextFrame(String message) { this.overlayMessageQueueForNextFrame.add(message); }
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
-32
@@ -10,13 +10,13 @@ import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent
|
|||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
||||||
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
|
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to manage the level keys.
|
* This class is used to manage the level keys.
|
||||||
@@ -30,9 +30,6 @@ public class ClientPluginChannelApi
|
|||||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
||||||
|
|
||||||
private final Consumer<IServerKeyedClientLevel> levelLoadHandler;
|
|
||||||
private final Consumer<IClientLevelWrapper> levelUnloadHandler;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public NetworkSession networkSession;
|
public NetworkSession networkSession;
|
||||||
|
|
||||||
@@ -42,10 +39,8 @@ public class ClientPluginChannelApi
|
|||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
|
||||||
public ClientPluginChannelApi(Consumer<IServerKeyedClientLevel> levelLoadHandler, Consumer<IClientLevelWrapper> levelUnloadHandler)
|
public ClientPluginChannelApi()
|
||||||
{
|
{
|
||||||
this.levelLoadHandler = levelLoadHandler;
|
|
||||||
this.levelUnloadHandler = levelUnloadHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -88,38 +83,27 @@ public class ClientPluginChannelApi
|
|||||||
throw new IllegalArgumentException("Server sent invalid level key.");
|
throw new IllegalArgumentException("Server sent invalid level key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.info("Server level key received: [" + msg.levelKey + "].");
|
LOGGER.info("Level init received for [" + msg.dimensionResourceLocation + "]: server key [" + msg.serverKey + "], level key [" + msg.levelKey + "]");
|
||||||
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("ClientPluginChannelApi onLevelInitMessage", () ->
|
||||||
{
|
{
|
||||||
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
|
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
|
||||||
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
|
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(clientLevel);
|
||||||
|
|
||||||
if (existingKeyedClientLevel != null)
|
|
||||||
{
|
|
||||||
if (!existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
|
|
||||||
{
|
|
||||||
LOGGER.info("Unloading previous level with key: [" + existingKeyedClientLevel.getServerLevelKey() + "].");
|
|
||||||
this.levelUnloadHandler.accept(existingKeyedClientLevel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.info("Level key matches the previous level key, ignoring the message.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDhIdentifier() + "].");
|
|
||||||
this.levelUnloadHandler.accept(clientLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingKeyedClientLevel == null
|
if (existingKeyedClientLevel == null
|
||||||
|| !existingKeyedClientLevel.getServerKey().equals(msg.serverKey)
|
|| !existingKeyedClientLevel.getServerKey().equals(msg.serverKey)
|
||||||
|| !existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
|
|| !existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
|
||||||
{
|
{
|
||||||
LOGGER.info("Loading level with key: [" + msg.levelKey + "].");
|
LOGGER.info("Loading level with key: [" + msg.levelKey + "].");
|
||||||
IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.serverKey, msg.levelKey);
|
|
||||||
this.levelLoadHandler.accept(keyedLevel);
|
IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.dimensionResourceLocation, msg.serverKey, msg.levelKey);
|
||||||
|
|
||||||
|
if (keyedLevel != null) {
|
||||||
|
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||||
|
if (world != null) {
|
||||||
|
world.getOrLoadLevel(keyedLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,10 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.api.internal;
|
package com.seibel.distanthorizons.core.api.internal;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
|
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
|
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
|
||||||
import com.seibel.distanthorizons.core.world.*;
|
import com.seibel.distanthorizons.core.world.*;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
@@ -77,7 +74,6 @@ public class ServerApi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// level events //
|
// level events //
|
||||||
//==============//
|
//==============//
|
||||||
@@ -90,7 +86,6 @@ public class ServerApi
|
|||||||
if (serverWorld != null)
|
if (serverWorld != null)
|
||||||
{
|
{
|
||||||
serverWorld.getOrLoadLevel(levelWrapper);
|
serverWorld.getOrLoadLevel(levelWrapper);
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(levelWrapper));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void serverLevelUnloadEvent(IServerLevelWrapper level)
|
public void serverLevelUnloadEvent(IServerLevelWrapper level)
|
||||||
@@ -101,12 +96,10 @@ public class ServerApi
|
|||||||
if (serverWorld != null)
|
if (serverWorld != null)
|
||||||
{
|
{
|
||||||
serverWorld.unloadLevel(level);
|
serverWorld.unloadLevel(level);
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(level));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======================//
|
//=======================//
|
||||||
// chunk modified events //
|
// chunk modified events //
|
||||||
//=======================//
|
//=======================//
|
||||||
@@ -122,7 +115,7 @@ public class ServerApi
|
|||||||
|
|
||||||
public void serverPlayerJoinEvent(IServerPlayerWrapper player)
|
public void serverPlayerJoinEvent(IServerPlayerWrapper player)
|
||||||
{
|
{
|
||||||
if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly())
|
if (DhApiWorldProxy.INSTANCE.tryGetReadOnly())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -136,7 +129,7 @@ public class ServerApi
|
|||||||
}
|
}
|
||||||
public void serverPlayerDisconnectEvent(IServerPlayerWrapper player)
|
public void serverPlayerDisconnectEvent(IServerPlayerWrapper player)
|
||||||
{
|
{
|
||||||
if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly())
|
if (DhApiWorldProxy.INSTANCE.tryGetReadOnly())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -150,7 +143,7 @@ public class ServerApi
|
|||||||
}
|
}
|
||||||
public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel)
|
public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel)
|
||||||
{
|
{
|
||||||
if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly())
|
if (DhApiWorldProxy.INSTANCE.tryGetReadOnly())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -170,7 +163,7 @@ public class ServerApi
|
|||||||
*/
|
*/
|
||||||
public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message)
|
public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message)
|
||||||
{
|
{
|
||||||
if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly())
|
if (DhApiWorldProxy.INSTANCE.tryGetReadOnly())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.seibel.distanthorizons.core.level.IDhLevel;
|
|||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
||||||
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||||
@@ -63,6 +64,7 @@ public class SharedApi
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static AbstractDhWorld currentWorld;
|
private static AbstractDhWorld currentWorld;
|
||||||
|
private static final Object worldLockObject = new Object();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +74,6 @@ public class SharedApi
|
|||||||
//region
|
//region
|
||||||
|
|
||||||
private SharedApi() { }
|
private SharedApi() { }
|
||||||
public static void init() { Initializer.init(); }
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -87,46 +88,51 @@ public class SharedApi
|
|||||||
|
|
||||||
public static void setDhWorld(AbstractDhWorld newWorld)
|
public static void setDhWorld(AbstractDhWorld newWorld)
|
||||||
{
|
{
|
||||||
AbstractDhWorld oldWorld = currentWorld;
|
synchronized (worldLockObject)
|
||||||
if (oldWorld != null)
|
|
||||||
{
|
{
|
||||||
oldWorld.close();
|
AbstractDhWorld oldWorld = currentWorld;
|
||||||
}
|
if (oldWorld != null)
|
||||||
currentWorld = newWorld;
|
|
||||||
|
|
||||||
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
|
|
||||||
// access the MC level at inappropriate times, which can cause exceptions
|
|
||||||
if (currentWorld != null)
|
|
||||||
{
|
|
||||||
ThreadPoolUtil.setupThreadPools();
|
|
||||||
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThreadPoolUtil.shutdownThreadPools();
|
|
||||||
|
|
||||||
// delayed get because SharedApi will be created before the singleton has been bound
|
|
||||||
AbstractDebugWireframeRenderer debugWireframeRenderer = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
|
|
||||||
debugWireframeRenderer.clearRenderables();
|
|
||||||
|
|
||||||
if (MC_RENDER != null)
|
|
||||||
{
|
{
|
||||||
MC_RENDER.clearTargetFrameBuffer();
|
oldWorld.close();
|
||||||
}
|
}
|
||||||
|
currentWorld = newWorld;
|
||||||
|
|
||||||
// shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed
|
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
|
||||||
AbstractDhRepo.closeAllConnections();
|
// access the MC level at inappropriate times, which can cause exceptions
|
||||||
// needs to be closed on world shutdown to clear out un-processed chunks
|
if (currentWorld != null)
|
||||||
WORLD_CHUNK_UPDATE_MANAGER.clear();
|
{
|
||||||
|
ThreadPoolUtil.setupThreadPools();
|
||||||
|
|
||||||
// recommend that the garbage collector cleans up any objects from the old world and thread pools
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
|
||||||
System.gc();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThreadPoolUtil.shutdownThreadPools();
|
||||||
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldUnloadEvent.class, new DhApiWorldUnloadEvent.EventParam());
|
// delayed get because SharedApi will be created before the singleton has been bound
|
||||||
|
AbstractDebugWireframeRenderer debugWireframeRenderer = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
|
||||||
|
debugWireframeRenderer.clearRenderables();
|
||||||
|
|
||||||
// fired after the unload event so API users can't change the read-only for any new worlds
|
if (MC_RENDER != null)
|
||||||
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
|
{
|
||||||
|
MC_RENDER.clearTargetFrameBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed
|
||||||
|
AbstractDhRepo.closeAllConnections();
|
||||||
|
// needs to be closed on world shutdown to clear out un-processed chunks
|
||||||
|
WORLD_CHUNK_UPDATE_MANAGER.clear();
|
||||||
|
|
||||||
|
RenderThreadTaskHandler.INSTANCE.clearDebugStats();
|
||||||
|
|
||||||
|
// recommend that the garbage collector cleans up any objects from the old world and thread pools
|
||||||
|
System.gc();
|
||||||
|
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldUnloadEvent.class, new DhApiWorldUnloadEvent.EventParam());
|
||||||
|
|
||||||
|
// fired after the unload event so API users can't change the read-only for any new worlds
|
||||||
|
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,14 +204,14 @@ public class SharedApi
|
|||||||
// If the client world isn't loaded yet, keep track of which chunks were loaded so we can use them later.
|
// If the client world isn't loaded yet, keep track of which chunks were loaded so we can use them later.
|
||||||
// This may happen if the client world and client level load events happen out of order
|
// This may happen if the client world and client level load events happen out of order
|
||||||
IClientLevelWrapper clientLevel = (IClientLevelWrapper) levelWrapper;
|
IClientLevelWrapper clientLevel = (IClientLevelWrapper) levelWrapper;
|
||||||
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.replace(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
|
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.put(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore updates if the world is read-only
|
// ignore updates if the world is read-only
|
||||||
if (DhApiWorldProxy.INSTANCE.getReadOnly())
|
if (DhApiWorldProxy.INSTANCE.tryGetReadOnly())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -218,7 +224,7 @@ public class SharedApi
|
|||||||
{
|
{
|
||||||
// the client level isn't loaded yet
|
// the client level isn't loaded yet
|
||||||
IClientLevelWrapper clientLevel = (IClientLevelWrapper) levelWrapper;
|
IClientLevelWrapper clientLevel = (IClientLevelWrapper) levelWrapper;
|
||||||
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.replace(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
|
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.put(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
+10
@@ -11,6 +11,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@@ -28,6 +30,9 @@ public class WorldChunkUpdateManager
|
|||||||
/** singleton since we only expect to have one world loaded at a time */
|
/** singleton since we only expect to have one world loaded at a time */
|
||||||
public static final WorldChunkUpdateManager INSTANCE = new WorldChunkUpdateManager();
|
public static final WorldChunkUpdateManager INSTANCE = new WorldChunkUpdateManager();
|
||||||
|
|
||||||
|
public static final Set<String> LOGGED_GET_ERROR_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues are only removed during world shutdown.
|
* Queues are only removed during world shutdown.
|
||||||
* The assumption is that there will be a limited number of {@link ILevelWrapper}'s
|
* The assumption is that there will be a limited number of {@link ILevelWrapper}'s
|
||||||
@@ -37,6 +42,7 @@ public class WorldChunkUpdateManager
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
@@ -62,6 +68,7 @@ public class WorldChunkUpdateManager
|
|||||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||||
if (world == null)
|
if (world == null)
|
||||||
{
|
{
|
||||||
|
// world isn't loaded, no warnings need to be logged
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +80,8 @@ public class WorldChunkUpdateManager
|
|||||||
// but this check confirms it
|
// but this check confirms it
|
||||||
&& !(levelWrapper instanceof IClientLevelWrapper))
|
&& !(levelWrapper instanceof IClientLevelWrapper))
|
||||||
{
|
{
|
||||||
|
// how did we get a server level wrapper on the client?
|
||||||
|
// this shouldn't happen, but just in case
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
@@ -81,6 +90,7 @@ public class WorldChunkUpdateManager
|
|||||||
// when hosting a server we only care about the server wrappers
|
// when hosting a server we only care about the server wrappers
|
||||||
&& !(levelWrapper instanceof IServerLevelWrapper))
|
&& !(levelWrapper instanceof IServerLevelWrapper))
|
||||||
{
|
{
|
||||||
|
// ignore client updates on the server
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
package com.seibel.distanthorizons.core.api.internal.rendering;
|
package com.seibel.distanthorizons.core.api.internal.rendering;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
import com.seibel.distanthorizons.core.util.math.DhMat4f;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
|
|||||||
*/
|
*/
|
||||||
public class DhRenderState
|
public class DhRenderState
|
||||||
{
|
{
|
||||||
public Mat4f mcModelViewMatrix = null;
|
public DhMat4f mcModelViewMatrix = null;
|
||||||
public Mat4f mcProjectionMatrix = null;
|
public DhMat4f mcProjectionMatrix = null;
|
||||||
/**
|
/**
|
||||||
* percentage of time into the current client tick. <br><br>
|
* percentage of time into the current client tick. <br><br>
|
||||||
*
|
*
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+84
@@ -0,0 +1,84 @@
|
|||||||
|
package com.seibel.distanthorizons.core.config.eventHandlers;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||||
|
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
public abstract class AbstractDelayedConfigEventHandler implements IConfigListener
|
||||||
|
{
|
||||||
|
public static final long DEFAULT_TIMEOUT_IN_MS = 2_000L;
|
||||||
|
|
||||||
|
/** how long to wait in milliseconds before applying the config changes */
|
||||||
|
private final long timeoutInMs;
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public AbstractDelayedConfigEventHandler(long timeoutInMs) { this.timeoutInMs = timeoutInMs; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==================//
|
||||||
|
// abstract methods //
|
||||||
|
//==================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public abstract void onConfigTimeout();
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//========//
|
||||||
|
// events //
|
||||||
|
//========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigValueSet()
|
||||||
|
{
|
||||||
|
if (this.timeoutInMs > 0)
|
||||||
|
{
|
||||||
|
this.refreshRenderDataAfterTimeout();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.onConfigTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calling this method multiple times will reset the timer */
|
||||||
|
private synchronized void refreshRenderDataAfterTimeout() // synchronized to prevent potential threading issues when adding/removing the timer
|
||||||
|
{
|
||||||
|
// stop the previous timer if one exists
|
||||||
|
if (this.timer != null)
|
||||||
|
{
|
||||||
|
this.timer.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new timer task
|
||||||
|
TimerTask timerTask = new TimerTask()
|
||||||
|
{
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
AbstractDelayedConfigEventHandler.this.onConfigTimeout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.timer = TimerUtil.CreateTimer("AbstractDelayedConfigTimer");
|
||||||
|
this.timer.schedule(timerTask, this.timeoutInMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+11
-44
@@ -27,72 +27,36 @@ import com.seibel.distanthorizons.core.util.TimerUtil;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
public class ReloadLodsConfigEventHandler implements IConfigListener
|
public class ReloadLodsConfigEventHandler extends AbstractDelayedConfigEventHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* should be used for user facing UI options
|
* should be used for user facing UI options
|
||||||
* this allows the user a second to click through options before they're applied
|
* this allows the user a second to click through options before they're applied
|
||||||
*/
|
*/
|
||||||
public static ReloadLodsConfigEventHandler DELAYED_INSTANCE = new ReloadLodsConfigEventHandler(2_000L);
|
public static ReloadLodsConfigEventHandler DELAYED_INSTANCE = new ReloadLodsConfigEventHandler(AbstractDelayedConfigEventHandler.DEFAULT_TIMEOUT_IN_MS);
|
||||||
/** should be used for debug options so their change can be seen instantly */
|
/** should be used for debug options so their change can be seen instantly */
|
||||||
public static ReloadLodsConfigEventHandler INSTANT_INSTANCE = new ReloadLodsConfigEventHandler(0);
|
public static ReloadLodsConfigEventHandler INSTANT_INSTANCE = new ReloadLodsConfigEventHandler(0);
|
||||||
|
|
||||||
/** how long to wait in milliseconds before applying the config changes */
|
|
||||||
private final long timeoutInMs;
|
|
||||||
private Timer cacheClearingTimer;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
public ReloadLodsConfigEventHandler(long timeoutInMs)
|
public ReloadLodsConfigEventHandler(long timeoutInMs) { super(timeoutInMs); }
|
||||||
{
|
|
||||||
this.timeoutInMs = timeoutInMs;
|
//endregion
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//========//
|
//========//
|
||||||
// events //
|
// events //
|
||||||
//========//
|
//========//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigValueSet()
|
public void onConfigTimeout()
|
||||||
{
|
|
||||||
if (this.timeoutInMs > 0)
|
|
||||||
{
|
|
||||||
this.refreshRenderDataAfterTimeout();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
clearRenderDataCache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Calling this method multiple times will reset the timer */
|
|
||||||
private synchronized void refreshRenderDataAfterTimeout() // synchronized to prevent potential threading issues when adding/removing the timer
|
|
||||||
{
|
|
||||||
// stop the previous timer if one exists
|
|
||||||
if (this.cacheClearingTimer != null)
|
|
||||||
{
|
|
||||||
this.cacheClearingTimer.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new timer task
|
|
||||||
TimerTask timerTask = new TimerTask()
|
|
||||||
{
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
clearRenderDataCache();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.cacheClearingTimer = TimerUtil.CreateTimer("RenderCacheClearConfigTimer");
|
|
||||||
this.cacheClearingTimer.schedule(timerTask, this.timeoutInMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void clearRenderDataCache()
|
|
||||||
{
|
{
|
||||||
IDhApiRenderProxy renderProxy = DhApi.Delayed.renderProxy;
|
IDhApiRenderProxy renderProxy = DhApi.Delayed.renderProxy;
|
||||||
if (renderProxy != null)
|
if (renderProxy != null)
|
||||||
@@ -101,5 +65,8 @@ public class ReloadLodsConfigEventHandler implements IConfigListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.core.config.eventHandlers;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.DhApi;
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||||
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
|
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||||
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
|
||||||
|
public class RenderBlockCacheCsvHandler extends AbstractDelayedConfigEventHandler
|
||||||
|
{
|
||||||
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
|
public static RenderBlockCacheCsvHandler INSTANCE = new RenderBlockCacheCsvHandler();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/** private since we only ever need one handler at a time */
|
||||||
|
private RenderBlockCacheCsvHandler() { super(AbstractDelayedConfigEventHandler.DEFAULT_TIMEOUT_IN_MS); }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// config handling //
|
||||||
|
//=================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigTimeout()
|
||||||
|
{
|
||||||
|
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||||
|
if (wrapperFactory != null)
|
||||||
|
{
|
||||||
|
wrapperFactory.resetCachedIgnoredBlocksSets();
|
||||||
|
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -78,7 +78,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
|
|||||||
this.put(EDhApiQualityPreset.HIGH, EDhApiTransparency.COMPLETE);
|
this.put(EDhApiQualityPreset.HIGH, EDhApiTransparency.COMPLETE);
|
||||||
this.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
|
this.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
|
||||||
}});
|
}});
|
||||||
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Ssao.enableSsao,
|
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.enableSsao,
|
||||||
new HashMap<EDhApiQualityPreset, Boolean>()
|
new HashMap<EDhApiQualityPreset, Boolean>()
|
||||||
{{
|
{{
|
||||||
this.put(EDhApiQualityPreset.MINIMUM, false);
|
this.put(EDhApiQualityPreset.MINIMUM, false);
|
||||||
|
|||||||
@@ -55,6 +55,16 @@ public class ConfigEntry<T> extends AbstractConfigBase<T>
|
|||||||
@Nullable
|
@Nullable
|
||||||
private T apiValue;
|
private T apiValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will be null if un-set. <br> <br>
|
||||||
|
*
|
||||||
|
* Some options aren't supported on all Minecraft versions,
|
||||||
|
* in those cases this value will be set to override the
|
||||||
|
* config file option.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private T mcVersionOverrideValue;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
@@ -128,6 +138,13 @@ public class ConfigEntry<T> extends AbstractConfigBase<T>
|
|||||||
&& this.apiValue != null;
|
&& this.apiValue != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** setting to null will allow the config to be used normally */
|
||||||
|
public void setMcVersionOverrideValue(@Nullable T value)
|
||||||
|
{ this.mcVersionOverrideValue = value; }
|
||||||
|
|
||||||
|
public boolean mcVersionOverridePresent()
|
||||||
|
{ return this.mcVersionOverrideValue != null; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should only be used when loading the config from file. <Br>
|
* Should only be used when loading the config from file. <Br>
|
||||||
* Sets the value without informing the rest of the code (ie, it doesn't call listeners, or saving the value to file).
|
* Sets the value without informing the rest of the code (ie, it doesn't call listeners, or saving the value to file).
|
||||||
@@ -183,6 +200,12 @@ public class ConfigEntry<T> extends AbstractConfigBase<T>
|
|||||||
@Override
|
@Override
|
||||||
public T get()
|
public T get()
|
||||||
{
|
{
|
||||||
|
// always use the MC version specific option if defined
|
||||||
|
if (this.mcVersionOverrideValue != null)
|
||||||
|
{
|
||||||
|
return this.mcVersionOverrideValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.allowApiOverride
|
if (this.allowApiOverride
|
||||||
&& this.apiValue != null)
|
&& this.apiValue != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+64
-62
@@ -41,35 +41,47 @@ public final class BufferQuad
|
|||||||
public static final int MAX_QUAD_WIDTH_FOR_EARTH_CURVATURE = LodUtil.CHUNK_WIDTH;
|
public static final int MAX_QUAD_WIDTH_FOR_EARTH_CURVATURE = LodUtil.CHUNK_WIDTH;
|
||||||
|
|
||||||
|
|
||||||
public final short x;
|
public short x;
|
||||||
public final short y;
|
public short y;
|
||||||
public final short z;
|
public short z;
|
||||||
|
|
||||||
public short widthEastWest;
|
public short widthEastWest;
|
||||||
/** This is both North/South and Up/Down since the merging logic is the same either way */
|
/** This is both North/South and Up/Down since the merging logic is the same either way */
|
||||||
public short widthNorthSouthOrHeight;
|
public short widthNorthSouthOrHeight;
|
||||||
|
|
||||||
public final int color;
|
public int color;
|
||||||
/** used by the Iris shader mod to determine how each LOD should be rendered */
|
/** used by the Iris shader mod to determine how each LOD should be rendered */
|
||||||
public final byte irisBlockMaterialId;
|
public byte irisBlockMaterialId;
|
||||||
|
|
||||||
public final byte skyLight;
|
public byte skyLight;
|
||||||
public final byte blockLight;
|
public byte blockLight;
|
||||||
public final EDhDirection direction;
|
public EDhDirection direction;
|
||||||
|
|
||||||
public boolean hasError = false;
|
public boolean hasError = false;
|
||||||
|
|
||||||
|
// Pre-computed sort keys to avoid recomputing on every comparison
|
||||||
|
// Slight increase in memory for reduction in cpu usage
|
||||||
|
public long sortKeyEastWest;
|
||||||
|
public long sortKeyNorthSouth;
|
||||||
|
|
||||||
|
|
||||||
BufferQuad(
|
|
||||||
short x, short y, short z, short widthEastWest, short widthNorthSouthOrHeight,
|
//=============//
|
||||||
int color, byte irisBlockMaterialId, byte skylight, byte blockLight,
|
// constructor //
|
||||||
EDhDirection direction)
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public BufferQuad() {}
|
||||||
|
|
||||||
|
public void set(short x, short y, short z, short widthEastWest, short widthNorthSouthOrHeight,
|
||||||
|
int color, byte irisBlockMaterialId, byte skylight, byte blockLight,
|
||||||
|
EDhDirection direction)
|
||||||
{
|
{
|
||||||
if (widthEastWest == 0 || widthNorthSouthOrHeight == 0)
|
if (widthEastWest == 0 || widthNorthSouthOrHeight == 0)
|
||||||
{
|
{
|
||||||
throw new IllegalArgumentException("Size 0 quad!");
|
throw new IllegalArgumentException("Size 0 quad!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widthEastWest < 0 || widthNorthSouthOrHeight < 0)
|
if (widthEastWest < 0 || widthNorthSouthOrHeight < 0)
|
||||||
{
|
{
|
||||||
throw new IllegalArgumentException("Negative sized quad!");
|
throw new IllegalArgumentException("Negative sized quad!");
|
||||||
@@ -85,64 +97,46 @@ public final class BufferQuad
|
|||||||
this.skyLight = skylight;
|
this.skyLight = skylight;
|
||||||
this.blockLight = blockLight;
|
this.blockLight = blockLight;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
|
this.sortKeyEastWest = computeSortKey(direction, true);
|
||||||
|
this.sortKeyNorthSouth = computeSortKey(direction, false);
|
||||||
}
|
}
|
||||||
|
private long computeSortKey(EDhDirection dir, boolean eastWest)
|
||||||
|
|
||||||
|
|
||||||
/** a rough but fast calculation */
|
|
||||||
double calculateDistance(double relativeX, double relativeY, double relativeZ)
|
|
||||||
{
|
{
|
||||||
return Math.pow(relativeX - this.x, 2) + Math.pow(relativeY - this.y, 2) + Math.pow(relativeZ - this.z, 2);
|
if (eastWest)
|
||||||
|
{
|
||||||
|
switch (dir.axis)
|
||||||
|
{
|
||||||
|
case X: return (long) x << 48 | (long) y << 32 | (long) z << 16;
|
||||||
|
case Y: return (long) y << 48 | (long) z << 32 | (long) x << 16;
|
||||||
|
case Z: return (long) z << 48 | (long) y << 32 | (long) x << 16;
|
||||||
|
default: throw new IllegalArgumentException("Invalid Axis enum: [" + dir.axis + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (dir.axis)
|
||||||
|
{
|
||||||
|
case X: return (long) x << 48 | (long) z << 32 | (long) y << 16;
|
||||||
|
case Y: return (long) y << 48 | (long) x << 32 | (long) z << 16;
|
||||||
|
case Z: return (long) z << 48 | (long) x << 32 | (long) y << 16;
|
||||||
|
default: throw new IllegalArgumentException("Invalid Axis enum: [" + dir.axis + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** compares this quad's position to the given quad */
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** compares this quad's position to the given quad using pre-computed sort keys */
|
||||||
public int compare(BufferQuad quad, BufferMergeDirectionEnum compareDirection)
|
public int compare(BufferQuad quad, BufferMergeDirectionEnum compareDirection)
|
||||||
{
|
{
|
||||||
if (this.direction != quad.direction)
|
if (this.direction != quad.direction)
|
||||||
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + this.direction);
|
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + this.direction);
|
||||||
|
|
||||||
if (compareDirection == BufferMergeDirectionEnum.EastWest)
|
return compareDirection == BufferMergeDirectionEnum.EastWest
|
||||||
{
|
? Long.compare(this.sortKeyEastWest, quad.sortKeyEastWest)
|
||||||
switch (this.direction.axis)
|
: Long.compare(this.sortKeyNorthSouth, quad.sortKeyNorthSouth);
|
||||||
{
|
|
||||||
case X:
|
|
||||||
return threeDimensionalCompare(this.x, this.y, this.z, quad.x, quad.y, quad.z);
|
|
||||||
case Y:
|
|
||||||
return threeDimensionalCompare(this.y, this.z, this.x, quad.y, quad.z, quad.x);
|
|
||||||
case Z:
|
|
||||||
return threeDimensionalCompare(this.z, this.y, this.x, quad.z, quad.y, quad.x);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Invalid Axis enum: [" + this.direction.axis + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (this.direction.axis)
|
|
||||||
{
|
|
||||||
case X:
|
|
||||||
return threeDimensionalCompare(this.x, this.z, this.y, quad.x, quad.z, quad.y);
|
|
||||||
case Y:
|
|
||||||
return threeDimensionalCompare(this.y, this.x, this.z, quad.y, quad.x, quad.z);
|
|
||||||
case Z:
|
|
||||||
return threeDimensionalCompare(this.z, this.x, this.y, quad.z, quad.x, quad.y);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Invalid Axis enum: [" + this.direction.axis + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Compares two 3D points A and B. <br>
|
|
||||||
* The X, Y, and Z coordinates can be passed into parameters 0, 1, and 2 in any order
|
|
||||||
* provided they are in the same order for both A and B. <br>
|
|
||||||
* With the 0th parameter being the most significant when comparing.
|
|
||||||
*/
|
|
||||||
private static int threeDimensionalCompare(short a0, short a1, short a2, short b0, short b1, short b2)
|
|
||||||
{
|
|
||||||
long a = (long) a0 << 48 | (long) a1 << 32 | (long) a2 << 16;
|
|
||||||
long b = (long) b0 << 48 | (long) b1 << 32 | (long) b2 << 16;
|
|
||||||
return Long.compare(a, b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -154,11 +148,15 @@ public final class BufferQuad
|
|||||||
public boolean tryMerge(BufferQuad quad, BufferMergeDirectionEnum mergeDirection)
|
public boolean tryMerge(BufferQuad quad, BufferMergeDirectionEnum mergeDirection)
|
||||||
{
|
{
|
||||||
if (quad.hasError || this.hasError)
|
if (quad.hasError || this.hasError)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// only merge quads that are in the same direction
|
// only merge quads that are in the same direction
|
||||||
if (this.direction != quad.direction)
|
if (this.direction != quad.direction)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure these quads share the same perpendicular axis
|
// make sure these quads share the same perpendicular axis
|
||||||
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y)
|
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y)
|
||||||
@@ -175,7 +173,6 @@ public final class BufferQuad
|
|||||||
short otherParallelCompareStartPos;
|
short otherParallelCompareStartPos;
|
||||||
switch (this.direction.axis)
|
switch (this.direction.axis)
|
||||||
{
|
{
|
||||||
default: // shouldn't normally happen, just here to make the compiler happy
|
|
||||||
case X:
|
case X:
|
||||||
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
||||||
{
|
{
|
||||||
@@ -232,6 +229,9 @@ public final class BufferQuad
|
|||||||
otherParallelCompareStartPos = quad.z;
|
otherParallelCompareStartPos = quad.z;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default: // shouldn't normally happen, just here to make the compiler happy
|
||||||
|
throw new IllegalArgumentException("Unsupported axis: ["+this.direction.axis+"]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the width of this quad in the relevant axis
|
// get the width of this quad in the relevant axis
|
||||||
@@ -333,4 +333,6 @@ public final class BufferQuad
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+76
-61
@@ -19,12 +19,15 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
|
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListCheckout;
|
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListCheckout;
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnRenderView;
|
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnRenderView;
|
||||||
@@ -51,7 +54,7 @@ public class ColumnBox
|
|||||||
|
|
||||||
public static void addBoxQuadsToBuilder(
|
public static void addBoxQuadsToBuilder(
|
||||||
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout, IDhClientLevel clientLevel,
|
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout, IDhClientLevel clientLevel,
|
||||||
short width, short yHeight,
|
short blockWidth, short yHeight,
|
||||||
short minX, short minY, short minZ,
|
short minX, short minY, short minZ,
|
||||||
int color, byte irisBlockMaterialId, byte skyLight, byte blockLight,
|
int color, byte irisBlockMaterialId, byte skyLight, byte blockLight,
|
||||||
long topData, long bottomData, ColumnRenderView[] adjData, boolean[] isAdjDataSameDetailLevel)
|
long topData, long bottomData, ColumnRenderView[] adjData, boolean[] isAdjDataSameDetailLevel)
|
||||||
@@ -60,14 +63,19 @@ public class ColumnBox
|
|||||||
// variable setup //
|
// variable setup //
|
||||||
//================//
|
//================//
|
||||||
|
|
||||||
short maxX = (short) (minX + width);
|
IClientLevelWrapper clientLevelWrapper = clientLevel.getClientLevelWrapper();
|
||||||
|
if (clientLevelWrapper == null)
|
||||||
|
{
|
||||||
|
LodUtil.assertNotReach("addBoxQuadsToBuilder getClientLevelWrapper should always succeed");
|
||||||
|
}
|
||||||
|
|
||||||
|
short maxX = (short) (minX + blockWidth);
|
||||||
short maxY = (short) (minY + yHeight);
|
short maxY = (short) (minY + yHeight);
|
||||||
short maxZ = (short) (minZ + width);
|
short maxZ = (short) (minZ + blockWidth);
|
||||||
byte skyLightTop = skyLight;
|
byte skyLightTop = skyLight;
|
||||||
byte skyLightBot = RenderDataPointUtil.doesDataPointExist(bottomData) ? RenderDataPointUtil.getLightSky(bottomData) : 0;
|
byte skyLightBot = RenderDataPointUtil.doesDataPointExist(bottomData) ? RenderDataPointUtil.getLightSky(bottomData) : 0;
|
||||||
|
|
||||||
boolean transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
|
boolean transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get() == EDhApiTransparency.COMPLETE;
|
||||||
boolean fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled;
|
|
||||||
|
|
||||||
boolean isTransparent = ColorUtil.getAlpha(color) < 255 && transparencyEnabled;
|
boolean isTransparent = ColorUtil.getAlpha(color) < 255 && transparencyEnabled;
|
||||||
boolean overVoid = !RenderDataPointUtil.doesDataPointExist(bottomData);
|
boolean overVoid = !RenderDataPointUtil.doesDataPointExist(bottomData);
|
||||||
@@ -92,24 +100,6 @@ public class ColumnBox
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// fake ocean transparency
|
|
||||||
if (transparencyEnabled && fakeOceanFloor)
|
|
||||||
{
|
|
||||||
if (!isTransparent && isTopTransparent && RenderDataPointUtil.doesDataPointExist(topData))
|
|
||||||
{
|
|
||||||
skyLightTop = (byte) MathUtil.clamp(0, 15 - (RenderDataPointUtil.getYMax(topData) - minY), 15);
|
|
||||||
yHeight = (short) (RenderDataPointUtil.getYMax(topData) - minY - 1);
|
|
||||||
}
|
|
||||||
else if (isTransparent && !isBottomTransparent && RenderDataPointUtil.doesDataPointExist(bottomData))
|
|
||||||
{
|
|
||||||
minY = (short) (minY + yHeight - 1);
|
|
||||||
yHeight = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
maxY = (short) (minY + yHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========================//
|
//==========================//
|
||||||
// add top and bottom faces //
|
// add top and bottom faces //
|
||||||
@@ -122,7 +112,7 @@ public class ColumnBox
|
|||||||
&& !isTopTransparent;
|
&& !isTopTransparent;
|
||||||
if (!skipTop)
|
if (!skipTop)
|
||||||
{
|
{
|
||||||
builder.addQuadUp(minX, maxY, minZ, width, ColorUtil.applyShade(color, MC_RENDER.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight);
|
builder.addQuadUp(minX, maxY, minZ, blockWidth, ColorUtil.applyShade(color, clientLevelWrapper.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +123,7 @@ public class ColumnBox
|
|||||||
&& !isBottomTransparent;
|
&& !isBottomTransparent;
|
||||||
if (!skipBottom)
|
if (!skipBottom)
|
||||||
{
|
{
|
||||||
builder.addQuadDown(minX, minY, minZ, width, ColorUtil.applyShade(color, MC_RENDER.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight);
|
builder.addQuadDown(minX, minY, minZ, blockWidth, ColorUtil.applyShade(color, clientLevelWrapper.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,16 +146,16 @@ public class ColumnBox
|
|||||||
builder.addQuadAdj(
|
builder.addQuadAdj(
|
||||||
EDhDirection.NORTH,
|
EDhDirection.NORTH,
|
||||||
minX, minY, minZ,
|
minX, minY, minZ,
|
||||||
width, yHeight,
|
blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH,
|
||||||
minX, minY, minZ, width, yHeight,
|
minX, minY, minZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,16 +171,16 @@ public class ColumnBox
|
|||||||
builder.addQuadAdj(
|
builder.addQuadAdj(
|
||||||
EDhDirection.SOUTH,
|
EDhDirection.SOUTH,
|
||||||
minX, minY, maxZ,
|
minX, minY, maxZ,
|
||||||
width, yHeight,
|
blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH,
|
||||||
minX, minY, maxZ, width, yHeight,
|
minX, minY, maxZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,16 +196,16 @@ public class ColumnBox
|
|||||||
builder.addQuadAdj(
|
builder.addQuadAdj(
|
||||||
EDhDirection.WEST,
|
EDhDirection.WEST,
|
||||||
minX, minY, minZ,
|
minX, minY, minZ,
|
||||||
width, yHeight,
|
blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST,
|
||||||
minX, minY, minZ, width, yHeight,
|
minX, minY, minZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,25 +221,25 @@ public class ColumnBox
|
|||||||
builder.addQuadAdj(
|
builder.addQuadAdj(
|
||||||
EDhDirection.EAST,
|
EDhDirection.EAST,
|
||||||
maxX, minY, minZ,
|
maxX, minY, minZ,
|
||||||
width, yHeight,
|
blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST,
|
||||||
maxX, minY, minZ, width, yHeight,
|
maxX, minY, minZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void makeAdjVerticalQuad(
|
private static void makeAdjVerticalQuad(
|
||||||
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout,
|
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout, IClientLevelWrapper clientLevelWrapper,
|
||||||
@NotNull ColumnRenderView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
|
@NotNull ColumnRenderView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
|
||||||
short x, short yMin, short z, short horizontalWidth, short ySize,
|
short x, short yMin, short z, short horizontalBlockWidth, short ySize,
|
||||||
int color, byte irisBlockMaterialId, byte blockLight)
|
int color, byte irisBlockMaterialId, byte blockLight)
|
||||||
{
|
{
|
||||||
// pooled arrays
|
// pooled arrays
|
||||||
@@ -263,12 +253,12 @@ public class ColumnBox
|
|||||||
// no adjacent data //
|
// no adjacent data //
|
||||||
//==================//
|
//==================//
|
||||||
|
|
||||||
color = ColorUtil.applyShade(color, MC_RENDER.getShade(direction));
|
color = ColorUtil.applyShade(color, clientLevelWrapper.getShade(direction));
|
||||||
|
|
||||||
if (adjColumnView.size == 0
|
if (adjColumnView.size == 0
|
||||||
|| RenderDataPointUtil.hasZeroHeight(adjColumnView.get(0)))
|
|| RenderDataPointUtil.hasZeroHeight(adjColumnView.get(0)))
|
||||||
{
|
{
|
||||||
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
builder.addQuadAdj(direction, x, yMin, z, horizontalBlockWidth, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +268,7 @@ public class ColumnBox
|
|||||||
// determine face visibility/light //
|
// determine face visibility/light //
|
||||||
//=================================//
|
//=================================//
|
||||||
|
|
||||||
boolean transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
|
boolean transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get() == EDhApiTransparency.COMPLETE;
|
||||||
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && transparencyEnabled;
|
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && transparencyEnabled;
|
||||||
short yMax = (short) (yMin + ySize);
|
short yMax = (short) (yMin + ySize);
|
||||||
|
|
||||||
@@ -320,18 +310,34 @@ public class ColumnBox
|
|||||||
if (!adjTransparent)
|
if (!adjTransparent)
|
||||||
{
|
{
|
||||||
// Adjacent is opaque
|
// Adjacent is opaque
|
||||||
boolean adjacentCoversThis =
|
|
||||||
!adjacentIsSameDetailLevel
|
|
||||||
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY
|
|
||||||
&&
|
|
||||||
(
|
|
||||||
(x == 0 && direction == EDhDirection.WEST)
|
|
||||||
|| (z == 0 && direction == EDhDirection.NORTH)
|
|
||||||
|| (x == 256 && direction == EDhDirection.EAST)
|
|
||||||
|| (z == 256 && direction == EDhDirection.SOUTH)
|
|
||||||
);
|
|
||||||
|
|
||||||
lightToApply = adjacentCoversThis ? adjSkyLight : SKYLIGHT_COVERED;
|
// The following logic is done to provide a little bit of overdraw to
|
||||||
|
// prevent holes when low detail LODs are replaced by higher-detail ones
|
||||||
|
// when moving.
|
||||||
|
// If not done higher quality LODs can cause holes due to not
|
||||||
|
// covering the whole face like the lower detail LODs they replaced,
|
||||||
|
// while still culling most LODs that are covered by other blocks.
|
||||||
|
|
||||||
|
boolean onBorder =
|
||||||
|
(direction == EDhDirection.WEST && x == 0)
|
||||||
|
|| (direction == EDhDirection.NORTH && z == 0)
|
||||||
|
|| (direction == EDhDirection.EAST && x == ((horizontalBlockWidth) * (ColumnRenderSource.WIDTH)))
|
||||||
|
|| (direction == EDhDirection.SOUTH && z == ((horizontalBlockWidth) * (ColumnRenderSource.WIDTH)));
|
||||||
|
|
||||||
|
boolean isLit =
|
||||||
|
RenderDataPointUtil.getLightSky(adjPoint) != LodUtil.MIN_MC_LIGHT
|
||||||
|
|| RenderDataPointUtil.getLightBlock(adjPoint) != LodUtil.MIN_MC_LIGHT;
|
||||||
|
|
||||||
|
// render the face if...
|
||||||
|
boolean useAdjLighting =
|
||||||
|
// we're on the border... (holes can only happen on LOD borders since faces inside an LOD will always be the same detail level)
|
||||||
|
onBorder
|
||||||
|
// ...this face has some sort of lighting... (0 light generally means the face is covered by other blocks)
|
||||||
|
&& isLit
|
||||||
|
// ...and is above the culling height
|
||||||
|
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY;
|
||||||
|
|
||||||
|
lightToApply = useAdjLighting ? adjSkyLight : SKYLIGHT_COVERED;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -341,13 +347,24 @@ public class ColumnBox
|
|||||||
|
|
||||||
|
|
||||||
// Apply light to the range [adjMinY, adjMaxY)
|
// Apply light to the range [adjMinY, adjMaxY)
|
||||||
applyLightToRange(segments, newSegments, adjMinY, adjMaxY, lightToApply);
|
applyLightToRangeAndPopulateNewSgements(segments, newSegments, adjMinY, adjMaxY, lightToApply);
|
||||||
|
{
|
||||||
|
// swap references so we can use the newly populated segments
|
||||||
|
LongArrayList temp = segments;
|
||||||
|
segments = newSegments;
|
||||||
|
newSegments = temp;
|
||||||
|
}
|
||||||
|
|
||||||
// Fill overhang area [adjMaxY, adjAboveMinY) with adjSkyLight
|
// Fill overhang area [adjMaxY, adjAboveMinY) with adjSkyLight
|
||||||
short adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint);
|
short adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint);
|
||||||
if (adjMaxY < adjAboveMinY)
|
if (adjMaxY < adjAboveMinY)
|
||||||
{
|
{
|
||||||
applyLightToRange(segments, newSegments, adjMaxY, adjAboveMinY, adjSkyLight);
|
applyLightToRangeAndPopulateNewSgements(segments, newSegments, adjMaxY, adjAboveMinY, adjSkyLight);
|
||||||
|
{
|
||||||
|
LongArrayList temp = segments;
|
||||||
|
segments = newSegments;
|
||||||
|
newSegments = temp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +380,7 @@ public class ColumnBox
|
|||||||
long segment = segments.getLong(i);
|
long segment = segments.getLong(i);
|
||||||
tryAddVerticalFaceWithSkyLightToBuilder(
|
tryAddVerticalFaceWithSkyLightToBuilder(
|
||||||
builder, direction,
|
builder, direction,
|
||||||
x, z, horizontalWidth,
|
x, z, horizontalBlockWidth,
|
||||||
color, irisBlockMaterialId, blockLight,
|
color, irisBlockMaterialId, blockLight,
|
||||||
YSegmentUtil.getSkyLight(segment), inputTransparent, YSegmentUtil.getEndY(segment), YSegmentUtil.getStartY(segment)
|
YSegmentUtil.getSkyLight(segment), inputTransparent, YSegmentUtil.getEndY(segment), YSegmentUtil.getStartY(segment)
|
||||||
);
|
);
|
||||||
@@ -373,10 +390,11 @@ public class ColumnBox
|
|||||||
/**
|
/**
|
||||||
* Apply the new light value over the given y range,
|
* Apply the new light value over the given y range,
|
||||||
* splitting segments as needed
|
* splitting segments as needed
|
||||||
|
* and putting the new segments into "newSegments"
|
||||||
* <p>
|
* <p>
|
||||||
* source: claude.ai
|
* source: claude.ai
|
||||||
*/
|
*/
|
||||||
private static void applyLightToRange(
|
private static void applyLightToRangeAndPopulateNewSgements(
|
||||||
LongArrayList segments, LongArrayList newSegments,
|
LongArrayList segments, LongArrayList newSegments,
|
||||||
short rangeStart, short rangeEnd,
|
short rangeStart, short rangeEnd,
|
||||||
byte newLight)
|
byte newLight)
|
||||||
@@ -419,9 +437,6 @@ public class ColumnBox
|
|||||||
newSegments.add(YSegmentUtil.encode(rangeEnd, endY, skyLight));
|
newSegments.add(YSegmentUtil.encode(rangeEnd, endY, skyLight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
segments.clear();
|
|
||||||
segments.addAll(newSegments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void tryAddVerticalFaceWithSkyLightToBuilder(
|
private static void tryAddVerticalFaceWithSkyLightToBuilder(
|
||||||
|
|||||||
+2
-22
@@ -31,7 +31,7 @@ import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListChec
|
|||||||
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListPool;
|
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListPool;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnRenderView;
|
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnRenderView;
|
||||||
@@ -55,26 +55,6 @@ public class ColumnRenderBufferBuilder
|
|||||||
// vbo building //
|
// vbo building //
|
||||||
//==============//
|
//==============//
|
||||||
|
|
||||||
/** @link adjData should be null for adjacent sections that cross detail level boundaries */
|
|
||||||
public static CompletableFuture<LodBufferContainer> uploadBuffersAsync(
|
|
||||||
IDhClientLevel clientLevel,
|
|
||||||
long pos,
|
|
||||||
LodQuadBuilder quadBuilder
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DhBlockPos minBlockPos = new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos));
|
|
||||||
LodBufferContainer bufferContainer = new LodBufferContainer(pos, minBlockPos);
|
|
||||||
CompletableFuture<LodBufferContainer> uploadFuture = bufferContainer.makeAndUploadBuffersAsync(quadBuilder);
|
|
||||||
uploadFuture.whenComplete((uploadedBuffer, exception) ->
|
|
||||||
{
|
|
||||||
// clean up if not uploaded
|
|
||||||
if (uploadedBuffer != null && !uploadedBuffer.buffersUploaded)
|
|
||||||
{
|
|
||||||
uploadedBuffer.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return uploadFuture;
|
|
||||||
}
|
|
||||||
public static void makeLodRenderData(
|
public static void makeLodRenderData(
|
||||||
LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel,
|
LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel,
|
||||||
ColumnRenderSource[] adjRegions, boolean[] isSameDetailLevel)
|
ColumnRenderSource[] adjRegions, boolean[] isSameDetailLevel)
|
||||||
@@ -328,7 +308,7 @@ public class ColumnRenderBufferBuilder
|
|||||||
|
|
||||||
int color;
|
int color;
|
||||||
boolean fullBright = false;
|
boolean fullBright = false;
|
||||||
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRendering.get();
|
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRenderingColors.get();
|
||||||
switch (debugging)
|
switch (debugging)
|
||||||
{
|
{
|
||||||
case OFF:
|
case OFF:
|
||||||
|
|||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
public class IndexBufferBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//==========//
|
||||||
|
// building //
|
||||||
|
//==========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public static ByteBuffer createBuffer(int quadCount)
|
||||||
|
{
|
||||||
|
int indexCount = quadCount * 6; // 2 triangles per quad
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocateDirect(indexCount * Integer.BYTES);
|
||||||
|
buffer.order(ByteOrder.nativeOrder());
|
||||||
|
buildBufferInt(quadCount, buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
private static void buildBufferByte(int quadCount, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < quadCount; i++)
|
||||||
|
{
|
||||||
|
int vIndex = i * 4;
|
||||||
|
// First triangle
|
||||||
|
buffer.put((byte) (vIndex));
|
||||||
|
buffer.put((byte) (vIndex + 1));
|
||||||
|
buffer.put((byte) (vIndex + 2));
|
||||||
|
// Second triangle
|
||||||
|
buffer.put((byte) (vIndex + 2));
|
||||||
|
buffer.put((byte) (vIndex + 3));
|
||||||
|
buffer.put((byte) (vIndex));
|
||||||
|
}
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
|
||||||
|
}
|
||||||
|
buffer.rewind();
|
||||||
|
}
|
||||||
|
private static void buildBufferShort(int quadCount, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < quadCount; i++)
|
||||||
|
{
|
||||||
|
int vIndex = i * 4;
|
||||||
|
// First triangle
|
||||||
|
buffer.putShort((short) (vIndex));
|
||||||
|
buffer.putShort((short) (vIndex + 1));
|
||||||
|
buffer.putShort((short) (vIndex + 2));
|
||||||
|
// Second triangle
|
||||||
|
buffer.putShort((short) (vIndex + 2));
|
||||||
|
buffer.putShort((short) (vIndex + 3));
|
||||||
|
buffer.putShort((short) (vIndex));
|
||||||
|
}
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
|
||||||
|
}
|
||||||
|
buffer.rewind();
|
||||||
|
}
|
||||||
|
private static void buildBufferInt(int quadCount, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < quadCount; i++)
|
||||||
|
{
|
||||||
|
int vIndex = i * 4;
|
||||||
|
// First triangle
|
||||||
|
buffer.putInt(vIndex);
|
||||||
|
buffer.putInt(vIndex + 1);
|
||||||
|
buffer.putInt(vIndex + 2);
|
||||||
|
// Second triangle
|
||||||
|
buffer.putInt(vIndex + 2);
|
||||||
|
buffer.putInt(vIndex + 3);
|
||||||
|
buffer.putInt(vIndex);
|
||||||
|
}
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
|
||||||
|
}
|
||||||
|
buffer.rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+269
-114
@@ -20,22 +20,22 @@
|
|||||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||||
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java representation of one or more OpenGL buffers for rendering.
|
* Java representation of one or more OpenGL buffers for rendering.
|
||||||
@@ -47,6 +47,7 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||||
|
private static final AbstractDhRenderApiDefinition RENDER_DEF = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
|
||||||
|
|
||||||
|
|
||||||
/** the position closest to minimum X/Z infinity and the level's lowest Y */
|
/** the position closest to minimum X/Z infinity and the level's lowest Y */
|
||||||
@@ -55,13 +56,11 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
|
|
||||||
public boolean buffersUploaded = false;
|
public boolean buffersUploaded = false;
|
||||||
|
|
||||||
public IVertexBufferWrapper[] vbos;
|
public IVertexBufferWrapper[] vboOpaqueWrappers;
|
||||||
public IVertexBufferWrapper[] vbosTransparent;
|
public IVertexBufferWrapper[] vboTransparentWrappers;
|
||||||
|
|
||||||
public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper();
|
public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper();
|
||||||
|
|
||||||
private final AtomicReference<CompletableFuture<LodBufferContainer>> uploadFutureRef = new AtomicReference<>(null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
@@ -69,14 +68,12 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
//==============//
|
//==============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public LodBufferContainer(long pos, DhBlockPos minCornerBlockPos)
|
private LodBufferContainer(long pos, DhBlockPos minCornerBlockPos)
|
||||||
{
|
{
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
this.minCornerBlockPos = minCornerBlockPos;
|
this.minCornerBlockPos = minCornerBlockPos;
|
||||||
this.vbos = new IVertexBufferWrapper[0];
|
this.vboOpaqueWrappers = new IVertexBufferWrapper[0];
|
||||||
this.vbosTransparent = new IVertexBufferWrapper[0];
|
this.vboTransparentWrappers = new IVertexBufferWrapper[0];
|
||||||
|
|
||||||
this.uniformContainer.createUniformData(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -89,47 +86,50 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
//region
|
//region
|
||||||
|
|
||||||
/** Should be run on a DH thread. */
|
/** Should be run on a DH thread. */
|
||||||
public synchronized CompletableFuture<LodBufferContainer> makeAndUploadBuffersAsync(LodQuadBuilder builder)
|
public static CompletableFuture<LodBufferContainer> tryMakeAndUploadBuffersAsync(
|
||||||
|
long pos, IDhClientLevel clientLevel,
|
||||||
|
ArrayList<ByteBuffer> opaqueBuffers,
|
||||||
|
ArrayList<ByteBuffer> transparentBuffers
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// separate variable to prevent race condition when checking null
|
|
||||||
CompletableFuture<LodBufferContainer> oldFuture = this.uploadFutureRef.get();
|
|
||||||
if (oldFuture != null)
|
|
||||||
{
|
|
||||||
// upload already in process
|
|
||||||
return oldFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
// new upload needed
|
// new upload needed
|
||||||
CompletableFuture<LodBufferContainer> future = new CompletableFuture<>();
|
CompletableFuture<LodBufferContainer> future = new CompletableFuture<>();
|
||||||
future.handle((lodBufferContainer, throwable) ->
|
|
||||||
{
|
|
||||||
if (!this.uploadFutureRef.compareAndSet(future, null))
|
|
||||||
{
|
|
||||||
LOGGER.warn("upload future ref changed for pos ["+DhSectionPos.toString(this.pos)+"].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.uploadFutureRef.compareAndSet(null, future))
|
|
||||||
{
|
|
||||||
oldFuture = this.uploadFutureRef.get();
|
|
||||||
LodUtil.assertTrue(oldFuture != null, "Concurrency error");
|
|
||||||
return oldFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// make the buffers
|
//================//
|
||||||
ArrayList<ByteBuffer> opaqueBuffers = builder.makeOpaqueVertexBuffers();
|
// create buffers //
|
||||||
ArrayList<ByteBuffer> transparentBuffers = builder.makeTransparentVertexBuffers();
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
this.vbos = resizeBufferArray(this.vbos, opaqueBuffers.size());
|
DhBlockPos minCornerBlockPos = new DhBlockPos(
|
||||||
this.vbosTransparent = resizeBufferArray(this.vbosTransparent, transparentBuffers.size());
|
DhSectionPos.getMinCornerBlockX(pos),
|
||||||
|
clientLevel.getLevelWrapper().getMinHeight(),
|
||||||
|
DhSectionPos.getMinCornerBlockZ(pos));
|
||||||
|
LodBufferContainer bufferContainer = new LodBufferContainer(pos, minCornerBlockPos);
|
||||||
|
|
||||||
|
// update arrays to contain buffers
|
||||||
|
bufferContainer.vboOpaqueWrappers = resizeWrapperArray(bufferContainer.vboOpaqueWrappers, opaqueBuffers.size());
|
||||||
|
bufferContainer.vboTransparentWrappers = resizeWrapperArray(bufferContainer.vboTransparentWrappers, transparentBuffers.size());
|
||||||
|
|
||||||
|
// create CPU index buffers if needed.
|
||||||
|
// Mac requires separate IBO objects for each VBO when using OpenGL,
|
||||||
|
// all other OS's can share a single IBO for quicker loading times
|
||||||
|
boolean useSingleIbo = RENDER_DEF.useSingleIbo();
|
||||||
|
@Nullable ArrayList<ByteBuffer> opaqueIndexBuffers = useSingleIbo ? null : bufferContainer.createIndexBuffers(opaqueBuffers);
|
||||||
|
@Nullable ArrayList<ByteBuffer> transparentIndexBuffers = useSingleIbo ? null : bufferContainer.createIndexBuffers(transparentBuffers);
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
// upload on MC's render thread
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
//=============//
|
||||||
|
// create VBOs //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
CompletableFuture<Void> createFuture = new CompletableFuture<Void>();
|
||||||
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Setup", () ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -140,43 +140,95 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload on the render thread
|
createBufferWrappers(bufferContainer.vboOpaqueWrappers, opaqueBuffers);
|
||||||
uploadBuffers(this.vbos, opaqueBuffers);
|
createBufferWrappers(bufferContainer.vboTransparentWrappers, transparentBuffers);
|
||||||
uploadBuffers(this.vbosTransparent, transparentBuffers);
|
|
||||||
this.buffersUploaded = true;
|
|
||||||
|
|
||||||
// success
|
createFuture.complete(null);
|
||||||
future.complete(this);
|
|
||||||
}
|
|
||||||
catch (InterruptedException ignore)
|
|
||||||
{
|
|
||||||
future.complete(this);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
LOGGER.error("Unexpected issue uploading buffer ["+this.minCornerBlockPos +"], error: ["+e.getMessage()+"].", e);
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
|
||||||
future.completeExceptionally(e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// all the buffers must be manually freed to prevent memory leaks
|
|
||||||
|
|
||||||
for (ByteBuffer buffer : opaqueBuffers)
|
|
||||||
{
|
{
|
||||||
MemoryUtil.memFree(buffer);
|
LOGGER.error("Unexpected issue creating buffers for pos: ["+DhSectionPos.toString(bufferContainer.pos)+"], error: ["+e.getMessage()+"].", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ByteBuffer buffer : transparentBuffers)
|
bufferContainer.close();
|
||||||
{
|
createFuture.completeExceptionally(e);
|
||||||
MemoryUtil.memFree(buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//====================//
|
||||||
|
// upload VBOs to GPU //
|
||||||
|
//====================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
createFuture.exceptionally((Throwable e) ->
|
||||||
|
{
|
||||||
|
// create VBOs failed //
|
||||||
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue creating buffer [" + bufferContainer.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferContainer.close();
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
createFuture.thenRun(() ->
|
||||||
|
{
|
||||||
|
CompletableFuture<Void> opaqueFuture = uploadBuffersAsync(future, bufferContainer.vboOpaqueWrappers, opaqueBuffers, opaqueIndexBuffers);
|
||||||
|
CompletableFuture<Void> transparentFuture = uploadBuffersAsync(future, bufferContainer.vboTransparentWrappers, transparentBuffers, transparentIndexBuffers);
|
||||||
|
CompletableFuture<Void> uploadFuture = CompletableFuture.allOf(opaqueFuture, transparentFuture);
|
||||||
|
uploadFuture.exceptionally((Throwable e) ->
|
||||||
|
{
|
||||||
|
// upload failed //
|
||||||
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue uploading buffer [" + bufferContainer.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferContainer.close();
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
uploadFuture.thenRun(() ->
|
||||||
|
{
|
||||||
|
// upload success //
|
||||||
|
bufferContainer.buffersUploaded = true;
|
||||||
|
future.complete(bufferContainer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
private static IVertexBufferWrapper[] resizeBufferArray(IVertexBufferWrapper[] vbos, int newSize)
|
|
||||||
|
|
||||||
|
private ArrayList<ByteBuffer> createIndexBuffers(ArrayList<ByteBuffer> vertexBuffers)
|
||||||
|
{
|
||||||
|
ArrayList<ByteBuffer> indexBuffers = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexBuffers.size(); i++)
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = vertexBuffers.get(i);
|
||||||
|
int size = buffer.limit() - buffer.position();
|
||||||
|
int maxVertexCount = size / LodQuadBuilder.BYTES_PER_VERTEX;
|
||||||
|
int quadCount = (maxVertexCount / 4);
|
||||||
|
ByteBuffer indexBuffer = IndexBufferBuilder.createBuffer(quadCount);
|
||||||
|
indexBuffers.add(indexBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return indexBuffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IVertexBufferWrapper[] resizeWrapperArray(IVertexBufferWrapper[] vbos, int newSize)
|
||||||
{
|
{
|
||||||
if (vbos.length == newSize)
|
if (vbos.length == newSize)
|
||||||
{
|
{
|
||||||
@@ -197,46 +249,141 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
}
|
}
|
||||||
return newVbos;
|
return newVbos;
|
||||||
}
|
}
|
||||||
private static void uploadBuffers(IVertexBufferWrapper[] vbos, ArrayList<ByteBuffer> byteBuffers) throws InterruptedException
|
|
||||||
|
private static void createBufferWrappers(IVertexBufferWrapper[] vboWrappers, ArrayList<ByteBuffer> vertexBuffers)
|
||||||
{
|
{
|
||||||
int vboIndex = 0;
|
for (int i = 0; i < vertexBuffers.size(); i++)
|
||||||
for (int i = 0; i < byteBuffers.size(); i++)
|
|
||||||
{
|
{
|
||||||
if (vboIndex >= vbos.length)
|
if (i >= vboWrappers.length)
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Too many vertex buffers!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vboWrappers[i] == null)
|
||||||
|
{
|
||||||
|
vboWrappers[i] = WRAPPER_FACTORY.createVboWrapper("distantHorizons:TerrainRenderer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Index buffers should be null if {@link AbstractDhRenderApiDefinition#useSingleIbo()} returns true. */
|
||||||
|
private static CompletableFuture<Void> uploadBuffersAsync(
|
||||||
|
CompletableFuture<LodBufferContainer> parentFuture,
|
||||||
|
IVertexBufferWrapper[] vboWrappers,
|
||||||
|
ArrayList<ByteBuffer> vertexBuffers, @Nullable ArrayList<ByteBuffer> indexBuffers
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ArrayList<CompletableFuture<Void>> uploadFutureList = new ArrayList<>();
|
||||||
|
int vboIndex = 0;
|
||||||
|
for (int i = 0; i < vertexBuffers.size(); i++)
|
||||||
|
{
|
||||||
|
if (vboIndex >= vboWrappers.length)
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Too many vertex buffers!!");
|
throw new RuntimeException("Too many vertex buffers!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get or create the VBO
|
|
||||||
if (vbos[vboIndex] == null)
|
|
||||||
{
|
|
||||||
vbos[vboIndex] = SingletonInjector.INSTANCE.get(IWrapperFactory.class).createVboWrapper("distantHorizons:McLodRenderer");
|
|
||||||
}
|
|
||||||
IVertexBufferWrapper vbo = vbos[vboIndex];
|
|
||||||
|
|
||||||
ByteBuffer buffer = byteBuffers.get(i);
|
// final variables for use in lambdas //
|
||||||
int size = buffer.limit() - buffer.position();
|
|
||||||
int vertexCount = size / LodQuadBuilder.BYTES_PER_VERTEX;
|
|
||||||
|
|
||||||
try
|
final IVertexBufferWrapper finalVboWrapper = vboWrappers[vboIndex];
|
||||||
|
|
||||||
|
final ByteBuffer finalVertexBuffer = vertexBuffers.get(vboIndex);
|
||||||
|
// index buffers are optional
|
||||||
|
@Nullable final ByteBuffer finalIndexBuffer = (indexBuffers != null) ? indexBuffers.get(vboIndex) : null;
|
||||||
|
|
||||||
|
final int finalVertexCount = vertexByteBufferToVertexCount(finalVertexBuffer);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===============//
|
||||||
|
// vertex upload //
|
||||||
|
//===============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
CompletableFuture<Void> vertexUploadFuture = new CompletableFuture<>();
|
||||||
|
uploadFutureList.add(vertexUploadFuture);
|
||||||
|
|
||||||
|
|
||||||
|
final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
|
||||||
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer VBO Upload", () ->
|
||||||
{
|
{
|
||||||
vbo.upload(buffer, vertexCount);
|
try
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
// skip this event if requested
|
||||||
|
if (Thread.interrupted()
|
||||||
|
|| parentFuture.isCancelled())
|
||||||
|
{
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
finalVboWrapper.uploadVertexBuffer(finalVertexBuffer, finalVertexCount);
|
||||||
|
vertexUploadFuture.complete(null);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Failed to upload buffer. Error: [" + e.getMessage() + "].", e);
|
||||||
|
vertexUploadFuture.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// index upload //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
if (finalIndexBuffer != null)
|
||||||
{
|
{
|
||||||
vbos[vboIndex] = null;
|
CompletableFuture<Void> indexUploadFuture = new CompletableFuture<>();
|
||||||
vbo.close();
|
uploadFutureList.add(indexUploadFuture);
|
||||||
LOGGER.error("Failed to upload buffer. Error: ["+e.getMessage()+"].", e);
|
|
||||||
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer IBO Upload", () ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// skip this event if requested
|
||||||
|
if (Thread.interrupted()
|
||||||
|
|| parentFuture.isCancelled())
|
||||||
|
{
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
finalVboWrapper.uploadIndexBuffer(finalIndexBuffer, finalVertexCount);
|
||||||
|
indexUploadFuture.complete(null);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
finalVboWrapper.close();
|
||||||
|
indexUploadFuture.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vboIndex++;
|
vboIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vboIndex < vbos.length)
|
if (vboIndex < vboWrappers.length)
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Too few vertex buffers!!");
|
throw new RuntimeException("Too few vertex buffers!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// merge futures //
|
||||||
|
|
||||||
|
CompletableFuture<?>[] futureArray = new CompletableFuture[uploadFutureList.size()];
|
||||||
|
for (int i = 0; i < uploadFutureList.size(); i++)
|
||||||
|
{
|
||||||
|
futureArray[i] = uploadFutureList.get(i);
|
||||||
|
}
|
||||||
|
return CompletableFuture.allOf(futureArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -248,29 +395,34 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
//================//
|
//================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
|
private static int vertexByteBufferToVertexCount(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
int size = buffer.limit() - buffer.position();
|
||||||
|
int vertexCount = size / LodQuadBuilder.BYTES_PER_VERTEX;
|
||||||
|
return vertexCount;
|
||||||
|
}
|
||||||
|
|
||||||
/** can be used when debugging */
|
/** can be used when debugging */
|
||||||
public boolean hasNonNullVbos() { return this.vbos != null || this.vbosTransparent != null; }
|
public boolean hasNonNullVbos() { return this.vboOpaqueWrappers != null || this.vboTransparentWrappers != null; }
|
||||||
|
|
||||||
/** can be used when debugging */
|
/** can be used when debugging */
|
||||||
public int vboBufferCount()
|
public int vboBufferCount()
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
if (this.vbos != null)
|
if (this.vboOpaqueWrappers != null)
|
||||||
{
|
{
|
||||||
count += this.vbos.length;
|
count += this.vboOpaqueWrappers.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.vbosTransparent != null)
|
if (this.vboTransparentWrappers != null)
|
||||||
{
|
{
|
||||||
count += this.vbosTransparent.length;
|
count += this.vboTransparentWrappers.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean uploadInProgress() { return this.uploadFutureRef.get() != null; }
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
@@ -291,28 +443,31 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
{
|
{
|
||||||
this.buffersUploaded = false;
|
this.buffersUploaded = false;
|
||||||
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Close", () ->
|
||||||
{
|
{
|
||||||
for (IVertexBufferWrapper buffer : this.vbos)
|
tryCloseBufferWrapperArray(this.vboOpaqueWrappers);
|
||||||
{
|
tryCloseBufferWrapperArray(this.vboTransparentWrappers);
|
||||||
if (buffer != null)
|
|
||||||
{
|
|
||||||
buffer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IVertexBufferWrapper buffer : this.vbosTransparent)
|
|
||||||
{
|
|
||||||
if (buffer != null)
|
|
||||||
{
|
|
||||||
buffer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uniformContainer.close();
|
this.uniformContainer.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void tryCloseBufferWrapperArray(@Nullable IVertexBufferWrapper[] bufferWrappers)
|
||||||
|
{
|
||||||
|
if (bufferWrappers != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bufferWrappers.length; i++)
|
||||||
|
{
|
||||||
|
IVertexBufferWrapper buffer = bufferWrappers[i];
|
||||||
|
bufferWrappers[i] = null;
|
||||||
|
if (buffer != null)
|
||||||
|
{
|
||||||
|
buffer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+136
-45
@@ -20,6 +20,7 @@
|
|||||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering;
|
import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering;
|
||||||
@@ -30,38 +31,29 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|||||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to create the quads before they are converted to render-able buffers. <br><br>
|
* Used to create the quads before they are converted to render-able buffers. <br><br>
|
||||||
*
|
*
|
||||||
* Note: the magic number 6 you see throughout this method represents the number of sides on a cube.
|
* Note: the magic number 6 you see throughout this method represents the number of sides on a cube.
|
||||||
*/
|
*/
|
||||||
public class LodQuadBuilder
|
public class LodQuadBuilder implements AutoCloseable
|
||||||
{
|
{
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
/** ThreadLocal is the simplest way to allow each LOD loading thread to have their own builder */
|
||||||
private final ArrayList<BufferQuad>[] opaqueQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
private static final ThreadLocal<LodQuadBuilder> THREAD_LOCAL = ThreadLocal.withInitial(LodQuadBuilder::new);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private final ArrayList<BufferQuad>[] transparentQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
|
||||||
|
|
||||||
private final boolean doTransparency;
|
/** the number of bytes for a single vertex */
|
||||||
private final IClientLevelWrapper clientLevelWrapper;
|
public static final int BYTES_PER_VERTEX = 16;
|
||||||
|
public static final int BYTES_PER_QUAD = BYTES_PER_VERTEX * 4;
|
||||||
private final EDhApiDebugRendering debugRenderingMode;
|
|
||||||
private final EDhApiGrassSideRendering grassSideRenderingMode;
|
|
||||||
|
|
||||||
/** the number of bytes for */
|
|
||||||
public static final int BYTES_PER_VERTEX = 14;
|
|
||||||
|
|
||||||
public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][]
|
public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][]
|
||||||
///region
|
//region
|
||||||
{
|
{
|
||||||
// X,Z //
|
// X,Z //
|
||||||
{ // UP
|
{ // UP
|
||||||
@@ -109,7 +101,27 @@ public class LodQuadBuilder
|
|||||||
{0, 0}, // 3
|
{0, 0}, // 3
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private final ArrayList<BufferQuad>[] opaqueQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private final ArrayList<BufferQuad>[] transparentQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caching the BufferQuad objects reduces overhead slightly. <br>
|
||||||
|
* Caching is handled per builder (vs globally in {@link BufferQuad} itself)
|
||||||
|
* to prevent concurrency overhead.
|
||||||
|
*/
|
||||||
|
private final ArrayList<BufferQuad> bufferQuadCacheList = new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean doTransparency;
|
||||||
|
private IClientLevelWrapper clientLevelWrapper;
|
||||||
|
|
||||||
|
private EDhApiDebugRendering debugRenderingMode;
|
||||||
|
private EDhApiGrassSideRendering grassSideRenderingMode;
|
||||||
|
|
||||||
private int premergeCount = 0;
|
private int premergeCount = 0;
|
||||||
|
|
||||||
@@ -120,20 +132,31 @@ public class LodQuadBuilder
|
|||||||
//=============//
|
//=============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public LodQuadBuilder(boolean doTransparency, IClientLevelWrapper clientLevelWrapper)
|
private LodQuadBuilder()
|
||||||
{
|
{
|
||||||
this.doTransparency = doTransparency;
|
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
this.opaqueQuads[i] = new ArrayList<>();
|
this.opaqueQuads[i] = new ArrayList<>();
|
||||||
this.transparentQuads[i] = new ArrayList<>();
|
this.transparentQuads[i] = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LodQuadBuilder getBuilder(boolean doTransparency, IClientLevelWrapper clientLevelWrapper)
|
||||||
|
{
|
||||||
|
LodQuadBuilder builder = THREAD_LOCAL.get();
|
||||||
|
builder.set(doTransparency, clientLevelWrapper);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
private void set(boolean doTransparency, IClientLevelWrapper clientLevelWrapper)
|
||||||
|
{
|
||||||
|
this.doTransparency = doTransparency;
|
||||||
|
|
||||||
this.clientLevelWrapper = clientLevelWrapper;
|
this.clientLevelWrapper = clientLevelWrapper;
|
||||||
|
|
||||||
this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get();
|
this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRenderingColors.get();
|
||||||
this.grassSideRenderingMode = Config.Client.Advanced.Graphics.Quality.grassSideRendering.get();
|
this.grassSideRenderingMode = Config.Client.Advanced.Graphics.Quality.grassSideRendering.get();
|
||||||
|
|
||||||
|
this.premergeCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -143,7 +166,7 @@ public class LodQuadBuilder
|
|||||||
//===========//
|
//===========//
|
||||||
// add quads //
|
// add quads //
|
||||||
//===========//
|
//===========//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
public void addQuadAdj(
|
public void addQuadAdj(
|
||||||
EDhDirection dir,
|
EDhDirection dir,
|
||||||
@@ -167,7 +190,8 @@ public class LodQuadBuilder
|
|||||||
quadList = this.opaqueQuads[dir.ordinal()];
|
quadList = this.opaqueQuads[dir.ordinal()];
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferQuad quad = new BufferQuad(x, y, z, width, height, color, irisBlockMaterialId, skyLight, blockLight, dir);
|
BufferQuad quad = this.getOrCreateBufferQuad();
|
||||||
|
quad.set(x, y, z, width, height, color, irisBlockMaterialId, skyLight, blockLight, dir);
|
||||||
if (!quadList.isEmpty()
|
if (!quadList.isEmpty()
|
||||||
&& (
|
&& (
|
||||||
quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|
quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|
||||||
@@ -182,35 +206,37 @@ public class LodQuadBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XZ
|
// XZ
|
||||||
public void addQuadUp(short minX, short maxY, short minZ, short width, int color, byte irisBlockMaterialId, byte skylight, byte blocklight)
|
public void addQuadUp(short minX, short maxY, short minZ, short blockWidth, int color, byte irisBlockMaterialId, byte skylight, byte blocklight)
|
||||||
{
|
{
|
||||||
boolean isTransparent = (this.doTransparency && ColorUtil.getAlpha(color) < 255);
|
boolean isTransparent = (this.doTransparency && ColorUtil.getAlpha(color) < 255);
|
||||||
ArrayList<BufferQuad> quadList = isTransparent
|
ArrayList<BufferQuad> quadList = isTransparent
|
||||||
? this.transparentQuads[EDhDirection.UP.ordinal()]
|
? this.transparentQuads[EDhDirection.UP.ordinal()]
|
||||||
: this.opaqueQuads[EDhDirection.UP.ordinal()];
|
: this.opaqueQuads[EDhDirection.UP.ordinal()];
|
||||||
|
|
||||||
BufferQuad quad = new BufferQuad(minX, maxY, minZ, width, width, color, irisBlockMaterialId, skylight, blocklight, EDhDirection.UP);
|
BufferQuad quad = this.getOrCreateBufferQuad();
|
||||||
|
quad.set(minX, maxY, minZ, blockWidth, blockWidth, color, irisBlockMaterialId, skylight, blocklight, EDhDirection.UP);
|
||||||
quadList.add(quad);
|
quadList.add(quad);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addQuadDown(short x, short y, short z, short width, int color, byte irisBlockMaterialId, byte skylight, byte blocklight)
|
public void addQuadDown(short x, short y, short z, short blockWidth, int color, byte irisBlockMaterialId, byte skylight, byte blocklight)
|
||||||
{
|
{
|
||||||
ArrayList<BufferQuad> quadArray = (this.doTransparency && ColorUtil.getAlpha(color) < 255)
|
ArrayList<BufferQuad> quadArray = (this.doTransparency && ColorUtil.getAlpha(color) < 255)
|
||||||
? this.transparentQuads[EDhDirection.DOWN.ordinal()]
|
? this.transparentQuads[EDhDirection.DOWN.ordinal()]
|
||||||
: this.opaqueQuads[EDhDirection.DOWN.ordinal()];
|
: this.opaqueQuads[EDhDirection.DOWN.ordinal()];
|
||||||
|
|
||||||
BufferQuad quad = new BufferQuad(x, y, z, width, width, color, irisBlockMaterialId, skylight, blocklight, EDhDirection.DOWN);
|
BufferQuad quad = this.getOrCreateBufferQuad();
|
||||||
|
quad.set(x, y, z, blockWidth, blockWidth, color, irisBlockMaterialId, skylight, blocklight, EDhDirection.DOWN);
|
||||||
quadArray.add(quad);
|
quadArray.add(quad);
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//=================//
|
||||||
// data finalizing //
|
// data finalizing //
|
||||||
//=================//
|
//=================//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
/** Uses Greedy meshing to merge this builder's Quads. */
|
/** Uses Greedy meshing to merge this builder's Quads. */
|
||||||
public void mergeQuads()
|
public void mergeQuads()
|
||||||
@@ -279,14 +305,14 @@ public class LodQuadBuilder
|
|||||||
return mergeCount;
|
return mergeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// buffer setup //
|
// buffer setup //
|
||||||
//==============//
|
//==============//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
public ArrayList<ByteBuffer> makeOpaqueVertexBuffers() { return this.makeVertexBuffers(this.opaqueQuads); }
|
public ArrayList<ByteBuffer> makeOpaqueVertexBuffers() { return this.makeVertexBuffers(this.opaqueQuads); }
|
||||||
public ArrayList<ByteBuffer> makeTransparentVertexBuffers() { return this.makeVertexBuffers(this.transparentQuads); }
|
public ArrayList<ByteBuffer> makeTransparentVertexBuffers() { return this.makeVertexBuffers(this.transparentQuads); }
|
||||||
@@ -308,9 +334,11 @@ public class LodQuadBuilder
|
|||||||
{
|
{
|
||||||
// if this is the first iteration or the buffer is full,
|
// if this is the first iteration or the buffer is full,
|
||||||
// create a new buffer
|
// create a new buffer
|
||||||
if (buffer == null || !buffer.hasRemaining())
|
if (buffer == null
|
||||||
|
|| buffer.remaining() < BYTES_PER_QUAD)
|
||||||
{
|
{
|
||||||
buffer = MemoryUtil.memAlloc(getMaxBufferByteSize());
|
buffer = ByteBuffer.allocateDirect(getMaxBufferByteSize());
|
||||||
|
buffer.order(ByteOrder.nativeOrder());
|
||||||
byteBufferList.add(buffer);
|
byteBufferList.add(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +421,7 @@ public class LodQuadBuilder
|
|||||||
// for horizontal and bottom faces of grass blocks, use the dirt color to
|
// for horizontal and bottom faces of grass blocks, use the dirt color to
|
||||||
// prevent green cliff walls
|
// prevent green cliff walls
|
||||||
color = this.clientLevelWrapper.getDirtBlockColor();
|
color = this.clientLevelWrapper.getDirtBlockColor();
|
||||||
color = ColorUtil.applyShade(color, MC_RENDER.getShade(quad.direction));
|
color = ColorUtil.applyShade(color, this.clientLevelWrapper.getShade(quad.direction));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,14 +478,14 @@ public class LodQuadBuilder
|
|||||||
bb.putShort((short) 0); // padding to make sure the vertex format as a whole is a multiple of 4
|
bb.putShort((short) 0); // padding to make sure the vertex format as a whole is a multiple of 4
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
//=========//
|
||||||
// getters //
|
// getters //
|
||||||
//=========//
|
//=========//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
public int getCurrentOpaqueQuadsCount()
|
public int getCurrentOpaqueQuadsCount()
|
||||||
{
|
{
|
||||||
@@ -498,19 +526,82 @@ public class LodQuadBuilder
|
|||||||
return maxBufferByteSize;
|
return maxBufferByteSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of bytes a single quad takes
|
// 2 MB
|
||||||
int QUADS_BYTE_SIZE = BYTES_PER_VERTEX * 4;
|
// note: this is relatively small (10 MB was the previous max) to reduce stuttering
|
||||||
// how big a single VBO can be in bytes
|
// during the upload process by having smaller upload steps
|
||||||
int MAX_VBO_BYTE_SIZE = 10 * 1024 * 1024; // 10 MB
|
int maxVboByteSize = 2 * 1024 * 1024;
|
||||||
int MAX_QUADS_PER_BUFFER = MAX_VBO_BYTE_SIZE / QUADS_BYTE_SIZE;
|
|
||||||
int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE;
|
|
||||||
|
|
||||||
maxBufferByteSize = FULL_SIZED_BUFFER;
|
int maxQuadsPerBuffer = maxVboByteSize / BYTES_PER_QUAD;
|
||||||
|
// integer truncation to remove decimal component
|
||||||
|
int fullSizedBuffer = maxQuadsPerBuffer * BYTES_PER_QUAD;
|
||||||
|
|
||||||
return FULL_SIZED_BUFFER;
|
maxBufferByteSize = fullSizedBuffer;
|
||||||
|
|
||||||
|
return fullSizedBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=====================//
|
||||||
|
// buffer quad pooling //
|
||||||
|
//=====================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
private BufferQuad getOrCreateBufferQuad()
|
||||||
|
{
|
||||||
|
// start from the back of the list so we don't have
|
||||||
|
// to move the array around
|
||||||
|
int index = bufferQuadCacheList.size() - 1;
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
// cache empty, create a new object
|
||||||
|
return new BufferQuad();
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferQuad quad = bufferQuadCacheList.remove(index);
|
||||||
|
if (quad != null) // shouldn't happen, but just in case
|
||||||
|
{
|
||||||
|
return quad;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BufferQuad();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void returnQuadsToCache(ArrayList<BufferQuad> quadCache, ArrayList<BufferQuad>[] quadsToReturn)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < quadsToReturn.length; i++)
|
||||||
|
{
|
||||||
|
// manual add and loop to reduce GC pressure due to addAll() doing unnecessary
|
||||||
|
// array copies
|
||||||
|
for (int j = 0; j < quadsToReturn[i].size(); j++)
|
||||||
|
{
|
||||||
|
quadCache.add(quadsToReturn[i].get(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
quadsToReturn[i].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
// can be used/closed multiple times
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
returnQuadsToCache(this.bufferQuadCacheList, this.opaqueQuads);
|
||||||
|
returnQuadsToCache(this.bufferQuadCacheList, this.transparentQuads);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+63
-20
@@ -37,6 +37,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
|
|||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
@@ -128,6 +129,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
ColumnRenderView tempExpandingColumnView = ColumnRenderView.getPooled();
|
ColumnRenderView tempExpandingColumnView = ColumnRenderView.getPooled();
|
||||||
RenderDataPointReducingList reducingList = new RenderDataPointReducingList())
|
RenderDataPointReducingList reducingList = new RenderDataPointReducingList())
|
||||||
{
|
{
|
||||||
|
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable();
|
||||||
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
|
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
|
||||||
{
|
{
|
||||||
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
|
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
|
||||||
@@ -141,7 +143,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
baseX + BitShiftUtil.pow(x, dataDetail), baseZ + BitShiftUtil.pow(z, dataDetail),
|
baseX + BitShiftUtil.pow(x, dataDetail), baseZ + BitShiftUtil.pow(z, dataDetail),
|
||||||
columnArrayView, dataColumn,
|
columnArrayView, dataColumn,
|
||||||
// pooled references so we don't need to re-allocate/get them 4000 times per render source
|
// pooled references so we don't need to re-allocate/get them 4000 times per render source
|
||||||
phantomCheckout, tempExpandingColumnView, reducingList);
|
phantomCheckout, tempExpandingColumnView, reducingList, mutableBlockPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +158,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
ColumnRenderView columnArrayView,
|
ColumnRenderView columnArrayView,
|
||||||
LongArrayList fullDataColumn,
|
LongArrayList fullDataColumn,
|
||||||
// pooled references
|
// pooled references
|
||||||
PhantomArrayListCheckout phantomCheckout, ColumnRenderView tempExpandingColumnView, RenderDataPointReducingList reducingList)
|
PhantomArrayListCheckout phantomCheckout, ColumnRenderView tempExpandingColumnView, RenderDataPointReducingList reducingList, DhBlockPosMutable mutableBlockPos)
|
||||||
{
|
{
|
||||||
// we can't do anything if the full data is missing or empty
|
// we can't do anything if the full data is missing or empty
|
||||||
if (fullDataColumn == null
|
if (fullDataColumn == null
|
||||||
@@ -169,7 +171,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
if (fullDataLength <= columnArrayView.maxVerticalSliceCount)
|
if (fullDataLength <= columnArrayView.maxVerticalSliceCount)
|
||||||
{
|
{
|
||||||
// Directly use the arrayView since it fits.
|
// Directly use the arrayView since it fits.
|
||||||
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, columnArrayView, fullDataColumn);
|
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, columnArrayView, fullDataColumn, mutableBlockPos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -177,7 +179,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
|
|
||||||
// expand the ColumnArrayView to fit the new larger max vertical size
|
// expand the ColumnArrayView to fit the new larger max vertical size
|
||||||
tempExpandingColumnView.populate(dataArrayList, fullDataLength, 0, fullDataLength);
|
tempExpandingColumnView.populate(dataArrayList, fullDataLength, 0, fullDataLength);
|
||||||
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, tempExpandingColumnView, fullDataColumn);
|
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, tempExpandingColumnView, fullDataColumn, mutableBlockPos);
|
||||||
|
|
||||||
columnArrayView.changeVerticalSizeFrom(tempExpandingColumnView, reducingList);
|
columnArrayView.changeVerticalSizeFrom(tempExpandingColumnView, reducingList);
|
||||||
}
|
}
|
||||||
@@ -185,17 +187,21 @@ public class FullDataToRenderDataTransformer
|
|||||||
private static void setRenderColumnView(
|
private static void setRenderColumnView(
|
||||||
IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource,
|
IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource,
|
||||||
int blockX, int blockZ,
|
int blockX, int blockZ,
|
||||||
ColumnRenderView renderColumnData, LongArrayList fullColumnData)
|
ColumnRenderView renderColumnData, LongArrayList fullColumnData, DhBlockPosMutable mutableBlockPos)
|
||||||
{
|
{
|
||||||
//===============//
|
//===============//
|
||||||
// config values //
|
// config values //
|
||||||
//===============//
|
//===============//
|
||||||
|
|
||||||
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
|
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Culling.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
|
||||||
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
|
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Culling.tintWithAvoidedBlocks.get();
|
||||||
|
|
||||||
|
final ObjectOpenHashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
|
||||||
|
final ObjectOpenHashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
|
||||||
|
final ObjectOpenHashSet<IBlockStateWrapper> waterSubsurfaceReplacementBlocks = WRAPPER_FACTORY.getWaterSubsurfaceReplacementBlocks(levelWrapper);
|
||||||
|
final ObjectOpenHashSet<IBlockStateWrapper> waterSurfaceReplacementBlocks = WRAPPER_FACTORY.getWaterSurfaceReplacementBlocks(levelWrapper);
|
||||||
|
final IBlockStateWrapper water = WRAPPER_FACTORY.getWaterBlockStateWrapper(levelWrapper);
|
||||||
|
|
||||||
ObjectOpenHashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
|
|
||||||
ObjectOpenHashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
|
|
||||||
|
|
||||||
// build snow block cache if needed
|
// build snow block cache if needed
|
||||||
if (snowLayerBlockStates == null)
|
if (snowLayerBlockStates == null)
|
||||||
@@ -223,6 +229,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
int colorToApplyToNextBlock = -1;
|
int colorToApplyToNextBlock = -1;
|
||||||
int lastColor = 0;
|
int lastColor = 0;
|
||||||
int lastBottom = -10_000;
|
int lastBottom = -10_000;
|
||||||
|
IBlockStateWrapper lastBlock = null;
|
||||||
|
|
||||||
int skylightToApplyToNextBlock = -1;
|
int skylightToApplyToNextBlock = -1;
|
||||||
int blocklightToApplyToNextBlock = -1;
|
int blocklightToApplyToNextBlock = -1;
|
||||||
@@ -236,7 +243,8 @@ public class FullDataToRenderDataTransformer
|
|||||||
|
|
||||||
FullDataPointIdMap fullDataMapping = fullDataSource.mapping;
|
FullDataPointIdMap fullDataMapping = fullDataSource.mapping;
|
||||||
|
|
||||||
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockX, 0, blockZ);
|
mutableBlockPos.setX(blockX);
|
||||||
|
mutableBlockPos.setZ(blockZ);
|
||||||
|
|
||||||
// goes from the top down
|
// goes from the top down
|
||||||
for (int fullDataIndex = 0; fullDataIndex < fullColumnData.size(); fullDataIndex++)
|
for (int fullDataIndex = 0; fullDataIndex < fullColumnData.size(); fullDataIndex++)
|
||||||
@@ -283,6 +291,12 @@ public class FullDataToRenderDataTransformer
|
|||||||
// cave culling check //
|
// cave culling check //
|
||||||
//====================//
|
//====================//
|
||||||
|
|
||||||
|
if (waterSubsurfaceReplacementBlocks.contains(block)
|
||||||
|
&& (lastBlock == null || lastBlock.isAir()))
|
||||||
|
{
|
||||||
|
block = water;
|
||||||
|
}
|
||||||
|
|
||||||
boolean ignoreBlock = blockStatesToIgnore.contains(block);
|
boolean ignoreBlock = blockStatesToIgnore.contains(block);
|
||||||
boolean caveBlock = caveBlockStatesToIgnore.contains(block);
|
boolean caveBlock = caveBlockStatesToIgnore.contains(block);
|
||||||
if (caveBlock
|
if (caveBlock
|
||||||
@@ -328,6 +342,9 @@ public class FullDataToRenderDataTransformer
|
|||||||
else if (ignoreBlock)
|
else if (ignoreBlock)
|
||||||
{
|
{
|
||||||
// this is an ignored block, but shouldn't be merged like a cave block
|
// this is an ignored block, but shouldn't be merged like a cave block
|
||||||
|
|
||||||
|
// applying this sky light to the next block should prevent black spots for opaque covering blocks
|
||||||
|
skylightToApplyToNextBlock = skyLight;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,21 +360,37 @@ public class FullDataToRenderDataTransformer
|
|||||||
&& !block.isLiquid()
|
&& !block.isLiquid()
|
||||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE;
|
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE;
|
||||||
|
|
||||||
// merge snow into the block below it
|
// handle height reduction
|
||||||
if (snowLayerBlockStates.contains(block))
|
boolean isSnowLayer = snowLayerBlockStates.contains(block);
|
||||||
|
boolean isWaterSurfaceReplacement = waterSurfaceReplacementBlocks.contains(block);
|
||||||
|
if (isSnowLayer || isWaterSurfaceReplacement)
|
||||||
{
|
{
|
||||||
// sometimes a snow datapoint will be multiple blocks tall,
|
if (isWaterSurfaceReplacement)
|
||||||
|
{
|
||||||
|
// replace the block with water
|
||||||
|
block = WRAPPER_FACTORY.getWaterBlockStateWrapper(levelWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sometimes a datapoint will be multiple blocks tall,
|
||||||
// in that case we just want to drop the top by 1
|
// in that case we just want to drop the top by 1
|
||||||
blockHeight -= 1;
|
blockHeight -= 1;
|
||||||
if (blockHeight == 0)
|
if (blockHeight == 0)
|
||||||
{
|
{
|
||||||
// this snow block was entirely removed, just color the block below it
|
// this block was entirely removed, just color the block below it
|
||||||
ignoreNonSolidBlock = true;
|
ignoreNonSolidBlock = true;
|
||||||
|
|
||||||
// snow is a special case where it should always tint the block
|
|
||||||
// below it, if not done grass will appear as gray
|
if (isSnowLayer)
|
||||||
int snowColor = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
|
{
|
||||||
colorToApplyToNextBlock = ColorUtil.setAlpha(snowColor, 255);
|
// snow is a special case where it should always tint the block
|
||||||
|
// below it, if not done grass will appear as gray
|
||||||
|
int snowColor = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
|
||||||
|
colorToApplyToNextBlock = ColorUtil.setAlpha(snowColor, 255);
|
||||||
|
}
|
||||||
|
else //if (isWaterSurfaceReplacement)
|
||||||
|
{
|
||||||
|
colorToApplyToNextBlock = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,8 +452,17 @@ public class FullDataToRenderDataTransformer
|
|||||||
// use the previous block's color
|
// use the previous block's color
|
||||||
color = colorToApplyToNextBlock;
|
color = colorToApplyToNextBlock;
|
||||||
colorToApplyToNextBlock = -1;
|
colorToApplyToNextBlock = -1;
|
||||||
skyLight = skylightToApplyToNextBlock;
|
|
||||||
blockLight = blocklightToApplyToNextBlock;
|
// use the skylight override if present
|
||||||
|
if (skylightToApplyToNextBlock != -1)
|
||||||
|
{
|
||||||
|
skyLight = skylightToApplyToNextBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blocklightToApplyToNextBlock != -1)
|
||||||
|
{
|
||||||
|
blockLight = blocklightToApplyToNextBlock;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -449,6 +491,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
}
|
}
|
||||||
lastBottom = bottomY;
|
lastBottom = bottomY;
|
||||||
lastColor = color;
|
lastColor = color;
|
||||||
|
lastBlock = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.enums;
|
package com.seibel.distanthorizons.core.enums;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3i;
|
import com.seibel.distanthorizons.core.util.math.DhVec3i;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Up <Br>
|
* Up <Br>
|
||||||
@@ -32,17 +32,17 @@ import com.seibel.distanthorizons.core.util.math.Vec3i;
|
|||||||
public enum EDhDirection
|
public enum EDhDirection
|
||||||
{
|
{
|
||||||
/** negative Y */
|
/** negative Y */
|
||||||
DOWN("down", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Y, new Vec3i(0, -1, 0), -1),
|
DOWN("down", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Y, new DhVec3i(0, -1, 0), -1),
|
||||||
/** positive Y */
|
/** positive Y */
|
||||||
UP("up", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Y, new Vec3i(0, 1, 0), -1),
|
UP("up", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Y, new DhVec3i(0, 1, 0), -1),
|
||||||
/** negative Z */
|
/** negative Z */
|
||||||
NORTH("north", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Z, new Vec3i(0, 0, -1), 0),
|
NORTH("north", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Z, new DhVec3i(0, 0, -1), 0),
|
||||||
/** positive Z */
|
/** positive Z */
|
||||||
SOUTH("south", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Z, new Vec3i(0, 0, 1), 1),
|
SOUTH("south", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Z, new DhVec3i(0, 0, 1), 1),
|
||||||
/** negative X */
|
/** negative X */
|
||||||
WEST("west", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.X, new Vec3i(-1, 0, 0), 2),
|
WEST("west", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.X, new DhVec3i(-1, 0, 0), 2),
|
||||||
/** positive X */
|
/** positive X */
|
||||||
EAST("east", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.X, new Vec3i(1, 0, 0), 3);
|
EAST("east", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.X, new DhVec3i(1, 0, 0), 3);
|
||||||
|
|
||||||
|
|
||||||
/** Up, Down, West, East, North, South */
|
/** Up, Down, West, East, North, South */
|
||||||
@@ -68,7 +68,7 @@ public enum EDhDirection
|
|||||||
public final String name;
|
public final String name;
|
||||||
public final EDhDirection.Axis axis;
|
public final EDhDirection.Axis axis;
|
||||||
public final EDhDirection.AxisDirection axisDirection;
|
public final EDhDirection.AxisDirection axisDirection;
|
||||||
public final Vec3i normal;
|
public final DhVec3i normal;
|
||||||
/** -1 if not a {@link EDhDirection#CARDINAL_COMPASS} direction */
|
/** -1 if not a {@link EDhDirection#CARDINAL_COMPASS} direction */
|
||||||
public final int compassIndex;
|
public final int compassIndex;
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ public enum EDhDirection
|
|||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
|
||||||
EDhDirection(String name, EDhDirection.AxisDirection axisDirection, EDhDirection.Axis axis, Vec3i normal, int compassIndex)
|
EDhDirection(String name, EDhDirection.AxisDirection axisDirection, EDhDirection.Axis axis, DhVec3i normal, int compassIndex)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.axis = axis;
|
this.axis = axis;
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProvid
|
|||||||
import com.seibel.distanthorizons.core.util.delayedSaveCache.DelayedDataSourceSaveCache;
|
import com.seibel.distanthorizons.core.util.delayedSaveCache.DelayedDataSourceSaveCache;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
||||||
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
||||||
import com.seibel.distanthorizons.core.generation.IFullDataSourceRetrievalQueue;
|
import com.seibel.distanthorizons.core.generation.queues.IFullDataSourceRetrievalQueue;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.ERetrievalResultState;
|
import com.seibel.distanthorizons.core.generation.tasks.ERetrievalResultState;
|
||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
|
|||||||
+2
-2
@@ -22,10 +22,10 @@ package com.seibel.distanthorizons.core.file.fullDatafile;
|
|||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
||||||
import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue;
|
import com.seibel.distanthorizons.core.generation.queues.RemoteWorldRetrievalQueue;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
import com.seibel.distanthorizons.core.level.LodRequestModule;
|
import com.seibel.distanthorizons.core.generation.queues.LodRequestModule;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.ERetrievalResultState;
|
import com.seibel.distanthorizons.core.generation.tasks.ERetrievalResultState;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue;
|
import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|||||||
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.ERetrievalResultState;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.util.FormatUtil;
|
import com.seibel.distanthorizons.core.util.FormatUtil;
|
||||||
@@ -93,7 +92,8 @@ public class PregenManager
|
|||||||
private final AtomicInteger nextSectionSpiralIndex = new AtomicInteger(0);
|
private final AtomicInteger nextSectionSpiralIndex = new AtomicInteger(0);
|
||||||
|
|
||||||
private final AtomicLong lastTaskFinishTime = new AtomicLong(System.currentTimeMillis());
|
private final AtomicLong lastTaskFinishTime = new AtomicLong(System.currentTimeMillis());
|
||||||
private final RollingAverage averageTaskCompletionIntervalMs = new RollingAverage(1000);
|
private RollingAverage averageTaskCompletionIntervalMs = new RollingAverage(1000);
|
||||||
|
private final RollingAverage averageTaskCompletionIntervalMsShort = new RollingAverage(50);
|
||||||
|
|
||||||
private final AtomicLong lastLogTime = new AtomicLong();
|
private final AtomicLong lastLogTime = new AtomicLong();
|
||||||
|
|
||||||
@@ -112,6 +112,7 @@ public class PregenManager
|
|||||||
|
|
||||||
long timeSincePreviousTaskFinish = System.currentTimeMillis() - this.lastTaskFinishTime.getAndSet(System.currentTimeMillis());
|
long timeSincePreviousTaskFinish = System.currentTimeMillis() - this.lastTaskFinishTime.getAndSet(System.currentTimeMillis());
|
||||||
this.averageTaskCompletionIntervalMs.add(timeSincePreviousTaskFinish);
|
this.averageTaskCompletionIntervalMs.add(timeSincePreviousTaskFinish);
|
||||||
|
this.averageTaskCompletionIntervalMsShort.add(timeSincePreviousTaskFinish);
|
||||||
|
|
||||||
PregenState.this.fillPendingQueue();
|
PregenState.this.fillPendingQueue();
|
||||||
})
|
})
|
||||||
@@ -182,6 +183,13 @@ public class PregenManager
|
|||||||
int chunkRatePerSecond = (int) (1000 / this.averageTaskCompletionIntervalMs.getAverage() * 4 * 4);
|
int chunkRatePerSecond = (int) (1000 / this.averageTaskCompletionIntervalMs.getAverage() * 4 * 4);
|
||||||
double etaMs = this.averageTaskCompletionIntervalMs.getAverage() * (this.sectionsToGenerate - this.nextSectionSpiralIndex.get());
|
double etaMs = this.averageTaskCompletionIntervalMs.getAverage() * (this.sectionsToGenerate - this.nextSectionSpiralIndex.get());
|
||||||
|
|
||||||
|
// Reset long rolling average if short average diverged too much (<0.5 / >2.0)
|
||||||
|
double averageRatio = this.averageTaskCompletionIntervalMsShort.getAverage() / this.averageTaskCompletionIntervalMs.getAverage();
|
||||||
|
if (averageRatio < 0.5 || averageRatio > 2.0)
|
||||||
|
{
|
||||||
|
this.averageTaskCompletionIntervalMs = new RollingAverage(1000);
|
||||||
|
}
|
||||||
|
|
||||||
return MessageFormat.format("Generated radius: {0,number,#.###} / {1,number,#.#} chunks ({2} cps, {3,number,#.###%}), ETA: {4}",
|
return MessageFormat.format("Generated radius: {0,number,#.###} / {1,number,#.#} chunks ({2} cps, {3,number,#.###%}), ETA: {4}",
|
||||||
this.generatedRadius.getValue(),
|
this.generatedRadius.getValue(),
|
||||||
chunksToGenerate,
|
chunksToGenerate,
|
||||||
|
|||||||
+227
@@ -0,0 +1,227 @@
|
|||||||
|
package com.seibel.distanthorizons.core.generation.queues;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
|
||||||
|
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||||
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
|
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
||||||
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||||
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
|
import com.seibel.distanthorizons.core.util.FormatUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||||
|
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
||||||
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the {@link IFullDataSourceRetrievalQueue} and any other necessary world gen information.
|
||||||
|
*
|
||||||
|
* @see LodRequestModule
|
||||||
|
* @see IFullDataSourceRetrievalQueue
|
||||||
|
*/
|
||||||
|
public abstract class AbstractLodRequestState
|
||||||
|
{
|
||||||
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
|
/** static so we only send the disable message once per session */
|
||||||
|
private static long firstProgressMessageSentMs = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public final IDhLevel dhLevel;
|
||||||
|
public final IFullDataSourceRetrievalQueue retrievalQueue;
|
||||||
|
|
||||||
|
private final ThreadPoolExecutor progressUpdaterThread = ThreadUtil.makeSingleDaemonThreadPool("World Gen Progress Updater");
|
||||||
|
private boolean progressUpdateThreadRunning = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===========================//
|
||||||
|
// request queue and logging //
|
||||||
|
//===========================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public AbstractLodRequestState(IDhLevel dhLevel, IFullDataSourceRetrievalQueue retrievalQueue)
|
||||||
|
{
|
||||||
|
this.dhLevel = dhLevel;
|
||||||
|
this.retrievalQueue = retrievalQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===========================//
|
||||||
|
// request queue and logging //
|
||||||
|
//===========================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/** @param targetPosForRequest the position that world generation should be centered around */
|
||||||
|
public void startRequestQueueAndSetTargetPos(DhBlockPos2D targetPosForRequest)
|
||||||
|
{
|
||||||
|
this.retrievalQueue.startAndSetTargetPos(targetPosForRequest);
|
||||||
|
this.startProgressUpdateThread();
|
||||||
|
}
|
||||||
|
private void startProgressUpdateThread()
|
||||||
|
{
|
||||||
|
// only start the thread once
|
||||||
|
if (!this.progressUpdateThreadRunning)
|
||||||
|
{
|
||||||
|
this.progressUpdateThreadRunning = true;
|
||||||
|
|
||||||
|
progressUpdaterThread.execute(() ->
|
||||||
|
{
|
||||||
|
while (this.progressUpdateThreadRunning)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.sendRetrievalProgress();
|
||||||
|
|
||||||
|
// sleep so we only see an update once in a while
|
||||||
|
int sleepTimeInSec = Config.Common.WorldGenerator.generationProgressDisplayIntervalInSeconds.get();
|
||||||
|
Thread.sleep(sleepTimeInSec * 1_000L);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue displaying chunk retrieval progress [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void sendRetrievalProgress()
|
||||||
|
{
|
||||||
|
// format the remaining chunks
|
||||||
|
int remainingChunkCount = this.retrievalQueue.getRetrievalEstimatedRemainingChunkCount();
|
||||||
|
remainingChunkCount += this.retrievalQueue.getQueuedChunkCount();
|
||||||
|
String remainingChunkCountStr = F3Screen.NUMBER_FORMAT.format(remainingChunkCount);
|
||||||
|
|
||||||
|
String message = "DH is "+this.retrievalQueue.getRetrievalTypeName()+". ";
|
||||||
|
if (this.dhLevel.getClass() == DhServerLevel.class)
|
||||||
|
{
|
||||||
|
// server levels can have multiple world generators running at once,
|
||||||
|
// this helps us track of which queue is which
|
||||||
|
message += "For " + this.dhLevel.getLevelWrapper().getDimensionName() + " ";
|
||||||
|
}
|
||||||
|
message += remainingChunkCountStr + " left.";
|
||||||
|
|
||||||
|
// show a message about how to disable progress logging if requested
|
||||||
|
int msToShowDisableInstructions = Config.Common.WorldGenerator.generationProgressDisableMessageDisplayTimeInSeconds.get() * 1_000;
|
||||||
|
if (msToShowDisableInstructions > 0)
|
||||||
|
{
|
||||||
|
long timeSinceFirstMessageInMs = (System.currentTimeMillis() - firstProgressMessageSentMs);
|
||||||
|
// always show this message for the first tick
|
||||||
|
if (firstProgressMessageSentMs == 0
|
||||||
|
// show this message if there is still time
|
||||||
|
|| timeSinceFirstMessageInMs < msToShowDisableInstructions)
|
||||||
|
{
|
||||||
|
// append to the current message
|
||||||
|
message += " This message can be hidden in the DH config.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the remaining time estimate if available
|
||||||
|
double chunksPerSec = this.getEstimatedChunksPerSecond();
|
||||||
|
if (chunksPerSec > 0)
|
||||||
|
{
|
||||||
|
long estimatedRemainingTime = (long) (remainingChunkCount / chunksPerSec);
|
||||||
|
message += " ETA: " + FormatUtil.formatEta(Duration.ofSeconds(estimatedRemainingTime));
|
||||||
|
|
||||||
|
if (Config.Common.WorldGenerator.generationProgressIncludeChunksPerSecond.get())
|
||||||
|
{
|
||||||
|
message += " at " + F3Screen.NUMBER_FORMAT.format(chunksPerSec) + " chunks/sec";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only log if there are chunks needing to be generated
|
||||||
|
if (remainingChunkCount != 0)
|
||||||
|
{
|
||||||
|
// determine where to log
|
||||||
|
EDhApiDistantGeneratorProgressDisplayLocation displayLocation = Config.Common.WorldGenerator.showGenerationProgress.get();
|
||||||
|
if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY)
|
||||||
|
{
|
||||||
|
ClientApi.INSTANCE.showOverlayMessageNextFrame(message);
|
||||||
|
}
|
||||||
|
else if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.CHAT)
|
||||||
|
{
|
||||||
|
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||||
|
}
|
||||||
|
else if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.LOG)
|
||||||
|
{
|
||||||
|
LOGGER.info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// mark when the first message was sent
|
||||||
|
if (firstProgressMessageSentMs == 0)
|
||||||
|
{
|
||||||
|
firstProgressMessageSentMs = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return -1 if this method isn't supported or available */
|
||||||
|
public double getEstimatedChunksPerSecond()
|
||||||
|
{
|
||||||
|
RollingAverage avg = this.retrievalQueue.getRollingAverageChunkGenTimeInMs();
|
||||||
|
if (avg == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PriorityTaskPicker.Executor executor = ThreadPoolUtil.getWorldGenExecutor();
|
||||||
|
int threadCount = 1;
|
||||||
|
if (executor != null)
|
||||||
|
{
|
||||||
|
threadCount = executor.getPoolSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert chunk generation time in milliseconds to chunks per second
|
||||||
|
double chunksPerSecond = (1 / avg.getAverage()) * 1_000;
|
||||||
|
// estimate the number of chunks that can be processed per second by all threads
|
||||||
|
// Note: this is probably higher than the actual number, we might want to drop this by 1 or 2 to give a more realistic estimate
|
||||||
|
chunksPerSecond = threadCount * chunksPerSecond;
|
||||||
|
|
||||||
|
return chunksPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public CompletableFuture<Void> closeAsync(boolean doInterrupt)
|
||||||
|
{
|
||||||
|
// this should stop the updater thread
|
||||||
|
this.progressUpdateThreadRunning = false;
|
||||||
|
|
||||||
|
return this.retrievalQueue.startClosingAsync(true, doInterrupt)
|
||||||
|
.exceptionally(e ->
|
||||||
|
{
|
||||||
|
LOGGER.error("Error during first stage of generation queue shutdown, Error: [" + e.getMessage() + "].", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
).thenRun(this.retrievalQueue::close)
|
||||||
|
.exceptionally(e ->
|
||||||
|
{
|
||||||
|
LOGGER.error("Error during second stage of generation queue shutdown, Error: [" + e.getMessage() + "].", e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+9
-1
@@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.seibel.distanthorizons.core.generation;
|
package com.seibel.distanthorizons.core.generation.queues;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
@@ -42,6 +42,8 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
* Used by both world gen and server networking.
|
* Used by both world gen and server networking.
|
||||||
*
|
*
|
||||||
* @see LodQuadTree
|
* @see LodQuadTree
|
||||||
|
* @see AbstractLodRequestState
|
||||||
|
* @see LodRequestModule
|
||||||
*/
|
*/
|
||||||
public interface IFullDataSourceRetrievalQueue extends Closeable
|
public interface IFullDataSourceRetrievalQueue extends Closeable
|
||||||
{
|
{
|
||||||
@@ -63,6 +65,12 @@ public interface IFullDataSourceRetrievalQueue extends Closeable
|
|||||||
*/
|
*/
|
||||||
byte highestDataDetail();
|
byte highestDataDetail();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value like "downloading" or "generating" depending on how the LODs are being retrieved.
|
||||||
|
* Used to make the progress message easier to understand.
|
||||||
|
*/
|
||||||
|
String getRetrievalTypeName();
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
+50
-181
@@ -17,39 +17,35 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.seibel.distanthorizons.core.level;
|
package com.seibel.distanthorizons.core.generation.queues;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.generation.IFullDataSourceRetrievalQueue;
|
import com.seibel.distanthorizons.core.level.*;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.util.FormatUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
|
||||||
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
|
||||||
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
|
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles both single-player/server-side world gen and client side LOD requests.
|
* Handles both single-player/server-side world gen and client side LOD requests.
|
||||||
|
*
|
||||||
|
* @see AbstractLodRequestState
|
||||||
|
* @see IFullDataSourceRetrievalQueue
|
||||||
*/
|
*/
|
||||||
public class LodRequestModule implements Closeable
|
public class LodRequestModule implements Closeable
|
||||||
{
|
{
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
|
|
||||||
|
private final IDhLevel level;
|
||||||
private final GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener;
|
private final GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener;
|
||||||
private final ThreadPoolExecutor tickerThread;
|
private final ThreadPoolExecutor tickerThread;
|
||||||
|
|
||||||
@@ -63,6 +59,7 @@ public class LodRequestModule implements Closeable
|
|||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
public LodRequestModule(
|
public LodRequestModule(
|
||||||
IDhLevel level,
|
IDhLevel level,
|
||||||
@@ -71,29 +68,50 @@ public class LodRequestModule implements Closeable
|
|||||||
Supplier<? extends AbstractLodRequestState> worldGenStateSupplier
|
Supplier<? extends AbstractLodRequestState> worldGenStateSupplier
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
this.level = level;
|
||||||
this.onWorldGenCompleteListener = onWorldGenCompleteListener;
|
this.onWorldGenCompleteListener = onWorldGenCompleteListener;
|
||||||
this.dataSourceProvider = dataSourceProvider;
|
this.dataSourceProvider = dataSourceProvider;
|
||||||
this.worldGenStateSupplier = worldGenStateSupplier;
|
this.worldGenStateSupplier = worldGenStateSupplier;
|
||||||
|
|
||||||
String levelId = level.getLevelWrapper().getDhIdentifier();
|
String levelId = this.level.getLevelWrapper().getDhIdentifier();
|
||||||
this.tickerThread = ThreadUtil.makeSingleDaemonThreadPool("Request Module Ticker ["+levelId+"]");
|
this.tickerThread = ThreadUtil.makeSingleDaemonThreadPool("Request Module Ticker ["+levelId+"]");
|
||||||
this.tickerThread.execute(this::tickLoop);
|
this.tickerThread.execute(this::tickLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
//=========//
|
||||||
// ticking //
|
// ticking //
|
||||||
//=========//
|
//=========//
|
||||||
|
//region
|
||||||
|
|
||||||
private void tickLoop()
|
private void tickLoop()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Initial wait is to prevent an issue
|
||||||
|
// where this starts before the child object's constructor finishes,
|
||||||
|
// causing null pointers on final non-null references.
|
||||||
|
// The try-catch in the while loop should also handle this
|
||||||
|
// but this way we shouldn't have error logs.
|
||||||
|
Thread.sleep(500);
|
||||||
|
|
||||||
|
// run until the threadpool is shut down
|
||||||
while (!Thread.interrupted())
|
while (!Thread.interrupted())
|
||||||
{
|
{
|
||||||
Thread.sleep(20);
|
try
|
||||||
this.tick();
|
{
|
||||||
|
Thread.sleep(20);
|
||||||
|
|
||||||
|
this.tick();
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) { throw e; }
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected error in [" + LodRequestModule.class.getSimpleName() + "] tick loop, error: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InterruptedException ignore) { }
|
catch (InterruptedException ignore) { }
|
||||||
@@ -101,8 +119,11 @@ public class LodRequestModule implements Closeable
|
|||||||
private void tick()
|
private void tick()
|
||||||
{
|
{
|
||||||
boolean shouldDoWorldGen = this.onWorldGenCompleteListener.shouldDoWorldGen();
|
boolean shouldDoWorldGen = this.onWorldGenCompleteListener.shouldDoWorldGen();
|
||||||
|
|
||||||
// if the world is read only don't generate anything
|
// if the world is read only don't generate anything
|
||||||
shouldDoWorldGen &= !DhApiWorldProxy.INSTANCE.getReadOnly();
|
shouldDoWorldGen &= !DhApiWorldProxy.INSTANCE.tryGetReadOnly();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
boolean isWorldGenRunning = this.isWorldGenRunning();
|
boolean isWorldGenRunning = this.isWorldGenRunning();
|
||||||
if (shouldDoWorldGen && !isWorldGenRunning)
|
if (shouldDoWorldGen && !isWorldGenRunning)
|
||||||
@@ -130,11 +151,14 @@ public class LodRequestModule implements Closeable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
//===================//
|
||||||
// world gen control //
|
// world gen control //
|
||||||
//===================//
|
//===================//
|
||||||
|
//region
|
||||||
|
|
||||||
public void startWorldGen(GeneratedFullDataSourceProvider dataFileHandler, AbstractLodRequestState newWgs)
|
public void startWorldGen(GeneratedFullDataSourceProvider dataFileHandler, AbstractLodRequestState newWgs)
|
||||||
{
|
{
|
||||||
@@ -168,15 +192,19 @@ public class LodRequestModule implements Closeable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataFileHandler.clearRetrievalQueue();
|
dataFileHandler.clearRetrievalQueue();
|
||||||
worldGenState.closeAsync(true).join(); //TODO: Make it async.
|
// synchronized shutdown necessary to make sure the tasks are all handled correctly
|
||||||
|
worldGenState.closeAsync(true).join();
|
||||||
dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener);
|
dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======================//
|
//=======================//
|
||||||
// base method overrides //
|
// base method overrides //
|
||||||
//=======================//
|
//=======================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
@@ -198,16 +226,20 @@ public class LodRequestModule implements Closeable
|
|||||||
|
|
||||||
if (worldGenState != null)
|
if (worldGenState != null)
|
||||||
{
|
{
|
||||||
worldGenState.closeAsync(true).join(); //TODO: Make it async.
|
// synchronized shutdown necessary to make sure the tasks are all handled correctly
|
||||||
|
worldGenState.closeAsync(true).join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
//=========//
|
||||||
// getters //
|
// getters //
|
||||||
//=========//
|
//=========//
|
||||||
|
//region
|
||||||
|
|
||||||
public boolean isWorldGenRunning() { return this.lodRequestStateRef.get() != null; }
|
public boolean isWorldGenRunning() { return this.lodRequestStateRef.get() != null; }
|
||||||
|
|
||||||
@@ -239,170 +271,7 @@ public class LodRequestModule implements Closeable
|
|||||||
worldGenState.retrievalQueue.addDebugMenuStringsToList(messageList);
|
worldGenState.retrievalQueue.addDebugMenuStringsToList(messageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper classes //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
/** Handles the {@link IFullDataSourceRetrievalQueue} and any other necessary world gen information. */
|
|
||||||
public static abstract class AbstractLodRequestState
|
|
||||||
{
|
|
||||||
/** static so we only send the disable message once per session */
|
|
||||||
private static long firstProgressMessageSentMs = 0;
|
|
||||||
|
|
||||||
public IFullDataSourceRetrievalQueue retrievalQueue;
|
|
||||||
|
|
||||||
private static final ThreadPoolExecutor PROGRESS_UPDATER_THREAD = ThreadUtil.makeSingleDaemonThreadPool("World Gen Progress Updater");
|
|
||||||
private boolean progressUpdateThreadRunning = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** @param targetPosForRequest the position that world generation should be centered around */
|
|
||||||
public void startRequestQueueAndSetTargetPos(DhBlockPos2D targetPosForRequest)
|
|
||||||
{
|
|
||||||
this.retrievalQueue.startAndSetTargetPos(targetPosForRequest);
|
|
||||||
this.startProgressUpdateThread();
|
|
||||||
}
|
|
||||||
private void startProgressUpdateThread()
|
|
||||||
{
|
|
||||||
// only start the thread once
|
|
||||||
if (!this.progressUpdateThreadRunning)
|
|
||||||
{
|
|
||||||
this.progressUpdateThreadRunning = true;
|
|
||||||
|
|
||||||
PROGRESS_UPDATER_THREAD.execute(() ->
|
|
||||||
{
|
|
||||||
while (this.progressUpdateThreadRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.sendRetrievalProgress();
|
|
||||||
|
|
||||||
// sleep so we only see an update once in a while
|
|
||||||
int sleepTimeInSec = Config.Common.WorldGenerator.generationProgressDisplayIntervalInSeconds.get();
|
|
||||||
Thread.sleep(sleepTimeInSec * 1_000L);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Unexpected issue displaying chunk retrieval progress [" + e.getMessage() + "].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void sendRetrievalProgress()
|
|
||||||
{
|
|
||||||
// format the remaining chunks
|
|
||||||
int remainingChunkCount = this.retrievalQueue.getRetrievalEstimatedRemainingChunkCount();
|
|
||||||
remainingChunkCount += this.retrievalQueue.getQueuedChunkCount();
|
|
||||||
String remainingChunkCountStr = F3Screen.NUMBER_FORMAT.format(remainingChunkCount);
|
|
||||||
|
|
||||||
String message = "DH is generating chunks. " + remainingChunkCountStr + " left."; // TODO getting stuck at 32 chunks remaining
|
|
||||||
|
|
||||||
// show a message about how to disable progress logging if requested
|
|
||||||
int msToShowDisableInstructions = Config.Common.WorldGenerator.generationProgressDisableMessageDisplayTimeInSeconds.get() * 1_000;
|
|
||||||
if (msToShowDisableInstructions > 0)
|
|
||||||
{
|
|
||||||
long timeSinceFirstMessageInMs = (System.currentTimeMillis() - firstProgressMessageSentMs);
|
|
||||||
// always show this message for the first tick
|
|
||||||
if (firstProgressMessageSentMs == 0
|
|
||||||
// show this message if there is still time
|
|
||||||
|| timeSinceFirstMessageInMs < msToShowDisableInstructions)
|
|
||||||
{
|
|
||||||
// append to the current message
|
|
||||||
message += " This message can be hidden in the DH config.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the remaining time estimate if available
|
|
||||||
double chunksPerSec = this.getEstimatedChunksPerSecond();
|
|
||||||
if (chunksPerSec > 0)
|
|
||||||
{
|
|
||||||
long estimatedRemainingTime = (long) (remainingChunkCount / chunksPerSec);
|
|
||||||
message += " ETA: " + FormatUtil.formatEta(Duration.ofSeconds(estimatedRemainingTime));
|
|
||||||
|
|
||||||
if (Config.Common.WorldGenerator.generationProgressIncludeChunksPerSecond.get())
|
|
||||||
{
|
|
||||||
message += " at " + F3Screen.NUMBER_FORMAT.format(chunksPerSec) + " chunks/sec";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// only log if there are chunks needing to be generated
|
|
||||||
if (remainingChunkCount != 0)
|
|
||||||
{
|
|
||||||
// determine where to log
|
|
||||||
EDhApiDistantGeneratorProgressDisplayLocation displayLocation = Config.Common.WorldGenerator.showGenerationProgress.get();
|
|
||||||
if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY)
|
|
||||||
{
|
|
||||||
ClientApi.INSTANCE.showOverlayMessageNextFrame(message);
|
|
||||||
}
|
|
||||||
else if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.CHAT)
|
|
||||||
{
|
|
||||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
|
||||||
}
|
|
||||||
else if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.LOG)
|
|
||||||
{
|
|
||||||
LOGGER.info(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// mark when the first message was sent
|
|
||||||
if (firstProgressMessageSentMs == 0)
|
|
||||||
{
|
|
||||||
firstProgressMessageSentMs = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return -1 if this method isn't supported or available */
|
|
||||||
public double getEstimatedChunksPerSecond()
|
|
||||||
{
|
|
||||||
RollingAverage avg = this.retrievalQueue.getRollingAverageChunkGenTimeInMs();
|
|
||||||
if (avg == null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PriorityTaskPicker.Executor executor = ThreadPoolUtil.getWorldGenExecutor();
|
|
||||||
int threadCount = 1;
|
|
||||||
if (executor != null)
|
|
||||||
{
|
|
||||||
threadCount = executor.getPoolSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert chunk generation time in milliseconds to chunks per second
|
|
||||||
double chunksPerSecond = (1 / avg.getAverage()) * 1_000;
|
|
||||||
// estimate the number of chunks that can be processed per second by all threads
|
|
||||||
// Note: this is probably higher than the actual number, we might want to drop this by 1 or 2 to give a more realistic estimate
|
|
||||||
chunksPerSecond = threadCount * chunksPerSecond;
|
|
||||||
|
|
||||||
return chunksPerSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CompletableFuture<Void> closeAsync(boolean doInterrupt)
|
|
||||||
{
|
|
||||||
// this should stop the updater thread
|
|
||||||
this.progressUpdateThreadRunning = false;
|
|
||||||
|
|
||||||
return this.retrievalQueue.startClosingAsync(true, doInterrupt)
|
|
||||||
.exceptionally(e ->
|
|
||||||
{
|
|
||||||
LOGGER.error("Error during first stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
).thenRun(this.retrievalQueue::close)
|
|
||||||
.exceptionally(e ->
|
|
||||||
{
|
|
||||||
LOGGER.error("Error during second stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
package com.seibel.distanthorizons.core.generation;
|
package com.seibel.distanthorizons.core.generation.queues;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
||||||
@@ -15,7 +15,6 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
|||||||
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
||||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
@@ -52,6 +51,9 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue
|
|||||||
@Override
|
@Override
|
||||||
public byte highestDataDetail() { return LodUtil.BLOCK_DETAIL_LEVEL; }
|
public byte highestDataDetail() { return LodUtil.BLOCK_DETAIL_LEVEL; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRetrievalTypeName() { return "downloading LODs"; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<DataSourceRetrievalResult> submitRetrievalTask(long sectionPos, byte requiredDataDetail)
|
public CompletableFuture<DataSourceRetrievalResult> submitRetrievalTask(long sectionPos, byte requiredDataDetail)
|
||||||
{
|
{
|
||||||
+5
-2
@@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.seibel.distanthorizons.core.generation;
|
package com.seibel.distanthorizons.core.generation.queues;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||||
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
|
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
|
||||||
@@ -27,6 +27,7 @@ import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
|
|||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
||||||
|
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalTask;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalTask;
|
||||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
||||||
@@ -201,7 +202,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
|||||||
private synchronized void tryQueueNewWorldGenRequestsAsync()
|
private synchronized void tryQueueNewWorldGenRequestsAsync()
|
||||||
{
|
{
|
||||||
if (!DhApiWorldProxy.INSTANCE.worldLoaded()
|
if (!DhApiWorldProxy.INSTANCE.worldLoaded()
|
||||||
|| DhApiWorldProxy.INSTANCE.getReadOnly())
|
|| DhApiWorldProxy.INSTANCE.tryGetReadOnly())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -592,6 +593,8 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
|||||||
@Override public byte lowestDataDetail() { return this.lowestDataDetail; }
|
@Override public byte lowestDataDetail() { return this.lowestDataDetail; }
|
||||||
@Override public byte highestDataDetail() { return this.highestDataDetail; }
|
@Override public byte highestDataDetail() { return this.highestDataDetail; }
|
||||||
|
|
||||||
|
@Override public String getRetrievalTypeName() { return "generating chunks"; }
|
||||||
|
|
||||||
@Override public int getEstimatedRemainingTaskCount() { return this.estimatedRemainingTaskCount; }
|
@Override public int getEstimatedRemainingTaskCount() { return this.estimatedRemainingTaskCount; }
|
||||||
@Override public void setEstimatedRemainingTaskCount(int newEstimate) { this.estimatedRemainingTaskCount = newEstimate; }
|
@Override public void setEstimatedRemainingTaskCount(int newEstimate) { this.estimatedRemainingTaskCount = newEstimate; }
|
||||||
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.core.jar;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A fork of iris' dark mode detector (https://github.com/IrisShaders/Iris-Installer/blob/master/src/main/java/net/hypercubemc/iris_installer/DarkModeDetector.java)
|
|
||||||
* Which is a fork of HanSolo's dark mode detector (https://gist.github.com/HanSolo/7cf10b86efff8ca2845bf5ec2dd0fe1d)
|
|
||||||
*
|
|
||||||
* This fork has better support for Linux
|
|
||||||
*
|
|
||||||
* @author HanSolo
|
|
||||||
* @author IMS
|
|
||||||
* @author coolGi
|
|
||||||
*/
|
|
||||||
public class DarkModeDetector
|
|
||||||
{
|
|
||||||
private static final String REGQUERY_UTIL = "reg query ";
|
|
||||||
private static final String REGDWORD_TOKEN = "REG_DWORD";
|
|
||||||
private static final String DARK_THEME_CMD = REGQUERY_UTIL + "\"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\"" + " /v AppsUseLightTheme";
|
|
||||||
|
|
||||||
public static boolean isDarkMode()
|
|
||||||
{
|
|
||||||
switch (EPlatform.get())
|
|
||||||
{
|
|
||||||
case WINDOWS:
|
|
||||||
return isWindowsDarkMode();
|
|
||||||
case MACOS:
|
|
||||||
return isMacOsDarkMode();
|
|
||||||
case LINUX:
|
|
||||||
// Most Unix(-like) distros also use a lot of the same things as Linux (like desktop environments and window managers)
|
|
||||||
case BSD:
|
|
||||||
case UNIX:
|
|
||||||
return checkLinuxDark();
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needs checking as I dont use Mac
|
|
||||||
public static boolean isMacOsDarkMode()
|
|
||||||
{
|
|
||||||
boolean isDarkMode = false;
|
|
||||||
String line = query("defaults read -g AppleInterfaceStyle");
|
|
||||||
if (line.equals("Dark"))
|
|
||||||
{
|
|
||||||
isDarkMode = true;
|
|
||||||
}
|
|
||||||
return isDarkMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needs checking as I don't use Windows
|
|
||||||
public static boolean isWindowsDarkMode()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
String result = query(DARK_THEME_CMD);
|
|
||||||
int p = result.indexOf(REGDWORD_TOKEN);
|
|
||||||
|
|
||||||
if (p == -1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 == Light Mode, 0 == Dark Mode
|
|
||||||
String temp = result.substring(p + REGDWORD_TOKEN.length()).trim();
|
|
||||||
return ((Integer.parseInt(temp.substring("0x".length()), 16))) == 0;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// On Linux there are 2 popular formats for theming
|
|
||||||
// They are qt and gtk. We check the desktop environment and use that to pick which one to use (if none work then use GTK)
|
|
||||||
public static boolean checkLinuxDark()
|
|
||||||
{
|
|
||||||
// Checks "/usr/bin" as "echo $XDG_CURRENT_DESKTOP" dosnt work in java and dosnt detect window managers
|
|
||||||
File de_location = new File("/usr/bin");
|
|
||||||
// System.out.println(de_location.list());
|
|
||||||
for (String de : de_location.list())
|
|
||||||
{
|
|
||||||
// System.out.println(de);
|
|
||||||
if (de.contains("gnome-session")) // Gnome uses GTK
|
|
||||||
{
|
|
||||||
return GTKChecker();
|
|
||||||
}
|
|
||||||
if (de.contains("plasma_session")) // KDE plasma uses QT
|
|
||||||
{
|
|
||||||
return QTChecker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return GTKChecker(); // GTK works best with non plasma desktops (desktops includes window managers)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean GTKChecker()
|
|
||||||
{
|
|
||||||
// Checks if the return to "gsettings get org.gnome.desktop.interface color-scheme" in terminal is 'prefer-dark' or contains the word dark in it
|
|
||||||
final Pattern darkThemeNamePattern = Pattern.compile(".*dark.*", Pattern.CASE_INSENSITIVE);
|
|
||||||
return darkThemeNamePattern.matcher(query("gsettings get org.gnome.desktop.interface color-scheme")).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean QTChecker()
|
|
||||||
{
|
|
||||||
// Get the contents of "~/.config/Trolltech.conf" then check "KWinPalette\activeBackground"
|
|
||||||
// With that you grayscale the rgb and check if it is over/under 128
|
|
||||||
|
|
||||||
// If there is a better way of doing this then please let me know
|
|
||||||
// This seems like the best way as qt dosnt have a dark/light preference and just stores pure color values
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File themeFile = new File(System.getProperty("user.home") + "/.config/Trolltech.conf");
|
|
||||||
|
|
||||||
BufferedReader reader = new BufferedReader(new FileReader(themeFile));
|
|
||||||
String themeLine = reader.readLine();
|
|
||||||
while (themeLine != null)
|
|
||||||
{ // Go through each line till you find "KWinPalette\activeBackground"
|
|
||||||
if (themeLine.contains("KWinPalette\\activeBackground"))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
themeLine = reader.readLine();
|
|
||||||
}
|
|
||||||
reader.close();
|
|
||||||
|
|
||||||
// Get where the # is then read the hex numbers after it
|
|
||||||
short index = (short) themeLine.indexOf("#");
|
|
||||||
short r = (short) Integer.parseInt("" + themeLine.charAt(index + 1) + themeLine.charAt(index + 2), 16);
|
|
||||||
short g = (short) Integer.parseInt("" + themeLine.charAt(index + 3) + themeLine.charAt(index + 4), 16);
|
|
||||||
short b = (short) Integer.parseInt("" + themeLine.charAt(index + 5) + themeLine.charAt(index + 6), 16);
|
|
||||||
if ((r + g + b) / 2 >= 128)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace(); return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Runs a command trough command line */
|
|
||||||
private static String query(String cmd)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Process process = Runtime.getRuntime().exec(cmd);
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())))
|
|
||||||
{
|
|
||||||
String actualReadLine;
|
|
||||||
while ((actualReadLine = reader.readLine()) != null)
|
|
||||||
{
|
|
||||||
if (stringBuilder.length() != 0)
|
|
||||||
{
|
|
||||||
stringBuilder.append('\n');
|
|
||||||
}
|
|
||||||
stringBuilder.append(actualReadLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stringBuilder.toString();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
System.out.println("Exception caught while querying the OS:");
|
|
||||||
e.printStackTrace();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -94,7 +94,10 @@ public class GitlabGetter
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pipelineInfo.put(pipeline, WebDownloader.parseWebJsonList(this.GitProjID + "pipelines/" + pipeline + "/jobs"));
|
pipelineInfo.put(pipeline, WebDownloader.parseWebJsonList(this.GitProjID + "pipelines/" + pipeline + "/jobs?per_page=100"));
|
||||||
|
// max page size is 100,
|
||||||
|
// if we ever support more than 100 MC versions this logic will need to handle pagination
|
||||||
|
// https://docs.gitlab.com/api/rest/#pagination
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,16 +28,13 @@ import com.seibel.distanthorizons.core.jar.installer.GitlabGetter;
|
|||||||
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
|
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
|
||||||
import com.seibel.distanthorizons.core.jar.installer.WebDownloader;
|
import com.seibel.distanthorizons.core.jar.installer.WebDownloader;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
|
||||||
import com.seibel.distanthorizons.core.util.NativeDialogUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||||
import com.seibel.distanthorizons.coreapi.util.jar.DeleteOnUnlock;
|
import com.seibel.distanthorizons.coreapi.util.jar.DeleteOnUnlock;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -59,12 +56,14 @@ public class SelfUpdater
|
|||||||
{
|
{
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
|
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
|
private static final IVersionConstants VERSION_CONSTANTS = SingletonInjector.INSTANCE.get(IVersionConstants.class);
|
||||||
|
|
||||||
|
private static final String MC_VERSION = VERSION_CONSTANTS.getMinecraftVersion();
|
||||||
|
|
||||||
/** As we cannot delete(or replace) the jar while the mod is running, we just have this to delete it once the game closes */
|
/** As we cannot delete(or replace) the jar while the mod is running, we just have this to delete it once the game closes */
|
||||||
public static boolean deleteOldJarOnJvmShutdown = false;
|
public static boolean deleteOldJarOnJvmShutdown = false;
|
||||||
|
|
||||||
private static String currentJarSha = "";
|
|
||||||
private static String mcVersion = SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion();
|
|
||||||
|
|
||||||
public static File newFileLocation;
|
public static File newFileLocation;
|
||||||
|
|
||||||
|
|
||||||
@@ -76,29 +75,33 @@ public class SelfUpdater
|
|||||||
*/
|
*/
|
||||||
public static boolean onStart()
|
public static boolean onStart()
|
||||||
{
|
{
|
||||||
LOGGER.info("Checking for Distant Horizons update");
|
if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get())
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
currentJarSha = JarUtils.getFileChecksum(MessageDigest.getInstance("SHA"), JarUtils.jarFile);
|
LOGGER.info("Distant Horizons auto update disabled.");
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Unable to get existing jar checksum, error: ["+e.getMessage()+"].", e);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean returnValue = false;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
|
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
|
||||||
returnValue = (updateBranch == EDhApiUpdateBranch.STABLE) ? onStableStart() : onNightlyStart();
|
|
||||||
|
LOGGER.info("Checking for Distant Horizons ["+updateBranch+"] update for MC ["+MC_VERSION+"]...");
|
||||||
|
|
||||||
|
if (updateBranch == EDhApiUpdateBranch.STABLE)
|
||||||
|
{
|
||||||
|
return onStableStart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return onNightlyStart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) // Shouldn't be needed, but just in case
|
catch (Exception e) // Shouldn't be needed, but just in case
|
||||||
{
|
{
|
||||||
LOGGER.warn("Unexpected updater startup error: ["+e.getMessage()+"].", e);
|
LOGGER.warn("Unexpected updater startup error: ["+e.getMessage()+"].", e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
private static boolean onStableStart()
|
private static boolean onStableStart()
|
||||||
{
|
{
|
||||||
@@ -109,15 +112,19 @@ public class SelfUpdater
|
|||||||
LOGGER.warn("Unable to find any nightly build pipelines, auto update will be unavailable.");
|
LOGGER.warn("Unable to find any nightly build pipelines, auto update will be unavailable.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ModrinthGetter.mcVersions.contains(mcVersion))
|
if (!ModrinthGetter.mcVersions.contains(MC_VERSION))
|
||||||
{
|
{
|
||||||
LOGGER.warn("Minecraft version ["+ mcVersion +"] is not findable on Modrinth, only findable versions are ["+ StringUtil.join(", ", ModrinthGetter.mcVersions) +"]");
|
LOGGER.warn("Minecraft version ["+ MC_VERSION +"] is not findable on Modrinth, only findable versions are ["+ StringUtil.join(", ", ModrinthGetter.mcVersions) +"]");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
newFileLocation = JarUtils.jarFile.getParentFile().toPath().resolve("update").resolve(ModInfo.NAME + "-" + ModrinthGetter.getLatestNameForVersion(mcVersion) + "-" + mcVersion + ".jar").toFile();
|
newFileLocation = JarUtils.jarFile
|
||||||
|
.getParentFile().toPath()
|
||||||
|
.resolve("update")
|
||||||
|
.resolve(ModInfo.NAME + "-" + ModrinthGetter.getLatestNameForVersion(MC_VERSION) + "-" + MC_VERSION + ".jar")
|
||||||
|
.toFile();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -125,8 +132,19 @@ public class SelfUpdater
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String currentJarSha;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentJarSha = JarUtils.getFileChecksum(MessageDigest.getInstance("SHA"), JarUtils.jarFile);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unable to get existing jar checksum, error: ["+e.getMessage()+"].", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check the sha's of both our stuff
|
// Check the sha's of both our stuff
|
||||||
if (currentJarSha.equals(ModrinthGetter.getLatestShaForVersion(mcVersion)))
|
if (currentJarSha.equals(ModrinthGetter.getLatestShaForVersion(MC_VERSION)))
|
||||||
{
|
{
|
||||||
LOGGER.info("Distant Horizons already up to date.");
|
LOGGER.info("Distant Horizons already up to date.");
|
||||||
return false;
|
return false;
|
||||||
@@ -138,21 +156,23 @@ public class SelfUpdater
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LOGGER.info("New version (" + ModrinthGetter.getLatestNameForVersion(mcVersion) + ") of Distant Horizons is available");
|
LOGGER.info("New version (" + ModrinthGetter.getLatestNameForVersion(MC_VERSION) + ") of Distant Horizons is available");
|
||||||
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
|
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
|
||||||
{
|
{
|
||||||
// Auto-update mod
|
// Auto-update mod
|
||||||
updateMod(mcVersion, newFileLocation);
|
updateMod(MC_VERSION, newFileLocation);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGGER.info("Download link: " + ModrinthGetter.getLatestDownloadForVersion(mcVersion));
|
LOGGER.info("Download link: " + ModrinthGetter.getLatestDownloadForVersion(MC_VERSION));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
private static boolean onNightlyStart()
|
private static boolean onNightlyStart()
|
||||||
{
|
{
|
||||||
|
LOGGER.info("Checking for Distant Horizons Nightly update...");
|
||||||
|
|
||||||
if (GitlabGetter.INSTANCE.projectPipelines.size() == 0)
|
if (GitlabGetter.INSTANCE.projectPipelines.size() == 0)
|
||||||
{
|
{
|
||||||
LOGGER.info("Unable to find any nightly build pipelines, auto update will be unavailable.");
|
LOGGER.info("Unable to find any nightly build pipelines, auto update will be unavailable.");
|
||||||
@@ -172,9 +192,9 @@ public class SelfUpdater
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).containsKey(mcVersion))
|
if (!GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).containsKey(MC_VERSION))
|
||||||
{
|
{
|
||||||
LOGGER.warn("Minecraft version ["+ mcVersion +"] is not findable on Gitlab, findable versions are ["+ StringUtil.join(", ", GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).keySet().toArray()) +"].");
|
LOGGER.warn("Minecraft version ["+ MC_VERSION +"] is not findable on Gitlab, findable versions are ["+ StringUtil.join(", ", GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).keySet().toArray()) +"].");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,12 +221,12 @@ public class SelfUpdater
|
|||||||
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
|
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
|
||||||
{
|
{
|
||||||
// Auto-update mod
|
// Auto-update mod
|
||||||
updateMod(mcVersion, newFileLocation);
|
updateMod(MC_VERSION, newFileLocation);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGGER.info("Download link: " + GitlabGetter.getLatestForVersion(mcVersion));
|
LOGGER.info("Download link: " + GitlabGetter.getLatestForVersion(MC_VERSION));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -264,7 +284,7 @@ public class SelfUpdater
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, successMessage, "ok", "info");
|
MC_CLIENT.showDialog(ModInfo.READABLE_NAME, successMessage, "ok", "info");
|
||||||
}
|
}
|
||||||
catch (Exception ignore) { }
|
catch (Exception ignore) { }
|
||||||
}).start();
|
}).start();
|
||||||
@@ -287,7 +307,7 @@ public class SelfUpdater
|
|||||||
LOGGER.error(failMessage, e);
|
LOGGER.error(failMessage, e);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, failMessage, "ok", "error");
|
MC_CLIENT.showDialog(ModInfo.READABLE_NAME, failMessage, "ok", "error");
|
||||||
}
|
}
|
||||||
catch (Exception ignore) { }
|
catch (Exception ignore) { }
|
||||||
|
|
||||||
@@ -386,7 +406,7 @@ public class SelfUpdater
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, successMessage, "ok", "info");
|
MC_CLIENT.showDialog(ModInfo.READABLE_NAME, successMessage, "ok", "info");
|
||||||
}
|
}
|
||||||
catch (Exception ignore) { }
|
catch (Exception ignore) { }
|
||||||
}).start();
|
}).start();
|
||||||
@@ -424,7 +444,7 @@ public class SelfUpdater
|
|||||||
LOGGER.error(failMessage, e);
|
LOGGER.error(failMessage, e);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, failMessage, "ok", "error");
|
MC_CLIENT.showDialog(ModInfo.READABLE_NAME, failMessage, "ok", "error");
|
||||||
}
|
}
|
||||||
catch (Exception ignore) { }
|
catch (Exception ignore) { }
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
protected AbstractDhLevel() { }
|
protected AbstractDhLevel() { }
|
||||||
|
|
||||||
@@ -135,11 +136,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//=================//
|
||||||
// default methods //
|
// default methods //
|
||||||
//=================//
|
//=================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateChunkAsync(IChunkWrapper chunkWrapper, int chunkHash)
|
public void updateChunkAsync(IChunkWrapper chunkWrapper, int chunkHash)
|
||||||
@@ -208,11 +212,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======//
|
//=======//
|
||||||
// repos //
|
// repos //
|
||||||
//=======//
|
//=======//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getChunkHash(DhChunkPos pos)
|
public int getChunkHash(DhChunkPos pos)
|
||||||
@@ -226,11 +233,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
return (dto != null) ? dto.chunkHash : 0;
|
return (dto != null) ? dto.chunkHash : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//=================//
|
||||||
// beacon handling //
|
// beacon handling //
|
||||||
//=================//
|
//=================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBeaconBeamsForSectionPos(long sectionPos, List<BeaconBeamDTO> activeBeamList)
|
public void updateBeaconBeamsForSectionPos(long sectionPos, List<BeaconBeamDTO> activeBeamList)
|
||||||
@@ -362,6 +372,8 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
@Nullable
|
@Nullable
|
||||||
public BeaconBeamRepo getBeaconBeamRepo() { return this.beaconBeamRepo; }
|
public BeaconBeamRepo getBeaconBeamRepo() { return this.beaconBeamRepo; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
|
|||||||
+10
-29
@@ -9,7 +9,6 @@ import com.seibel.distanthorizons.core.multiplayer.server.FullDataSourceRequestH
|
|||||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.RequestOutOfRangeException;
|
import com.seibel.distanthorizons.core.network.exceptions.RequestOutOfRangeException;
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException;
|
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.SectionRequiresSplittingException;
|
import com.seibel.distanthorizons.core.network.exceptions.SectionRequiresSplittingException;
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage;
|
import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage;
|
||||||
@@ -22,14 +21,14 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
|||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -72,9 +71,11 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
boolean runRepoReliantSetup
|
boolean runRepoReliantSetup
|
||||||
) throws SQLException, IOException
|
) throws SQLException, IOException
|
||||||
{
|
{
|
||||||
if (saveStructure.getSaveFolder(serverLevelWrapper).mkdirs())
|
File saveFolder = saveStructure.getSaveFolder(serverLevelWrapper);
|
||||||
|
saveFolder.mkdirs();
|
||||||
|
if (!saveFolder.exists())
|
||||||
{
|
{
|
||||||
LOGGER.warn("unable to create data folder.");
|
throw new IOException("unable to create save folder at ["+saveFolder.getPath()+"]. If you're on Windows you may need to enable long file paths.");
|
||||||
}
|
}
|
||||||
this.serverLevelWrapper = serverLevelWrapper;
|
this.serverLevelWrapper = serverLevelWrapper;
|
||||||
this.serverside = new ServerLevelModule(this, saveStructure);
|
this.serverside = new ServerLevelModule(this, saveStructure);
|
||||||
@@ -98,7 +99,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldDoWorldGen()
|
public boolean shouldDoWorldGen()
|
||||||
{ return Config.Common.WorldGenerator.enableDistantGeneration.get() && !this.worldGenPlayerCenteringQueue.isEmpty(); }
|
{ return Config.Common.WorldGenerator.enableDistantGeneration.get(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DhBlockPos2D getTargetPosForGeneration()
|
public DhBlockPos2D getTargetPosForGeneration()
|
||||||
@@ -114,7 +115,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
this.worldGenPlayerCenteringQueue.add(firstPlayer);
|
this.worldGenPlayerCenteringQueue.add(firstPlayer);
|
||||||
this.worldGenPlayerCenteringQueue.remove(firstPlayer);
|
this.worldGenPlayerCenteringQueue.remove(firstPlayer);
|
||||||
|
|
||||||
Vec3d position = firstPlayer.getPosition();
|
DhVec3d position = firstPlayer.getPosition();
|
||||||
return new DhBlockPos2D((int) position.x, (int) position.z);
|
return new DhBlockPos2D((int) position.x, (int) position.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition();
|
DhVec3d playerPosition = serverPlayerState.getServerPlayer().getPosition();
|
||||||
int distanceFromPlayer = DhSectionPos.getChebyshevSignedBlockDistance(message.sectionPos, new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16;
|
int distanceFromPlayer = DhSectionPos.getChebyshevSignedBlockDistance(message.sectionPos, new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16;
|
||||||
|
|
||||||
ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this);
|
ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this);
|
||||||
@@ -200,26 +201,6 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
|
|
||||||
LodUtil.assertTrue(message.getSession().serverPlayer != null);
|
LodUtil.assertTrue(message.getSession().serverPlayer != null);
|
||||||
|
|
||||||
// Check if the player is in this dimension,
|
|
||||||
// since handling multiple dimensions isn't allowed
|
|
||||||
if (message.getSession().serverPlayer.getLevel() != this.getLevelWrapper())
|
|
||||||
{
|
|
||||||
// If the message can be replied to - reply with an error, otherwise just ignore
|
|
||||||
if (message instanceof AbstractTrackableMessage)
|
|
||||||
{
|
|
||||||
((AbstractTrackableMessage) message).sendResponse(
|
|
||||||
new RequestRejectedException(
|
|
||||||
"Generation not allowed. " +
|
|
||||||
"Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " +
|
|
||||||
"player dimension: [" + message.getSession().serverPlayer.getLevel().getDhIdentifier() + "], " +
|
|
||||||
"handler dimension: [" + this.getLevelWrapper().getDhIdentifier() + "]"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +251,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition();
|
DhVec3d playerPosition = serverPlayerState.getServerPlayer().getPosition();
|
||||||
int distanceFromPlayer = DhSectionPos.getChebyshevSignedBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16;
|
int distanceFromPlayer = DhSectionPos.getChebyshevSignedBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16;
|
||||||
if (distanceFromPlayer <= serverPlayerState.sessionConfig.getMaxUpdateDistanceRadius())
|
if (distanceFromPlayer <= serverPlayerState.sessionConfig.getMaxUpdateDistanceRadius())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
|||||||
import com.seibel.distanthorizons.core.render.QuadTree.LodQuadTree;
|
import com.seibel.distanthorizons.core.render.QuadTree.LodQuadTree;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
@@ -44,6 +46,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
{
|
{
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||||
|
|
||||||
private final IDhClientLevel clientLevel;
|
private final IDhClientLevel clientLevel;
|
||||||
@@ -106,7 +109,21 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
this.ClientRenderStateRef.set(clientRenderState);
|
this.ClientRenderStateRef.set(clientRenderState);
|
||||||
}
|
}
|
||||||
|
|
||||||
clientRenderState.quadtree.tryTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
|
|
||||||
|
DhBlockPos2D quadTreeTickBlockPos;
|
||||||
|
if (Config.Client.Advanced.Graphics.Quality.useCameraPositionForQualityDropOff.get())
|
||||||
|
{
|
||||||
|
// use camera position allow free cam mods work better
|
||||||
|
DhVec3d cameraDoublePos = MC_RENDER.getCameraExactPosition();
|
||||||
|
quadTreeTickBlockPos = new DhBlockPos2D((int)cameraDoublePos.x, (int)cameraDoublePos.z);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// player position allows multi-cam mods to work better
|
||||||
|
quadTreeTickBlockPos = new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
clientRenderState.quadtree.tryTick(quadTreeTickBlockPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -120,7 +137,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
ClientRenderState clientRenderState = new ClientRenderState(this.clientLevel, this.clientLevel.getFullDataProvider());
|
ClientRenderState clientRenderState = new ClientRenderState(this.clientLevel, this.clientLevel.getFullDataProvider());
|
||||||
if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
|
if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
|
||||||
{
|
{
|
||||||
LOGGER.warn("Renderer already started for ["+this+"].");
|
LOGGER.warn("Renderer already started for ["+this.clientLevel.getClientLevelWrapper()+"].");
|
||||||
clientRenderState.close();
|
clientRenderState.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,6 +187,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.fullDataSourceProvider.removeDataSourceUpdateListener(this);
|
this.fullDataSourceProvider.removeDataSourceUpdateListener(this);
|
||||||
|
this.genericRenderer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -259,7 +277,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName());
|
//LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName());
|
||||||
this.quadtree.close();
|
this.quadtree.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,16 @@ package com.seibel.distanthorizons.core.level;
|
|||||||
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
|
import com.seibel.distanthorizons.core.config.eventHandlers.IgnoredDimensionCsvHandler;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
||||||
import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataSourceProvider;
|
import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
||||||
import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue;
|
import com.seibel.distanthorizons.core.generation.queues.RemoteWorldRetrievalQueue;
|
||||||
|
import com.seibel.distanthorizons.core.generation.queues.AbstractLodRequestState;
|
||||||
|
import com.seibel.distanthorizons.core.generation.queues.LodRequestModule;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
|
import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
|
||||||
@@ -112,16 +115,19 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
File saveFolder = saveStructure.getSaveFolder(clientLevelWrapper);
|
File saveFolder = saveStructure.getSaveFolder(clientLevelWrapper);
|
||||||
File pre23Folder = saveStructure.getPre23SaveFolder(clientLevelWrapper);
|
File pre23Folder = saveStructure.getPre23SaveFolder(clientLevelWrapper);
|
||||||
|
|
||||||
|
saveFolder.mkdirs();
|
||||||
|
|
||||||
if (pre23Folder.exists())
|
if (pre23Folder.exists())
|
||||||
{
|
{
|
||||||
if (!pre23Folder.renameTo(saveFolder))
|
if (!pre23Folder.renameTo(saveFolder))
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Could not move old save data folder: " + pre23Folder.getAbsolutePath() + " to " + saveFolder.getAbsolutePath());
|
throw new RuntimeException("Could not move old save data folder: [" + pre23Folder.getAbsolutePath() + "] to [" + saveFolder.getAbsolutePath() + "].");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (saveStructure.getSaveFolder(clientLevelWrapper).mkdirs())
|
|
||||||
|
if (!saveFolder.exists())
|
||||||
{
|
{
|
||||||
LOGGER.warn("unable to create data folder.");
|
throw new IOException("unable to create save folder at ["+saveFolder.getPath()+"]. If you're on Windows you may need to enable long file paths.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.levelWrapper = clientLevelWrapper;
|
this.levelWrapper = clientLevelWrapper;
|
||||||
@@ -165,15 +171,17 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check this before decoding data to prevent errors if multiple client levels
|
||||||
|
// are receiving data at once (Immersive Portals compatibility).
|
||||||
|
boolean isSameLevel = message.isSameLevelAs(this.levelWrapper);
|
||||||
|
//NETWORK_LOGGER.debug("Buffer ["+message.payload.dtoBufferId+"] isSameLevel: ["+isSameLevel+"]");
|
||||||
|
if (!isSameLevel)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try (FullDataSourceV2DTO dataSourceDto = this.networkState.fullDataPayloadReceiver.decodeDataSource(message.payload))
|
try (FullDataSourceV2DTO dataSourceDto = this.networkState.fullDataPayloadReceiver.decodeDataSource(message.payload))
|
||||||
{
|
{
|
||||||
boolean isSameLevel = message.isSameLevelAs(this.levelWrapper);
|
|
||||||
NETWORK_LOGGER.debug("Buffer ["+message.payload.dtoBufferId+"] isSameLevel: ["+isSameLevel+"]");
|
|
||||||
if (!isSameLevel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Executor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
Executor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||||
if (executor != null)
|
if (executor != null)
|
||||||
@@ -182,7 +190,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// TODO this has a lock which can cause stuttering/lag issues
|
|
||||||
this.updateBeaconBeamsForSectionPos(dataSourceDto.pos, message.payload.beaconBeams);
|
this.updateBeaconBeamsForSectionPos(dataSourceDto.pos, message.payload.beaconBeams);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -218,6 +225,15 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// only tick the level the player is currently in
|
||||||
|
// (done to prevent ticking LodQuadTree's for levels that aren't rendering)
|
||||||
|
IClientLevelWrapper clientLevelWrapper = MC_CLIENT.getWrappedClientLevel();
|
||||||
|
if (clientLevelWrapper == null
|
||||||
|
|| clientLevelWrapper.getDhLevel() != this)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.clientside.clientTick();
|
this.clientside.clientTick();
|
||||||
|
|
||||||
if (this.syncOnLoadRequestQueue != null)
|
if (this.syncOnLoadRequestQueue != null)
|
||||||
@@ -332,12 +348,15 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
String o = MinecraftTextFormat.ORANGE;
|
String o = MinecraftTextFormat.ORANGE;
|
||||||
String y = MinecraftTextFormat.YELLOW;
|
String y = MinecraftTextFormat.YELLOW;
|
||||||
String g = MinecraftTextFormat.GREEN;
|
String g = MinecraftTextFormat.GREEN;
|
||||||
|
String r = MinecraftTextFormat.RED;
|
||||||
String cf = MinecraftTextFormat.CLEAR_FORMATTING;
|
String cf = MinecraftTextFormat.CLEAR_FORMATTING;
|
||||||
|
|
||||||
|
|
||||||
String dimName = this.levelWrapper.getDhIdentifier();
|
String dimName = this.levelWrapper.getDhIdentifier();
|
||||||
boolean rendering = this.clientside.isRendering();
|
boolean rendering =
|
||||||
String renderingString = rendering ? (g+"yes"+cf) : (o+"no"+cf);
|
this.clientside.isRendering()
|
||||||
|
&& !IgnoredDimensionCsvHandler.INSTANCE.dimensionNameShouldBeIgnored(dimName);
|
||||||
|
String renderingString = rendering ? (g+"yes"+cf) : (r+"no"+cf);
|
||||||
messageList.add("["+y+dimName+cf+"] rendering: "+renderingString);
|
messageList.add("["+y+dimName+cf+"] rendering: "+renderingString);
|
||||||
|
|
||||||
|
|
||||||
@@ -397,12 +416,10 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
//================//
|
//================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private static class LodRequestState extends LodRequestModule.AbstractLodRequestState
|
private static class LodRequestState extends AbstractLodRequestState
|
||||||
{
|
{
|
||||||
LodRequestState(DhClientLevel clientLevel, ClientNetworkState networkState)
|
LodRequestState(DhClientLevel clientLevel, ClientNetworkState networkState)
|
||||||
{
|
{ super(clientLevel, new RemoteWorldRetrievalQueue(networkState, clientLevel)); }
|
||||||
this.retrievalQueue = new RemoteWorldRetrievalQueue(networkState, clientLevel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.level;
|
package com.seibel.distanthorizons.core.level;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.core.config.eventHandlers.IgnoredDimensionCsvHandler;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
||||||
@@ -71,7 +72,19 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
|
|||||||
//region
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clientTick() { this.clientside.clientTick(); }
|
public void clientTick()
|
||||||
|
{
|
||||||
|
// only tick the level the player is currently in
|
||||||
|
// (done to prevent ticking LodQuadTree's for levels that aren't rendering)
|
||||||
|
IClientLevelWrapper clientLevelWrapper = MC_CLIENT.getWrappedClientLevel();
|
||||||
|
if (clientLevelWrapper == null
|
||||||
|
|| clientLevelWrapper.getDhLevel() != this)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientside.clientTick();
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -121,12 +134,15 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
|
|||||||
String o = MinecraftTextFormat.ORANGE;
|
String o = MinecraftTextFormat.ORANGE;
|
||||||
String y = MinecraftTextFormat.YELLOW;
|
String y = MinecraftTextFormat.YELLOW;
|
||||||
String g = MinecraftTextFormat.GREEN;
|
String g = MinecraftTextFormat.GREEN;
|
||||||
|
String r = MinecraftTextFormat.RED;
|
||||||
String cf = MinecraftTextFormat.CLEAR_FORMATTING;
|
String cf = MinecraftTextFormat.CLEAR_FORMATTING;
|
||||||
|
|
||||||
|
|
||||||
String dimName = this.serverLevelWrapper.getDhIdentifier();
|
String dimName = this.serverLevelWrapper.getDhIdentifier();
|
||||||
boolean rendering = this.clientside.isRendering();
|
boolean rendering =
|
||||||
String renderingString = rendering ? (g+"yes"+cf) : (o+"no"+cf);
|
this.clientside.isRendering()
|
||||||
|
&& !IgnoredDimensionCsvHandler.INSTANCE.dimensionNameShouldBeIgnored(dimName);
|
||||||
|
String renderingString = rendering ? (g+"yes"+cf) : (r+"no"+cf);
|
||||||
messageList.add("["+y+dimName+cf+"] rendering: "+renderingString);
|
messageList.add("["+y+dimName+cf+"] rendering: "+renderingString);
|
||||||
|
|
||||||
super.addDebugMenuStringsToList(messageList);
|
super.addDebugMenuStringsToList(messageList);
|
||||||
|
|||||||
+3
-3
@@ -28,9 +28,9 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab
|
|||||||
*/
|
*/
|
||||||
public interface IKeyedClientLevelManager extends IBindable
|
public interface IKeyedClientLevelManager extends IBindable
|
||||||
{
|
{
|
||||||
IServerKeyedClientLevel getServerKeyedLevel();
|
IServerKeyedClientLevel getServerKeyedLevel(IClientLevelWrapper levelWrapper);
|
||||||
/** Called when a client level is wrapped by a ServerEnhancedClientLevel, for integration into mod internals. */
|
/** Called when a client level is wrapped by a ServerKeyedClientLevel, for integration into mod internals. */
|
||||||
IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String serverKey, String levelKey);
|
IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String dimensionResource, String serverKey, String levelKey);
|
||||||
|
|
||||||
void clearKeyedLevel();
|
void clearKeyedLevel();
|
||||||
boolean isEnabled();
|
boolean isEnabled();
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiW
|
|||||||
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
|
||||||
import com.seibel.distanthorizons.core.generation.BatchGenerator;
|
import com.seibel.distanthorizons.core.generation.BatchGenerator;
|
||||||
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
|
import com.seibel.distanthorizons.core.generation.queues.IFullDataSourceRetrievalQueue;
|
||||||
|
import com.seibel.distanthorizons.core.generation.queues.WorldGenerationQueue;
|
||||||
|
import com.seibel.distanthorizons.core.generation.queues.AbstractLodRequestState;
|
||||||
|
import com.seibel.distanthorizons.core.generation.queues.LodRequestModule;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.WorldGeneratorInjector;
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.WorldGeneratorInjector;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
@@ -75,9 +78,11 @@ public class ServerLevelModule implements AutoCloseable
|
|||||||
// helper classes //
|
// helper classes //
|
||||||
//================//
|
//================//
|
||||||
|
|
||||||
public static class LodRequestState extends LodRequestModule.AbstractLodRequestState
|
public static class LodRequestState extends AbstractLodRequestState
|
||||||
{
|
{
|
||||||
LodRequestState(IDhServerLevel level)
|
LodRequestState(IDhServerLevel level)
|
||||||
|
{ super(level, createRetrievalQueue(level)); }
|
||||||
|
private static IFullDataSourceRetrievalQueue createRetrievalQueue(IDhServerLevel level)
|
||||||
{
|
{
|
||||||
IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper());
|
IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper());
|
||||||
if (worldGenerator == null)
|
if (worldGenerator == null)
|
||||||
@@ -88,9 +93,11 @@ public class ServerLevelModule implements AutoCloseable
|
|||||||
// since core world generator's should have the lowest override priority
|
// since core world generator's should have the lowest override priority
|
||||||
WorldGeneratorInjector.INSTANCE.bind(level.getLevelWrapper(), worldGenerator);
|
WorldGeneratorInjector.INSTANCE.bind(level.getLevelWrapper(), worldGenerator);
|
||||||
}
|
}
|
||||||
this.retrievalQueue = new WorldGenerationQueue(worldGenerator, level);
|
return new WorldGenerationQueue(worldGenerator, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.seibel.distanthorizons.core.jar.ModJarInfo;
|
|||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListPool;
|
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListPool;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
@@ -127,7 +128,7 @@ public class F3Screen
|
|||||||
messageList.add("LOD Pos: "+y+detailLevel+"*"+posX+","+posZ+cf);
|
messageList.add("LOD Pos: "+y+detailLevel+"*"+posX+","+posZ+cf);
|
||||||
|
|
||||||
AbstractDhRenderApiDefinition renderApiDef = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
|
AbstractDhRenderApiDefinition renderApiDef = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
|
||||||
messageList.add("Rendering API: "+a+renderApiDef.getApiName()+cf);
|
messageList.add("Rendering API: "+a+renderApiDef.getEngineName()+cf);
|
||||||
}
|
}
|
||||||
messageList.add("");
|
messageList.add("");
|
||||||
}
|
}
|
||||||
@@ -149,6 +150,13 @@ public class F3Screen
|
|||||||
messageList.add("");
|
messageList.add("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render thread tasks
|
||||||
|
if (Config.Client.Advanced.Debugging.F3Screen.showRenderThreadTasks.get())
|
||||||
|
{
|
||||||
|
RenderThreadTaskHandler.INSTANCE.addDebugMenuStringsToList(messageList);
|
||||||
|
messageList.add("");
|
||||||
|
}
|
||||||
|
|
||||||
// combined object pools
|
// combined object pools
|
||||||
if (Config.Client.Advanced.Debugging.F3Screen.showCombinedObjectPools.get())
|
if (Config.Client.Advanced.Debugging.F3Screen.showCombinedObjectPools.get())
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-2
@@ -151,8 +151,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
|
|||||||
|
|
||||||
public synchronized boolean tick(DhBlockPos2D targetPos)
|
public synchronized boolean tick(DhBlockPos2D targetPos)
|
||||||
{
|
{
|
||||||
if (DhApiWorldProxy.INSTANCE.worldLoaded()
|
if (DhApiWorldProxy.INSTANCE.tryGetReadOnly())
|
||||||
&& DhApiWorldProxy.INSTANCE.getReadOnly())
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -13,6 +13,7 @@ import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource;
|
|||||||
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
||||||
|
import com.seibel.distanthorizons.core.network.messages.base.RequestLevelInitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
||||||
@@ -164,7 +165,8 @@ public class ClientNetworkState implements Closeable
|
|||||||
// send message //
|
// send message //
|
||||||
//==============//
|
//==============//
|
||||||
|
|
||||||
|
public void sendLevelInitRequest(String clientLevelKey)
|
||||||
|
{ this.getSession().sendMessage(new RequestLevelInitMessage(clientLevelKey)); }
|
||||||
|
|
||||||
public void sendConfigMessage() { this.sendConfigMessage(true); }
|
public void sendConfigMessage() { this.sendConfigMessage(true); }
|
||||||
public void sendConfigMessage(boolean blocking)
|
public void sendConfigMessage(boolean blocking)
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
package com.seibel.distanthorizons.core.multiplayer.client;
|
package com.seibel.distanthorizons.core.multiplayer.client;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue;
|
import com.seibel.distanthorizons.core.generation.queues.RemoteWorldRetrievalQueue;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
||||||
import com.seibel.distanthorizons.core.level.DhClientLevel;
|
import com.seibel.distanthorizons.core.level.DhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
|
|||||||
+3
-3
@@ -7,10 +7,10 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|||||||
import com.seibel.distanthorizons.core.network.INetworkObject;
|
import com.seibel.distanthorizons.core.network.INetworkObject;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
||||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import io.netty.buffer.CompositeByteBuf;
|
import io.netty.buffer.CompositeByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ public class FullDataPayloadReceiver implements AutoCloseable
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
private final ConcurrentMap<Integer, CompositeByteBuf> buffersById = CacheBuilder.newBuilder()
|
private final ConcurrentMap<Integer, CompositeByteBuf> buffersById = CacheBuilder.newBuilder()
|
||||||
.expireAfterAccess(10, TimeUnit.SECONDS)
|
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||||
.<Integer, CompositeByteBuf>build().asMap();
|
.<Integer, CompositeByteBuf>build().asMap();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,7 +56,7 @@ public class FullDataPayloadReceiver implements AutoCloseable
|
|||||||
public FullDataSourceV2DTO decodeDataSource(FullDataPayload payload)
|
public FullDataSourceV2DTO decodeDataSource(FullDataPayload payload)
|
||||||
{
|
{
|
||||||
CompositeByteBuf compositeByteBuffer = this.buffersById.get(payload.dtoBufferId);
|
CompositeByteBuf compositeByteBuffer = this.buffersById.get(payload.dtoBufferId);
|
||||||
LodUtil.assertTrue(compositeByteBuffer != null, "decoded data source missing byte buffer");
|
Objects.requireNonNull(compositeByteBuffer, "Unable to get a complete buffer for a received payload. Ignore this if it doesn't spam similar errors");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
+24
-23
@@ -1,7 +1,7 @@
|
|||||||
package com.seibel.distanthorizons.core.multiplayer.server;
|
package com.seibel.distanthorizons.core.multiplayer.server;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
|
import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.fullData.FullDataPayloadSender;
|
import com.seibel.distanthorizons.core.multiplayer.fullData.FullDataPayloadSender;
|
||||||
@@ -9,13 +9,16 @@ import com.seibel.distanthorizons.core.multiplayer.fullData.SharedBandwidthLimit
|
|||||||
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
||||||
|
import com.seibel.distanthorizons.core.network.messages.base.RequestLevelInitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
||||||
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
|
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
||||||
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
||||||
import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter;
|
import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
@@ -23,16 +26,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
|
|
||||||
public class ServerPlayerState implements Closeable
|
public class ServerPlayerState implements Closeable
|
||||||
{
|
{
|
||||||
private final ConfigChangeListener<String> levelKeyPrefixChangeListener
|
private final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
|
||||||
= new ConfigChangeListener<>(Config.Server.levelKeyPrefix, this::onLevelKeyPrefixConfigChanged);
|
|
||||||
private final SessionConfig.AnyChangeListener configAnyChangeListener = new SessionConfig.AnyChangeListener(this::sendConfigMessage);
|
|
||||||
|
|
||||||
|
private final SessionConfig.AnyChangeListener configAnyChangeListener = new SessionConfig.AnyChangeListener(this::sendConfigMessage);
|
||||||
|
|
||||||
private final String serverKeyWithoutId = Config.Server.serverKey.get();
|
private final String serverKeyWithoutId = Config.Server.serverKey.get();
|
||||||
private final String serverKey = (this.serverKeyWithoutId.isEmpty() ? "" : Config.Server.serverId.get() + "_" + this.serverKeyWithoutId.trim())
|
private final String serverKey = (this.serverKeyWithoutId.isEmpty() ? "" : Config.Server.serverId.get() + "_" + this.serverKeyWithoutId.trim())
|
||||||
.replaceAll("[^" + LevelInitMessage.ALLOWED_CHARS_REGEX + " ]", "")
|
.replaceAll("[^" + LevelInitMessage.ALLOWED_CHARS_REGEX + " ]", "")
|
||||||
.replaceAll(" ", "_");
|
.replaceAll(" ", "_");
|
||||||
private String lastLevelKey = "";
|
|
||||||
|
|
||||||
|
|
||||||
public final NetworkSession networkSession;
|
public final NetworkSession networkSession;
|
||||||
@@ -61,11 +62,27 @@ public class ServerPlayerState implements Closeable
|
|||||||
this.networkSession.registerHandler(SessionConfigMessage.class, (sessionConfigMessage) ->
|
this.networkSession.registerHandler(SessionConfigMessage.class, (sessionConfigMessage) ->
|
||||||
{
|
{
|
||||||
this.sessionConfig.constrainingConfig = sessionConfigMessage.config;
|
this.sessionConfig.constrainingConfig = sessionConfigMessage.config;
|
||||||
|
|
||||||
this.sendLevelKey();
|
|
||||||
this.sendConfigMessage();
|
this.sendConfigMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.networkSession.registerHandler(RequestLevelInitMessage.class, msg ->
|
||||||
|
{
|
||||||
|
if (!Config.Server.sendLevelKeys.get())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IServerLevelWrapper serverLevelWrapper = MC_SHARED.getLevelWrapper(msg.dimensionResourceLocation);
|
||||||
|
if (serverLevelWrapper == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String levelKey = serverLevelWrapper.getKeyedLevelDimensionName();
|
||||||
|
this.networkSession.sendMessage(new LevelInitMessage(msg.dimensionResourceLocation, this.serverKey, levelKey));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.networkSession.registerHandler(CloseInternalEvent.class, event -> {
|
this.networkSession.registerHandler(CloseInternalEvent.class, event -> {
|
||||||
// No-op. prevents "Unhandled message" log entries
|
// No-op. prevents "Unhandled message" log entries
|
||||||
});
|
});
|
||||||
@@ -84,21 +101,6 @@ public class ServerPlayerState implements Closeable
|
|||||||
// client updating //
|
// client updating //
|
||||||
//=================//
|
//=================//
|
||||||
|
|
||||||
private void onLevelKeyPrefixConfigChanged(String newLevelKey) { this.sendLevelKey(); }
|
|
||||||
private void sendLevelKey()
|
|
||||||
{
|
|
||||||
if (Config.Server.sendLevelKeys.get())
|
|
||||||
{
|
|
||||||
// let the client's know about the change
|
|
||||||
String levelKey = this.getServerPlayer().getLevel().getKeyedLevelDimensionName();
|
|
||||||
if (!levelKey.equals(this.lastLevelKey))
|
|
||||||
{
|
|
||||||
this.lastLevelKey = levelKey;
|
|
||||||
this.networkSession.sendMessage(new LevelInitMessage(this.serverKey, levelKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendConfigMessage()
|
private void sendConfigMessage()
|
||||||
{
|
{
|
||||||
double coordinateScale = this.getServerPlayer().getLevel().getDimensionType().getCoordinateScale();
|
double coordinateScale = this.getServerPlayer().getLevel().getDimensionType().getCoordinateScale();
|
||||||
@@ -118,7 +120,6 @@ public class ServerPlayerState implements Closeable
|
|||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
this.fullDataPayloadSender.close();
|
this.fullDataPayloadSender.close();
|
||||||
this.levelKeyPrefixChangeListener.close();
|
|
||||||
this.configAnyChangeListener.close();
|
this.configAnyChangeListener.close();
|
||||||
this.networkSession.close();
|
this.networkSession.close();
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -21,12 +21,9 @@ package com.seibel.distanthorizons.core.network.messages;
|
|||||||
|
|
||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.*;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage;
|
import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage;
|
import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
||||||
@@ -60,6 +57,7 @@ public class MessageRegistry
|
|||||||
|
|
||||||
// Level keys
|
// Level keys
|
||||||
this.registerMessage(LevelInitMessage.class, LevelInitMessage::new);
|
this.registerMessage(LevelInitMessage.class, LevelInitMessage::new);
|
||||||
|
this.registerMessage(RequestLevelInitMessage.class, RequestLevelInitMessage::new);
|
||||||
|
|
||||||
// Config (for full DH support)
|
// Config (for full DH support)
|
||||||
this.registerMessage(SessionConfigMessage.class, SessionConfigMessage::new);
|
this.registerMessage(SessionConfigMessage.class, SessionConfigMessage::new);
|
||||||
|
|||||||
+6
-1
@@ -21,6 +21,7 @@ public class LevelInitMessage extends AbstractNetworkMessage
|
|||||||
MAX_LENGTH, ALLOWED_CHARS_REGEX, ALLOWED_CHARS_REGEX, ALLOWED_CHARS_REGEX);
|
MAX_LENGTH, ALLOWED_CHARS_REGEX, ALLOWED_CHARS_REGEX, ALLOWED_CHARS_REGEX);
|
||||||
|
|
||||||
|
|
||||||
|
public String dimensionResourceLocation;
|
||||||
public String serverKey;
|
public String serverKey;
|
||||||
public String levelKey;
|
public String levelKey;
|
||||||
public long serverTime;
|
public long serverTime;
|
||||||
@@ -32,8 +33,9 @@ public class LevelInitMessage extends AbstractNetworkMessage
|
|||||||
//==============//
|
//==============//
|
||||||
|
|
||||||
public LevelInitMessage() { }
|
public LevelInitMessage() { }
|
||||||
public LevelInitMessage(String serverKey, String levelKey)
|
public LevelInitMessage(String dimensionResourceLocation, String serverKey, String levelKey)
|
||||||
{
|
{
|
||||||
|
this.dimensionResourceLocation = dimensionResourceLocation;
|
||||||
this.serverKey = serverKey;
|
this.serverKey = serverKey;
|
||||||
this.levelKey = levelKey;
|
this.levelKey = levelKey;
|
||||||
this.serverTime = System.currentTimeMillis();
|
this.serverTime = System.currentTimeMillis();
|
||||||
@@ -48,6 +50,7 @@ public class LevelInitMessage extends AbstractNetworkMessage
|
|||||||
@Override
|
@Override
|
||||||
public void encode(ByteBuf out)
|
public void encode(ByteBuf out)
|
||||||
{
|
{
|
||||||
|
this.writeString(this.dimensionResourceLocation, out);
|
||||||
this.writeString(this.serverKey, out);
|
this.writeString(this.serverKey, out);
|
||||||
this.writeString(this.levelKey, out);
|
this.writeString(this.levelKey, out);
|
||||||
out.writeLong(this.serverTime);
|
out.writeLong(this.serverTime);
|
||||||
@@ -56,6 +59,7 @@ public class LevelInitMessage extends AbstractNetworkMessage
|
|||||||
@Override
|
@Override
|
||||||
public void decode(ByteBuf in)
|
public void decode(ByteBuf in)
|
||||||
{
|
{
|
||||||
|
this.dimensionResourceLocation = this.readString(in);
|
||||||
this.serverKey = this.readString(in);
|
this.serverKey = this.readString(in);
|
||||||
this.levelKey = this.readString(in);
|
this.levelKey = this.readString(in);
|
||||||
this.serverTime = in.readLong();
|
this.serverTime = in.readLong();
|
||||||
@@ -71,6 +75,7 @@ public class LevelInitMessage extends AbstractNetworkMessage
|
|||||||
public MoreObjects.ToStringHelper toStringHelper()
|
public MoreObjects.ToStringHelper toStringHelper()
|
||||||
{
|
{
|
||||||
return super.toStringHelper()
|
return super.toStringHelper()
|
||||||
|
.add("dimensionResourceLocation", this.dimensionResourceLocation)
|
||||||
.add("serverKey", this.serverKey)
|
.add("serverKey", this.serverKey)
|
||||||
.add("levelKey", this.levelKey)
|
.add("levelKey", this.levelKey)
|
||||||
.add("serverTime", this.serverTime);
|
.add("serverTime", this.serverTime);
|
||||||
|
|||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.core.network.messages.base;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
/** used for full DH support */
|
||||||
|
public class RequestLevelInitMessage extends AbstractNetworkMessage
|
||||||
|
{
|
||||||
|
public String dimensionResourceLocation;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
public RequestLevelInitMessage() { }
|
||||||
|
public RequestLevelInitMessage(String dimensionResourceLocation) { this.dimensionResourceLocation = dimensionResourceLocation; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===============//
|
||||||
|
// serialization //
|
||||||
|
//===============//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf out) { this.writeString(this.dimensionResourceLocation, out); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteBuf in) { this.dimensionResourceLocation = this.readString(in); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MoreObjects.ToStringHelper toStringHelper()
|
||||||
|
{
|
||||||
|
return super.toStringHelper()
|
||||||
|
.add("dimensionResourceLocation", this.dimensionResourceLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ package com.seibel.distanthorizons.core.pos;
|
|||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* immutable <br><br>
|
* immutable <br><br>
|
||||||
@@ -65,7 +65,7 @@ public class DhChunkPos
|
|||||||
// >> 4 is the Same as div 16
|
// >> 4 is the Same as div 16
|
||||||
this(blockPos.x >> 4, blockPos.z >> 4);
|
this(blockPos.x >> 4, blockPos.z >> 4);
|
||||||
}
|
}
|
||||||
public DhChunkPos(Vec3d pos)
|
public DhChunkPos(DhVec3d pos)
|
||||||
{
|
{
|
||||||
// >> 4 is the Same as div 16
|
// >> 4 is the Same as div 16
|
||||||
this(((int)pos.x) >> 4, ((int)pos.z) >> 4);
|
this(((int)pos.x) >> 4, ((int)pos.z) >> 4);
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.render;
|
package com.seibel.distanthorizons.core.render;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderingApi;
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderingEngine;
|
||||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
|
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
|
||||||
import com.seibel.distanthorizons.api.objects.DhApiResult;
|
import com.seibel.distanthorizons.api.objects.DhApiResult;
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||||
@@ -28,6 +30,8 @@ import com.seibel.distanthorizons.core.level.IDhLevel;
|
|||||||
import com.seibel.distanthorizons.core.util.RenderUtil;
|
import com.seibel.distanthorizons.core.util.RenderUtil;
|
||||||
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to interact with Distant Horizons' rendering systems.
|
* Used to interact with Distant Horizons' rendering systems.
|
||||||
@@ -43,6 +47,18 @@ public class DhApiRenderProxy implements IDhApiRenderProxy
|
|||||||
|
|
||||||
private boolean deferTransparentRendering = false;
|
private boolean deferTransparentRendering = false;
|
||||||
|
|
||||||
|
private static AbstractDhRenderApiDefinition renderApiDef = null;
|
||||||
|
@Nullable
|
||||||
|
private static AbstractDhRenderApiDefinition tryGetApiDef()
|
||||||
|
{
|
||||||
|
if (renderApiDef == null)
|
||||||
|
{
|
||||||
|
renderApiDef = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderApiDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
@@ -57,6 +73,7 @@ public class DhApiRenderProxy implements IDhApiRenderProxy
|
|||||||
// methods //
|
// methods //
|
||||||
//=========//
|
//=========//
|
||||||
|
|
||||||
|
@Override
|
||||||
public DhApiResult<Boolean> clearRenderDataCache()
|
public DhApiResult<Boolean> clearRenderDataCache()
|
||||||
{
|
{
|
||||||
// make sure this is a valid time to run the method
|
// make sure this is a valid time to run the method
|
||||||
@@ -80,6 +97,32 @@ public class DhApiRenderProxy implements IDhApiRenderProxy
|
|||||||
return DhApiResult.createSuccess();
|
return DhApiResult.createSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EDhApiRenderingApi getRenderingApi() throws IllegalStateException
|
||||||
|
{
|
||||||
|
AbstractDhRenderApiDefinition apiDef = tryGetApiDef();
|
||||||
|
if (apiDef == null)
|
||||||
|
{
|
||||||
|
// The rendering API hasn't been set up yet
|
||||||
|
throw new IllegalStateException("Distant Horizons hasn't finished setup yet. No renderer has been set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiDef.getRenderApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNativeRenderer() throws IllegalStateException
|
||||||
|
{
|
||||||
|
AbstractDhRenderApiDefinition apiDef = tryGetApiDef();
|
||||||
|
if (apiDef == null)
|
||||||
|
{
|
||||||
|
// The rendering API hasn't been set up yet
|
||||||
|
throw new IllegalStateException("Distant Horizons hasn't finished setup yet. No renderer has been set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiDef.isNativeRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static int activeOpenGlDhDepthTextureId = -1;
|
public static int activeOpenGlDhDepthTextureId = -1;
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.seibel.distanthorizons.core.render;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FORWARD_Z, <br>
|
||||||
|
* REVERSE_Z, <br>
|
||||||
|
*/
|
||||||
|
public enum EDhRenderDepth
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* AKA Zero to One <br>
|
||||||
|
* MC 26.1.2 and older (OpenGL) = false (near = 0.0f, far = 1.0f)
|
||||||
|
*/
|
||||||
|
FORWARD_Z(0.0f, 1.0f),
|
||||||
|
/**
|
||||||
|
* AKA One to Zero <br>
|
||||||
|
* MC 26.2.0 and newer (Vulkan/GL) = true (near = 1.0f, far = 0.0f)
|
||||||
|
*/
|
||||||
|
REVERSE_Z(1.0f, 0.0f);
|
||||||
|
|
||||||
|
|
||||||
|
public final float nearDepth;
|
||||||
|
public final float farDepth;
|
||||||
|
|
||||||
|
EDhRenderDepth(float nearDepth, float farDepth)
|
||||||
|
{
|
||||||
|
this.nearDepth = nearDepth;
|
||||||
|
this.farDepth = farDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+337
-110
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.render.QuadTree;
|
|||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||||
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||||
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
|
||||||
@@ -34,9 +35,12 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.BeaconRenderHandler;
|
import com.seibel.distanthorizons.core.render.renderer.BeaconRenderHandler;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||||
|
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
||||||
|
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||||
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
||||||
@@ -54,10 +58,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import javax.annotation.WillNotClose;
|
import javax.annotation.WillNotClose;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
private static final AbstractDebugWireframeRenderer DEBUG_RENDERER = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
|
private static final AbstractDebugWireframeRenderer DEBUG_RENDERER = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
|
||||||
|
|
||||||
/** there should only ever be one {@link LodQuadTree} so having the thread static should be fine */
|
/** there should only ever be one {@link LodQuadTree} so having the thread static should be fine */
|
||||||
private static final ThreadPoolExecutor FULL_DATA_RETRIEVAL_QUEUE_THREAD = ThreadUtil.makeSingleThreadPool("LodQuadTree Data Retrieval Queue");
|
private static final ThreadPoolExecutor FULL_DATA_RETRIEVAL_QUEUE_THREAD = ThreadUtil.makeSingleDaemonThreadPool("LodQuadTree Data Retrieval Queue");
|
||||||
|
|
||||||
|
|
||||||
public final int blockRenderDistanceDiameter;
|
public final int blockRenderDistanceDiameter;
|
||||||
@@ -96,8 +97,19 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
private final AtomicBoolean queueThreadRunningRef = new AtomicBoolean(false);
|
private final AtomicBoolean queueThreadRunningRef = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contains the list of beacons currently being rendered in this section
|
||||||
|
* if this list is modified the {@link LodQuadTree#beaconRenderHandler} should be updated to match.
|
||||||
|
*/
|
||||||
|
private final ArrayList<BeaconRenderHandler.BeaconBeamWithWidth> beaconList = new ArrayList<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
public final BeaconRenderHandler beaconRenderHandler;
|
private final BeaconRenderHandler beaconRenderHandler;
|
||||||
|
@Nullable
|
||||||
|
private final BeaconBeamRepo beaconBeamRepo;
|
||||||
|
/** used to prevent updating the beacons concurrently */
|
||||||
|
@NotNull
|
||||||
|
private CompletableFuture<Void> beaconUpdateFuture = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
|
|
||||||
/** the smallest numerical detail level number that can be rendered */
|
/** the smallest numerical detail level number that can be rendered */
|
||||||
private byte maxLeafRenderDetailLevel;
|
private byte maxLeafRenderDetailLevel;
|
||||||
@@ -137,7 +149,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
int initialPlayerBlockX, int initialPlayerBlockZ,
|
int initialPlayerBlockX, int initialPlayerBlockZ,
|
||||||
FullDataSourceProviderV2 fullDataSourceProvider)
|
FullDataSourceProviderV2 fullDataSourceProvider)
|
||||||
{
|
{
|
||||||
super(viewDiameterInBlocks, new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
super(viewDiameterInBlocks, FullDataSourceV2.WIDTH,
|
||||||
|
new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||||
|
|
||||||
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
||||||
|
|
||||||
@@ -148,6 +161,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
IDhGenericRenderer genericObjectRenderer = this.level.getGenericRenderer();
|
IDhGenericRenderer genericObjectRenderer = this.level.getGenericRenderer();
|
||||||
this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null;
|
this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null;
|
||||||
|
|
||||||
|
this.beaconBeamRepo = this.level.getBeaconBeamRepo();
|
||||||
|
|
||||||
Config.Common.WorldGenerator.enableDistantGeneration.addListener(this);
|
Config.Common.WorldGenerator.enableDistantGeneration.addListener(this);
|
||||||
Config.Server.enableServerGeneration.addListener(this);
|
Config.Server.enableServerGeneration.addListener(this);
|
||||||
|
|
||||||
@@ -170,7 +185,13 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
this.enabledRenderSectionLock.lock();
|
this.enabledRenderSectionLock.lock();
|
||||||
|
|
||||||
tempProcessNodeList.clear();
|
tempProcessNodeList.clear();
|
||||||
tempProcessNodeList.addAll(this.enabledSections);
|
|
||||||
|
// manual add and loop to reduce GC pressure due to addAll() doing unnecessary
|
||||||
|
// array copies
|
||||||
|
for (int i = 0; i < this.enabledSections.size(); i++)
|
||||||
|
{
|
||||||
|
tempProcessNodeList.add(this.enabledSections.get(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -232,17 +253,38 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
//===================//
|
//===================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
this.setCenterBlockPos(playerPos, (renderSection) ->
|
// remove out of bound sections
|
||||||
{
|
this.setCenterBlockPos(playerPos,
|
||||||
// removing out of bounds sections
|
// remove completely out of bound nodes
|
||||||
if (renderSection != null)
|
// (the root node is no longer in bounds)
|
||||||
|
(renderSection) ->
|
||||||
{
|
{
|
||||||
this.fullDataSourceProvider.removeRetrievalRequestIf((long genPos) -> DhSectionPos.contains(renderSection.pos, genPos));
|
if (renderSection != null)
|
||||||
this.missingGenerationPosSet.remove(renderSection.pos);
|
{
|
||||||
this.queuedGenerationPosSet.remove(renderSection.pos);
|
this.fullDataSourceProvider.removeRetrievalRequestIf((long genPos) -> DhSectionPos.contains(renderSection.pos, genPos));
|
||||||
renderSection.close();
|
|
||||||
|
// unfortunately we have to fully go through each set
|
||||||
|
// since a removed position may be larger than the multiple generated positions
|
||||||
|
// it contains
|
||||||
|
this.missingGenerationPosSet.removeIf((Long genPos) -> DhSectionPos.contains(renderSection.pos, genPos));
|
||||||
|
this.queuedGenerationPosSet.removeIf((Long genPos) -> DhSectionPos.contains(renderSection.pos, genPos));
|
||||||
|
|
||||||
|
renderSection.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// mutate partially out of bound nodes
|
||||||
|
// (the root node is still in bounds, but this individual child node isn't)
|
||||||
|
(renderSection) ->
|
||||||
|
{
|
||||||
|
if (renderSection != null)
|
||||||
|
{
|
||||||
|
// when this node comes back into render distance
|
||||||
|
// we'll need to re-load it since the full data
|
||||||
|
// may have been modified while it was out of bounds
|
||||||
|
renderSection.renderDataDirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -291,7 +333,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.value.retreivedMissingSectionsForRetreival = false;
|
node.value.queuedMissingSectionsForRetrieval = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,28 +401,35 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
//=========================//
|
//=========================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
// also handles disabling beacons
|
|
||||||
|
|
||||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getDisableNodes())
|
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getDisableNodes())
|
||||||
{
|
{
|
||||||
if (node == null || node.value == null) { continue; }
|
if (node == null || node.value == null) { continue; }
|
||||||
|
|
||||||
node.value.setRenderingEnabled(false);
|
node.value.setRenderingEnabled(false);
|
||||||
node.value.tryDisableBeacons();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnableDeleteChildrenNodes())
|
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnableDeleteChildrenNodes())
|
||||||
{
|
{
|
||||||
if (node == null || node.value == null) { continue; }
|
if (node == null
|
||||||
|
|| node.value == null
|
||||||
node.deleteAllChildren((childRenderSection) ->
|
// only clear the children if there are children to clear
|
||||||
|
|| node.getDirectChildCount() == 0)
|
||||||
{
|
{
|
||||||
if (childRenderSection != null)
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// run this on the render thread to hopefully prevent
|
||||||
|
// closing render data while rendering is happening
|
||||||
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodQuadTree delayed child cleanup", () ->
|
||||||
|
{
|
||||||
|
node.deleteAllChildren((childRenderSection) ->
|
||||||
{
|
{
|
||||||
childRenderSection.setRenderingEnabled(false);
|
if (childRenderSection != null)
|
||||||
childRenderSection.tryDisableBeacons();
|
{
|
||||||
childRenderSection.close();
|
childRenderSection.setRenderingEnabled(false);
|
||||||
}
|
childRenderSection.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,21 +442,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
//=================//
|
//=================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
// must be handled after beacon disabling
|
this.tryRefreshRenderingBeaconsAsync(playerPos);
|
||||||
// otherwise the beacons will be missing
|
|
||||||
|
|
||||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnabledNodes())
|
|
||||||
{
|
|
||||||
if (node == null || node.value == null) { continue; }
|
|
||||||
|
|
||||||
node.value.tryEnableBeacons();
|
|
||||||
}
|
|
||||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnableDeleteChildrenNodes())
|
|
||||||
{
|
|
||||||
if (node == null || node.value == null) { continue; }
|
|
||||||
|
|
||||||
node.value.tryEnableBeacons();
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -441,9 +476,9 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
|
|
||||||
// since this section wants to render
|
// since this section wants to render
|
||||||
// check if it needs any generation to do so
|
// check if it needs any generation to do so
|
||||||
if (!node.value.retreivedMissingSectionsForRetreival)
|
if (!node.value.queuedMissingSectionsForRetrieval)
|
||||||
{
|
{
|
||||||
node.value.retreivedMissingSectionsForRetreival = true;
|
node.value.queuedMissingSectionsForRetrieval = true;
|
||||||
this.tryQueuePosForRetrieval(node.value.pos); // can be quite slow
|
this.tryQueuePosForRetrieval(node.value.pos); // can be quite slow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,9 +503,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
//=========================//
|
//=========================//
|
||||||
// tick - recursive update //
|
// tick - recursive update //
|
||||||
//=========================//
|
//=========================//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
private void recursivelyUpdateRenderSectionNode(
|
/** @return true if the node at this position has uploaded its render data */
|
||||||
|
private boolean recursivelyUpdateRenderSectionNode(
|
||||||
@NotNull DhBlockPos2D playerPos,
|
@NotNull DhBlockPos2D playerPos,
|
||||||
@NotNull QuadNode<LodRenderSection> rootNode,
|
@NotNull QuadNode<LodRenderSection> rootNode,
|
||||||
@Nullable QuadNode<LodRenderSection> parentNode,
|
@Nullable QuadNode<LodRenderSection> parentNode,
|
||||||
@@ -482,19 +518,22 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
// get/create the node //
|
// get/create the node //
|
||||||
// and render section //
|
// and render section //
|
||||||
//=====================//
|
//=====================//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
// create the node
|
quadNode = this.tryAddNodeToTree(rootNode, quadNode, sectionPos);
|
||||||
if (quadNode == null)
|
|
||||||
|
|
||||||
|
// Skip sections that are out-of-bounds.
|
||||||
|
// If not done some sections will appear and/or generate
|
||||||
|
// outside the desired render distance
|
||||||
|
if (!this.isSectionPosInBounds(quadNode.sectionPos))
|
||||||
{
|
{
|
||||||
rootNode.setValue(sectionPos, new LodRenderSection(sectionPos, this, this.level, this.fullDataSourceProvider));
|
this.tickNodeHolder.addDisableNode(quadNode);
|
||||||
quadNode = rootNode.getNode(sectionPos);
|
this.recursivelyDisableChildNodes(quadNode);
|
||||||
}
|
return true;
|
||||||
if (quadNode == null)
|
|
||||||
{
|
|
||||||
LodUtil.assertNotReach("Unable to add node with pos ["+DhSectionPos.toString(sectionPos)+"] to tree root ["+rootNode+"].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// make sure the render section is created (shouldn't be necessary, but just in case)
|
// make sure the render section is created (shouldn't be necessary, but just in case)
|
||||||
LodRenderSection renderSection = quadNode.value;
|
LodRenderSection renderSection = quadNode.value;
|
||||||
if (renderSection == null)
|
if (renderSection == null)
|
||||||
@@ -503,7 +542,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
quadNode.setValue(sectionPos, renderSection);
|
quadNode.setValue(sectionPos, renderSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -511,7 +550,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
// handle enabling, loading, //
|
// handle enabling, loading, //
|
||||||
// and disabling render sections //
|
// and disabling render sections //
|
||||||
//===============================//
|
//===============================//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
// load every node for rendering
|
// load every node for rendering
|
||||||
if (!renderSection.gpuUploadInProgress()
|
if (!renderSection.gpuUploadInProgress()
|
||||||
@@ -527,94 +566,153 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
|
|
||||||
if (DhSectionPos.getDetailLevel(quadNode.sectionPos) > expectedDetailLevel)
|
if (DhSectionPos.getDetailLevel(quadNode.sectionPos) > expectedDetailLevel)
|
||||||
{
|
{
|
||||||
this.onDetailLevelTooHigh(playerPos, rootNode, quadNode);
|
return this.onDetailLevelTooLow(playerPos, rootNode, quadNode);
|
||||||
}
|
}
|
||||||
// the (expectedDetailLevel-1) fixes corners being cut out due to distance calculations using the LOD center
|
// the (expectedDetailLevel-1) fixes corners being cut out due to distance calculations using the LOD center
|
||||||
else if (DhSectionPos.getDetailLevel(quadNode.sectionPos) == expectedDetailLevel
|
else if (DhSectionPos.getDetailLevel(quadNode.sectionPos) == expectedDetailLevel
|
||||||
|| DhSectionPos.getDetailLevel(quadNode.sectionPos) == expectedDetailLevel - 1)
|
|| DhSectionPos.getDetailLevel(quadNode.sectionPos) == expectedDetailLevel - 1)
|
||||||
{
|
{
|
||||||
this.onDesiredDetailLevel(quadNode, parentNode);
|
return this.onDesiredDetailLevel(quadNode, parentNode);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("LodQuadTree shouldn't be updating renderSections below the expected detail level: [" + expectedDetailLevel + "].");
|
throw new IllegalStateException("LodQuadTree shouldn't be updating renderSections below the expected detail level: [" + expectedDetailLevel + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
//endregion
|
||||||
}
|
}
|
||||||
private void onDetailLevelTooHigh(
|
/** @return true if the node at this position has uploaded its render data */
|
||||||
|
private boolean onDetailLevelTooLow(
|
||||||
@NotNull DhBlockPos2D playerPos,
|
@NotNull DhBlockPos2D playerPos,
|
||||||
@NotNull QuadNode<LodRenderSection> rootNode, @NotNull QuadNode<LodRenderSection> quadNode)
|
@NotNull QuadNode<LodRenderSection> rootNode,
|
||||||
|
@NotNull QuadNode<LodRenderSection> quadNode)
|
||||||
{
|
{
|
||||||
// recursively update each child node
|
// recursively update each child node
|
||||||
boolean allChildNodesCanRender = true;
|
int childNodeRenderCount = 0;
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
QuadNode<LodRenderSection> childNode = quadNode.getChildByIndex(i);
|
|
||||||
long childPos = DhSectionPos.getChildByIndex(quadNode.sectionPos, i);
|
long childPos = DhSectionPos.getChildByIndex(quadNode.sectionPos, i);
|
||||||
this.recursivelyUpdateRenderSectionNode(
|
QuadNode<LodRenderSection> childNode = quadNode.getChildByIndex(i);
|
||||||
|
|
||||||
|
boolean childCanRender = this.recursivelyUpdateRenderSectionNode(
|
||||||
playerPos,
|
playerPos,
|
||||||
rootNode, quadNode, childNode, childPos);
|
rootNode, quadNode, childNode, childPos);
|
||||||
childNode = quadNode.getChildByIndex(i); // needs to be gotten again in case a new node was added to the tree (this will often happen when moving into new areas where the children were deleted)
|
if (childCanRender)
|
||||||
|
|
||||||
// nodes shouldn't be null, but just in case
|
|
||||||
if (childNode != null
|
|
||||||
&& childNode.value != null
|
|
||||||
&& !childNode.value.gpuUploadComplete())
|
|
||||||
{
|
{
|
||||||
// the node is present but not uploaded yet
|
// node can be rendered
|
||||||
allChildNodesCanRender = false;
|
childNodeRenderCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isRootNode = (quadNode == rootNode);
|
||||||
if (allChildNodesCanRender)
|
if (isRootNode)
|
||||||
{
|
{
|
||||||
// all child nodes can render, this node isn't needed
|
// Never render the root node.
|
||||||
|
// This is done to prevent flashing when moving across root node
|
||||||
|
// boundaries.
|
||||||
|
// Otherwise, when moving, new empty nodes will be added at the edge of the tree
|
||||||
|
// which will require the root node to render to cover the "empty" area.
|
||||||
this.tickNodeHolder.addDisableNode(quadNode);
|
this.tickNodeHolder.addDisableNode(quadNode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (childNodeRenderCount >= 4)
|
||||||
|
{
|
||||||
|
this.tickNodeHolder.addDisableNode(quadNode);
|
||||||
|
|
||||||
|
// all children can render,
|
||||||
|
// the area will be filled when rendering
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// not all child positions are loaded yet, this one should be rendered instead
|
boolean nodeCanRender = quadNode.value != null
|
||||||
this.tickNodeHolder.addEnableNode(quadNode);
|
&& quadNode.value.canRender();
|
||||||
|
if (nodeCanRender)
|
||||||
|
{
|
||||||
|
// not all child positions are loaded yet, this one should be rendered instead
|
||||||
|
this.tickNodeHolder.addEnableNode(quadNode);
|
||||||
|
this.recursivelyDisableChildNodes(quadNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.tickNodeHolder.addDisableNode(quadNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeCanRender;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void onDesiredDetailLevel(
|
|
||||||
@NotNull QuadNode<LodRenderSection> quadNode, @Nullable QuadNode<LodRenderSection> parentNode)
|
|
||||||
{
|
|
||||||
boolean allAdjNodesCanRender = true;
|
|
||||||
|
|
||||||
// if the parent node is null, that means we're at the root node
|
/** @return true if the node at this position has uploaded its render data */
|
||||||
// and we should always render
|
private boolean onDesiredDetailLevel(
|
||||||
if (parentNode != null)
|
@NotNull QuadNode<LodRenderSection> quadNode,
|
||||||
|
@Nullable QuadNode<LodRenderSection> parentNode)
|
||||||
|
{
|
||||||
|
// Skip sections that are out-of-bounds.
|
||||||
|
// If not done some sections will appear and/or generate
|
||||||
|
// outside the desired render distance
|
||||||
|
if (!this.isSectionPosInBounds(quadNode.sectionPos))
|
||||||
{
|
{
|
||||||
// check if all adjacent nodes are ready to render
|
return true;
|
||||||
// this check is done to prevent some overlapping due to the parent node
|
}
|
||||||
// still being active
|
|
||||||
for (int i = 0; i < 4; i++)
|
if (quadNode.value != null
|
||||||
|
&& quadNode.value.canRender())
|
||||||
|
{
|
||||||
|
if (!this.tickNodeHolder.getEnabledNodes().contains(parentNode))
|
||||||
{
|
{
|
||||||
QuadNode<LodRenderSection> adjNode = parentNode.getChildByIndex(i);
|
this.tickNodeHolder.addEnableDeleteChildrenNode(quadNode);
|
||||||
// nodes shouldn't be null, but just in case there's an issue
|
return true;
|
||||||
if (adjNode != null
|
}
|
||||||
&& adjNode.value != null
|
else
|
||||||
&& !adjNode.value.gpuUploadComplete())
|
{
|
||||||
{
|
this.tickNodeHolder.addDisableNode(quadNode);
|
||||||
// the node is present but not uploaded yet
|
return false;
|
||||||
allAdjNodesCanRender = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (allAdjNodesCanRender
|
|
||||||
&& quadNode.value != null
|
|
||||||
&& quadNode.value.gpuUploadComplete())
|
|
||||||
{
|
{
|
||||||
this.tickNodeHolder.addEnableDeleteChildrenNode(quadNode);
|
this.tickNodeHolder.addDisableNode(quadNode);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///endregion
|
@NotNull
|
||||||
|
private QuadNode<LodRenderSection> tryAddNodeToTree(
|
||||||
|
@NotNull QuadNode<LodRenderSection> rootNode,
|
||||||
|
@Nullable QuadNode<LodRenderSection> quadNode,
|
||||||
|
long sectionPos // section pos is needed here since the quad node may be null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// create the node
|
||||||
|
if (quadNode == null)
|
||||||
|
{
|
||||||
|
rootNode.setValue(sectionPos, new LodRenderSection(sectionPos, this, this.level, this.fullDataSourceProvider));
|
||||||
|
quadNode = rootNode.getNode(sectionPos);
|
||||||
|
}
|
||||||
|
if (quadNode == null)
|
||||||
|
{
|
||||||
|
LodUtil.assertNotReach("Unable to add node with pos ["+DhSectionPos.toString(sectionPos)+"] to tree root ["+rootNode+"].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return quadNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recursivelyDisableChildNodes(@NotNull QuadNode<LodRenderSection> quadNode)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
QuadNode<LodRenderSection> childNode = quadNode.getChildByIndex(i);
|
||||||
|
this.tickNodeHolder.removeEnableAndDisableNode(childNode);
|
||||||
|
|
||||||
|
if (childNode != null)
|
||||||
|
{
|
||||||
|
this.recursivelyDisableChildNodes(childNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
//=====================//
|
//=====================//
|
||||||
// tick - work queuing //
|
// tick - work queuing //
|
||||||
@@ -860,6 +958,133 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// beacon handling //
|
||||||
|
//=================//
|
||||||
|
//region beacon handling
|
||||||
|
|
||||||
|
/** gets the active beacon list and stops/starts beacon rendering as necessary */
|
||||||
|
private void tryRefreshRenderingBeaconsAsync(DhBlockPos2D playerPos)
|
||||||
|
{
|
||||||
|
// do nothing if beacon rendering or repos are unavailable
|
||||||
|
if (this.beaconBeamRepo == null
|
||||||
|
|| this.beaconRenderHandler == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||||
|
if (executor == null)
|
||||||
|
{
|
||||||
|
// shouldn't normally happen, but just in case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.beaconUpdateFuture.isDone())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
this.beaconUpdateFuture = future;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
executor.execute(() ->
|
||||||
|
{
|
||||||
|
this.refreshRenderingBeacons(playerPos);
|
||||||
|
|
||||||
|
try { Thread.sleep(2_000); } catch (InterruptedException ignore) { }
|
||||||
|
future.complete(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (RejectedExecutionException e)
|
||||||
|
{
|
||||||
|
// the thread pool was probably shut down because it's size is being changed, just wait a sec and it should be back
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void refreshRenderingBeacons(DhBlockPos2D playerPos)
|
||||||
|
{
|
||||||
|
if (this.beaconBeamRepo == null
|
||||||
|
|| this.beaconRenderHandler == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Synchronized to prevent two threads for accessing the array at once
|
||||||
|
synchronized (this.beaconList)
|
||||||
|
{
|
||||||
|
// get beacons //
|
||||||
|
|
||||||
|
int blockDistanceRadius = (this.blockRenderDistanceDiameter / 2);
|
||||||
|
int minBlockPosX = playerPos.x - blockDistanceRadius;
|
||||||
|
int minBlockPosZ = playerPos.z - blockDistanceRadius;
|
||||||
|
int maxBlockPosX = playerPos.x + blockDistanceRadius;
|
||||||
|
int maxBlockPosZ = playerPos.z + blockDistanceRadius;
|
||||||
|
|
||||||
|
ArrayList<BeaconBeamDTO> dbBeacons = this.beaconBeamRepo.getAllBeamsInBlockPosRange(
|
||||||
|
minBlockPosX, maxBlockPosX,
|
||||||
|
minBlockPosZ, maxBlockPosZ
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// convert DB beacons //
|
||||||
|
|
||||||
|
ArrayList<BeaconRenderHandler.BeaconBeamWithWidth> newBeaconList = new ArrayList<>(this.beaconList.size());
|
||||||
|
for (BeaconBeamDTO beaconBeam : dbBeacons)
|
||||||
|
{
|
||||||
|
byte beaconDetailLevel = this.calcExpectedDetailLevel(playerPos, beaconBeam.blockPos.getX(), beaconBeam.blockPos.getZ());
|
||||||
|
newBeaconList.add(new BeaconRenderHandler.BeaconBeamWithWidth(beaconBeam, beaconDetailLevel));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean replaceBeacons = false;
|
||||||
|
if (this.beaconList.size() != newBeaconList.size())
|
||||||
|
{
|
||||||
|
// lists are different sizes
|
||||||
|
replaceBeacons = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// sort the beacons so they can be compared
|
||||||
|
this.beaconList.sort(BeaconRenderHandler.NegativeInfiniteBlockPosComparator.INSTANCE);
|
||||||
|
newBeaconList.sort(BeaconRenderHandler.NegativeInfiniteBlockPosComparator.INSTANCE);
|
||||||
|
|
||||||
|
for (int i = 0; i < this.beaconList.size(); i++)
|
||||||
|
{
|
||||||
|
BeaconRenderHandler.BeaconBeamWithWidth oldBeam = this.beaconList.get(i);
|
||||||
|
BeaconRenderHandler.BeaconBeamWithWidth newBeam = newBeaconList.get(i);
|
||||||
|
if (!oldBeam.equals(newBeam))
|
||||||
|
{
|
||||||
|
replaceBeacons = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// only replace the beacons if something changed
|
||||||
|
// this is done to prevent constantly re-uploading the render data
|
||||||
|
if (replaceBeacons)
|
||||||
|
{
|
||||||
|
this.beaconList.clear();
|
||||||
|
this.beaconList.addAll(newBeaconList);
|
||||||
|
this.beaconRenderHandler.replaceRenderingBeacons(this.beaconList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue updating beacons, error: ["+e.getMessage()+"].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion beacon handling
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//====================//
|
//====================//
|
||||||
// detail level logic //
|
// detail level logic //
|
||||||
//====================//
|
//====================//
|
||||||
@@ -873,8 +1098,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
* @return detail level of this section pos
|
* @return detail level of this section pos
|
||||||
*/
|
*/
|
||||||
public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, long sectionPos)
|
public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, long sectionPos)
|
||||||
|
{ return this.calcExpectedDetailLevel(playerPos, DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos)); }
|
||||||
|
public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, int targetBlockPosX, int targetBlockPosZ)
|
||||||
{
|
{
|
||||||
double blockDistance = playerPos.dist(DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos));
|
double blockDistance = playerPos.dist(targetBlockPosX, targetBlockPosZ);
|
||||||
return this.calcDetailLevelFromDistance(blockDistance);
|
return this.calcDetailLevelFromDistance(blockDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -990,7 +1217,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
{
|
{
|
||||||
color = Color.ORANGE;
|
color = Color.ORANGE;
|
||||||
}
|
}
|
||||||
else if (!renderSection.gpuUploadComplete())
|
else if (!renderSection.canRender())
|
||||||
{
|
{
|
||||||
// uploaded but the buffer is missing
|
// uploaded but the buffer is missing
|
||||||
color = Color.PINK;
|
color = Color.PINK;
|
||||||
@@ -1032,7 +1259,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
LOGGER.info("Shutting down LodQuadTree...");
|
//LOGGER.info("Shutting down LodQuadTree...");
|
||||||
|
|
||||||
DEBUG_RENDERER.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
DEBUG_RENDERER.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
||||||
Config.Common.WorldGenerator.enableDistantGeneration.removeListener(this);
|
Config.Common.WorldGenerator.enableDistantGeneration.removeListener(this);
|
||||||
@@ -1067,7 +1294,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
LOGGER.info("Finished shutting down LodQuadTree");
|
//LOGGER.info("Finished shutting down LodQuadTree");
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion base methods
|
//endregion base methods
|
||||||
|
|||||||
+120
-229
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.render.QuadTree;
|
package com.seibel.distanthorizons.core.render.QuadTree;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||||
@@ -34,11 +35,9 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.BeaconRenderHandler;
|
|
||||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
|
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
|
||||||
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||||
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
@@ -48,10 +47,10 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import javax.annotation.WillNotClose;
|
import javax.annotation.WillNotClose;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.*;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A render section represents an area that could be rendered.
|
* A render section represents an area that could be rendered.
|
||||||
@@ -67,31 +66,24 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
public final long pos;
|
public final long pos;
|
||||||
|
|
||||||
private final IDhClientLevel level;
|
private final IDhClientLevel clientLevel;
|
||||||
private final IClientLevelWrapper levelWrapper;
|
private final IClientLevelWrapper levelWrapper;
|
||||||
@WillNotClose
|
@WillNotClose
|
||||||
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
||||||
private final LodQuadTree quadTree;
|
private final LodQuadTree quadTree;
|
||||||
|
|
||||||
/**
|
|
||||||
* contains the list of beacons currently being rendered in this section
|
|
||||||
* if this list is modified the {@link LodRenderSection#beaconRenderHandler} should be updated to match.
|
|
||||||
*/
|
|
||||||
private final ArrayList<BeaconBeamDTO> activeBeaconList = new ArrayList<>();
|
|
||||||
@Nullable
|
|
||||||
public final BeaconRenderHandler beaconRenderHandler;
|
|
||||||
@Nullable
|
|
||||||
public final BeaconBeamRepo beaconBeamRepo;
|
|
||||||
/**
|
|
||||||
* locking is necessary to prevent some weird threading issues
|
|
||||||
* causing beacons to appear/disappear at the wrong times.
|
|
||||||
*/
|
|
||||||
private final ReentrantLock beaconRenderHandlingLock = new ReentrantLock();
|
|
||||||
|
|
||||||
|
|
||||||
private boolean renderingEnabled = false;
|
private boolean renderingEnabled = false;
|
||||||
private boolean beaconsRendering = false;
|
/**
|
||||||
public boolean retreivedMissingSectionsForRetreival = false;
|
* Used when a node goes out of render distance
|
||||||
|
* but isn't removed from the underlying quad tree structure. <br><br>
|
||||||
|
*
|
||||||
|
* In those cases we should act as if the node was removed
|
||||||
|
* for cached render data caching purposes, but not
|
||||||
|
* for re-creating missing nodes.
|
||||||
|
*/
|
||||||
|
public boolean renderDataDirty = false;
|
||||||
|
public boolean queuedMissingSectionsForRetrieval = false;
|
||||||
|
|
||||||
/** this reference is necessary so we can determine what VBO to render */
|
/** this reference is necessary so we can determine what VBO to render */
|
||||||
public LodBufferContainer renderBufferContainer;
|
public LodBufferContainer renderBufferContainer;
|
||||||
@@ -109,13 +101,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
*/
|
*/
|
||||||
private Runnable getAndBuildRenderDataRunnable = null;
|
private Runnable getAndBuildRenderDataRunnable = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents just uploading the {@link LodQuadBuilder} to the GPU. <br>
|
|
||||||
* Separate from {@link LodRenderSection#getAndBuildRenderDataFutureRef} because they run on
|
|
||||||
* different threads (buffer uploading is on the MC render thread) and need to be canceled separately.
|
|
||||||
*/
|
|
||||||
private final AtomicReference<CompletableFuture<LodBufferContainer>> bufferUploadFutureRef = new AtomicReference<>(null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
@@ -126,17 +111,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
public LodRenderSection(
|
public LodRenderSection(
|
||||||
long pos,
|
long pos,
|
||||||
LodQuadTree quadTree,
|
LodQuadTree quadTree,
|
||||||
IDhClientLevel level, FullDataSourceProviderV2 fullDataSourceProvider)
|
IDhClientLevel clientLevel, FullDataSourceProviderV2 fullDataSourceProvider)
|
||||||
{
|
{
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
this.quadTree = quadTree;
|
this.quadTree = quadTree;
|
||||||
this.level = level;
|
this.clientLevel = clientLevel;
|
||||||
this.levelWrapper = level.getClientLevelWrapper();
|
this.levelWrapper = clientLevel.getClientLevelWrapper();
|
||||||
this.fullDataSourceProvider = fullDataSourceProvider;
|
this.fullDataSourceProvider = fullDataSourceProvider;
|
||||||
|
|
||||||
this.beaconRenderHandler = this.quadTree.beaconRenderHandler;
|
|
||||||
this.beaconBeamRepo = this.level.getBeaconBeamRepo();
|
|
||||||
|
|
||||||
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
|
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +158,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// shouldn't happen since this method is synchronized, but just in case
|
||||||
|
// make sure we only ever start one upload task
|
||||||
if (!this.getAndBuildRenderDataFutureRef.compareAndSet(null, future))
|
if (!this.getAndBuildRenderDataFutureRef.compareAndSet(null, future))
|
||||||
{
|
{
|
||||||
CompletableFuture<Void> oldFuture = this.getAndBuildRenderDataFutureRef.get();
|
CompletableFuture<Void> oldFuture = this.getAndBuildRenderDataFutureRef.get();
|
||||||
@@ -188,26 +172,32 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.refreshActiveBeaconList();
|
// build LOD data on a DH thread
|
||||||
|
try (LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData())
|
||||||
LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData();
|
|
||||||
if (lodQuadBuilder == null)
|
|
||||||
{
|
{
|
||||||
future.complete(null);
|
if (lodQuadBuilder == null)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uploadToGpuAsync(lodQuadBuilder)
|
|
||||||
.thenRun(() ->
|
|
||||||
{
|
{
|
||||||
// the future is passed in separately (IE not using the local var) to prevent any possible race condition null pointers
|
|
||||||
future.complete(null);
|
future.complete(null);
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create CPU vertex buffers
|
||||||
|
ArrayList<ByteBuffer> opaqueBuffers = lodQuadBuilder.makeOpaqueVertexBuffers();
|
||||||
|
ArrayList<ByteBuffer> transparentBuffers = lodQuadBuilder.makeTransparentVertexBuffers();
|
||||||
|
|
||||||
|
// uploading will primarily happen on the render thread
|
||||||
|
this.uploadToGpuAsync(future, opaqueBuffers, transparentBuffers)
|
||||||
|
.thenRun(() ->
|
||||||
|
{
|
||||||
|
// the future is passed in separately (IE not using the local var) to prevent any possible race condition null pointers
|
||||||
|
future.complete(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
LOGGER.error("Unexpected issue creating render data for pos: ["+DhSectionPos.toString(this.pos)+"], error: ["+e.getMessage()+"].", e);
|
LOGGER.error("Unexpected issue creating render data for pos: ["+DhSectionPos.toString(this.pos)+"], error: ["+e.getMessage()+"].", e);
|
||||||
future.complete(null);
|
future.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
executor.execute(this.getAndBuildRenderDataRunnable);
|
executor.execute(this.getAndBuildRenderDataRunnable);
|
||||||
@@ -222,6 +212,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=======================//
|
||||||
|
// Get LOD ID data //
|
||||||
|
// and build render data //
|
||||||
|
//=======================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private synchronized LodQuadBuilder getAndBuildRenderData()
|
private synchronized LodQuadBuilder getAndBuildRenderData()
|
||||||
{
|
{
|
||||||
@@ -234,8 +232,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
|
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get() == EDhApiTransparency.COMPLETE;
|
||||||
LodQuadBuilder lodQuadBuilder = new LodQuadBuilder(enableTransparency, this.level.getClientLevelWrapper());
|
LodQuadBuilder lodQuadBuilder = LodQuadBuilder.getBuilder(enableTransparency, this.clientLevel.getClientLevelWrapper());
|
||||||
|
|
||||||
|
|
||||||
// get the adjacent positions
|
// get the adjacent positions
|
||||||
@@ -258,7 +256,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
// the render sources are only needed by this synchronous method,
|
// the render sources are only needed by this synchronous method,
|
||||||
// then they can be closed
|
// then they can be closed
|
||||||
ColumnRenderBufferBuilder.makeLodRenderData(lodQuadBuilder, thisRenderSource, this.level, adjacentRenderSections, adjIsSameDetailLevel);
|
ColumnRenderBufferBuilder.makeLodRenderData(lodQuadBuilder, thisRenderSource, this.clientLevel, adjacentRenderSections, adjIsSameDetailLevel);
|
||||||
return lodQuadBuilder;
|
return lodQuadBuilder;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -308,52 +306,64 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||||
return detailLevel == DhSectionPos.getDetailLevel(this.pos);
|
return detailLevel == DhSectionPos.getDetailLevel(this.pos);
|
||||||
}
|
}
|
||||||
private synchronized CompletableFuture<LodBufferContainer> uploadToGpuAsync(LodQuadBuilder lodQuadBuilder)
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
private synchronized CompletableFuture<LodBufferContainer> uploadToGpuAsync(
|
||||||
|
CompletableFuture<Void> parentFuture,
|
||||||
|
ArrayList<ByteBuffer> opaqueBuffers,
|
||||||
|
ArrayList<ByteBuffer> transparentBuffers)
|
||||||
{
|
{
|
||||||
CompletableFuture<LodBufferContainer> oldFuture = this.bufferUploadFutureRef.getAndSet(null);
|
CompletableFuture<LodBufferContainer> uploadFuture = LodBufferContainer.tryMakeAndUploadBuffersAsync(this.pos, this.clientLevel, opaqueBuffers, transparentBuffers);
|
||||||
if (oldFuture != null)
|
uploadFuture.whenComplete((bufferContainer, e) ->
|
||||||
{
|
{
|
||||||
// canceling the previous future
|
try
|
||||||
// prevents the CPU from working on something that won't be used
|
|
||||||
oldFuture.cancel(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompletableFuture<LodBufferContainer> future = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder);
|
|
||||||
future.handle((lodBufferContainer, throwable) ->
|
|
||||||
{
|
|
||||||
if (!this.bufferUploadFutureRef.compareAndSet(future, null)
|
|
||||||
// if the old future is canceled then the future ref will be different and that's expected
|
|
||||||
&& !future.isCancelled()
|
|
||||||
// if the old future is already done, then we don't care about the ref being swapped
|
|
||||||
&& !future.isDone())
|
|
||||||
{
|
{
|
||||||
LOGGER.warn("Buffer upload future ref changed for pos: ["+DhSectionPos.toString(this.pos)+"].");
|
// handle errors and early shutdown
|
||||||
|
if (e != null)
|
||||||
|
{
|
||||||
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue uploading buffers for pos: [" + DhSectionPos.toString(this.pos) + "], error: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferContainer != null)
|
||||||
|
{
|
||||||
|
// shouldn't happen, but just in case
|
||||||
|
bufferContainer.close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the old container
|
||||||
|
LodBufferContainer oldContainer = this.renderBufferContainer;
|
||||||
|
this.renderBufferContainer = bufferContainer.buffersUploaded ? bufferContainer : null;
|
||||||
|
if (oldContainer != null)
|
||||||
|
{
|
||||||
|
oldContainer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload complete
|
||||||
|
this.renderDataDirty = false;
|
||||||
|
|
||||||
|
|
||||||
|
if (parentFuture.isCancelled())
|
||||||
|
{
|
||||||
|
// if the parent future was canceled that likely means
|
||||||
|
// this LodRenderSection was closed before this point,
|
||||||
|
// meaning this buffer will become homeless,
|
||||||
|
// so we need to clean it up here
|
||||||
|
bufferContainer.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception finishEx)
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
future.thenAccept((LodBufferContainer buffer) ->
|
|
||||||
{
|
|
||||||
// needed to clean up the old data
|
|
||||||
LodBufferContainer previousContainer = this.renderBufferContainer;
|
|
||||||
|
|
||||||
// upload complete
|
|
||||||
this.renderBufferContainer = buffer.buffersUploaded ? buffer : null;
|
|
||||||
|
|
||||||
if (previousContainer != null)
|
|
||||||
{
|
{
|
||||||
previousContainer.close();
|
LOGGER.error("Unexpected buffer finish exception: ["+finishEx.getMessage()+"]", finishEx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return uploadFuture;
|
||||||
if (!this.bufferUploadFutureRef.compareAndSet(null, future))
|
|
||||||
{
|
|
||||||
LodUtil.assertNotReach("Buffer upload future ref couldn't be set due to concurrency error, pos: ["+DhSectionPos.toString(this.pos)+"].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return future;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion render data uploading
|
//endregion render data uploading
|
||||||
@@ -365,7 +375,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
//=================//
|
//=================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public boolean gpuUploadComplete() { return this.renderBufferContainer != null; }
|
public boolean canRender() { return this.renderBufferContainer != null; }
|
||||||
|
public boolean gpuUploadComplete()
|
||||||
|
{
|
||||||
|
return this.renderBufferContainer != null
|
||||||
|
// render dirty is here so we can trigger new GPU uploads
|
||||||
|
// even if the render data is present
|
||||||
|
&& !this.renderDataDirty;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getRenderingEnabled() { return this.renderingEnabled; }
|
public boolean getRenderingEnabled() { return this.renderingEnabled; }
|
||||||
public void setRenderingEnabled(boolean enabled) { this.renderingEnabled = enabled;}
|
public void setRenderingEnabled(boolean enabled) { this.renderingEnabled = enabled;}
|
||||||
@@ -376,130 +393,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// beacon handling //
|
|
||||||
//=================//
|
|
||||||
//region beacon handling
|
|
||||||
|
|
||||||
/** gets the active beacon list and stops/starts beacon rendering as necessary */
|
|
||||||
private void refreshActiveBeaconList()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.lock();
|
|
||||||
|
|
||||||
// do nothing if beacon rendering or repos are unavailable
|
|
||||||
if (this.beaconBeamRepo == null
|
|
||||||
|| this.beaconRenderHandler == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Synchronized to prevent two threads for accessing the array at once
|
|
||||||
synchronized (this.activeBeaconList)
|
|
||||||
{
|
|
||||||
ArrayList<BeaconBeamDTO> activeBeacons = this.beaconBeamRepo.getAllBeamsForPos(this.pos);
|
|
||||||
|
|
||||||
// swap old and new active beacon list
|
|
||||||
this.activeBeaconList.clear();
|
|
||||||
this.activeBeaconList.addAll(activeBeacons);
|
|
||||||
|
|
||||||
// if the beacons are currently rendering,
|
|
||||||
// re-create them so we can see any potential changes
|
|
||||||
if (this.beaconsRendering)
|
|
||||||
{
|
|
||||||
this.tryDisableBeacons();
|
|
||||||
this.tryEnableBeacons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryDisableBeacons()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.lock();
|
|
||||||
|
|
||||||
|
|
||||||
// do nothing if beacon rendering is unavailable
|
|
||||||
if (this.beaconRenderHandler == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.beaconsRendering)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.beaconsRendering = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus.get())
|
|
||||||
{
|
|
||||||
// show that this position has just been disabled
|
|
||||||
DEBUG_RENDERER.makeParticle(
|
|
||||||
new AbstractDebugWireframeRenderer.BoxParticle(
|
|
||||||
new AbstractDebugWireframeRenderer.Box(this.pos, 128f, 156f, 0.09f, Color.CYAN.darker()),
|
|
||||||
0.2, 32f
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (this.activeBeaconList)
|
|
||||||
{
|
|
||||||
this.beaconRenderHandler.stopRenderingBeaconsInRange(this.pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryEnableBeacons()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.lock();
|
|
||||||
|
|
||||||
|
|
||||||
// do nothing if beacon rendering is unavailable
|
|
||||||
if (this.beaconRenderHandler == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.beaconsRendering)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.beaconsRendering = true;
|
|
||||||
|
|
||||||
|
|
||||||
synchronized (this.activeBeaconList)
|
|
||||||
{
|
|
||||||
byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
|
||||||
this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion beacon handling
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// base methods //
|
// base methods //
|
||||||
//==============//
|
//==============//
|
||||||
@@ -518,14 +411,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
{
|
{
|
||||||
color = Color.yellow;
|
color = Color.yellow;
|
||||||
}
|
}
|
||||||
else if (this.gpuUploadComplete())
|
else if (this.canRender())
|
||||||
{
|
{
|
||||||
//color = Color.cyan;
|
//color = Color.cyan;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int levelMinY = this.level.getLevelWrapper().getMinHeight();
|
int levelMinY = this.clientLevel.getLevelWrapper().getMinHeight();
|
||||||
int levelMaxY = this.level.getLevelWrapper().getMaxHeight();
|
int levelMaxY = this.clientLevel.getLevelWrapper().getMaxHeight();
|
||||||
|
|
||||||
// show the wireframe a bit lower than world max height,
|
// show the wireframe a bit lower than world max height,
|
||||||
// since most worlds don't render all the way up to the max height
|
// since most worlds don't render all the way up to the max height
|
||||||
@@ -562,14 +455,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.tryDisableBeacons();
|
// render loading is no longer needed
|
||||||
|
|
||||||
if (this.renderBufferContainer != null)
|
|
||||||
{
|
|
||||||
this.renderBufferContainer.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes any in-progress futures since they aren't needed any more
|
|
||||||
CompletableFuture<Void> buildFuture = this.getAndBuildRenderDataFutureRef.get();
|
CompletableFuture<Void> buildFuture = this.getAndBuildRenderDataFutureRef.get();
|
||||||
if (buildFuture != null)
|
if (buildFuture != null)
|
||||||
{
|
{
|
||||||
@@ -585,12 +471,17 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
renderLoaderExecutor.remove(runnable);
|
renderLoaderExecutor.remove(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cancel the future after removing the runnable
|
||||||
|
// to make sure the runnable is properly removed
|
||||||
|
buildFuture.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<LodBufferContainer> uploadFuture = this.bufferUploadFutureRef.get();
|
|
||||||
if (uploadFuture != null)
|
this.setRenderingEnabled(false);
|
||||||
|
if (this.renderBufferContainer != null)
|
||||||
{
|
{
|
||||||
uploadFuture.cancel(true);
|
this.renderBufferContainer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-8
@@ -63,21 +63,39 @@ public class QuadTreeTickNodeHolder
|
|||||||
{
|
{
|
||||||
if(this.presentNodes.add(node))
|
if(this.presentNodes.add(node))
|
||||||
{
|
{
|
||||||
// not a big fan of having to check every node to prevent overlaps, but it does work
|
// in James testing as of 4-21-2026
|
||||||
this.nodesToEnable.removeIf((QuadNode<LodRenderSection> checkNode) ->
|
// this should no longer be needed to prevent overlaps,
|
||||||
|
// however I'm keeping it here as a quick fix solution if
|
||||||
|
// the problem comes up again
|
||||||
|
if (false)
|
||||||
{
|
{
|
||||||
boolean contained = DhSectionPos.contains(node.sectionPos, checkNode.sectionPos);
|
// not a big fan of having to check every node to prevent overlaps, but it does work
|
||||||
if (contained)
|
this.nodesToEnable.removeIf((QuadNode<LodRenderSection> checkNode) ->
|
||||||
{
|
{
|
||||||
this.nodesToDisable.add(checkNode);
|
boolean contained = DhSectionPos.contains(node.sectionPos, checkNode.sectionPos);
|
||||||
}
|
if (contained)
|
||||||
|
{
|
||||||
|
this.nodesToDisable.add(checkNode);
|
||||||
|
}
|
||||||
|
|
||||||
return contained;
|
return contained;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.nodesToEnable.add(node);
|
this.nodesToEnable.add(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public void removeEnableAndDisableNode(QuadNode<LodRenderSection> node)
|
||||||
|
{
|
||||||
|
this.nodesToEnable.remove(node);
|
||||||
|
this.nodesToEnableDeleteChildrenList.remove(node);
|
||||||
|
|
||||||
|
this.presentNodes.add(node); // should already be present, but re-added just in case
|
||||||
|
this.nodesToDisable.add(node); // node shouldn't be rendered since it's being disabled by a parent
|
||||||
|
}
|
||||||
|
|
||||||
public HashSet<QuadNode<LodRenderSection>> getEnabledNodes() { return this.nodesToEnable; }
|
public HashSet<QuadNode<LodRenderSection>> getEnabledNodes() { return this.nodesToEnable; }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+36
-15
@@ -36,14 +36,15 @@ import com.seibel.distanthorizons.core.render.QuadTree.LodRenderSection;
|
|||||||
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.cullingFrustum.DhFrustumBounds;
|
import com.seibel.distanthorizons.core.render.renderer.cullingFrustum.DhFrustumBounds;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.cullingFrustum.NeverCullFrustum;
|
import com.seibel.distanthorizons.core.render.renderer.cullingFrustum.NeverCullFrustum;
|
||||||
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
|
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
|
||||||
|
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
|
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
|
||||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
import com.seibel.distanthorizons.core.util.math.DhMat4f;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Matrix4fc;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -58,6 +59,13 @@ public class RenderBufferHandler implements AutoCloseable
|
|||||||
|
|
||||||
private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
|
private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
|
||||||
|
|
||||||
|
// static values for re-use to reduce GC pressure
|
||||||
|
private static final float[] JOML_TRANSPOSE_ARRAY = new float[16];
|
||||||
|
private static final Matrix4f WORLD_VIEW_JOML_MATRIX = new Matrix4f();
|
||||||
|
private static final Matrix4f WORLD_VIEW_PROJ_JOML_MATRIX = new Matrix4f();
|
||||||
|
private static final DhMat4f FRUSTOM_DH_MATRIX = new DhMat4f();
|
||||||
|
|
||||||
|
|
||||||
/** contains all relevant data */
|
/** contains all relevant data */
|
||||||
public final LodQuadTree lodQuadTree;
|
public final LodQuadTree lodQuadTree;
|
||||||
|
|
||||||
@@ -123,6 +131,14 @@ public class RenderBufferHandler implements AutoCloseable
|
|||||||
*/
|
*/
|
||||||
public void buildRenderList(RenderParams renderParams)
|
public void buildRenderList(RenderParams renderParams)
|
||||||
{
|
{
|
||||||
|
if (ModInfo.IS_DEV_BUILD)
|
||||||
|
{
|
||||||
|
if (!RenderThreadTaskHandler.INSTANCE.isCurrentThread())
|
||||||
|
{
|
||||||
|
LodUtil.assertNotReach("Should only be run on the render thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// clear the old list so we can start fresh
|
// clear the old list so we can start fresh
|
||||||
this.loadedNearToFarBuffers.clear();
|
this.loadedNearToFarBuffers.clear();
|
||||||
|
|
||||||
@@ -154,20 +170,21 @@ public class RenderBufferHandler implements AutoCloseable
|
|||||||
int worldMinY = renderParams.clientLevelWrapper.getMinHeight();
|
int worldMinY = renderParams.clientLevelWrapper.getMinHeight();
|
||||||
int worldHeight = renderParams.clientLevelWrapper.getMaxHeight();
|
int worldHeight = renderParams.clientLevelWrapper.getMaxHeight();
|
||||||
|
|
||||||
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
renderParams.mcModelViewMatrix.putValuesInArray(JOML_TRANSPOSE_ARRAY);
|
||||||
|
WORLD_VIEW_JOML_MATRIX
|
||||||
|
.setTransposed(JOML_TRANSPOSE_ARRAY)
|
||||||
|
.translate(
|
||||||
|
-(float) renderParams.exactCameraPosition.x,
|
||||||
|
-(float) renderParams.exactCameraPosition.y,
|
||||||
|
-(float) renderParams.exactCameraPosition.z);
|
||||||
|
|
||||||
Matrix4fc matWorldView = new Matrix4f()
|
renderParams.dhProjectionMatrix.putValuesInArray(JOML_TRANSPOSE_ARRAY);
|
||||||
.setTransposed(renderParams.mcModelViewMatrix.getValuesAsArray())
|
WORLD_VIEW_PROJ_JOML_MATRIX
|
||||||
.translate(
|
.setTransposed(JOML_TRANSPOSE_ARRAY)
|
||||||
-(float) cameraPos.x,
|
.mul(WORLD_VIEW_JOML_MATRIX);
|
||||||
-(float) cameraPos.y,
|
|
||||||
-(float) cameraPos.z);
|
|
||||||
|
|
||||||
Matrix4fc matWorldViewProjection = new Matrix4f()
|
FRUSTOM_DH_MATRIX.set(WORLD_VIEW_PROJ_JOML_MATRIX);
|
||||||
.setTransposed(renderParams.dhProjectionMatrix.getValuesAsArray())
|
frustum.update(worldMinY, worldMinY + worldHeight, FRUSTOM_DH_MATRIX);
|
||||||
.mul(matWorldView);
|
|
||||||
|
|
||||||
frustum.update(worldMinY, worldMinY + worldHeight, new Mat4f(matWorldViewProjection));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -175,6 +192,7 @@ public class RenderBufferHandler implements AutoCloseable
|
|||||||
//=========================//
|
//=========================//
|
||||||
// Update the section list //
|
// Update the section list //
|
||||||
//=========================//
|
//=========================//
|
||||||
|
//region
|
||||||
|
|
||||||
if (isShadowPass)
|
if (isShadowPass)
|
||||||
{
|
{
|
||||||
@@ -251,6 +269,9 @@ public class RenderBufferHandler implements AutoCloseable
|
|||||||
{
|
{
|
||||||
this.visibleBufferCount = this.loadedNearToFarBuffers.size();
|
this.visibleBufferCount = this.loadedNearToFarBuffers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|||||||
@@ -4,19 +4,17 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
|
|||||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||||
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
|
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
|
||||||
|
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.jar.EPlatform;
|
|
||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.util.RenderUtil;
|
import com.seibel.distanthorizons.core.util.RenderUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
import com.seibel.distanthorizons.core.util.math.DhMat4f;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
|
||||||
import com.seibel.distanthorizons.core.world.IDhClientWorld;
|
import com.seibel.distanthorizons.core.world.IDhClientWorld;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
|
|
||||||
@@ -30,8 +28,14 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
|
||||||
private static final long TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS = 10_000;
|
private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
|
||||||
private static boolean initialLoadingComplete = false;
|
|
||||||
|
/**
|
||||||
|
* Copy used for API events. <br>
|
||||||
|
* A separate copy is used to prevent API users from accidentally setting values
|
||||||
|
* that screw up DH's copy of the render parameters.
|
||||||
|
*/
|
||||||
|
public final DhApiRenderParam apiCopy = new DhApiRenderParam();
|
||||||
|
|
||||||
|
|
||||||
public IDhClientWorld dhClientWorld;
|
public IDhClientWorld dhClientWorld;
|
||||||
@@ -41,7 +45,7 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
public ILightMapWrapper lightmap;
|
public ILightMapWrapper lightmap;
|
||||||
public RenderBufferHandler renderBufferHandler;
|
public RenderBufferHandler renderBufferHandler;
|
||||||
public IDhGenericRenderer genericRenderer;
|
public IDhGenericRenderer genericRenderer;
|
||||||
public Vec3d exactCameraPosition;
|
public DhVec3d exactCameraPosition;
|
||||||
/** @see DhRenderState#vanillaFogEnabled */
|
/** @see DhRenderState#vanillaFogEnabled */
|
||||||
public boolean vanillaFogEnabled;
|
public boolean vanillaFogEnabled;
|
||||||
|
|
||||||
@@ -54,36 +58,27 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
//=============//
|
//=============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public RenderParams(EDhApiRenderPass renderPass, DhRenderState renderState)
|
public void update(EDhApiRenderPass renderPass, DhRenderState renderState)
|
||||||
{
|
{
|
||||||
this(renderPass,
|
RenderUtil.setDhProjectionMatrix(this.dhProjectionMatrix, renderState.mcProjectionMatrix);
|
||||||
renderState.partialTickTime,
|
this.dhModelViewMatrix.set(renderState.mcModelViewMatrix); // DH and MC MVM matrix are the same
|
||||||
renderState.mcProjectionMatrix, renderState.mcModelViewMatrix,
|
|
||||||
renderState.clientLevelWrapper,
|
|
||||||
renderState.vanillaFogEnabled
|
|
||||||
);
|
|
||||||
}
|
|
||||||
private RenderParams(
|
|
||||||
EDhApiRenderPass renderPass,
|
|
||||||
float newPartialTicks,
|
|
||||||
Mat4f newMcProjectionMatrix, Mat4f newMcModelViewMatrix,
|
|
||||||
IClientLevelWrapper clientLevelWrapper,
|
|
||||||
boolean vanillaFogEnabled
|
|
||||||
)
|
|
||||||
{
|
|
||||||
super(renderPass,
|
|
||||||
newPartialTicks,
|
|
||||||
RenderUtil.getNearClipPlaneInBlocks(), RenderUtil.getFarClipPlaneDistanceInBlocks(),
|
|
||||||
newMcProjectionMatrix, newMcModelViewMatrix,
|
|
||||||
RenderUtil.createLodProjectionMatrix(newMcProjectionMatrix), RenderUtil.createLodModelViewMatrix(newMcModelViewMatrix),
|
|
||||||
clientLevelWrapper.getMinHeight(),
|
|
||||||
clientLevelWrapper);
|
|
||||||
|
|
||||||
|
super.update(renderPass,
|
||||||
|
renderState.partialTickTime,
|
||||||
|
RenderUtil.getNearClipPlaneInBlocks(), RenderUtil.getFarClipPlaneDistanceInBlocks(),
|
||||||
|
renderState.mcProjectionMatrix, renderState.mcModelViewMatrix,
|
||||||
|
this.dhProjectionMatrix, this.dhModelViewMatrix,
|
||||||
|
renderState.clientLevelWrapper.getMinHeight(),
|
||||||
|
renderState.clientLevelWrapper);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.clientLevelWrapper = renderState.clientLevelWrapper;
|
||||||
|
|
||||||
this.dhClientWorld = SharedApi.tryGetDhClientWorld();
|
this.dhClientWorld = SharedApi.tryGetDhClientWorld();
|
||||||
if (this.dhClientWorld != null)
|
if (this.dhClientWorld != null)
|
||||||
{
|
{
|
||||||
this.dhClientLevel = (IDhClientLevel) this.dhClientWorld.getLevel(clientLevelWrapper);
|
this.dhClientLevel = this.dhClientWorld.getOrLoadClientLevel(clientLevelWrapper);
|
||||||
if (this.dhClientLevel != null)
|
if (this.dhClientLevel != null)
|
||||||
{
|
{
|
||||||
this.renderBufferHandler = this.dhClientLevel.getRenderBufferHandler();
|
this.renderBufferHandler = this.dhClientLevel.getRenderBufferHandler();
|
||||||
@@ -91,7 +86,6 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clientLevelWrapper = clientLevelWrapper;
|
|
||||||
this.lightmap = MC_RENDER.getLightmapWrapper(this.clientLevelWrapper);
|
this.lightmap = MC_RENDER.getLightmapWrapper(this.clientLevelWrapper);
|
||||||
|
|
||||||
if (MC_CLIENT.playerExists())
|
if (MC_CLIENT.playerExists())
|
||||||
@@ -99,8 +93,9 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
this.exactCameraPosition = MC_RENDER.getCameraExactPosition();
|
this.exactCameraPosition = MC_RENDER.getCameraExactPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.vanillaFogEnabled = vanillaFogEnabled;
|
this.vanillaFogEnabled = renderState.vanillaFogEnabled;
|
||||||
|
|
||||||
|
this.apiCopy.update(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -116,7 +111,7 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
* Should be called before rendering is done.
|
* Should be called before rendering is done.
|
||||||
* @return a message if LODs shouldn't be rendered, null if the LODs can render
|
* @return a message if LODs shouldn't be rendered, null if the LODs can render
|
||||||
*/
|
*/
|
||||||
public String getValidationErrorMessage(long firstRenderTimeMs)
|
public String getValidationErrorMessage()
|
||||||
{
|
{
|
||||||
// Note: all strings here should be constants to prevent String allocations
|
// Note: all strings here should be constants to prevent String allocations
|
||||||
|
|
||||||
@@ -158,13 +153,22 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
return "No Generic Renderer Present";
|
return "No Generic Renderer Present";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dhModelViewMatrix == null
|
if (this.dhModelViewMatrix.equals(DhMat4f.IDENTITY)
|
||||||
|| this.mcModelViewMatrix == null)
|
|| this.dhModelViewMatrix.equals(DhMat4f.EMPTY))
|
||||||
{
|
{
|
||||||
return "No MVM or Proj Matrix Given";
|
return "No DH MVM Matrix Given";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AbstractOptifineAccessor.optifinePresent()
|
if (this.mcModelViewMatrix.equals(DhMat4f.IDENTITY)
|
||||||
|
|| this.mcModelViewMatrix.equals(DhMat4f.EMPTY))
|
||||||
|
{
|
||||||
|
return "No MC MVM Matrix Given";
|
||||||
|
}
|
||||||
|
|
||||||
|
// projection matrix not checked since there are some MC versions where
|
||||||
|
// the MVM and projection matrices are pre-multiplied together
|
||||||
|
|
||||||
|
if (OPTIFINE_ACCESSOR != null
|
||||||
&& MC_RENDER.getTargetFramebuffer() == -1)
|
&& MC_RENDER.getTargetFramebuffer() == -1)
|
||||||
{
|
{
|
||||||
// wait for MC to finish setting up their renderer
|
// wait for MC to finish setting up their renderer
|
||||||
@@ -172,45 +176,6 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// potential fix for a segfault when
|
|
||||||
// Sodium and DH are running together
|
|
||||||
if (EPlatform.get() == EPlatform.MACOS
|
|
||||||
&& !initialLoadingComplete)
|
|
||||||
{
|
|
||||||
// Once MC starts rendering, wait a few seconds so
|
|
||||||
// MC/Sodium can finish their shader compiling before DH does its own.
|
|
||||||
// This will allow DH to compile its own shaders after Sodium finishes
|
|
||||||
// compiling its own.
|
|
||||||
long nowMs = System.currentTimeMillis();
|
|
||||||
long firstAllowedRenderTimeMs = firstRenderTimeMs + TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS;
|
|
||||||
if (nowMs < firstAllowedRenderTimeMs)
|
|
||||||
{
|
|
||||||
return "Waiting for initial MC compile...";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// null shouldn't happen, but just in case
|
|
||||||
PriorityTaskPicker.Executor renderLoadExecutor = ThreadPoolUtil.getRenderLoadingExecutor();
|
|
||||||
if (renderLoadExecutor == null)
|
|
||||||
{
|
|
||||||
return "Waiting for DH Threadpool...";
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for DH to finish loading, by the time that's done
|
|
||||||
// java should have finished all of DH's JIT compiling,
|
|
||||||
// which will hopefully mean less concurrency and thus a lower
|
|
||||||
// chance of breaking
|
|
||||||
// (plus this gives Sodium/vanill a bit longer to finish their setup)
|
|
||||||
int taskCount = renderLoadExecutor.getQueueSize();
|
|
||||||
if (taskCount > 0)
|
|
||||||
{
|
|
||||||
return "Waiting for DH JIT compiling...";
|
|
||||||
}
|
|
||||||
|
|
||||||
initialLoadingComplete = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+298
-36
@@ -2,32 +2,62 @@ package com.seibel.distanthorizons.core.render;
|
|||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||||
|
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
|
|
||||||
public class RenderThreadTaskHandler
|
public class RenderThreadTaskHandler
|
||||||
{
|
{
|
||||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
private static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||||
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
|
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static final ConcurrentLinkedQueue<Runnable> RENDER_THREAD_RUNNABLE_QUEUE = new ConcurrentLinkedQueue<>();
|
private static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
|
||||||
|
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
|
||||||
|
.maxCountPerSecond(4)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final ConcurrentLinkedQueue<QueuedRunnable> RENDER_THREAD_RUNNABLE_QUEUE = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
private static final ConcurrentHashMap<String, RollingAverage> AVERAGE_NANO_RUN_TIME_BY_TASK_NAME = new ConcurrentHashMap<>();
|
||||||
|
private static final LongAdder COMPLETED_TASK_COUNTER = new LongAdder();
|
||||||
|
private static final NumberFormat DECIMAL_NUMBER_FORMAT = NumberFormat.getNumberInstance();
|
||||||
|
private static final NumberFormat INT_NUMBER_FORMAT = NumberFormat.getIntegerInstance();
|
||||||
|
private static final boolean LOG_SLOW_TASKS = false;
|
||||||
|
|
||||||
private static final Timer TIMER = TimerUtil.CreateTimer("Cleanup timer");
|
private static final Timer TIMER = TimerUtil.CreateTimer("Cleanup timer");
|
||||||
private static final long MS_BETWEEN_CLEANUP_TICKS = 1_000L;
|
private static final long MS_BETWEEN_CLEANUP_TICKS = 1_000L;
|
||||||
private static final long MS_BEFORE_RUN_CLEANUP_TIMER = 1_000L;
|
private static final long NANOS_BEFORE_RUN_CLEANUP_TIMER = TimeUnit.NANOSECONDS.convert(1_000L, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
|
||||||
public static final RenderThreadTaskHandler INSTANCE = new RenderThreadTaskHandler();
|
public static final RenderThreadTaskHandler INSTANCE = new RenderThreadTaskHandler();
|
||||||
|
|
||||||
|
|
||||||
private long msSinceGlTasksRun = System.currentTimeMillis();
|
private long nanoSinceTasksRun = System.nanoTime();
|
||||||
|
private final boolean running;
|
||||||
|
|
||||||
|
private Thread renderThread;
|
||||||
|
/**
|
||||||
|
* the currently running {@link QueuedRunnable}
|
||||||
|
* will be null if nothing is running.
|
||||||
|
*/
|
||||||
|
private volatile @Nullable QueuedRunnable currentQueuedRunnable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +66,22 @@ public class RenderThreadTaskHandler
|
|||||||
//=============//
|
//=============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private RenderThreadTaskHandler() { TIMER.scheduleAtFixedRate(TimerUtil.createTimerTask(this::manualCleanupTick), MS_BETWEEN_CLEANUP_TICKS, MS_BETWEEN_CLEANUP_TICKS); }
|
private RenderThreadTaskHandler()
|
||||||
|
{
|
||||||
|
// we only want to run this when the client is available
|
||||||
|
IMinecraftSharedWrapper mcShared = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
|
||||||
|
if (!mcShared.isDedicatedServer())
|
||||||
|
{
|
||||||
|
LOGGER.debug("Starting ["+RenderThreadTaskHandler.class.getSimpleName()+"]...");
|
||||||
|
this.running = true;
|
||||||
|
TIMER.scheduleAtFixedRate(TimerUtil.createTimerTask(this::manualCleanupTick), MS_BETWEEN_CLEANUP_TICKS, MS_BETWEEN_CLEANUP_TICKS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.running = false;
|
||||||
|
LOGGER.debug("Skipping ["+RenderThreadTaskHandler.class.getSimpleName()+"] startup due to running on a dedicated server.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -47,23 +92,24 @@ public class RenderThreadTaskHandler
|
|||||||
//==============//
|
//==============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public void queueRunningOnRenderThread(Runnable renderCall)
|
public void queueRunningOnRenderThread(String name, Runnable renderCall)
|
||||||
{
|
{
|
||||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
// don't queuing tasks if they'll never be run
|
||||||
RENDER_THREAD_RUNNABLE_QUEUE.add(() -> this.createRenderThreadRunnable(renderCall, stackTrace));
|
if (!this.running)
|
||||||
}
|
|
||||||
private void createRenderThreadRunnable(Runnable renderCall, StackTraceElement[] stackTrace)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
renderCall.run();
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
|
|
||||||
|
// don't get the stacktrace on release to reduce GC pressure
|
||||||
|
StackTraceElement[] stackTrace = null;
|
||||||
|
if (ModInfo.IS_DEV_BUILD)
|
||||||
{
|
{
|
||||||
RuntimeException error = new RuntimeException("Uncaught Exception during GL call execution:", e);
|
stackTrace = Thread.currentThread().getStackTrace();
|
||||||
error.setStackTrace(stackTrace);
|
|
||||||
LOGGER.error("[" + Thread.currentThread().getName() + "] ran into an unexpected error running a GL call, Error: ["+ e.getMessage() +"].", error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueuedRunnable runnable = new QueuedRunnable(name, renderCall, stackTrace);
|
||||||
|
RENDER_THREAD_RUNNABLE_QUEUE.add(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -83,31 +129,72 @@ public class RenderThreadTaskHandler
|
|||||||
{
|
{
|
||||||
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
|
||||||
|
// https://fpstoms.com/
|
||||||
int frameLimit = MC_RENDER.getFrameLimit();
|
int frameLimit = MC_RENDER.getFrameLimit();
|
||||||
if (frameLimit <= 1)
|
if (frameLimit <= 1)
|
||||||
{
|
{
|
||||||
frameLimit = 4; // 240 FPS
|
frameLimit = 240;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://fpstoms.com/
|
|
||||||
int msPerFrame = 1000 / frameLimit;
|
|
||||||
this.runRenderThreadTasks(msPerFrame);
|
|
||||||
}
|
|
||||||
private void runRenderThreadTasks(long msMaxRunTime)
|
|
||||||
{
|
|
||||||
long startTimeMs = System.currentTimeMillis();
|
|
||||||
this.msSinceGlTasksRun = startTimeMs;
|
|
||||||
|
|
||||||
Runnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
|
int msPerFrame = 1000 / frameLimit;
|
||||||
|
long nanoPerFrame = msPerFrame * 1_000_000L;
|
||||||
|
nanoPerFrame /= 2; // divide the time in half so we can only impact half of the framerate at worst
|
||||||
|
this.runRenderThreadTasks(nanoPerFrame);
|
||||||
|
}
|
||||||
|
private void runRenderThreadTasks(long nanoMaxRunTime)
|
||||||
|
{
|
||||||
|
long loopStartTimeNano = System.nanoTime();
|
||||||
|
this.nanoSinceTasksRun = loopStartTimeNano;
|
||||||
|
|
||||||
|
|
||||||
|
if (this.renderThread == null)
|
||||||
|
{
|
||||||
|
this.renderThread = Thread.currentThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QueuedRunnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
|
||||||
while(runnable != null)
|
while(runnable != null)
|
||||||
{
|
{
|
||||||
|
long taskStartNano = System.nanoTime();
|
||||||
|
|
||||||
|
this.currentQueuedRunnable = runnable;
|
||||||
runnable.run();
|
runnable.run();
|
||||||
|
this.currentQueuedRunnable = null;
|
||||||
|
|
||||||
// only try running for a limited amount of time to prevent lag spikes
|
// only try running for a limited amount of time to prevent lag spikes
|
||||||
long currentTimeMs = System.currentTimeMillis();
|
long taskNano = System.nanoTime() - taskStartNano;
|
||||||
long runDuration = currentTimeMs - startTimeMs;
|
long totalLoopNano = System.nanoTime() - loopStartTimeNano;
|
||||||
if (runDuration > msMaxRunTime)
|
|
||||||
|
// stat tracking
|
||||||
|
if (ModInfo.IS_DEV_BUILD)
|
||||||
{
|
{
|
||||||
|
if (!AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.containsKey(runnable.name))
|
||||||
|
{
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.put(runnable.name, new RollingAverage(1_000));
|
||||||
|
}
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.get(runnable.name).add(totalLoopNano);
|
||||||
|
|
||||||
|
COMPLETED_TASK_COUNTER.increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// estimate when our ending nano-time would be once the next task is run
|
||||||
|
long expectedNextTaskNano = totalLoopNano
|
||||||
|
// doubling this task's time gives a rough over-estimate of how long the next task should take
|
||||||
|
+ (taskNano * 2);
|
||||||
|
// If the next task would push us over the max run time, stop now.
|
||||||
|
// This prevents stuttering at the cost of lower throughput.
|
||||||
|
if (expectedNextTaskNano >= nanoMaxRunTime)
|
||||||
|
{
|
||||||
|
if (LOG_SLOW_TASKS
|
||||||
|
&& totalLoopNano > nanoMaxRunTime)
|
||||||
|
{
|
||||||
|
// this task took longer than what we wanted
|
||||||
|
RATE_LIMITED_LOGGER.warn("["+runnable.name+"] slow, actual ["+totalLoopNano+"], allowed ["+nanoMaxRunTime+"].");
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,9 +208,9 @@ public class RenderThreadTaskHandler
|
|||||||
*/
|
*/
|
||||||
private void manualCleanupTick()
|
private void manualCleanupTick()
|
||||||
{
|
{
|
||||||
long nowMs = System.currentTimeMillis();
|
long nowNano = System.nanoTime();
|
||||||
long msSinceLast = nowMs - this.msSinceGlTasksRun;
|
long nanoSinceLast = nowNano - this.nanoSinceTasksRun;
|
||||||
if (msSinceLast > MS_BEFORE_RUN_CLEANUP_TIMER)
|
if (nanoSinceLast < NANOS_BEFORE_RUN_CLEANUP_TIMER)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -132,11 +219,186 @@ public class RenderThreadTaskHandler
|
|||||||
// this means we could have GL jobs building up.
|
// this means we could have GL jobs building up.
|
||||||
// Run the queued tasks on MC's executor (hopefully this should always run,
|
// Run the queued tasks on MC's executor (hopefully this should always run,
|
||||||
// even if DH's render code isn't being hit).
|
// even if DH's render code isn't being hit).
|
||||||
IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
IMinecraftClientWrapper mcClient = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
MC.executeOnRenderThread(() -> this.runRenderThreadTasks(1_000));
|
if (mcClient != null)
|
||||||
|
{
|
||||||
|
mcClient.executeOnRenderThread(() -> this.runRenderThreadTasks(500 * 1_000_000L));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// shouldn't happen, but just in case
|
||||||
|
|
||||||
|
// somehow the timer started when there wasn't a client wrapper
|
||||||
|
// this probably means the timer was started on a dedicated server
|
||||||
|
RATE_LIMITED_LOGGER.warn("["+RenderThreadTaskHandler.class.getSimpleName()+"] timer started when ["+IMinecraftClientWrapper.class.getSimpleName()+"] is null. This shouldn't happen but can likely be ignored.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//end region
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===========//
|
||||||
|
// debugging //
|
||||||
|
//===========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if tasks are currently queued the debug
|
||||||
|
* stats may not be zero after this method has been called.
|
||||||
|
*/
|
||||||
|
public void clearDebugStats()
|
||||||
|
{
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.clear();
|
||||||
|
COMPLETED_TASK_COUNTER.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDebugMenuStringsToList(List<String> messageList)
|
||||||
|
{
|
||||||
|
if (!ModInfo.IS_DEV_BUILD)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String o = MinecraftTextFormat.ORANGE;
|
||||||
|
String g = MinecraftTextFormat.GREEN;
|
||||||
|
String b = MinecraftTextFormat.DARK_BLUE;
|
||||||
|
String y = MinecraftTextFormat.YELLOW;
|
||||||
|
String cf = MinecraftTextFormat.CLEAR_FORMATTING;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String queueSize = DECIMAL_NUMBER_FORMAT.format(RENDER_THREAD_RUNNABLE_QUEUE.size());
|
||||||
|
String completedCount = DECIMAL_NUMBER_FORMAT.format(COMPLETED_TASK_COUNTER.sum());
|
||||||
|
|
||||||
|
String messageHeader = "Render Tasks, Queue: "+o+queueSize+cf+", Done: "+g+completedCount+cf;
|
||||||
|
messageList.add(messageHeader);
|
||||||
|
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.forEach((name, rollingAverage) ->
|
||||||
|
{
|
||||||
|
// thread runtime
|
||||||
|
String runTimeAvgStr;
|
||||||
|
double runTimeAvgInNano = rollingAverage.getAverage();
|
||||||
|
if (!Double.isNaN(runTimeAvgInNano))
|
||||||
|
{
|
||||||
|
double runTimeAvgInMs = runTimeAvgInNano / 1_000_000.0;
|
||||||
|
runTimeAvgStr = DECIMAL_NUMBER_FORMAT.format(runTimeAvgInMs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runTimeAvgStr = "<0";
|
||||||
|
}
|
||||||
|
|
||||||
|
String lifetimeCount = INT_NUMBER_FORMAT.format(rollingAverage.getLifetimeCount());
|
||||||
|
|
||||||
|
String message = name+" Avg: "+b+runTimeAvgStr+"ms"+cf+" #: "+y+lifetimeCount+cf;
|
||||||
|
messageList.add(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns true if the currently running thread is being run by this handler */
|
||||||
|
public boolean isCurrentThread()
|
||||||
|
{
|
||||||
|
if (this.renderThread != null)
|
||||||
|
{
|
||||||
|
return Thread.currentThread() == this.renderThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't normally be needed, but can be used if this
|
||||||
|
// handler hasn't been run yet
|
||||||
|
return Thread.currentThread().getName().equals("Render thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only recommended to be used by the task that's currently being run.
|
||||||
|
* Use {@link RenderThreadTaskHandler#isCurrentThread()} to check. <br>
|
||||||
|
* Can be used to get stack traces for render thread tasks while they're being run.
|
||||||
|
*/
|
||||||
|
public @Nullable QueuedRunnable getCurrentlyRunningTask() { return this.currentQueuedRunnable; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// helper classes //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public static class QueuedRunnable implements Runnable
|
||||||
|
{
|
||||||
|
/** used to easily track what's being done on the render thread */
|
||||||
|
public final String name;
|
||||||
|
public final Runnable renderCall;
|
||||||
|
/** will be null on release build to reduce GC pressure */
|
||||||
|
@Nullable
|
||||||
|
public final StackTraceElement[] stackTrace;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public QueuedRunnable(String name, Runnable renderCall, @Nullable StackTraceElement[] stackTrace)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.renderCall = renderCall;
|
||||||
|
this.stackTrace = stackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=========//
|
||||||
|
// running //
|
||||||
|
//=========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.renderCall.run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeException error = new RuntimeException("Uncaught Exception during GL call execution. StackTrace: ["+(this.stackTrace != null ? "Present" : "Missing")+"] Error: ["+e.getMessage()+"]", e);
|
||||||
|
if (this.stackTrace != null)
|
||||||
|
{
|
||||||
|
error.setStackTrace(this.stackTrace);
|
||||||
|
}
|
||||||
|
LOGGER.error("[" + Thread.currentThread().getName() + "] ran into an unexpected error running a GL call, Error: ["+ e.getMessage() +"].", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() { return this.name; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+14
-15
@@ -7,9 +7,9 @@ import com.seibel.distanthorizons.core.logging.DhLogger;
|
|||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.RenderParams;
|
import com.seibel.distanthorizons.core.render.RenderParams;
|
||||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
import com.seibel.distanthorizons.core.util.math.DhMat4f;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3f;
|
import com.seibel.distanthorizons.core.util.math.DhVec3f;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -35,8 +35,8 @@ public abstract class AbstractDebugWireframeRenderer implements IBindable
|
|||||||
protected final PriorityBlockingQueue<BoxParticle> particles = new PriorityBlockingQueue<>();
|
protected final PriorityBlockingQueue<BoxParticle> particles = new PriorityBlockingQueue<>();
|
||||||
|
|
||||||
// used when rendering
|
// used when rendering
|
||||||
protected Mat4f dhMvmProjMatrixThisFrame;
|
protected DhMat4f dhMvmProjMatrixThisFrame;
|
||||||
protected Vec3f camPosFloatThisFrame;
|
protected DhVec3f camPosFloatThisFrame;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -47,9 +47,8 @@ public abstract class AbstractDebugWireframeRenderer implements IBindable
|
|||||||
|
|
||||||
public void render(RenderParams renderParams)
|
public void render(RenderParams renderParams)
|
||||||
{
|
{
|
||||||
this.dhMvmProjMatrixThisFrame = new Mat4f(renderParams.dhMvmProjMatrix);
|
this.dhMvmProjMatrixThisFrame = new DhMat4f(renderParams.dhMvmProjMatrix);
|
||||||
Vec3d camPos = MC_RENDER.getCameraExactPosition();
|
this.camPosFloatThisFrame = new DhVec3f(renderParams.exactCameraPosition);
|
||||||
this.camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z);
|
|
||||||
|
|
||||||
|
|
||||||
this.rendererLists.render(this);
|
this.rendererLists.render(this);
|
||||||
@@ -113,8 +112,8 @@ public abstract class AbstractDebugWireframeRenderer implements IBindable
|
|||||||
|
|
||||||
public static final class Box
|
public static final class Box
|
||||||
{
|
{
|
||||||
public Vec3f minPos;
|
public DhVec3f minPos;
|
||||||
public Vec3f maxPos;
|
public DhVec3f maxPos;
|
||||||
public Color color;
|
public Color color;
|
||||||
|
|
||||||
|
|
||||||
@@ -128,13 +127,13 @@ public abstract class AbstractDebugWireframeRenderer implements IBindable
|
|||||||
int maxBlockPosX = minBlockPosX + DhSectionPos.getBlockWidth(pos);
|
int maxBlockPosX = minBlockPosX + DhSectionPos.getBlockWidth(pos);
|
||||||
int maxBlockPosZ = minBlockPosZ + DhSectionPos.getBlockWidth(pos);
|
int maxBlockPosZ = minBlockPosZ + DhSectionPos.getBlockWidth(pos);
|
||||||
|
|
||||||
this.minPos = new Vec3f(minBlockPosX + edgeOffset, minY, minBlockPosZ + edgeOffset);
|
this.minPos = new DhVec3f(minBlockPosX + edgeOffset, minY, minBlockPosZ + edgeOffset);
|
||||||
this.maxPos = new Vec3f(maxBlockPosX - edgeOffset, maxY, maxBlockPosZ - edgeOffset);
|
this.maxPos = new DhVec3f(maxBlockPosX - edgeOffset, maxY, maxBlockPosZ - edgeOffset);
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** only used for */
|
/** only used for */
|
||||||
public Box(Vec3f minPos, Vec3f maxPos, Color color)
|
public Box(DhVec3f minPos, DhVec3f maxPos, Color color)
|
||||||
{
|
{
|
||||||
this.minPos = minPos;
|
this.minPos = minPos;
|
||||||
this.maxPos = maxPos;
|
this.maxPos = maxPos;
|
||||||
@@ -177,8 +176,8 @@ public abstract class AbstractDebugWireframeRenderer implements IBindable
|
|||||||
float yDiff = this.yChange * percent;
|
float yDiff = this.yChange * percent;
|
||||||
|
|
||||||
return new Box(
|
return new Box(
|
||||||
new Vec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z),
|
new DhVec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z),
|
||||||
new Vec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z),
|
new DhVec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z),
|
||||||
this.box.color);
|
this.box.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+129
-127
@@ -34,7 +34,7 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
|||||||
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.RenderUtil;
|
import com.seibel.distanthorizons.core.util.RenderUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
||||||
@@ -46,7 +46,6 @@ import java.util.*;
|
|||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class BeaconRenderHandler
|
public class BeaconRenderHandler
|
||||||
{
|
{
|
||||||
@@ -57,8 +56,6 @@ public class BeaconRenderHandler
|
|||||||
/** how often should we check if a beacon should be culled? */
|
/** how often should we check if a beacon should be culled? */
|
||||||
private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000;
|
private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000;
|
||||||
|
|
||||||
private static final Comparator<BeaconBeamDTO> NEGATIVE_BLOCKPOS_COMPARATOR = new NegativeInfiniteBlockPosComparator();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final ReentrantLock updateLock = new ReentrantLock();
|
private final ReentrantLock updateLock = new ReentrantLock();
|
||||||
@@ -67,8 +64,6 @@ public class BeaconRenderHandler
|
|||||||
private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup;
|
private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup;
|
||||||
/** contains all beacons that could be rendered (including those that are being culled) */
|
/** contains all beacons that could be rendered (including those that are being culled) */
|
||||||
private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
|
private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
|
||||||
/** contains all beacons that could be rendered */
|
|
||||||
private final HashSet<DhBlockPos> fullBeaconBlockPosSet = new HashSet<>();
|
|
||||||
|
|
||||||
private boolean cullingThreadRunning = false;
|
private boolean cullingThreadRunning = false;
|
||||||
private boolean updateRenderDataNextFrame = false;
|
private boolean updateRenderDataNextFrame = false;
|
||||||
@@ -96,125 +91,11 @@ public class BeaconRenderHandler
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//===============//
|
||||||
// render handling //
|
// before render //
|
||||||
//=================//
|
//===============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.updateLock.lock();
|
|
||||||
|
|
||||||
|
|
||||||
// how wide should each beacon be?
|
|
||||||
int beaconBlockWidth = 1;
|
|
||||||
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
|
||||||
{
|
|
||||||
beaconBlockWidth = DhSectionPos.getBlockWidth(detailLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ArrayList<BeaconBeamDTO> sortedBeaconList = new ArrayList<>(beaconList);
|
|
||||||
|
|
||||||
// merge distant beams if requested
|
|
||||||
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
|
||||||
{
|
|
||||||
// sort beacons from neg inf -> pos inf
|
|
||||||
// so we can consistently merge adjacent beacons
|
|
||||||
sortedBeaconList.sort(NEGATIVE_BLOCKPOS_COMPARATOR);
|
|
||||||
|
|
||||||
// go through each beacon...
|
|
||||||
for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++)
|
|
||||||
{
|
|
||||||
BeaconBeamDTO outerBeacon = sortedBeaconList.get(outerIndex);
|
|
||||||
DhBlockPos outerBlockPos = outerBeacon.blockPos;
|
|
||||||
|
|
||||||
// ...and remove any beacons that are within the block width to prevent overlaps
|
|
||||||
for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++)
|
|
||||||
{
|
|
||||||
BeaconBeamDTO beaconToMerge = sortedBeaconList.get(mergeIndex);
|
|
||||||
DhBlockPos mergeBlockPos = beaconToMerge.blockPos;
|
|
||||||
|
|
||||||
int xDiff = mergeBlockPos.getX() - outerBlockPos.getX();
|
|
||||||
int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ();
|
|
||||||
|
|
||||||
// merge (remove) this beacon if
|
|
||||||
// it's close to the outer beacon
|
|
||||||
if (xDiff < beaconBlockWidth
|
|
||||||
&& zDiff < beaconBlockWidth)
|
|
||||||
{
|
|
||||||
sortedBeaconList.remove(mergeIndex);
|
|
||||||
mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//LOGGER.info("startRenderingBeacons ["+sortedBeaconList+"]");
|
|
||||||
|
|
||||||
// add each beacon to the renderer
|
|
||||||
for (int i = 0; i < sortedBeaconList.size(); i++)
|
|
||||||
{
|
|
||||||
BeaconBeamDTO beacon = sortedBeaconList.get(i);
|
|
||||||
if (!this.fullBeaconBlockPosSet.add(beacon.blockPos))
|
|
||||||
{
|
|
||||||
// skip already present beacons
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get();
|
|
||||||
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
|
|
||||||
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
|
|
||||||
new DhApiVec3d(beacon.blockPos.getX() + beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beaconBlockWidth),
|
|
||||||
beacon.color,
|
|
||||||
EDhApiBlockMaterial.ILLUMINATED
|
|
||||||
);
|
|
||||||
|
|
||||||
this.activeBeaconBoxRenderGroup.add(beaconBox);
|
|
||||||
this.fullBeaconBoxList.add(beaconBox);
|
|
||||||
this.activeBeaconBoxRenderGroup.triggerBoxChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.updateLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopRenderingBeaconsInRange(long pos)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.updateLock.lock();
|
|
||||||
|
|
||||||
Predicate<DhApiRenderableBox> removeBoxPredicate = (DhApiRenderableBox box) ->
|
|
||||||
{
|
|
||||||
DhBlockPos blockPos = new DhBlockPos((int)box.minPos.x, (int)box.minPos.y, (int)box.minPos.z);
|
|
||||||
boolean contains = DhSectionPos.contains(pos, blockPos);
|
|
||||||
//if (contains)
|
|
||||||
//{
|
|
||||||
// LOGGER.info("stopRenderingBeaconsInRange ["+DhSectionPos.toString(pos)+"] ["+blockPos+"]");
|
|
||||||
//}
|
|
||||||
return contains;
|
|
||||||
};
|
|
||||||
this.activeBeaconBoxRenderGroup.removeIf(removeBoxPredicate);
|
|
||||||
this.fullBeaconBoxList.removeIf(removeBoxPredicate);
|
|
||||||
|
|
||||||
this.fullBeaconBlockPosSet.removeIf((DhBlockPos blockPos) -> DhSectionPos.contains(pos, blockPos));
|
|
||||||
|
|
||||||
this.activeBeaconBoxRenderGroup.triggerBoxChange();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.updateLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void beforeRender(DhApiRenderParam renderEventParam)
|
private void beforeRender(DhApiRenderParam renderEventParam)
|
||||||
{
|
{
|
||||||
if (Config.Client.Advanced.Graphics.Culling.disableBeaconDistanceCulling.get())
|
if (Config.Client.Advanced.Graphics.Culling.disableBeaconDistanceCulling.get())
|
||||||
@@ -257,7 +138,7 @@ public class BeaconRenderHandler
|
|||||||
// lock to make sure we don't try adding beacons to the arrays while processing them
|
// lock to make sure we don't try adding beacons to the arrays while processing them
|
||||||
this.updateLock.lock();
|
this.updateLock.lock();
|
||||||
|
|
||||||
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
DhVec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
||||||
|
|
||||||
// fading by the overdraw prevention amount helps reduce beacons from rendering strangely
|
// fading by the overdraw prevention amount helps reduce beacons from rendering strangely
|
||||||
// on the border of DH's render distance
|
// on the border of DH's render distance
|
||||||
@@ -275,7 +156,7 @@ public class BeaconRenderHandler
|
|||||||
for (DhApiRenderableBox box : this.fullBeaconBoxList)
|
for (DhApiRenderableBox box : this.fullBeaconBoxList)
|
||||||
{
|
{
|
||||||
// if a beacon is outside the vanilla render distance render it
|
// if a beacon is outside the vanilla render distance render it
|
||||||
double distance = Vec3d.getHorizontalDistance(cameraPos, box.minPos);
|
double distance = DhVec3d.getHorizontalDistance(cameraPos, box.minPos);
|
||||||
if (distance > dhFadeDistance)
|
if (distance > dhFadeDistance)
|
||||||
{
|
{
|
||||||
this.activeBeaconBoxRenderGroup.add(box);
|
this.activeBeaconBoxRenderGroup.add(box);
|
||||||
@@ -304,15 +185,136 @@ public class BeaconRenderHandler
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// registration //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public void replaceRenderingBeacons(ArrayList<BeaconBeamWithWidth> beaconList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.updateLock.lock();
|
||||||
|
|
||||||
|
ArrayList<BeaconBeamWithWidth> sortedBeaconList = new ArrayList<>(beaconList);
|
||||||
|
|
||||||
|
// merge distant beams if requested
|
||||||
|
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
||||||
|
{
|
||||||
|
// sort beacons from neg inf -> pos inf
|
||||||
|
// so we can consistently merge adjacent beacons
|
||||||
|
sortedBeaconList.sort(NegativeInfiniteBlockPosComparator.INSTANCE);
|
||||||
|
|
||||||
|
// go through each beacon...
|
||||||
|
for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++)
|
||||||
|
{
|
||||||
|
BeaconBeamWithWidth outerBeacon = sortedBeaconList.get(outerIndex);
|
||||||
|
DhBlockPos outerBlockPos = outerBeacon.blockPos;
|
||||||
|
|
||||||
|
// ...and remove any beacons that are within the block width to prevent overlaps
|
||||||
|
for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++)
|
||||||
|
{
|
||||||
|
BeaconBeamWithWidth beaconToMerge = sortedBeaconList.get(mergeIndex);
|
||||||
|
DhBlockPos mergeBlockPos = beaconToMerge.blockPos;
|
||||||
|
|
||||||
|
int xDiff = mergeBlockPos.getX() - outerBlockPos.getX();
|
||||||
|
int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ();
|
||||||
|
|
||||||
|
// merge (remove) this beacon if
|
||||||
|
// it's close to the outer beacon
|
||||||
|
if (xDiff < beaconToMerge.beaconBlockWidth
|
||||||
|
&& zDiff < beaconToMerge.beaconBlockWidth)
|
||||||
|
{
|
||||||
|
sortedBeaconList.remove(mergeIndex);
|
||||||
|
mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.activeBeaconBoxRenderGroup.clear();
|
||||||
|
this.fullBeaconBoxList.clear();
|
||||||
|
|
||||||
|
// add each beacon to the renderer
|
||||||
|
for (int i = 0; i < sortedBeaconList.size(); i++)
|
||||||
|
{
|
||||||
|
BeaconBeamWithWidth beacon = sortedBeaconList.get(i);
|
||||||
|
int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get();
|
||||||
|
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
|
||||||
|
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
|
||||||
|
new DhApiVec3d(beacon.blockPos.getX() + beacon.beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beacon.beaconBlockWidth),
|
||||||
|
beacon.color,
|
||||||
|
EDhApiBlockMaterial.ILLUMINATED
|
||||||
|
);
|
||||||
|
|
||||||
|
this.activeBeaconBoxRenderGroup.add(beaconBox);
|
||||||
|
this.fullBeaconBoxList.add(beaconBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeBeaconBoxRenderGroup.triggerBoxChange();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.updateLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// helper classes //
|
// helper classes //
|
||||||
//================//
|
//================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO>
|
public static class BeaconBeamWithWidth extends BeaconBeamDTO
|
||||||
{
|
{
|
||||||
|
public final int beaconBlockWidth;
|
||||||
|
|
||||||
|
public BeaconBeamWithWidth(BeaconBeamDTO beaconBeamDTO, byte lodDetailLevel)
|
||||||
|
{
|
||||||
|
super(beaconBeamDTO.blockPos, beaconBeamDTO.color);
|
||||||
|
|
||||||
|
|
||||||
|
// how wide should each beacon be?
|
||||||
|
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
||||||
|
{
|
||||||
|
this.beaconBlockWidth = DhSectionPos.getBlockWidth(lodDetailLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.beaconBlockWidth = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(BeaconBeamDTO beacon1, BeaconBeamDTO beacon2)
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (obj == null
|
||||||
|
|| obj.getClass() != this.getClass())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeaconBeamWithWidth that = (BeaconBeamWithWidth) obj;
|
||||||
|
if (that.beaconBlockWidth != this.beaconBlockWidth)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.equals(that);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamWithWidth>
|
||||||
|
{
|
||||||
|
public static final NegativeInfiniteBlockPosComparator INSTANCE = new NegativeInfiniteBlockPosComparator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(BeaconBeamWithWidth beacon1, BeaconBeamWithWidth beacon2)
|
||||||
{
|
{
|
||||||
DhBlockPos blockPos1 = beacon1.blockPos;
|
DhBlockPos blockPos1 = beacon1.blockPos;
|
||||||
DhBlockPos blockPos2 = beacon2.blockPos;
|
DhBlockPos blockPos2 = beacon2.blockPos;
|
||||||
|
|||||||
+111
-78
@@ -31,8 +31,8 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
import com.seibel.distanthorizons.core.util.math.DhVec3d;
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3f;
|
import com.seibel.distanthorizons.core.util.math.DhVec3f;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
@@ -74,31 +74,31 @@ public class CloudRenderHandler
|
|||||||
*/
|
*/
|
||||||
private static final int CLOUD_INSTANCE_RADIUS_COUNT = 5;
|
private static final int CLOUD_INSTANCE_RADIUS_COUNT = 5;
|
||||||
|
|
||||||
|
/** if multi-layer clouds are enabled how many layers should be rendered? */
|
||||||
|
private static final int CLOUD_LAYER_COUNT = 3;
|
||||||
|
|
||||||
private static final float MOVE_SPEED_IN_BLOCKS_PER_SECOND = 6.0f;
|
private static final float MOVE_SPEED_IN_BLOCKS_PER_SECOND = 6.0f;
|
||||||
|
|
||||||
|
|
||||||
private final IDhApiRenderableBoxGroup[][] boxGroupByOffset
|
private final IDhApiRenderableBoxGroup[][][] boxGroupByOffset
|
||||||
// radius * 2 to get the diameter
|
// radius * 2 to get the diameter
|
||||||
// + 1 so we get an odd number wide (needed so we can have a center position)
|
// + 1 so we get an odd number wide (needed so we can have a center position)
|
||||||
= new IDhApiRenderableBoxGroup[(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1][(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1];
|
= new IDhApiRenderableBoxGroup[CLOUD_LAYER_COUNT][(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1][(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1];
|
||||||
|
|
||||||
private final IDhClientLevel level;
|
private final IDhClientLevel level;
|
||||||
private final IDhGenericRenderer renderer;
|
private final IDhGenericRenderer renderer;
|
||||||
|
|
||||||
/** cached array so we don't need to re-create it each frame for each cloud group */
|
/** cached array so we don't need to re-create it each frame for each cloud group */
|
||||||
private final Vec3d[] cullingCorners = new Vec3d[]
|
private final DhVec3d[] cullingCorners = new DhVec3d[]
|
||||||
{
|
{
|
||||||
// the values of each will be overwritten during the culling pass
|
// the values of each will be overwritten during the culling pass
|
||||||
new Vec3d(),
|
new DhVec3d(),
|
||||||
new Vec3d(),
|
new DhVec3d(),
|
||||||
new Vec3d(),
|
new DhVec3d(),
|
||||||
new Vec3d(),
|
new DhVec3d(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
private boolean disabledWarningLogged = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
@@ -263,32 +263,42 @@ public class CloudRenderHandler
|
|||||||
|
|
||||||
// slightly lighter shading than the default
|
// slightly lighter shading than the default
|
||||||
DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded();
|
DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded();
|
||||||
cloudShading.north = cloudShading.south = 0.9f;
|
|
||||||
cloudShading.east = cloudShading.west = 0.8f;
|
|
||||||
cloudShading.top = 1.0f;
|
|
||||||
cloudShading.bottom = 0.7f;
|
|
||||||
|
|
||||||
|
|
||||||
for (int x = -CLOUD_INSTANCE_RADIUS_COUNT; x <= CLOUD_INSTANCE_RADIUS_COUNT; x++)
|
|
||||||
{
|
{
|
||||||
for (int z = -CLOUD_INSTANCE_RADIUS_COUNT; z <= CLOUD_INSTANCE_RADIUS_COUNT; z++)
|
cloudShading.north = 0.9f;
|
||||||
|
cloudShading.south = cloudShading.north;
|
||||||
|
|
||||||
|
cloudShading.east = 0.8f;
|
||||||
|
cloudShading.west = cloudShading.east;
|
||||||
|
|
||||||
|
cloudShading.top = 1.0f;
|
||||||
|
cloudShading.bottom = 0.7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = CLOUD_LAYER_COUNT-1; y >= 0; y--) // start from the top down so transparency could be attempted
|
||||||
|
{
|
||||||
|
for (int x = -CLOUD_INSTANCE_RADIUS_COUNT; x <= CLOUD_INSTANCE_RADIUS_COUNT; x++)
|
||||||
{
|
{
|
||||||
IDhApiRenderableBoxGroup boxGroup = GENERIC_OBJECT_FACTORY.createRelativePositionedGroup(
|
for (int z = -CLOUD_INSTANCE_RADIUS_COUNT; z <= CLOUD_INSTANCE_RADIUS_COUNT; z++)
|
||||||
|
{
|
||||||
|
IDhApiRenderableBoxGroup boxGroup = GENERIC_OBJECT_FACTORY.createRelativePositionedGroup(
|
||||||
ModInfo.NAME + ":Clouds",
|
ModInfo.NAME + ":Clouds",
|
||||||
new DhApiVec3d(0, 0, 0), // the offset will be set during rendering
|
new DhApiVec3d(0, 0, 0), // the offset will be set during rendering
|
||||||
boxList);
|
boxList);
|
||||||
|
|
||||||
// since cloud colors are set by the level based on the time of day lighting should affect it
|
// since cloud colors are set by the level based on the time of day lighting should affect it
|
||||||
boxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
|
boxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
|
||||||
boxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
|
boxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
|
||||||
boxGroup.setSsaoEnabled(false);
|
boxGroup.setSsaoEnabled(false);
|
||||||
boxGroup.setShading(cloudShading);
|
boxGroup.setShading(cloudShading);
|
||||||
|
|
||||||
CloudParams cloudParams = new CloudParams(textureWidth, x, z);
|
CloudParams cloudParams = new CloudParams(
|
||||||
boxGroup.setPreRenderFunc((renderParam) -> this.preRender(renderParam, cloudParams));
|
textureWidth,
|
||||||
|
y, x, z);
|
||||||
|
boxGroup.setPreRenderFunc((renderParam) -> this.preRender(renderParam, cloudParams));
|
||||||
|
|
||||||
renderer.add(boxGroup);
|
this.renderer.add(boxGroup);
|
||||||
this.boxGroupByOffset[x+CLOUD_INSTANCE_RADIUS_COUNT][z+CLOUD_INSTANCE_RADIUS_COUNT] = boxGroup;
|
this.boxGroupByOffset[y][x + CLOUD_INSTANCE_RADIUS_COUNT][z + CLOUD_INSTANCE_RADIUS_COUNT] = boxGroup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,59 +314,63 @@ public class CloudRenderHandler
|
|||||||
|
|
||||||
private void preRender(DhApiRenderParam renderParam, CloudParams cloudParams)
|
private void preRender(DhApiRenderParam renderParam, CloudParams cloudParams)
|
||||||
{
|
{
|
||||||
IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[cloudParams.instanceOffsetX+CLOUD_INSTANCE_RADIUS_COUNT][cloudParams.instanceOffsetZ+CLOUD_INSTANCE_RADIUS_COUNT];
|
IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[cloudParams.instanceOffsetY][cloudParams.instanceOffsetX+CLOUD_INSTANCE_RADIUS_COUNT][cloudParams.instanceOffsetZ+CLOUD_INSTANCE_RADIUS_COUNT];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
//===================//
|
||||||
// should we render? //
|
// should we render? //
|
||||||
//===================//
|
//===================//
|
||||||
|
//region
|
||||||
|
|
||||||
boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get();
|
boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get();
|
||||||
|
|
||||||
|
boolean renderSingleCloudLayer = !Config.Client.Advanced.Graphics.GenericRendering.enableMultiLayerClouds.get();
|
||||||
|
if (renderSingleCloudLayer
|
||||||
|
&& cloudParams.instanceOffsetY != 0)
|
||||||
|
{
|
||||||
|
renderClouds = false;
|
||||||
|
}
|
||||||
|
|
||||||
boxGroup.setActive(renderClouds);
|
boxGroup.setActive(renderClouds);
|
||||||
if(!renderClouds)
|
if(!renderClouds)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (!this.renderer.getInstancedRenderingAvailable())
|
|
||||||
//{
|
|
||||||
// if (!this.disabledWarningLogged)
|
|
||||||
// {
|
|
||||||
// this.disabledWarningLogged = true;
|
|
||||||
// LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled.");
|
|
||||||
// }
|
|
||||||
// boxGroup.setActive(false);
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
IClientLevelWrapper clientLevelWrapper = this.level.getClientLevelWrapper();
|
IClientLevelWrapper clientLevelWrapper = this.level.getClientLevelWrapper();
|
||||||
if (clientLevelWrapper == null)
|
if (clientLevelWrapper == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// cloud movement //
|
// cloud movement //
|
||||||
//================//
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
float deltaTime = (currentTime - cloudParams.lastFrameTime) / 1000.0f; // Delta time in seconds
|
float deltaTime = (currentTime - cloudParams.lastFrameTime) / 1000.0f; // Delta time in seconds
|
||||||
cloudParams.lastFrameTime = currentTime;
|
cloudParams.lastFrameTime = currentTime;
|
||||||
|
|
||||||
float deltaX = MOVE_SPEED_IN_BLOCKS_PER_SECOND * deltaTime;
|
float deltaX = (MOVE_SPEED_IN_BLOCKS_PER_SECOND + cloudParams.heightSpeedOffset) * deltaTime;
|
||||||
// negative delta is to match vanilla's cloud movement
|
// negative delta is to match vanilla's cloud movement
|
||||||
cloudParams.deltaOffsetX -= deltaX;
|
cloudParams.deltaOffsetX -= deltaX;
|
||||||
// wrap the cloud around after reaching the edge
|
// wrap the cloud around after reaching the edge
|
||||||
cloudParams.deltaOffsetX %= cloudParams.widthInBlocks;
|
cloudParams.deltaOffsetX %= cloudParams.widthInBlocks;
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============================//
|
//============================//
|
||||||
// camera movement and offset //
|
// camera movement and offset //
|
||||||
//============================//
|
//============================//
|
||||||
|
//region
|
||||||
|
|
||||||
// camera position
|
// camera position
|
||||||
int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x;
|
int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x;
|
||||||
@@ -374,13 +388,17 @@ public class CloudRenderHandler
|
|||||||
|
|
||||||
|
|
||||||
float newMinPosX =
|
float newMinPosX =
|
||||||
cloudParams.deltaOffsetX
|
cloudParams.deltaOffsetX
|
||||||
+ (cloudParams.instanceOffsetX * cloudParams.widthInBlocks)
|
+ (cloudParams.instanceOffsetX * cloudParams.widthInBlocks)
|
||||||
+ instanceOffsetX + cloudParams.halfWidthInBlocks;
|
+ instanceOffsetX + cloudParams.halfWidthInBlocks;
|
||||||
float newMinPosY = this.level.getLevelWrapper().getMaxHeight() + 200;
|
float newMinPosY =
|
||||||
float newMinPosZ = cloudParams.deltaOffsetZ
|
this.level.getLevelWrapper().getMaxHeight()
|
||||||
+ (cloudParams.instanceOffsetZ * cloudParams.widthInBlocks)
|
+ 100 // render clouds at least 200 blocks above the height limit to prevent players/blocks from intersecting (since DH always renders behind everything else)
|
||||||
+ instanceOffsetZ + cloudParams.halfWidthInBlocks;
|
+ cloudParams.heightOffset;
|
||||||
|
float newMinPosZ =
|
||||||
|
cloudParams.deltaOffsetZ
|
||||||
|
+ (cloudParams.instanceOffsetZ * cloudParams.widthInBlocks)
|
||||||
|
+ instanceOffsetZ + cloudParams.halfWidthInBlocks;
|
||||||
|
|
||||||
boolean cullCloud = this.shouldCloudBeCulled(
|
boolean cullCloud = this.shouldCloudBeCulled(
|
||||||
newMinPosX, newMinPosY, newMinPosZ,
|
newMinPosX, newMinPosY, newMinPosZ,
|
||||||
@@ -391,28 +409,25 @@ public class CloudRenderHandler
|
|||||||
boxGroup.setActive(false);
|
boxGroup.setActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boxGroup.setOriginBlockPos(new DhApiVec3d(newMinPosX, newMinPosY, newMinPosZ));
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========================//
|
//===========================//
|
||||||
// update color and position //
|
// update color and position //
|
||||||
//===========================//
|
//===========================//
|
||||||
|
//region
|
||||||
|
|
||||||
// if debug colors are enabled don't change them
|
// don't replace the debug colors
|
||||||
if (!DEBUG_BORDER_COLORS
|
if (!DEBUG_BORDER_COLORS)
|
||||||
// don't modify cloud groups that aren't active
|
|
||||||
&& boxGroup.isActive())
|
|
||||||
{
|
{
|
||||||
// cloud color changes based on the time of day and weather so we need to get it from the level
|
// cloud color changes based on the time of day and weather so we need to get it from the level
|
||||||
Color newCloudColor = clientLevelWrapper.getCloudColor(renderParam.partialTicks);
|
Color newCloudColor = clientLevelWrapper.getCloudColor(renderParam.partialTicks);
|
||||||
|
|
||||||
|
|
||||||
// all boxes should have the same color, so we can get their current color
|
|
||||||
// via the first box
|
|
||||||
DhApiRenderableBox firstBox = boxGroup.get(0);
|
|
||||||
Color currentBoxColor = firstBox.color;
|
|
||||||
|
|
||||||
// update the boxes if their color should be changed
|
// update the boxes if their color should be changed
|
||||||
if (!newCloudColor.equals(currentBoxColor))
|
if (!newCloudColor.equals(cloudParams.previousColor))
|
||||||
{
|
{
|
||||||
// Note: cloud instances may share boxes
|
// Note: cloud instances may share boxes
|
||||||
// because of that this method may only need to be called once per all clouds
|
// because of that this method may only need to be called once per all clouds
|
||||||
@@ -420,20 +435,20 @@ public class CloudRenderHandler
|
|||||||
{
|
{
|
||||||
box.color = newCloudColor;
|
box.color = newCloudColor;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// trigger an update if this cloud section has a different color
|
|
||||||
if (!cloudParams.previousColor.equals(newCloudColor))
|
|
||||||
{
|
|
||||||
cloudParams.previousColor = newCloudColor;
|
cloudParams.previousColor = newCloudColor;
|
||||||
|
|
||||||
boxGroup.triggerBoxChange();
|
boxGroup.triggerBoxChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boxGroup.setOriginBlockPos(new DhApiVec3d(newMinPosX, newMinPosY, newMinPosZ));
|
//endregion
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* based on the OpenGL spec <br>
|
||||||
|
* https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml
|
||||||
|
*/
|
||||||
|
private float mixColors(float x, float y, float a) { return x * (1 - a) + y * a; }
|
||||||
private boolean shouldCloudBeCulled(
|
private boolean shouldCloudBeCulled(
|
||||||
float minPosX, float minPosY, float minPosZ,
|
float minPosX, float minPosY, float minPosZ,
|
||||||
CloudParams cloudParams)
|
CloudParams cloudParams)
|
||||||
@@ -474,14 +489,17 @@ public class CloudRenderHandler
|
|||||||
this.cullingCorners[3].y = minPosY;
|
this.cullingCorners[3].y = minPosY;
|
||||||
this.cullingCorners[3].z = minPosZ + cloudParams.widthInBlocks;
|
this.cullingCorners[3].z = minPosZ + cloudParams.widthInBlocks;
|
||||||
|
|
||||||
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
DhVec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
||||||
Vec3f cameraLookAtVector = MC_RENDER.getLookAtVector();
|
DhVec3f cameraLookAtVector = MC_RENDER.getLookAtVector();
|
||||||
cameraLookAtVector.normalize();
|
cameraLookAtVector.normalize();
|
||||||
|
|
||||||
double renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()
|
double renderDistance =
|
||||||
// * 1.5 is so we have a little extra buffer where clouds will render further than
|
// minimum distance of 256 to handle 3-layer clouds correctly,
|
||||||
// necessary to prevent seeing the cloud border
|
// otherwise the upper layers will be culled
|
||||||
* LodUtil.CHUNK_WIDTH * 1.5;
|
Math.max(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get(), 256)
|
||||||
|
// * 1.5 is so we have a little extra buffer where clouds will render further than
|
||||||
|
// necessary to prevent seeing the cloud border
|
||||||
|
* LodUtil.CHUNK_WIDTH * 1.5;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -492,14 +510,14 @@ public class CloudRenderHandler
|
|||||||
boolean allOutsideRenderDistance = true;
|
boolean allOutsideRenderDistance = true;
|
||||||
boolean allBehindCamera = true;
|
boolean allBehindCamera = true;
|
||||||
|
|
||||||
for (Vec3d corner : this.cullingCorners)
|
for (DhVec3d corner : this.cullingCorners)
|
||||||
{
|
{
|
||||||
// Check if the corner is within the render distance
|
// Check if the corner is within the render distance
|
||||||
// (ignoring height, since LODs also ignore height)
|
// (ignoring height, since LODs also ignore height)
|
||||||
|
|
||||||
Vec3d cornerNoHeight = new Vec3d(corner);
|
DhVec3d cornerNoHeight = new DhVec3d(corner);
|
||||||
cornerNoHeight.y = 0;
|
cornerNoHeight.y = 0;
|
||||||
Vec3d cameraPosNoHeight = new Vec3d(cameraPos);
|
DhVec3d cameraPosNoHeight = new DhVec3d(cameraPos);
|
||||||
cameraPosNoHeight.y = 0;
|
cameraPosNoHeight.y = 0;
|
||||||
|
|
||||||
double cornerDistance = cornerNoHeight.getDistance(cameraPosNoHeight);
|
double cornerDistance = cornerNoHeight.getDistance(cameraPosNoHeight);
|
||||||
@@ -510,7 +528,7 @@ public class CloudRenderHandler
|
|||||||
|
|
||||||
|
|
||||||
// Check if the corner is in front of the camera (dot product > 0 means in front)
|
// Check if the corner is in front of the camera (dot product > 0 means in front)
|
||||||
Vec3f toCorner = new Vec3f(
|
DhVec3f toCorner = new DhVec3f(
|
||||||
(float) (corner.x - cameraPos.x),
|
(float) (corner.x - cameraPos.x),
|
||||||
(float) (corner.y - cameraPos.y),
|
(float) (corner.y - cameraPos.y),
|
||||||
(float) (corner.z - cameraPos.z));
|
(float) (corner.z - cameraPos.z));
|
||||||
@@ -582,9 +600,13 @@ public class CloudRenderHandler
|
|||||||
public final int widthInBlocks;
|
public final int widthInBlocks;
|
||||||
public final int halfWidthInBlocks;
|
public final int halfWidthInBlocks;
|
||||||
|
|
||||||
|
public final int instanceOffsetY;
|
||||||
public final int instanceOffsetX;
|
public final int instanceOffsetX;
|
||||||
public final int instanceOffsetZ;
|
public final int instanceOffsetZ;
|
||||||
|
|
||||||
|
public final int heightOffset;
|
||||||
|
public final float heightSpeedOffset;
|
||||||
|
|
||||||
|
|
||||||
/** how far this cloud group has moved in the X direction based on time */
|
/** how far this cloud group has moved in the X direction based on time */
|
||||||
public float deltaOffsetX = 0;
|
public float deltaOffsetX = 0;
|
||||||
@@ -600,14 +622,25 @@ public class CloudRenderHandler
|
|||||||
|
|
||||||
// constructor //
|
// constructor //
|
||||||
|
|
||||||
public CloudParams(int textureWidth, int instanceOffsetX, int instanceOffsetZ)
|
public CloudParams(int textureWidth, int instanceOffsetY, int instanceOffsetX, int instanceOffsetZ)
|
||||||
{
|
{
|
||||||
this.textureWidth = textureWidth;
|
this.textureWidth = textureWidth;
|
||||||
this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH);
|
this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH);
|
||||||
this.halfWidthInBlocks = this.widthInBlocks / 2;
|
this.halfWidthInBlocks = this.widthInBlocks / 2;
|
||||||
|
|
||||||
|
this.instanceOffsetY = instanceOffsetY;
|
||||||
this.instanceOffsetX = instanceOffsetX;
|
this.instanceOffsetX = instanceOffsetX;
|
||||||
this.instanceOffsetZ = instanceOffsetZ;
|
this.instanceOffsetZ = instanceOffsetZ;
|
||||||
|
|
||||||
|
|
||||||
|
// each layer up increases by 512 blocks
|
||||||
|
this.heightOffset = instanceOffsetY * 512;
|
||||||
|
// have higher cloud layers move faster
|
||||||
|
this.heightSpeedOffset = instanceOffsetY * 10f;
|
||||||
|
|
||||||
|
// offset each layer a bit so the duplicated texture use isn't as obvious
|
||||||
|
this.deltaOffsetX = this.widthInBlocks * instanceOffsetY * 0.75f;
|
||||||
|
this.deltaOffsetZ = this.widthInBlocks * instanceOffsetY * 1.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
package com.seibel.distanthorizons.core.render.renderer;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode;
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection;
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeFogRenderEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiFogRenderParam;
|
||||||
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderParams;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see DhApiFogRenderParam
|
||||||
|
* @see DhApiBeforeFogRenderEvent.EventParam
|
||||||
|
*/
|
||||||
|
public class FogRenderParamFactory
|
||||||
|
{
|
||||||
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
|
||||||
|
/** cached object to reduce GC pressure */
|
||||||
|
private static final DhApiBeforeFogRenderEvent.EventParam EVENT_PARAM = new DhApiBeforeFogRenderEvent.EventParam();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=========//
|
||||||
|
// methods //
|
||||||
|
//=========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/** returns a cached object to reduce GC pressure */
|
||||||
|
public static DhApiBeforeFogRenderEvent.EventParam getRenderParam(RenderParams renderParams)
|
||||||
|
{
|
||||||
|
Color fogColor = getFogColor(renderParams.partialTicks);
|
||||||
|
|
||||||
|
// far fog
|
||||||
|
EDhApiFogFalloff farFogFalloff = Config.Client.Advanced.Graphics.Fog.farFogFalloff.get();
|
||||||
|
float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get();
|
||||||
|
float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get();
|
||||||
|
float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get();
|
||||||
|
float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get();
|
||||||
|
float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get();
|
||||||
|
|
||||||
|
|
||||||
|
// height fog
|
||||||
|
EDhApiFogFalloff heightFogFalloff = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get();
|
||||||
|
EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get();
|
||||||
|
EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get();
|
||||||
|
|
||||||
|
float heightFogBaseHeight = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get();
|
||||||
|
float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get();
|
||||||
|
float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get();
|
||||||
|
float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get();
|
||||||
|
float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get();
|
||||||
|
float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get();
|
||||||
|
|
||||||
|
// override fog if underwater
|
||||||
|
if (MC_RENDER.isFogStateSpecial())
|
||||||
|
{
|
||||||
|
// hide everything behind fog
|
||||||
|
farFogStart = 0.0f;
|
||||||
|
farFogEnd = 1.0f;
|
||||||
|
farFogMin = 1.0f; // minimum fog thickness is 1 so everything renders in fog
|
||||||
|
farFogMax = 1.0f;
|
||||||
|
farFogDensity = 1.0f; // always render max density
|
||||||
|
}
|
||||||
|
|
||||||
|
DhApiFogRenderParam fogRenderParam = new DhApiFogRenderParam(
|
||||||
|
fogColor,
|
||||||
|
|
||||||
|
// far fog
|
||||||
|
farFogFalloff,
|
||||||
|
farFogStart, farFogEnd,
|
||||||
|
farFogMin, farFogMax,
|
||||||
|
farFogDensity,
|
||||||
|
|
||||||
|
// height fog
|
||||||
|
heightFogFalloff,
|
||||||
|
heightFogMixingMode, heightFogCameraDirection,
|
||||||
|
heightFogBaseHeight,
|
||||||
|
heightFogStart, heightFogEnd,
|
||||||
|
heightFogMin, heightFogMax,
|
||||||
|
heightFogDensity
|
||||||
|
);
|
||||||
|
|
||||||
|
EVENT_PARAM.update(renderParams, fogRenderParam);
|
||||||
|
return EVENT_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color getFogColor(float partialTicks)
|
||||||
|
{
|
||||||
|
Color fogColor;
|
||||||
|
|
||||||
|
if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EDhApiFogColorMode.USE_SKY_COLOR
|
||||||
|
// when underwater or special fogs are being used, don't use the sky color
|
||||||
|
&& !MC_RENDER.isFogStateSpecial())
|
||||||
|
{
|
||||||
|
fogColor = MC_RENDER.getSkyColor();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fogColor = MC_RENDER.getFogColor(partialTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fogColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+194
-154
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.render.renderer;
|
package com.seibel.distanthorizons.core.render.renderer;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
|
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
|
||||||
@@ -26,6 +27,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
|||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
import com.seibel.distanthorizons.core.render.RenderParams;
|
import com.seibel.distanthorizons.core.render.RenderParams;
|
||||||
@@ -36,6 +38,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccess
|
|||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is where all the magic happens. <br>
|
* This is where all the magic happens. <br>
|
||||||
* This is where LODs are draw to the world.
|
* This is where LODs are draw to the world.
|
||||||
@@ -101,7 +105,7 @@ public class LodRenderer
|
|||||||
* otherwise it will only render opaque LODs.
|
* otherwise it will only render opaque LODs.
|
||||||
*/
|
*/
|
||||||
public void render(RenderParams renderParams, IProfilerWrapper profiler)
|
public void render(RenderParams renderParams, IProfilerWrapper profiler)
|
||||||
{ this.renderLodPass(renderParams, profiler, false); }
|
{ this.renderTerrain(renderParams, profiler, false); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is designed for Iris to be able
|
* This method is designed for Iris to be able
|
||||||
@@ -110,13 +114,13 @@ public class LodRenderer
|
|||||||
* but shouldn't be activated as per deferWaterRendering.
|
* but shouldn't be activated as per deferWaterRendering.
|
||||||
*/
|
*/
|
||||||
public void renderDeferred(RenderParams renderParams, IProfilerWrapper profiler)
|
public void renderDeferred(RenderParams renderParams, IProfilerWrapper profiler)
|
||||||
{ this.renderLodPass(renderParams, profiler, true); }
|
{ this.renderTerrain(renderParams, profiler, true); }
|
||||||
|
|
||||||
private void renderLodPass(RenderParams renderParams, IProfilerWrapper profiler, boolean runningDeferredPass)
|
private void renderTerrain(RenderParams renderParams, IProfilerWrapper profiler, boolean runningDeferredPass)
|
||||||
{
|
{
|
||||||
//====================//
|
//===============//
|
||||||
// validate rendering //
|
// validate pass //
|
||||||
//====================//
|
//===============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
boolean deferTransparentRendering = DhApiRenderProxy.INSTANCE.getDeferTransparentRendering();
|
boolean deferTransparentRendering = DhApiRenderProxy.INSTANCE.getDeferTransparentRendering();
|
||||||
@@ -142,180 +146,218 @@ public class LodRenderer
|
|||||||
//=================//
|
//=================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderSetupEvent.class, renderParams);
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderSetupEvent.class, renderParams.apiCopy);
|
||||||
profiler.push("LOD GL setup");
|
try (IProfilerWrapper.IProfileBlock terrainRender_profile = profiler.push("LOD GL setup")) // starts the new profile block for most DH rendering
|
||||||
|
|
||||||
if (!this.renderersBound)
|
|
||||||
{
|
{
|
||||||
this.bindRenderers();
|
|
||||||
this.renderersBound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderBufferHandler renderBufferHandler = renderParams.renderBufferHandler;
|
if (!this.renderersBound)
|
||||||
IDhGenericRenderer genericRenderer = renderParams.genericRenderer;
|
|
||||||
|
|
||||||
|
|
||||||
this.metaRenderer.runRenderPassSetup(renderParams);
|
|
||||||
|
|
||||||
if (!this.vanillaSettingsOverridden)
|
|
||||||
{
|
|
||||||
// only do this once, that way they can still be reverted if desired
|
|
||||||
if (Config.Client.Advanced.Graphics.overrideVanillaGraphicsSettings.get())
|
|
||||||
{
|
{
|
||||||
LOGGER.info("Overriding vanilla MC settings to better fit Distant Horizons... This behavior can be disabled in the Distant Horizons config.");
|
this.bindRenderers();
|
||||||
|
this.renderersBound = true;
|
||||||
MC.disableVanillaClouds();
|
|
||||||
MC.disableVanillaChunkFadeIn();
|
|
||||||
MC.disableFabulousTransparency();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.vanillaSettingsOverridden = true;
|
RenderBufferHandler renderBufferHandler = renderParams.renderBufferHandler;
|
||||||
}
|
IDhGenericRenderer genericRenderer = renderParams.genericRenderer;
|
||||||
|
|
||||||
if (firstPass)
|
|
||||||
{
|
|
||||||
// we only need to sort/cull the LODs at the start of the frame
|
|
||||||
profiler.popPush("LOD build render list");
|
|
||||||
renderBufferHandler.buildRenderList(renderParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
this.metaRenderer.runRenderPassSetup(renderParams);
|
||||||
|
|
||||||
//===========//
|
if (!this.vanillaSettingsOverridden)
|
||||||
// rendering //
|
|
||||||
//===========//
|
|
||||||
|
|
||||||
if (!runningDeferredPass)
|
|
||||||
{
|
|
||||||
this.metaRenderer.clearDhDepthAndColorTextures(renderParams);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========================//
|
|
||||||
// opaque and non-deferred //
|
|
||||||
// transparent rendering //
|
|
||||||
//=========================//
|
|
||||||
|
|
||||||
// opaque LODs
|
|
||||||
profiler.popPush("LOD Opaque");
|
|
||||||
|
|
||||||
this.renderLodPass(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ true, profiler);
|
|
||||||
|
|
||||||
// custom objects with SSAO
|
|
||||||
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
|
|
||||||
{
|
{
|
||||||
profiler.popPush("Custom Objects");
|
// only do this once, that way they can still be reverted if desired
|
||||||
genericRenderer.render(renderParams, profiler, true);
|
if (Config.Client.Advanced.Graphics.overrideVanillaGraphicsSettings.get())
|
||||||
|
{
|
||||||
|
LOGGER.info("Overriding vanilla MC settings to better fit Distant Horizons... This behavior can be disabled in the Distant Horizons config.");
|
||||||
|
|
||||||
|
MC.disableVanillaClouds();
|
||||||
|
MC.disableVanillaChunkFadeIn();
|
||||||
|
MC.disableFabulousTransparency();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.vanillaSettingsOverridden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSAO
|
if (firstPass)
|
||||||
if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get())
|
|
||||||
{
|
{
|
||||||
profiler.popPush("LOD SSAO");
|
// we only need to sort/cull the LODs at the start of the frame
|
||||||
this.ssaoRenderer.render(renderParams);
|
profiler.popPush("LOD build render list");
|
||||||
}
|
renderBufferHandler.buildRenderList(renderParams);
|
||||||
|
|
||||||
// custom objects without SSAO
|
|
||||||
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
|
|
||||||
{
|
|
||||||
profiler.popPush("Custom Objects");
|
|
||||||
genericRenderer.render(renderParams, profiler, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// combined pass transparent rendering
|
|
||||||
if (!deferTransparentRendering
|
|
||||||
&& Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
|
||||||
{
|
|
||||||
profiler.popPush("LOD Transparent");
|
|
||||||
this.renderLodPass(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
|
||||||
}
|
|
||||||
|
|
||||||
// far plane clip fading
|
|
||||||
if (Config.Client.Advanced.Graphics.Quality.dhFadeFarClipPlane.get()
|
|
||||||
&& IRIS_ACCESSOR == null)
|
|
||||||
{
|
|
||||||
profiler.popPush("Fade Far Clip Fade");
|
|
||||||
this.farFadeRenderer.render(renderParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fog
|
|
||||||
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()
|
|
||||||
// this is done to fix issues with: underwater fog, blindness effect, etc.
|
|
||||||
|| renderParams.vanillaFogEnabled)
|
|
||||||
{
|
|
||||||
profiler.popPush("LOD Fog");
|
|
||||||
|
|
||||||
this.fogRenderer.render(renderParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean renderFog;
|
||||||
//=================//
|
Boolean apiFogOverride = Config.Client.Advanced.Graphics.Fog.enableDhFog.getApiValue();
|
||||||
// debug rendering //
|
if (apiFogOverride != null)
|
||||||
//=================//
|
|
||||||
|
|
||||||
if (Config.Client.Advanced.Debugging.DebugWireframe.enableRendering.get())
|
|
||||||
{
|
{
|
||||||
profiler.popPush("Debug wireframes");
|
// use whatever the API dictates if set
|
||||||
|
// (this could cause issues when underwater if a shader or something
|
||||||
// Note: this can be very slow if a lot of boxes are being rendered
|
// doesn't add their own, but that's relatively unlikely)
|
||||||
this.debugWireframeRenderer.render(renderParams);
|
renderFog = apiFogOverride;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderFog = Config.Client.Advanced.Graphics.Fog.enableDhFog.get();
|
||||||
|
// allow enabling fog when: underwater fog, blind, etc.
|
||||||
|
// otherwise LODs won't appear correctly
|
||||||
|
renderFog |= renderParams.vanillaFogEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DhApiBeforeFogRenderEvent.EventParam fogRenderEventParam = FogRenderParamFactory.getRenderParam(renderParams);
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
//=============================//
|
|
||||||
// Apply to the MC Framebuffer //
|
|
||||||
//=============================//
|
|
||||||
|
|
||||||
boolean cancelApplyShader = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeApplyShaderRenderEvent.class, renderParams);
|
//===========//
|
||||||
if (!cancelApplyShader)
|
// rendering //
|
||||||
|
//===========//
|
||||||
|
|
||||||
|
if (!runningDeferredPass)
|
||||||
{
|
{
|
||||||
profiler.popPush("Apply to MC");
|
// needs to be fired after all the textures have been created/bound
|
||||||
this.metaRenderer.applyToMcTexture(renderParams);
|
boolean clearTextures = !ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeTextureClearEvent.class, renderParams.apiCopy);
|
||||||
}
|
if (clearTextures)
|
||||||
|
{
|
||||||
}
|
this.metaRenderer.clearDhDepthAndColorTextures(renderParams);
|
||||||
else
|
}
|
||||||
{
|
|
||||||
//====================//
|
|
||||||
// deferred rendering //
|
|
||||||
//====================//
|
|
||||||
|
|
||||||
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
|
||||||
{
|
|
||||||
profiler.popPush("LOD Transparent");
|
|
||||||
this.renderLodPass(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
|
||||||
|
|
||||||
|
|
||||||
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()
|
|
||||||
// this is done to fix issues with: underwater fog, blindness effect, etc.
|
//=========================//
|
||||||
|| renderParams.vanillaFogEnabled)
|
// opaque and non-deferred //
|
||||||
|
// transparent rendering //
|
||||||
|
//=========================//
|
||||||
|
|
||||||
|
// opaque LODs
|
||||||
|
profiler.popPush("LOD Opaque");
|
||||||
|
|
||||||
|
this.renderTerrain(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ true, profiler);
|
||||||
|
|
||||||
|
// custom objects with SSAO
|
||||||
|
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
|
||||||
|
{
|
||||||
|
profiler.popPush("Custom Objects");
|
||||||
|
genericRenderer.render(renderParams, profiler, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSAO
|
||||||
|
if (Config.Client.Advanced.Graphics.enableSsao.get())
|
||||||
|
{
|
||||||
|
profiler.popPush("LOD SSAO");
|
||||||
|
this.ssaoRenderer.render(renderParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom objects without SSAO
|
||||||
|
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
|
||||||
|
{
|
||||||
|
profiler.popPush("Custom Objects");
|
||||||
|
genericRenderer.render(renderParams, profiler, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// combined pass transparent rendering
|
||||||
|
if (!deferTransparentRendering
|
||||||
|
&& Config.Client.Advanced.Graphics.Quality.transparency.get() == EDhApiTransparency.COMPLETE)
|
||||||
|
{
|
||||||
|
profiler.popPush("LOD Transparent");
|
||||||
|
this.renderTerrain(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fog
|
||||||
|
boolean cancelFogEvent = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeFogRenderEvent.class, fogRenderEventParam);
|
||||||
|
if (renderFog
|
||||||
|
&& !cancelFogEvent)
|
||||||
{
|
{
|
||||||
profiler.popPush("LOD Fog");
|
profiler.popPush("LOD Fog");
|
||||||
|
|
||||||
this.fogRenderer.render(renderParams);
|
this.fogRenderer.render(renderParams, fogRenderEventParam.getFogRenderParam());
|
||||||
|
}
|
||||||
|
|
||||||
|
// far plane clip fading
|
||||||
|
if (Config.Client.Advanced.Graphics.Quality.dhFadeFarClipPlane.get()
|
||||||
|
&& IRIS_ACCESSOR == null)
|
||||||
|
{
|
||||||
|
profiler.popPush("Fade Far Clip Fade");
|
||||||
|
this.farFadeRenderer.render(renderParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// debug rendering //
|
||||||
|
//=================//
|
||||||
|
|
||||||
|
if (Config.Client.Advanced.Debugging.DebugWireframe.enableRendering.get())
|
||||||
|
{
|
||||||
|
profiler.popPush("Debug wireframes");
|
||||||
|
|
||||||
|
// Note: this can be very slow if a lot of boxes are being rendered
|
||||||
|
this.debugWireframeRenderer.render(renderParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (Config.Client.Advanced.Debugging.PositionFinder.positionFinderEnable.get())
|
||||||
|
{
|
||||||
|
// can be used to find specific positions when debugging
|
||||||
|
this.debugWireframeRenderer.renderBox(new AbstractDebugWireframeRenderer.Box(
|
||||||
|
DhSectionPos.encode(
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderDetailLevel.get().byteValue(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderXPos.get(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderZPos.get()),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderMinBlockY.get(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderMaxBlockY.get(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderMarginPercent.get(),
|
||||||
|
Color.GREEN
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============================//
|
||||||
|
// Apply to the MC Framebuffer //
|
||||||
|
//=============================//
|
||||||
|
|
||||||
|
boolean cancelApplyShader = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeApplyShaderRenderEvent.class, renderParams.apiCopy);
|
||||||
|
if (!cancelApplyShader)
|
||||||
|
{
|
||||||
|
profiler.popPush("Apply to MC");
|
||||||
|
this.metaRenderer.applyToMcTexture(renderParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//====================//
|
||||||
|
// deferred rendering //
|
||||||
|
//====================//
|
||||||
|
|
||||||
|
if (Config.Client.Advanced.Graphics.Quality.transparency.get() == EDhApiTransparency.COMPLETE)
|
||||||
|
{
|
||||||
|
profiler.popPush("LOD Transparent");
|
||||||
|
this.renderTerrain(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
||||||
|
|
||||||
|
|
||||||
|
boolean cancelFogEvent = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeFogRenderEvent.class, fogRenderEventParam);
|
||||||
|
if (renderFog
|
||||||
|
&& !cancelFogEvent)
|
||||||
|
{
|
||||||
|
profiler.popPush("LOD Fog");
|
||||||
|
|
||||||
|
this.fogRenderer.render(renderParams, fogRenderEventParam.getFogRenderParam());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// render cleanup //
|
||||||
|
//================//
|
||||||
|
|
||||||
|
profiler.popPush("LOD cleanup");
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderCleanupEvent.class, renderParams.apiCopy);
|
||||||
|
|
||||||
|
this.metaRenderer.runRenderPassCleanup(renderParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// render cleanup //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
profiler.popPush("LOD cleanup");
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderCleanupEvent.class, renderParams);
|
|
||||||
|
|
||||||
this.metaRenderer.runRenderPassCleanup(renderParams);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// end of internal LOD profiling
|
|
||||||
profiler.pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -327,18 +369,16 @@ public class LodRenderer
|
|||||||
//===============//
|
//===============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private void renderLodPass(IDhTerrainRenderer lodRenderer, RenderBufferHandler lodBufferHandler, RenderParams renderEventParam, boolean opaquePass, IProfilerWrapper profilerWrapper)
|
private void renderTerrain(IDhTerrainRenderer terrainRenderer, RenderBufferHandler lodBufferHandler, RenderParams renderEventParam, boolean opaquePass, IProfilerWrapper profilerWrapper)
|
||||||
{
|
{
|
||||||
//===========//
|
//===========//
|
||||||
// rendering //
|
// rendering //
|
||||||
//===========//
|
//===========//
|
||||||
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
|
|
||||||
|
|
||||||
SortedArraySet<LodBufferContainer> lodBufferContainer = lodBufferHandler.getColumnRenderBuffers();
|
SortedArraySet<LodBufferContainer> lodBufferContainer = lodBufferHandler.getColumnRenderBuffers();
|
||||||
if (lodBufferContainer != null)
|
if (lodBufferContainer != null)
|
||||||
{
|
{
|
||||||
lodRenderer.render(renderEventParam, opaquePass, lodBufferContainer, profilerWrapper);
|
terrainRenderer.render(renderEventParam, opaquePass, lodBufferContainer, profilerWrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-8
@@ -56,7 +56,7 @@ public class RenderableBoxGroup
|
|||||||
|
|
||||||
public boolean active = true;
|
public boolean active = true;
|
||||||
public boolean ssaoEnabled = true;
|
public boolean ssaoEnabled = true;
|
||||||
private boolean vertexDataDirty = true;
|
private boolean vertexDataDirty = false;
|
||||||
|
|
||||||
public byte skyLight = LodUtil.MAX_MC_LIGHT;
|
public byte skyLight = LodUtil.MAX_MC_LIGHT;
|
||||||
public byte blockLight = LodUtil.MIN_MC_LIGHT;
|
public byte blockLight = LodUtil.MIN_MC_LIGHT;
|
||||||
@@ -198,27 +198,27 @@ public class RenderableBoxGroup
|
|||||||
if (this.altVertexBufferContainer.getState() == IDhGenericObjectVertexBufferContainer.EState.READY_TO_UPLOAD)
|
if (this.altVertexBufferContainer.getState() == IDhGenericObjectVertexBufferContainer.EState.READY_TO_UPLOAD)
|
||||||
{
|
{
|
||||||
this.altVertexBufferContainer.uploadDataToGpu();
|
this.altVertexBufferContainer.uploadDataToGpu();
|
||||||
|
this.altVertexBufferContainer.setState(IDhGenericObjectVertexBufferContainer.EState.RENDER);
|
||||||
|
|
||||||
// swap VBO references for rendering
|
// swap VBO references for rendering
|
||||||
IDhGenericObjectVertexBufferContainer temp = this.vertexBufferContainer;
|
IDhGenericObjectVertexBufferContainer temp = this.vertexBufferContainer;
|
||||||
this.vertexBufferContainer = this.altVertexBufferContainer;
|
this.vertexBufferContainer = this.altVertexBufferContainer;
|
||||||
this.altVertexBufferContainer = temp;
|
this.altVertexBufferContainer = temp;
|
||||||
|
|
||||||
this.vertexDataDirty = false;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if the vertex data is already up to date, do nothing
|
// executor should always be available, if not that probably means the level is being shut down
|
||||||
if (!this.vertexDataDirty)
|
PriorityTaskPicker.Executor executor = ThreadPoolUtil.getRenderLoadingExecutor();
|
||||||
|
if (executor == null || executor.isTerminated())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PriorityTaskPicker.Executor executor = ThreadPoolUtil.getRenderLoadingExecutor();
|
// if the vertex data is already up to date, do nothing
|
||||||
if (executor == null || executor.isTerminated())
|
if (!this.vertexDataDirty)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -248,6 +248,10 @@ public class RenderableBoxGroup
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.altVertexBufferContainer.updateVertexData(this.uploadBoxList);
|
this.altVertexBufferContainer.updateVertexData(this.uploadBoxList);
|
||||||
|
this.altVertexBufferContainer.setState(IDhGenericObjectVertexBufferContainer.EState.READY_TO_UPLOAD);
|
||||||
|
|
||||||
|
// upload complete
|
||||||
|
this.vertexDataDirty = false;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -346,7 +350,7 @@ public class RenderableBoxGroup
|
|||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("RenderBoxGroup Close", () ->
|
||||||
{
|
{
|
||||||
this.vertexBufferContainer.close();
|
this.vertexBufferContainer.close();
|
||||||
this.altVertexBufferContainer.close();
|
this.altVertexBufferContainer.close();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user