Compare commits

...

22 Commits

Author SHA1 Message Date
James Seibel 4a3c24f39e Add proof-of-concept dynamic fade 2026-01-17 10:16:35 -06:00
James Seibel 7b6fd03d78 Change render wrapper get Texture error returns 2026-01-17 09:56:21 -06:00
s809 1a540cf2bc Make sure payload chunk is readable 2026-01-14 22:17:46 +05:00
James Seibel 20fc2efb46 Improve concurrent iterating in QuadTree 2026-01-10 17:03:43 -06:00
James Seibel d8beba2498 minor cleanup in LodBufferContainer cleanup 2026-01-10 17:02:56 -06:00
James Seibel 9f0cb5a394 Add forge specific icon/logo
Done to fix a forge limitation where logos can't contain a file pathhttps://github.com/MinecraftForge/MinecraftForge/issues/7348
2026-01-10 11:56:08 -06:00
James Seibel df63401d11 DB updater use correct classloader 2026-01-10 08:21:09 -06:00
James Seibel db95951ade minor reformat and comment 2026-01-10 08:20:44 -06:00
s809 1e020f93a6 Reapply "Run plugin messages on a DH thread"
This reverts commit ff3145336d.
2026-01-09 20:29:23 +05:00
James Seibel 7aee6dfb44 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2026-01-07 07:50:25 -06:00
James Seibel 546a51a295 expand distant beacon beams for visiblity 2026-01-07 07:50:22 -06:00
James Seibel ec7e791e9f Change EMinecraftColor -> MinecraftTextFormat
No need for an enum when all the values are strings
2026-01-06 07:10:40 -06:00
James Seibel d60dec3d82 Merge branch 'main' into 'main'
Fix typo in high vanilla render distance warning

See merge request distant-horizons-team/distant-horizons-core!94
2026-01-05 13:00:17 +00:00
s809 89a80103f0 Wrong message target 2026-01-04 20:04:30 +05:00
s809 8e14a7223c Add a chat message for incompatible messages 2026-01-04 19:36:24 +05:00
meanwhile131 7cf1e901f5 Fix typo in high vanilla render distance warning 2026-01-01 15:06:30 +04:00
James Seibel ba923fa829 Fix neoforge thread causing resource loading to fail 2025-12-26 14:13:27 -06:00
s809 505dbe2f62 Replace the failure state with future exceptions 2025-12-27 00:51:30 +05:00
James Seibel 48c5828e8f up version number 2.4.5 -> 2.4.6-dev 2025-12-24 22:41:27 -06:00
James Seibel eb2317934f up version number 2.4.4 -> 2.4.5 2025-12-24 22:06:53 -06:00
James Seibel 60537cda1b Replace MC color code strings with an enum 2025-12-24 22:04:50 -06:00
James Seibel 508ff2b776 Fix null pointer in ChunkUpdateQueueManager 2025-12-24 21:53:39 -06:00
40 changed files with 866 additions and 235 deletions
@@ -38,7 +38,7 @@ public final class ModInfo
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.4-b"; public static final String VERSION = "2.4.6-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");
@@ -23,7 +23,9 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode; 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.api.objects.DhApiResult;
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState; import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
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;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry; import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
@@ -31,14 +33,15 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
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.Mat4f;
import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.util.objects.Pair;
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.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage; import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.network.session.NetworkSession; import com.seibel.distanthorizons.core.network.session.NetworkSession;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
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.level.IServerKeyedClientLevel;
@@ -52,11 +55,23 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
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 org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL46;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
/** /**
* This holds the methods that should be called * This holds the methods that should be called
@@ -152,10 +167,10 @@ public class ClientApi
if (Config.Common.Logging.Warning.showReplayWarningOnStartup.get()) if (Config.Common.Logging.Warning.showReplayWarningOnStartup.get())
{ {
MC_CLIENT.sendChatMessage("\u00A76" + "Distant Horizons: Replay detected." + "\u00A7r"); // gold color MC_CLIENT.sendChatMessage(MinecraftTextFormat.ORANGE + "Distant Horizons: Replay detected." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("DH may behave strangely or have missing functionality."); MC_CLIENT.sendChatMessage("DH may behave strangely or have missing functionality.");
MC_CLIENT.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:"); MC_CLIENT.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:");
MC_CLIENT.sendChatMessage("\u00A77"+".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+"\u00A7r"); // light gray color MC_CLIENT.sendChatMessage(MinecraftTextFormat.GRAY +".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+ MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging."); MC_CLIENT.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging.");
MC_CLIENT.sendChatMessage(""); MC_CLIENT.sendChatMessage("");
} }
@@ -327,12 +342,29 @@ public class ClientApi
* @see MessageRegistry * @see MessageRegistry
*/ */
public void pluginMessageReceived(@NotNull AbstractNetworkMessage message) public void pluginMessageReceived(@NotNull AbstractNetworkMessage message)
{
@Nullable ThreadPoolExecutor executor = ThreadPoolUtil.networkClientHandlerExecutor();
if (executor == null)
{
LOGGER.warn("warn");
return;
}
try
{
executor.execute(() ->
{ {
NetworkSession networkSession = this.pluginChannelApi.networkSession; NetworkSession networkSession = this.pluginChannelApi.networkSession;
if (networkSession != null) if (networkSession != null)
{ {
networkSession.tryHandleMessage(message); networkSession.tryHandleMessage(message);
} }
});
}
catch (RejectedExecutionException e)
{
LOGGER.warn("Plugin message executor rejected");
}
} }
@@ -507,10 +539,10 @@ public class ClientApi
this.rendererDisabledBecauseOfExceptions = true; this.rendererDisabledBecauseOfExceptions = true;
LOGGER.error("Unexpected Renderer error in render pass [" + renderPass + "]. Error: " + e.getMessage(), e); LOGGER.error("Unexpected Renderer error in render pass [" + renderPass + "]. Error: " + e.getMessage(), e);
MC_CLIENT.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons renderer has encountered an exception!"); MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "" + MinecraftTextFormat.BOLD + "ERROR: Distant Horizons renderer has encountered an exception!" + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("\u00A74Renderer disabled to try preventing GL state corruption."); MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Renderer disabled to try preventing GL state corruption." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("\u00A74Toggle DH rendering via the config UI to re-activate DH rendering."); MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Toggle DH rendering via the config UI to re-activate DH rendering." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("\u00A74Error: " + e); MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Error: " + MinecraftTextFormat.CLEAR_FORMATTING + e);
} }
@@ -529,8 +561,11 @@ public class ClientApi
* The first fade pass. * The first fade pass.
* Called after MC finishes rendering the opaque passes. * Called after MC finishes rendering the opaque passes.
*/ */
public void renderFadeOpaque() public void renderFadeOpaque() // TODO this is actually the transparent pass
{ {
DepthCalculator.INSTANCE.getMcTransparentDepthTexture();
DepthCalculator.INSTANCE.tryCalculateAsync();
// only fade when DH is rendering // only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT
&& &&
@@ -551,8 +586,10 @@ public class ClientApi
* Called after MC finishes rendering both opaque * Called after MC finishes rendering both opaque
* and transparent passes. * and transparent passes.
*/ */
public void renderFadeTransparent() public void renderFadeTransparent() // TODO this is actually the opaque pass
{ {
DepthCalculator.INSTANCE.getMcOpaqueDepthTexture();
// only fade when DH is rendering // only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT) if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
{ {
@@ -581,27 +618,30 @@ public class ClientApi
/** 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)
{ {
if (!Config.Client.Advanced.Debugging.enableDebugKeybindings.get()) //if (!Config.Client.Advanced.Debugging.enableDebugKeybindings.get())
{ //{
// keybindings are disabled // // keybindings are disabled
return; // return;
} //}
if (glfwKey == GLFW.GLFW_KEY_F8) if (glfwKey == GLFW.GLFW_KEY_F8)
{ {
Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get())); DepthCalculator.INSTANCE.pause = true;
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRendering.get()); //Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
//MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRendering.get());
} }
else if (glfwKey == GLFW.GLFW_KEY_F6) else if (glfwKey == GLFW.GLFW_KEY_F6)
{ {
Config.Client.Advanced.Debugging.rendererMode.set(EDhApiRendererMode.next(Config.Client.Advanced.Debugging.rendererMode.get())); DepthCalculator.INSTANCE.pause = true;
MC_CLIENT.sendChatMessage("F6: Set rendering to " + Config.Client.Advanced.Debugging.rendererMode.get()); //Config.Client.Advanced.Debugging.rendererMode.set(EDhApiRendererMode.next(Config.Client.Advanced.Debugging.rendererMode.get()));
//MC_CLIENT.sendChatMessage("F6: Set rendering to " + Config.Client.Advanced.Debugging.rendererMode.get());
} }
else if (glfwKey == GLFW.GLFW_KEY_P) else if (glfwKey == GLFW.GLFW_KEY_P)
{ {
prefLoggerEnabled = !prefLoggerEnabled; DepthCalculator.INSTANCE.pause = true;
MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled")); //prefLoggerEnabled = !prefLoggerEnabled;
//MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
} }
} }
@@ -655,8 +695,7 @@ public class ClientApi
// remind the user that this is a development build // remind the user that this is a development build
String message = String message =
// green text MinecraftTextFormat.DARK_GREEN + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"\u00A72" + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." + "\u00A7r\n" +
"Issues may occur with this version.\n" + "Issues may occur with this version.\n" +
"Here be dragons!\n"; "Here be dragons!\n";
MC_CLIENT.sendChatMessage(message); MC_CLIENT.sendChatMessage(message);
@@ -680,7 +719,7 @@ public class ClientApi
{ {
String message = String message =
// orange text // orange text
"\u00A76" + "Distant Horizons: Low memory detected." + "\u00A7r \n" + MinecraftTextFormat.ORANGE + "Distant Horizons: Low memory detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"Stuttering or low FPS may occur. \n" + "Stuttering or low FPS may occur. \n" +
"Please increase Minecraft's available memory to 4 GB or more. \n" + "Please increase Minecraft's available memory to 4 GB or more. \n" +
"This warning can be disabled in DH's config under Advanced -> Logging. \n"; "This warning can be disabled in DH's config under Advanced -> Logging. \n";
@@ -702,14 +741,12 @@ public class ClientApi
this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis(); this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis();
String message = String message =
// yellow text MinecraftTextFormat.YELLOW + "Distant Horizons: High vanilla render distance detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"\u00A7e" + "Distant Horizons: High vanilla render distance detected." + "\u00A7r \n" +
"Using a high vanilla render distance uses a lot of CPU power \n" + "Using a high vanilla render distance uses a lot of CPU power \n" +
"and doesn't improve graphics much after about 12.\n" + "and doesn't improve graphics much after about 12.\n" +
"Lowing your vanilla render distance will give you better FPS\n" + "Lowering your vanilla render distance will give you better FPS\n" +
"and reduce stuttering at a similar visual quality.\n" + "and reduce stuttering at a similar visual quality.\n" +
// gray text MinecraftTextFormat.GRAY + "A vanilla render distance of 8 is recommended." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"\u00A77" + "A vanilla render distance of 8 is recommended." + "\u00A7r \n" +
"This message can be disabled in DH's config under Advanced -> Logging.\n"; "This message can be disabled in DH's config under Advanced -> Logging.\n";
MC_CLIENT.sendChatMessage(message); MC_CLIENT.sendChatMessage(message);
} }
@@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.api.internal.chunkUpdating;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator; import java.util.Comparator;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -105,6 +106,7 @@ public class ChunkPosQueue
this.furthestQueue.remove(closest); this.furthestQueue.remove(closest);
return this.updateDataByChunkPos.remove(closest); return this.updateDataByChunkPos.remove(closest);
} }
@Nullable
public ChunkUpdateData popFurthest() public ChunkUpdateData popFurthest()
{ {
if (this.furthestQueue.isEmpty()) if (this.furthestQueue.isEmpty())
@@ -1,12 +1,11 @@
package com.seibel.distanthorizons.core.api.internal.chunkUpdating; package com.seibel.distanthorizons.core.api.internal.chunkUpdating;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.world.EWorldEnvironment; import com.seibel.distanthorizons.core.world.EWorldEnvironment;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -94,8 +93,11 @@ public class ChunkUpdateQueueManager
if (remainingSlots <= 0) if (remainingSlots <= 0)
{ {
ChunkUpdateData removedData = queue.popFurthest(); ChunkUpdateData removedData = queue.popFurthest();
if (removedData != null)
{
this.queuedChunkWrapperByChunkPos.remove(removedData.chunkWrapper.getChunkPos()); this.queuedChunkWrapperByChunkPos.remove(removedData.chunkWrapper.getChunkPos());
} }
}
queue.addItem(pos,updateData); queue.addItem(pos,updateData);
this.queuedChunkWrapperByChunkPos.putIfAbsent(pos, updateData.chunkWrapper); this.queuedChunkWrapperByChunkPos.putIfAbsent(pos, updateData.chunkWrapper);
@@ -116,7 +118,7 @@ public class ChunkUpdateQueueManager
{ {
lastOverloadedLogMessageMsTime = System.currentTimeMillis(); lastOverloadedLogMessageMsTime = System.currentTimeMillis();
String message = "\u00A76" + "Distant Horizons overloaded, too many chunks queued for LOD processing. " + "\u00A7r" + String message = MinecraftTextFormat.ORANGE + "Distant Horizons overloaded, too many chunks queued for LOD processing. " + MinecraftTextFormat.CLEAR_FORMATTING +
"\nThis may result in holes in your LODs. " + "\nThis may result in holes in your LODs. " +
"\nFix: move through the world slower, decrease your vanilla render distance, slow down your world pre-generator (IE Chunky), or increase the Distant Horizons' CPU thread counts. " + "\nFix: move through the world slower, decrease your vanilla render distance, slow down your world pre-generator (IE Chunky), or increase the Distant Horizons' CPU thread counts. " +
"\nMax queue count [" + SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.maxSize + "] ([" + SharedApi.MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER + "] per thread+players)."; "\nMax queue count [" + SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.maxSize + "] ([" + SharedApi.MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER + "] per thread+players).";
@@ -118,6 +118,20 @@ public class Config
public static ConfigEntry<Boolean> dynamicFadeUseOpaqueMcDepth = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "")
.build();
public static ConfigEntry<String> dynamicFadeExportPath = new ConfigEntry.Builder<String>()
.set("C:/Users/James_Seibel/Desktop/")
.comment(""
+ "")
.build();
public static class Advanced public static class Advanced
{ {
// common config links need to have their destination // common config links need to have their destination
@@ -457,6 +471,15 @@ public class Config
+ "") + "")
.build(); .build();
public static ConfigEntry<Boolean> expandDistantBeacons = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "If true LOD beacon beams will be rendered wider at extreme distances, \n"
+ "making them easier to see. \n"
+ "If false all LOD beacon beams will only ever be 1 block wide. \n"
+ "")
.build();
public static ConfigEntry<Boolean> enableCloudRendering = new ConfigEntry.Builder<Boolean>() public static ConfigEntry<Boolean> enableCloudRendering = new ConfigEntry.Builder<Boolean>()
.set(true) .set(true)
.comment("" .comment(""
@@ -295,8 +295,6 @@ public class LodBufferContainer implements AutoCloseable
{ {
this.buffersUploaded = false; this.buffersUploaded = false;
GLProxy.queueRunningOnRenderThread(() ->
{
for (GLVertexBuffer buffer : this.vbos) for (GLVertexBuffer buffer : this.vbos)
{ {
if (buffer != null) if (buffer != null)
@@ -312,7 +310,6 @@ public class LodBufferContainer implements AutoCloseable
buffer.destroyAsync(); buffer.destroyAsync();
} }
} }
});
} }
} }
@@ -1,12 +0,0 @@
package com.seibel.distanthorizons.core.enums;
/**
* TODO
* might be deprecated in the future? in that case we'll probably want a wrapper
* function to handle colors for new MC versions
*
* source: https://minecraft.wiki/w/Formatting_codes
*/
public class EMinecraftColor
{
}
@@ -0,0 +1,35 @@
package com.seibel.distanthorizons.core.enums;
/**
* might be deprecated in the future? in that case we'll probably want a wrapper
* function to handle colors for new MC versions
* <br><br>
* source: https://minecraft.wiki/w/Formatting_codes
*/
public class MinecraftTextFormat
{
public static final String BLACK = "\u00A70";
public static final String DARK_BLUE = "\u00A71";
public static final String DARK_GREEN = "\u00A72";
public static final String DARK_AQUA = "\u00A73";
public static final String DARK_RED = "\u00A74";
public static final String DARK_PURPLE = "\u00A75";
public static final String ORANGE = "\u00A76";
public static final String GRAY = "\u00A77";
public static final String DARK_GRAY = "\u00A78";
public static final String BLUE = "\u00A79";
public static final String GREEN = "\u00A7a";
public static final String AQUA = "\u00A7b";
public static final String RED = "\u00A7c";
public static final String LIGHT_PURPLE = "\u00A7d";
public static final String YELLOW = "\u00A7e";
public static final String WHITE = "\u00A7f";
public static final String OBFUSCATED = "\u00A7k";
public static final String BOLD = "\u00A7l";
public static final String STRIKETHROUGH = "\u00A7m";
public static final String UNDERLINE = "\u00A7n";
public static final String ITALIC = "\u00A7o";
public static final String CLEAR_FORMATTING = "\u00A7r";
}
@@ -44,6 +44,7 @@ import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
@@ -116,26 +117,22 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
// events // // events //
//========// //========//
private void onWorldGenTaskComplete(DataSourceRetrievalResult genTaskResult, Throwable exception) private void onWorldGenTaskComplete(@NotNull Long genPos, @Nullable DataSourceRetrievalResult genTaskResult, @Nullable Throwable exception)
{ {
try try
{ {
if (exception != null) if (exception != null)
{ {
return;
}
if (genTaskResult.state == ERetrievalResultState.FAIL)
{
LodUtil.assertTrue(genTaskResult.dataSource == null, "Errored retrieval object should not have a datasource.");
// don't log shutdown exceptions // don't log shutdown exceptions
if (!ExceptionUtil.isInterruptOrReject(exception)) if (!ExceptionUtil.isInterruptOrReject(exception))
{ {
LOGGER.error("Uncaught Gen Task Exception at [" + genTaskResult.pos + "], error: [" + exception.getMessage() + "].", exception); LOGGER.error("Uncaught Gen Task Exception at [" + genPos + "], error: [" + exception.getMessage() + "].", exception);
} }
return;
} }
else if (genTaskResult.state == ERetrievalResultState.SUCCESS)
Objects.requireNonNull(genTaskResult);
if (genTaskResult.state == ERetrievalResultState.SUCCESS)
{ {
LodUtil.assertTrue(genTaskResult.dataSource != null, "Successful retrieval object should have a datasource."); LodUtil.assertTrue(genTaskResult.dataSource != null, "Successful retrieval object should have a datasource.");
@@ -310,7 +307,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
} }
CompletableFuture<DataSourceRetrievalResult> worldGenFuture = worldGenQueue.submitRetrievalTask(genPos, (byte) (DhSectionPos.getDetailLevel(genPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)); CompletableFuture<DataSourceRetrievalResult> worldGenFuture = worldGenQueue.submitRetrievalTask(genPos, (byte) (DhSectionPos.getDetailLevel(genPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL));
worldGenFuture.whenComplete(this::onWorldGenTaskComplete); worldGenFuture.whenComplete((r, e) -> this.onWorldGenTaskComplete(genPos, r, e));
return worldGenFuture; return worldGenFuture;
} }
@@ -157,9 +157,9 @@ public class PregenManager
else else
{ {
this.fullDataSourceProvider.queuePositionForRetrieval(fullDataSource.getPos()) this.fullDataSourceProvider.queuePositionForRetrieval(fullDataSource.getPos())
.thenAccept((DataSourceRetrievalResult result) -> .whenComplete((DataSourceRetrievalResult result, Throwable throwable) ->
{ {
if (result.state == ERetrievalResultState.FAIL) if (throwable != null)
{ {
LOGGER.warn("Failed to generate section " + DhSectionPos.toString(result.pos)); LOGGER.warn("Failed to generate section " + DhSectionPos.toString(result.pos));
} }
@@ -92,7 +92,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
private int estimatedRemainingChunkCount = 0; private int estimatedRemainingChunkCount = 0;
private final RollingAverage rollingAverageChunkGenTimeInMs = new RollingAverage(Runtime.getRuntime().availableProcessors() * 500); private final RollingAverage rollingAverageChunkGenTimeInMs = new RollingAverage(Runtime.getRuntime().availableProcessors() * 500);
public RollingAverage getRollingAverageChunkGenTimeInMs() { return this.rollingAverageChunkGenTimeInMs; } @Override public RollingAverage getRollingAverageChunkGenTimeInMs() { return this.rollingAverageChunkGenTimeInMs; }
@@ -128,7 +128,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
// the generator is shutting down, don't add new tasks // the generator is shutting down, don't add new tasks
if (this.generatorClosingFuture != null) if (this.generatorClosingFuture != null)
{ {
return CompletableFuture.completedFuture(DataSourceRetrievalResult.CreateFail()); CompletableFuture<DataSourceRetrievalResult> f = new CompletableFuture<>();
f.completeExceptionally(new CancellationException());
return f;
} }
// use the existing task if present // use the existing task if present
@@ -362,7 +364,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
} }
LodUtil.assertTrue(fullDataSource == null); LodUtil.assertTrue(fullDataSource == null);
worldGenTask.future.complete(DataSourceRetrievalResult.CreateFail()); worldGenTask.future.completeExceptionally(exception);
} }
else else
{ {
@@ -40,7 +40,6 @@ public class DataSourceRetrievalResult
//==============// //==============//
public static DataSourceRetrievalResult CreateSplit() { return new DataSourceRetrievalResult(ERetrievalResultState.REQUIRES_SPLITTING, 0, null); } public static DataSourceRetrievalResult CreateSplit() { return new DataSourceRetrievalResult(ERetrievalResultState.REQUIRES_SPLITTING, 0, null); }
public static DataSourceRetrievalResult CreateFail() { return new DataSourceRetrievalResult(ERetrievalResultState.FAIL, 0, null); }
public static DataSourceRetrievalResult CreateSuccess(long pos, FullDataSourceV2 generatedDataSource) { return new DataSourceRetrievalResult(ERetrievalResultState.SUCCESS, pos, generatedDataSource); } public static DataSourceRetrievalResult CreateSuccess(long pos, FullDataSourceV2 generatedDataSource) { return new DataSourceRetrievalResult(ERetrievalResultState.SUCCESS, pos, generatedDataSource); }
private DataSourceRetrievalResult(ERetrievalResultState state, long pos, @Nullable FullDataSourceV2 dataSource) private DataSourceRetrievalResult(ERetrievalResultState state, long pos, @Nullable FullDataSourceV2 dataSource)
{ {
@@ -3,7 +3,6 @@ package com.seibel.distanthorizons.core.generation.tasks;
/** /**
* SUCCESS <br> * SUCCESS <br>
* REQUIRES_SPLITTING <br> * REQUIRES_SPLITTING <br>
* FAIL <br>
* *
* @see DataSourceRetrievalResult * @see DataSourceRetrievalResult
*/ */
@@ -11,5 +10,4 @@ public enum ERetrievalResultState
{ {
SUCCESS, SUCCESS,
REQUIRES_SPLITTING, REQUIRES_SPLITTING,
FAIL,
} }
@@ -104,7 +104,7 @@ public class JarUtils
*/ */
public static InputStream accessFile(String resource) public static InputStream accessFile(String resource)
{ {
final ClassLoader loader = Thread.currentThread().getContextClassLoader(); final ClassLoader loader = JarUtils.class.getClassLoader();
// this is the path within the jar file // this is the path within the jar file
InputStream input = loader.getResourceAsStream(resource); InputStream input = loader.getResourceAsStream(resource);
if (input == null) if (input == null)
@@ -24,6 +24,7 @@ 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.config.types.ConfigEntry; import com.seibel.distanthorizons.core.config.types.ConfigEntry;
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.util.ThreadUtil; import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -302,32 +303,32 @@ public class DhLogger implements IConfigListener
String prefix = "[" + ModInfo.READABLE_NAME + "] "; String prefix = "[" + ModInfo.READABLE_NAME + "] ";
if (logLevel == Level.ERROR) if (logLevel == Level.ERROR)
{ {
prefix += "\u00A74"; prefix += MinecraftTextFormat.DARK_RED;
} }
else if (logLevel == Level.WARN) else if (logLevel == Level.WARN)
{ {
prefix += "\u00A76"; prefix += MinecraftTextFormat.ORANGE;
} }
else if (logLevel == Level.INFO) else if (logLevel == Level.INFO)
{ {
prefix += "\u00A7f"; prefix += MinecraftTextFormat.AQUA;
} }
else if (logLevel == Level.DEBUG) else if (logLevel == Level.DEBUG)
{ {
prefix += "\u00A77"; prefix += MinecraftTextFormat.GREEN;
} }
else if (logLevel == Level.TRACE) else if (logLevel == Level.TRACE)
{ {
prefix += "\u00A78"; prefix += MinecraftTextFormat.DARK_GRAY;
} }
else else
{ {
prefix += "\u00A7f"; prefix += MinecraftTextFormat.WHITE;
} }
prefix += "\u00A7l\u00A7u"; prefix += MinecraftTextFormat.BOLD + "" + MinecraftTextFormat.WHITE;
prefix += logLevel.name(); prefix += logLevel.name();
prefix += ":\u00A7r "; prefix += MinecraftTextFormat.CLEAR_FORMATTING + " ";
mc_client.sendChatMessage(prefix + message); mc_client.sendChatMessage(prefix + message);
} }
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter;
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.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -139,9 +140,6 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
break; break;
case REQUIRES_SPLITTING: case REQUIRES_SPLITTING:
break; break;
case FAIL:
this.failedRequests.incrementAndGet();
break;
} }
}); });
@@ -220,11 +218,18 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
FullDataSourceResponseMessage.class FullDataSourceResponseMessage.class
); );
requestTask.networkDataSourceFuture = dataSourceNetworkFuture; requestTask.networkDataSourceFuture = dataSourceNetworkFuture;
dataSourceNetworkFuture.handle((FullDataSourceResponseMessage response, Throwable throwable) ->
Executor networkCompressionExecutor = ThreadPoolUtil.getNetworkCompressionExecutor();
if (networkCompressionExecutor == null)
{
return;
}
dataSourceNetworkFuture.handleAsync((FullDataSourceResponseMessage response, Throwable throwable) ->
{ {
this.handleNetResponse(requestTask, response, throwable); this.handleNetResponse(requestTask, response, throwable);
return null; return null;
}); }, networkCompressionExecutor);
} }
private void handleNetResponse(NetRequestTask requestTask, FullDataSourceResponseMessage response, Throwable throwable) private void handleNetResponse(NetRequestTask requestTask, FullDataSourceResponseMessage response, Throwable throwable)
{ {
@@ -269,7 +274,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
catch (RequestRejectedException e) catch (RequestRejectedException e)
{ {
LOGGER.info("Request rejected by the server, message: [" + e.getMessage() + "]."); LOGGER.info("Request rejected by the server, message: [" + e.getMessage() + "].");
requestTask.future.complete(DataSourceRetrievalResult.CreateFail()); requestTask.future.completeExceptionally(e);
} }
catch (RateLimitedException e) catch (RateLimitedException e)
{ {
@@ -298,7 +303,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
} }
else else
{ {
requestTask.future.complete(DataSourceRetrievalResult.CreateFail()); requestTask.future.completeExceptionally(e);
} }
} }
} }
@@ -1,8 +1,10 @@
package com.seibel.distanthorizons.core.multiplayer.client; package com.seibel.distanthorizons.core.multiplayer.client;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
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.config.listeners.ConfigChangeListener;
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.multiplayer.config.SessionConfig; import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
@@ -95,6 +97,15 @@ public class ClientNetworkState implements Closeable
|| Math.abs(event.protocolVersion - ModInfo.PROTOCOL_VERSION) < this.closestProtocolVersion) || Math.abs(event.protocolVersion - ModInfo.PROTOCOL_VERSION) < this.closestProtocolVersion)
{ {
this.closestProtocolVersion = event.protocolVersion; this.closestProtocolVersion = event.protocolVersion;
if (ModInfo.PROTOCOL_VERSION < event.protocolVersion)
{
ClientApi.INSTANCE.showChatMessageNextFrame(MinecraftTextFormat.ORANGE + "Distant Horizons: Your mod is outdated. Update to receive LODs on this server.");
}
else
{
ClientApi.INSTANCE.showChatMessageNextFrame(MinecraftTextFormat.ORANGE + "Distant Horizons: The server's mod is outdated. Ask the server's owner to update.");
}
} }
}); });
@@ -45,6 +45,7 @@ public class FullDataPayloadReceiver implements AutoCloseable
return null; return null;
} }
message.buffer.readerIndex(0);
composite.addComponent(message.buffer); composite.addComponent(message.buffer);
composite.writerIndex(composite.writerIndex() + message.buffer.writerIndex()); composite.writerIndex(composite.writerIndex() + message.buffer.writerIndex());
LOGGER.debug("Updated full data buffer [" + message.bufferId + "]: [" + composite + "]."); LOGGER.debug("Updated full data buffer [" + message.bufferId + "]: [" + composite + "].");
@@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.pooling;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
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.logging.f3.F3Screen; import com.seibel.distanthorizons.core.logging.f3.F3Screen;
@@ -12,7 +13,6 @@ import com.seibel.distanthorizons.coreapi.util.StringUtil;
import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList; import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -178,8 +178,7 @@ public class PhantomArrayListPool
{ {
lowMemoryWarningLogged = true; lowMemoryWarningLogged = true;
// orange text String message = MinecraftTextFormat.ORANGE + "Distant Horizons: Insufficient memory detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
String message = "\u00A76" + "Distant Horizons: Insufficient memory detected." + "\u00A7r \n" +
"This may cause stuttering or crashing. \n" + "This may cause stuttering or crashing. \n" +
"Potential causes: \n" + "Potential causes: \n" +
"1. your allocated memory isn't high enough \n" + "1. your allocated memory isn't high enough \n" +
@@ -78,6 +78,11 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
*/ */
private final ConcurrentLinkedQueue<Long> sectionsToReload = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue<Long> sectionsToReload = new ConcurrentLinkedQueue<>();
private final IDhClientLevel level; private final IDhClientLevel level;
/**
* Note: this doesn't lock all operations as some other threads/operations
* that may traverse the tree while it's being modified.
* IE {@link RenderBufferHandler} will walk through the tree each frame.
*/
private final ReentrantLock treeLock = new ReentrantLock(); private final ReentrantLock treeLock = new ReentrantLock();
private ArrayList<LodRenderSection> debugRenderSections = new ArrayList<>(); private ArrayList<LodRenderSection> debugRenderSections = new ArrayList<>();
@@ -166,7 +171,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
// don't traverse the tree if it is being modified // don't tick the tree if a modification is still going
// TODO is this lock necessary for anything beyond this tick method?
if (this.treeLock.tryLock()) if (this.treeLock.tryLock())
{ {
// this shouldn't be updated while the tree is being iterated through // this shouldn't be updated while the tree is being iterated through
@@ -508,10 +514,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
continue; continue;
} }
try
{
// the section only needs to be updated if a buffer is currently present // the section only needs to be updated if a buffer is currently present
LodRenderSection renderSection = this.getValue(pos); LodRenderSection renderSection = this.tryGetValue(pos);
if (renderSection != null) if (renderSection != null)
{ {
if (renderSection.canRender()) if (renderSection.canRender())
@@ -528,9 +532,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
} }
} }
} }
catch (IndexOutOfBoundsException e)
{ /* the section is now out of bounds, it doesn't need to be reloaded */ }
}
this.sectionsToReload.addAll(positionsToRequeue); this.sectionsToReload.addAll(positionsToRequeue);
} }
private void loadQueuedSections(DhBlockPos2D playerPos, HashSet<LodRenderSection> nodesNeedingLoading) private void loadQueuedSections(DhBlockPos2D playerPos, HashSet<LodRenderSection> nodesNeedingLoading)
@@ -622,12 +623,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
// task finished // task finished
this.queuedGenerationPosSet.remove(missingPos); this.queuedGenerationPosSet.remove(missingPos);
// if the task failed re-queue so we can try again if (result.state == ERetrievalResultState.REQUIRES_SPLITTING)
if (result.state == ERetrievalResultState.FAIL)
{
this.missingGenerationPosSet.add(missingPos);
}
else if (result.state == ERetrievalResultState.REQUIRES_SPLITTING)
{ {
DhSectionPos.forEachChild(missingPos, (long childPos) -> DhSectionPos.forEachChild(missingPos, (long childPos) ->
{ {
@@ -77,7 +77,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
* contains the list of beacons currently being rendered in this section * 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. * if this list is modified the {@link LodRenderSection#beaconRenderHandler} should be updated to match.
*/ */
private final List<BeaconBeamDTO> activeBeaconList = new ArrayList<>(); private final ArrayList<BeaconBeamDTO> activeBeaconList = new ArrayList<>();
@Nullable @Nullable
public final BeaconRenderHandler beaconRenderHandler; public final BeaconRenderHandler beaconRenderHandler;
@Nullable @Nullable
@@ -446,20 +446,15 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
// stop rendering current beacons // stop rendering current beacons
for (BeaconBeamDTO beam : this.activeBeaconList) this.beaconRenderHandler.stopRenderingBeacons(this.activeBeaconList);
{
this.beaconRenderHandler.stopRenderingBeaconAtPos(beam.blockPos);
}
// swap old and new active beacon list // swap old and new active beacon list
this.activeBeaconList.clear(); this.activeBeaconList.clear();
this.activeBeaconList.addAll(activeBeacons); this.activeBeaconList.addAll(activeBeacons);
// start rendering new beacon list // start rendering new beacon list
for (BeaconBeamDTO beam : this.activeBeaconList) byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
{ this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
this.beaconRenderHandler.startRenderingBeacon(beam);
}
} }
} }
@@ -474,10 +469,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
synchronized (this.activeBeaconList) synchronized (this.activeBeaconList)
{ {
for (BeaconBeamDTO beam : this.activeBeaconList) this.beaconRenderHandler.stopRenderingBeacons(this.activeBeaconList);
{
this.beaconRenderHandler.stopRenderingBeaconAtPos(beam.blockPos);
}
} }
} }
@@ -492,10 +484,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
synchronized (this.activeBeaconList) synchronized (this.activeBeaconList)
{ {
for (BeaconBeamDTO beam : this.activeBeaconList) byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
{ this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
this.beaconRenderHandler.startRenderingBeacon(beam);
}
} }
} }
@@ -0,0 +1,444 @@
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
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.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL46;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.function.Function;
public class DepthCalculator
{
public static DepthCalculator INSTANCE = new DepthCalculator();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private float[] mcOpaqueDepthTextureValues = new float[1];
private float[] mcTransparentDepthTextureValues = new float[1];
private float[] dhDepthTextureValues = new float[1];
private float[] outDepthTextureValues = new float[1];
private float lastClosestDhDepth = 1.0f;
public float actualMcBlockDistance = 8 * 16; // needs to be non-zero to start DH rendering
@Deprecated // Replace with thread pool and an AtomicBool for running state
public Thread thread = null;
public boolean pause = false;
private boolean gotDhDepthThisFrame = false;
private boolean gotMcDepthThisFrame = false;
//=============//
// constructor //
//=============//
private DepthCalculator() { }
//======//
// test //
//======//
public void getMcOpaqueDepthTexture() { this.gotMcDepthThisFrame = this.trySetDepthTexture(MC_RENDER.getDepthTextureId(), this.mcOpaqueDepthTextureValues); }
public void getMcTransparentDepthTexture() { this.gotMcDepthThisFrame = this.trySetDepthTexture(MC_RENDER.getDepthTextureId(), this.mcTransparentDepthTextureValues); }
public void trySetDhDepthTexture() { this.gotDhDepthThisFrame = this.trySetDepthTexture(LodRenderer.INSTANCE.getActiveDepthTextureId(), this.dhDepthTextureValues); }
private boolean trySetDepthTexture(int id, float[] outputRef)
{
// don't change the texture if a process is already running
if (this.thread != null)
{
return false;
}
this.resizeTexturesIfNeeded();
if (id == -1)
{
return false;
}
// FIXME this is slow and causes frame stuttering
GL46.glGetTextureImage(id, 0, GL32.GL_DEPTH_COMPONENT, GL32.GL_FLOAT, outputRef);
return true;
}
private void resizeTexturesIfNeeded()
{
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
int elementCount = width * height;
if (this.dhDepthTextureValues.length != elementCount)
{
this.mcOpaqueDepthTextureValues = new float[width * height];
this.mcTransparentDepthTextureValues = new float[width * height];
this.dhDepthTextureValues = new float[width * height];
this.outDepthTextureValues = new float[width * height];
}
}
public void tryCalculateAsync()
{
if (this.thread != null)
{
return;
}
//if (!this.gotDhDepthThisFrame
// || !this.gotMcDepthThisFrame)
//{
// return;
//}
if (this.pause)
{
int k = 0;
}
this.thread = new Thread(() ->
{
try
{
this.calculateDepth();
}
catch (Exception e)
{
LOGGER.error("async test: " + e.getMessage(), e);
}
finally
{
this.thread = null;
this.pause = false;
}
});
this.thread.start();
}
public void calculateDepth()
{
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
int elementCount = width * height;
// used to calculate the DH render matrices
RenderParams renderParams =
new RenderParams(
EDhApiRenderPass.OPAQUE,
ClientApi.RENDER_STATE.frameTime,
ClientApi.RENDER_STATE.mcProjectionMatrix, ClientApi.RENDER_STATE.mcModelViewMatrix,
ClientApi.RENDER_STATE.clientLevelWrapper
);
Mat4f dhInvProj = new Mat4f(renderParams.dhProjectionMatrix);
dhInvProj.invert();
Matrix4f dhInvProjJoml = dhInvProj.createJomlMatrix();
Mat4f dhInvMvm = new Mat4f(renderParams.dhModelViewMatrix);
dhInvMvm.invert();
Matrix4f dhInvMvmJoml = dhInvMvm.createJomlMatrix(); // TODO can we use JOML for MC 1.16?
float[] sampledDistances = new float[9];
// find the closest depth value MC hasn't drawn to
float closestDhDepth = 1.0f; float closestDhDistance = Float.MAX_VALUE;
int closeUIndex = 0; int closeVIndex = 0;
for (int u = 0; u < width; u++) // x
{
for (int v = 0; v < height; v++) // y
{
int invertedV = height - 1 - v;
int i = (invertedV * width) + u;
this.outDepthTextureValues[i] = 0.0f;
float mcDepth = Config.Client.dynamicFadeUseOpaqueMcDepth.get()
? this.mcOpaqueDepthTextureValues[i]
: this.mcTransparentDepthTextureValues[i];
if (mcDepth < 1.0f) // ignore positions MC has drawn to
{
continue;
}
float dhDepth = this.dhDepthTextureValues[i];
if (dhDepth == 1.0f) // ignore positions DH has NOT drawn to
{
continue;
}
this.outDepthTextureValues[i] = dhDepth;
// calculate this point's distance from the camera
float ndcU = u / (float)width;
float ndcV = invertedV / (float)height;
float dist = convertDepthToBlockDistance(ndcU, ndcV, dhInvProjJoml, dhInvMvmJoml, dhDepth);
//// sample the 9 surrounding pixels to account for off-by-one errors between the MC and DH depth textures
//int sampleIndex = 0;
//for (int relU = -1; relU < 1; relU++)
//{
// for (int relV = -1; relV < 1; relV++)
// {
// invertedV = height - 1 - v + relV;
// i = (invertedV * width) + u + relU;
// dhDepth = this.dhDepthTextureValues[i];
//
// if (v + relV < 0 || v + relV > height
// || u + relU < 0 || u + relU > width)
// {
// sampledDistances[sampleIndex] = 0.0f;
// sampleIndex++;
// }
//
// // calculate this point's distance from the camera
// float ndcU = u / (float)width;
// float ndcV = invertedV / (float)height;
// float dist = convertDepthToBlockDistance(ndcU, ndcV, dhInvProjJoml, dhInvMvmJoml, dhDepth);
//
// sampledDistances[sampleIndex] = dist;
// sampleIndex++;
// }
//}
//
//
//Arrays.sort(sampledDistances);
//// return the median element
//float dist = sampledDistances[sampledDistances.length / 2];
//if (dist == 0.0f)
//{
// // the median was 0, return the smallest non-zero element
// for (sampleIndex = 0; sampleIndex < sampledDistances.length; sampleIndex++)
// {
// if (sampledDistances[sampleIndex] != 0.0f)
// {
// dist = sampledDistances[sampleIndex];
// break;
// }
// }
//}
if (dist < closestDhDistance)
{
closestDhDepth = dhDepth;
closestDhDistance = dist;
closeUIndex = u;
closeVIndex = invertedV;
}
}
}
if (this.lastClosestDhDepth != closestDhDepth)
{
NumberFormat numForm = NumberFormat.getNumberInstance();
ClientApi.INSTANCE.showChatMessageNextFrame(
"closest: ["+numForm.format(closestDhDepth)+"]-b["+numForm.format(closestDhDistance)+"]c["+numForm.format(closestDhDistance/16)+"] ("+(closeUIndex)+","+(closeVIndex)+")" +
"");
this.actualMcBlockDistance = closestDhDistance;
}
this.lastClosestDhDepth = closestDhDepth;
if (this.pause)
{
// find the range of depth values used by both textures for clearer exporting
float closestMcDepth = 1.0f; float furthestMcDepth = 0.0f;
float minDhDepth = 1.0f; float maxDhDepth = 0.0f;
for (int i = 0; i < elementCount; i++)
{
float mcDepth = this.mcOpaqueDepthTextureValues[i];
if (mcDepth != 0.0f && mcDepth != 1.0f)
{
if (mcDepth < closestMcDepth)
{
closestMcDepth = mcDepth;
}
if (mcDepth > furthestMcDepth)
{
furthestMcDepth = mcDepth;
}
}
float dhDepth = this.dhDepthTextureValues[i];
if (dhDepth != 0.0f && dhDepth != 1.0f)
{
if (dhDepth < minDhDepth)
{
minDhDepth = dhDepth;
}
if (dhDepth > maxDhDepth)
{
maxDhDepth = dhDepth;
}
}
}
String exportPath = Config.Client.dynamicFadeExportPath.get();
ClientApi.INSTANCE.showChatMessageNextFrame("exporting debug textures to: ["+exportPath+"]...");
// mc opaque
createImg(
this.mcOpaqueDepthTextureValues,
closeUIndex, closeVIndex,
(Float depth) -> { return null; },
width, height, closestMcDepth, furthestMcDepth,
exportPath+"mc-opaque_depth.png");
// mc transparent
createImg(
this.mcTransparentDepthTextureValues,
closeUIndex, closeVIndex,
(Float depth) -> { return null; },
width, height, closestMcDepth, furthestMcDepth,
exportPath+"mc-tran_depth.png");
// dh
Function<Float, Color> customColorFunc = (Float depth) ->
{
if (depth == this.lastClosestDhDepth)
{
return Color.RED;
}
//else if (depth <= (lastClosestDhDepth + 0.01f))
//{
// return Color.ORANGE;
//}
return null;
};
createImg(
this.dhDepthTextureValues,
closeUIndex, closeVIndex,
customColorFunc,
width, height, minDhDepth, maxDhDepth,
exportPath+"dh_depth.png");
// temp
createImg(
this.outDepthTextureValues,
closeUIndex, closeVIndex,
(Float depth) -> { return null; },
width, height, minDhDepth, maxDhDepth,
exportPath+"temp_depth.png");
int breakPoint = 0;
}
}
/** NDC (Normalized Device Coordinates) must be between 0.0 and 1.0 (inclusive) */
private static float convertDepthToBlockDistance(float ndcU, float ndcV, Matrix4f invProj, Matrix4f invMvm, float depth)
{
// This assumes depth is scaled to [0, 1]
// Transform depth to clip space Z value
float z = depth * 2.0f - 1.0f;
// Create a vector in clip space
Vector4f clipSpacePosition = new Vector4f(ndcU, ndcV, z, 1.0f);
// Transform to world space
Vector4f worldSpacePosition = clipSpacePosition.mul(invProj);
worldSpacePosition.div(worldSpacePosition.w); // Perform perspective divide
// Finally apply the inverse model-view matrix to get world space coordinates
worldSpacePosition = worldSpacePosition.mul(invMvm);
// calculate distance from the camera
float distance = worldSpacePosition.distance(0, 0, 0, 0);
return distance;
}
private static void createImg(
float[] tex,
int nearestU, int nearestV,
Function<Float, Color> customColorFunc,
int width, int height,
float minDepth, float maxDepth,
String filePath)
{
// Create a BufferedImage
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int u = 0; u < width; u++)
{
for (int v = 0; v < height; v++)
{
int invertedY = height - 1 - v;
// Normalize the depth value to a grayscale pixel
float depthValue = tex[(invertedY * width) + u];
Color color = customColorFunc.apply(depthValue);
if (color == null)
{
float normalizedDepth = (depthValue - minDepth) / (maxDepth - minDepth); // Normalize to 0.0 to 1.0
normalizedDepth = Math.max(0.0f, Math.min(1.0f, normalizedDepth)); // Clamp to valid range
int gray = (int) (normalizedDepth * 255); // Map to 0-255
gray = Math.max(0, Math.min(255, gray)); // Clamp to valid range
color = new Color(gray, gray, gray);
}
if (depthValue == 1.0f)
{
color = new Color(0,0,0,0);
}
if (u == nearestU)
{
color = Color.MAGENTA;
}
else if (invertedY == nearestV)
{
color = Color.ORANGE;
}
image.setRGB(u, v, color.getRGB());
}
}
// Write the image to a file
try
{
ImageIO.write(image, "png", new File(filePath));
}
catch (IOException e)
{
LOGGER.error("Unable to write texture to file, error: ["+e.getMessage()+"].", e);
}
}
}
@@ -204,17 +204,17 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get()); this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
// Clip Uniform // Clip Uniform
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks); float dhNearClipDistance = 0.1f;//RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks);
if (!Config.Client.Advanced.Debugging.lodOnlyMode.get()) //if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
{ //{
// this added value prevents the near clip plane and discard circle from touching, which looks bad // // this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f; // dhNearClipDistance += 16f;
} //}
// if the player is very high up and the near clip plane has been modified, disable the distance clipping // if the player is very high up and the near clip plane has been modified, disable the distance clipping
// we're high enough that nothing will render on top of the player and this can cause issues otherwise // we're high enough that nothing will render on top of the player and this can cause issues otherwise
if (RenderUtil.getHeightBasedNearClipOverride() != -1) if (RenderUtil.getHeightBasedNearClipOverride() != -1)
{ {
dhNearClipDistance = 1.0f; dhNearClipDistance = 1.0f; // TODO does this actually disable anything?
} }
this.setUniform(this.uClipDistance, dhNearClipDistance); this.setUniform(this.uClipDistance, dhNearClipDistance);
} }
@@ -222,6 +222,8 @@ public class LodRenderer
profiler.popPush("LOD Opaque"); profiler.popPush("LOD Opaque");
this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ true); this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ true);
DepthCalculator.INSTANCE.trySetDhDepthTexture();
// custom objects with SSAO // custom objects with SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get()) if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{ {
@@ -536,7 +538,7 @@ public class LodRenderer
return true; return true;
} }
@SuppressWarnings( "deprecation" ) @SuppressWarnings( "deprecation" ) // done to ignore DhApiColorDepthTextureCreatedEvent
private void createAndBindTextures() private void createAndBindTextures()
{ {
int oldWidth = this.textureWidth; int oldWidth = this.textureWidth;
@@ -28,6 +28,7 @@ import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShad
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.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
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.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;
@@ -39,8 +40,7 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
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 java.util.ArrayList; import java.util.*;
import java.util.HashSet;
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;
@@ -54,6 +54,8 @@ 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();
@@ -89,18 +91,72 @@ public class BeaconRenderHandler
// render handling // // render handling //
//=================// //=================//
public void startRenderingBeacon(BeaconBeamDTO beacon) public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel)
{ {
try try
{ {
this.updateLock.lock(); this.updateLock.lock();
if (this.beaconBlockPosSet.add(beacon.blockPos))
// 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
}
}
}
}
// add each beacon to the renderer
for (int i = 0; i < sortedBeaconList.size(); i++)
{
BeaconBeamDTO beacon = sortedBeaconList.get(i);
if (!this.beaconBlockPosSet.add(beacon.blockPos))
{
continue;
}
int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get(); int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get();
DhApiRenderableBox beaconBox = new DhApiRenderableBox( DhApiRenderableBox beaconBox = new DhApiRenderableBox(
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()), new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
new DhApiVec3d(beacon.blockPos.getX() + 1, maxBeaconBeamHeight, beacon.blockPos.getZ() + 1), new DhApiVec3d(beacon.blockPos.getX() + beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beaconBlockWidth),
beacon.color, beacon.color,
EDhApiBlockMaterial.ILLUMINATED EDhApiBlockMaterial.ILLUMINATED
); );
@@ -116,14 +172,21 @@ public class BeaconRenderHandler
} }
} }
public void stopRenderingBeaconAtPos(DhBlockPos beaconPos) public void stopRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList)
{ {
try try
{ {
this.updateLock.lock(); this.updateLock.lock();
if (this.beaconBlockPosSet.remove(beaconPos)) for (int i = 0; i < beaconList.size(); i++)
{ {
BeaconBeamDTO beacon = beaconList.get(i);
DhBlockPos beaconPos = beacon.blockPos;
if (!this.beaconBlockPosSet.remove(beaconPos))
{
continue;
}
Predicate<DhApiRenderableBox> removePredicate = (DhApiRenderableBox box) -> Predicate<DhApiRenderableBox> removePredicate = (DhApiRenderableBox box) ->
{ {
return box.minPos.x == beaconPos.getX() return box.minPos.x == beaconPos.getX()
@@ -255,4 +318,28 @@ public class BeaconRenderHandler
} }
//================//
// helper classes //
//================//
private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO>
{
@Override
public int compare(BeaconBeamDTO beacon1, BeaconBeamDTO beacon2)
{
DhBlockPos blockPos1 = beacon1.blockPos;
DhBlockPos blockPos2 = beacon2.blockPos;
// sort by X, then by Z
if (blockPos1.getX() != blockPos2.getX())
{
return Integer.compare(blockPos1.getX(), blockPos2.getX());
}
return Integer.compare(blockPos1.getZ(), blockPos2.getZ());
}
}
} }
@@ -493,7 +493,7 @@ public class CloudRenderHandler
private static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException private static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException
{ {
final ClassLoader loader = Thread.currentThread().getContextClassLoader(); final ClassLoader loader = CloudRenderHandler.class.getClassLoader();
boolean[][] whitePixels = null; boolean[][] whitePixels = null;
try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH)) try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH))
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.render.renderer.shaders; package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
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.render.glObject.shader.ShaderProgram; import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
@@ -114,7 +115,7 @@ public class VanillaFadeShader extends AbstractShaderRenderer
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(partialTicks); float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(partialTicks);
// this added value prevents the near clip plane and discard circle from touching, which looks bad // this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f; //dhNearClipDistance += 16f;
// measured in blocks // measured in blocks
// these multipliers in James' tests should provide a fairly smooth transition // these multipliers in James' tests should provide a fairly smooth transition
@@ -173,7 +173,7 @@ public class DatabaseUpdater
/** @throws NullPointerException if any of the script files failed to be read. */ /** @throws NullPointerException if any of the script files failed to be read. */
private static ArrayList<SqlScript> getAutoUpdateScripts() throws NullPointerException, IOException private static ArrayList<SqlScript> getAutoUpdateScripts() throws NullPointerException, IOException
{ {
final ClassLoader loader = Thread.currentThread().getContextClassLoader(); final ClassLoader loader = DatabaseUpdater.class.getClassLoader();
// get the script list // get the script list
@@ -76,6 +76,9 @@ public class BeaconBeamDTO implements IBaseDTO<DhBlockPos>, INetworkObject
public void close() public void close()
{ /* no closing needed */ } { /* no closing needed */ }
@Override
public String toString() { return this.blockPos + " " + this.color; }
} }
@@ -19,8 +19,10 @@
package com.seibel.distanthorizons.core.util; package com.seibel.distanthorizons.core.util;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
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.render.renderer.DepthCalculator;
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.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -127,13 +129,16 @@ public class RenderUtil
} }
public static float getNearClipPlaneInBlocksForFading(float partialTicks) public static float getNearClipPlaneInBlocksForFading(float partialTicks)
{ {
float overdraw = getAutoOverdrawPrevention(); //float overdraw = getAutoOverdrawPrevention();
return getNearClipPlaneDistanceInBlocks(partialTicks, overdraw); //return getNearClipPlaneDistanceInBlocks(partialTicks, overdraw);
return DepthCalculator.INSTANCE.actualMcBlockDistance;
} }
private static float getNearClipPlaneDistanceInBlocks(float partialTicks, float overdrawPreventionPercent) private static float getNearClipPlaneDistanceInBlocks(float partialTicks, float overdrawPreventionPercent)
{ {
int chunkRenderDistance = MC_RENDER.getRenderDistance(); int chunkRenderDistance = MC_RENDER.getRenderDistance();
int vanillaBlockRenderedDistance = chunkRenderDistance * LodUtil.CHUNK_WIDTH; //float chunkRenderDistance = ClientApi.actualMcBlockDistance / 16.0f;
float vanillaBlockRenderedDistance = chunkRenderDistance * LodUtil.CHUNK_WIDTH;
float nearClipPlane; float nearClipPlane;
if (Config.Client.Advanced.Debugging.lodOnlyMode.get()) if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
@@ -31,12 +31,10 @@ import com.seibel.distanthorizons.coreapi.util.MathUtil;
import com.seibel.distanthorizons.core.util.gridList.MovableGridRingList; import com.seibel.distanthorizons.core.util.gridList.MovableGridRingList;
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongIterator;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.LongConsumer; import java.util.function.LongConsumer;
/** /**
@@ -98,9 +96,27 @@ public class QuadTree<T>
// getters and setters // // getters and setters //
//=====================// //=====================//
/** @return the value at the given section position. Null will be returned if the value is missing or the position is out of bounds. */
@Nullable
public final T tryGetValue(long pos)
{
QuadNode<T> node = this.tryGetNode(pos);
if (node != null)
{
return node.value;
}
return null;
}
/** @return the node at the given section position, null if out of bounds */
@Nullable
public final QuadNode<T> tryGetNode(long pos) { return this.getOrSetNode(pos, false, null, false); }
/** @return the node at the given section position */ /** @return the node at the given section position */
@Nullable @Nullable
public final QuadNode<T> getNode(long pos) throws IndexOutOfBoundsException { return this.getOrSetNode(pos, false, null, true); } public final QuadNode<T> getNode(long pos) throws IndexOutOfBoundsException { return this.getOrSetNode(pos, false, null, true); }
/** @return the value at the given section position */ /** @return the value at the given section position */
@Nullable @Nullable
public final T getValue(long pos) throws IndexOutOfBoundsException public final T getValue(long pos) throws IndexOutOfBoundsException
@@ -122,16 +138,24 @@ public class QuadTree<T>
return previousValue; return previousValue;
} }
/** @param runBoundaryChecks should only ever be set to true internally for removing out of bound nodes */ /** @param throwIfOutOfBounds if false returns null */
@Nullable @Nullable
protected final QuadNode<T> getOrSetNode(long pos, boolean setNewValue, T newValue, boolean runBoundaryChecks) throws IndexOutOfBoundsException protected final QuadNode<T> getOrSetNode(long pos, boolean setNewValue, T newValue, boolean throwIfOutOfBounds) throws IndexOutOfBoundsException
{ {
if (runBoundaryChecks && !this.isSectionPosInBounds(pos)) if (!this.isSectionPosInBounds(pos))
{
// how should out-of-bounds positions be handled?
if (throwIfOutOfBounds)
{ {
int radius = this.diameterInBlocks() / 2; int radius = this.diameterInBlocks() / 2;
DhBlockPos2D minPos = this.getCenterBlockPos().add(new DhBlockPos2D(-radius, -radius)); DhBlockPos2D minBlockPos = this.getCenterBlockPos().add(new DhBlockPos2D(-radius, -radius));
DhBlockPos2D maxPos = this.getCenterBlockPos().add(new DhBlockPos2D(radius, radius)); DhBlockPos2D maxBlockPos = this.getCenterBlockPos().add(new DhBlockPos2D(radius, radius));
throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: " + minPos + ", max pos: " + maxPos + ", min detail level: " + this.treeLeafDetailLevel + ", max detail level: " + this.treeRootDetailLevel + ". Given Position: [" + DhSectionPos.toString(pos) + "] = block pos: " + DhSectionPos.convertToDetailLevel(pos, LodUtil.BLOCK_DETAIL_LEVEL)); throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min block pos: [" + minBlockPos + "], max block pos: [" + maxBlockPos + "], leaf detail level: [" + this.treeLeafDetailLevel + "], root detail level: [" + this.treeRootDetailLevel + "]. Requested section pos: [" + DhSectionPos.toString(pos) + "].");
}
else
{
return null;
}
} }
@@ -278,46 +302,6 @@ public class QuadTree<T>
removedItemConsumer.accept(quadNode.value); removedItemConsumer.accept(quadNode.value);
} }
}); });
// // remove out of bound nodes and clean up empty nodes
// // Note: this will iterate over a lot of unnecessary nodes, hopefully speed won't be an issue
// Iterator<DhSectionPos> rootNodePosIterator = this.rootNodePosIterator();
// while (rootNodePosIterator.hasNext())
// {
// // get the root node (regular nodeIterators won't return them if they are out of bounds)
// DhSectionPos rootPos = rootNodePosIterator.next();
// QuadNode<T> rootNode = this.getOrSetNode(rootPos, false, null, false);
// if (rootNode == null)
// {
// continue;
// }
//
// // remove any child nodes that are out of bounds
// Iterator<QuadNode<T>> nodeIterator = this.nodeIterator();
// while (nodeIterator.hasNext())
// {
// QuadNode<T> node = nodeIterator.next();
// if(!this.isSectionPosInBounds(node.sectionPos))
// {
// // node is out of bounds
//
// // FIXME(?) this appears to potentially return large nodes that are partially or entirely in bounds
//
// if (node.getNonNullChildCount() == 0)
// {
// // no child nodes, can be safely removed
// nodeIterator.remove();
// }
// else
// {
// // node can't be removed, but its value can be set to null
// node.value = null;
// }
// }
// }
// }
} }
public final DhBlockPos2D getCenterBlockPos() { return this.centerBlockPos; } public final DhBlockPos2D getCenterBlockPos() { return this.centerBlockPos; }
@@ -544,7 +528,9 @@ public class QuadTree<T>
&& this.rootNodeIterator.hasNext()) && this.rootNodeIterator.hasNext())
{ {
long sectionPos = this.rootNodeIterator.nextLong(); long sectionPos = this.rootNodeIterator.nextLong();
QuadNode<T> rootNode = QuadTree.this.getNode(sectionPos);
// try-get to prevent concurrency errors if the tree is being moved while we walk through it
QuadNode<T> rootNode = QuadTree.this.tryGetNode(sectionPos);
if (rootNode != null) if (rootNode != null)
{ {
nodeIterator = this.onlyReturnLeaves ? rootNode.getLeafNodeIterator() : rootNode.getNodeIterator(this.stopIteratingFunc); nodeIterator = this.onlyReturnLeaves ? rootNode.getLeafNodeIterator() : rootNode.getNodeIterator(this.stopIteratingFunc);
@@ -30,7 +30,7 @@ import java.util.function.Consumer;
public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>> public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
{ {
/** lowest numerical value, inclusive */ /** lowest numerical value, inclusive */
private final byte highestDetailLevel; private final byte leafDetailLevel;
private final Queue<QuadNode<T>> validNodesForDetailLevel = new ArrayDeque<>(); private final Queue<QuadNode<T>> validNodesForDetailLevel = new ArrayDeque<>();
@@ -48,8 +48,7 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
{ {
this.onlyReturnLeafValues = onlyReturnLeafValues; this.onlyReturnLeafValues = onlyReturnLeafValues;
this.stopIteratingFunc = stopIteratingFunc; this.stopIteratingFunc = stopIteratingFunc;
// TODO the naming conversion for these are flipped in a lot of places this.leafDetailLevel = rootNode.parentTreeLeafDetailLevel;
this.highestDetailLevel = rootNode.parentTreeLeafDetailLevel;
this.iteratorDetailLevel = DhSectionPos.getDetailLevel(rootNode.sectionPos); this.iteratorDetailLevel = DhSectionPos.getDetailLevel(rootNode.sectionPos);
@@ -110,9 +109,9 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
@Override @Override
public QuadNode<T> next() public QuadNode<T> next()
{ {
if (this.iteratorDetailLevel < this.highestDetailLevel) if (this.iteratorDetailLevel < this.leafDetailLevel)
{ {
throw new NoSuchElementException("Highest detail level reached [" + this.highestDetailLevel + "]."); throw new NoSuchElementException("Leaf detail level reached [" + this.leafDetailLevel + "].");
} }
if (this.iteratorNodeQueue.size() == 0) if (this.iteratorNodeQueue.size() == 0)
{ {
@@ -133,7 +132,7 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
this.iteratorDetailLevel--; this.iteratorDetailLevel--;
// only continue if we can go down farther // only continue if we can go down farther
if (this.iteratorDetailLevel >= this.highestDetailLevel) if (this.iteratorDetailLevel >= this.leafDetailLevel)
{ {
Queue<QuadNode<T>> parentNodes = new LinkedList<>(this.validNodesForDetailLevel); Queue<QuadNode<T>> parentNodes = new LinkedList<>(this.validNodesForDetailLevel);
this.validNodesForDetailLevel.clear(); this.validNodesForDetailLevel.clear();
@@ -69,10 +69,14 @@ public class ThreadPoolUtil
@Nullable @Nullable
public static ThreadPoolExecutor getBeaconCullingExecutor() { return beaconCullingThreadPool; } public static ThreadPoolExecutor getBeaconCullingExecutor() { return beaconCullingThreadPool; }
// The main distinction between these thread pools is that one for compression has multiple threads and client handler is single-threaded
private static PriorityTaskPicker.Executor networkCompressionThreadPool; private static PriorityTaskPicker.Executor networkCompressionThreadPool;
@Nullable @Nullable
public static PriorityTaskPicker.Executor getNetworkCompressionExecutor() { return networkCompressionThreadPool; } public static PriorityTaskPicker.Executor getNetworkCompressionExecutor() { return networkCompressionThreadPool; }
private static ThreadPoolExecutor networkClientHandlerThreadPool;
@Nullable
public static ThreadPoolExecutor networkClientHandlerExecutor() { return networkClientHandlerThreadPool; }
public static final String FULL_DATA_MIGRATION_THREAD_NAME = "Full Data Migration"; public static final String FULL_DATA_MIGRATION_THREAD_NAME = "Full Data Migration";
@@ -103,7 +107,8 @@ public class ThreadPoolUtil
} }
taskPicker = new PriorityTaskPicker(); taskPicker = new PriorityTaskPicker();
networkCompressionThreadPool = taskPicker.createExecutor("Network"); networkCompressionThreadPool = taskPicker.createExecutor("Network Compression");
networkClientHandlerThreadPool = ThreadUtil.makeSingleThreadPool("Network Client Handler");
fileHandlerThreadPool = taskPicker.createExecutor("IO"); fileHandlerThreadPool = taskPicker.createExecutor("IO");
renderSectionLoadThreadPool = taskPicker.createExecutor("Render Loader"); renderSectionLoadThreadPool = taskPicker.createExecutor("Render Loader");
chunkToLodBuilderThreadPool = taskPicker.createExecutor("LOD Builder"); chunkToLodBuilderThreadPool = taskPicker.createExecutor("LOD Builder");
@@ -133,6 +138,7 @@ public class ThreadPoolUtil
public static void shutdownThreadPools() public static void shutdownThreadPools()
{ {
// standalone threads // standalone threads
networkClientHandlerThreadPool.shutdownNow();
taskPicker.shutdownNow(); taskPicker.shutdownNow();
beaconCullingThreadPool.shutdown(); beaconCullingThreadPool.shutdown();
fullDataMigrationThreadPool.shutdown(); fullDataMigrationThreadPool.shutdown();
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core.world; package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.level.DhClientServerLevel; import com.seibel.distanthorizons.core.level.DhClientServerLevel;
import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
@@ -82,7 +83,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
LOGGER.fatal("Failed to load client-server level, error: ["+e.getMessage()+"].", e); LOGGER.fatal("Failed to load client-server level, error: ["+e.getMessage()+"].", e);
ClientApi.INSTANCE.showChatMessageNextFrame(// red text ClientApi.INSTANCE.showChatMessageNextFrame(// red text
"\u00A7c" + "Distant Horizons: ClientServer level loading failed." + "\u00A7r \n" + MinecraftTextFormat.RED + "Distant Horizons: ClientServer level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"Unable to load level ["+levelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information."); "Unable to load level ["+levelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
return null; return null;
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core.world; package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
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.level.DhClientLevel; import com.seibel.distanthorizons.core.level.DhClientLevel;
import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhLevel;
@@ -94,8 +95,8 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
{ {
LOGGER.fatal("Failed to load client level, error: ["+e.getMessage()+"].", e); LOGGER.fatal("Failed to load client level, error: ["+e.getMessage()+"].", e);
ClientApi.INSTANCE.showChatMessageNextFrame(// red text ClientApi.INSTANCE.showChatMessageNextFrame(
"\u00A7c" + "Distant Horizons: Client level loading failed." + "\u00A7r \n" + MinecraftTextFormat.RED + "Distant Horizons: Client level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"Unable to load level ["+clientLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information."); "Unable to load level ["+clientLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
return null; return null;
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core.world; package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.DhServerLevel;
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;
@@ -62,8 +63,8 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
{ {
LOGGER.fatal("Failed to load server level, error: ["+e.getMessage()+"].", e); LOGGER.fatal("Failed to load server level, error: ["+e.getMessage()+"].", e);
ClientApi.INSTANCE.showChatMessageNextFrame(// red text ClientApi.INSTANCE.showChatMessageNextFrame(
"\u00A7c" + "Distant Horizons: Server level loading failed." + "\u00A7r \n" + MinecraftTextFormat.RED + "Distant Horizons: Server level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"Unable to load level ["+serverLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information."); "Unable to load level ["+serverLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
return null; return null;
@@ -63,7 +63,9 @@ public interface IMinecraftRenderWrapper extends IBindable
/** @return -1 if no valid framebuffer is available yet */ /** @return -1 if no valid framebuffer is available yet */
int getTargetFramebuffer(); // Note: Iris is now hooking onto this for DH + Iris compat, try not to change (unless we wanna deal with some annoyances) int getTargetFramebuffer(); // Note: Iris is now hooking onto this for DH + Iris compat, try not to change (unless we wanna deal with some annoyances)
// Iris commit: https://github.com/IrisShaders/Iris/commit/a76a240527e93780bbcba57c09bef377419d47a7#diff-7b9ded0c79bbcdb130010373387756a28ee8d3640d522c0a5b7acd0abbfc20aeR16 // Iris commit: https://github.com/IrisShaders/Iris/commit/a76a240527e93780bbcba57c09bef377419d47a7#diff-7b9ded0c79bbcdb130010373387756a28ee8d3640d522c0a5b7acd0abbfc20aeR16
/** @return -1 if there was an issue or no texture exists */
int getDepthTextureId(); int getDepthTextureId();
/** @return -1 if there was an issue or no texture exists */
int getColorTextureId(); int getColorTextureId();
int getTargetFramebufferViewportWidth(); int getTargetFramebufferViewportWidth();
int getTargetFramebufferViewportHeight(); int getTargetFramebufferViewportHeight();
@@ -85,6 +85,12 @@
"distanthorizons.config.client.optionsButton.@tooltip": "distanthorizons.config.client.optionsButton.@tooltip":
"Show the config button to the left of the fov button", "Show the config button to the left of the fov button",
"distanthorizons.config.client.dynamicFadeUseOpaqueMcDepth":
"Dynamic Fade Use MC Opaque Depth",
"distanthorizons.config.client.dynamicFadeExportPath":
"Dynamic Fade Export Path",
"distanthorizons.config.client.dynamicFadeExportPath.@tooltip":
"Press 'p' to export the depth textures for troubleshooting",
"distanthorizons.config.client.advanced": "distanthorizons.config.client.advanced":
@@ -227,6 +233,10 @@
"Beacon render height", "Beacon render height",
"distanthorizons.config.client.advanced.graphics.genericRendering.beaconRenderHeight.@tooltip": "distanthorizons.config.client.advanced.graphics.genericRendering.beaconRenderHeight.@tooltip":
"Sets the maximum height at which beacons will render. \nThis will only affect new beacons coming into LOD render distance. \nBeacons currently visible in LOD chunks will not be affected.", "Sets the maximum height at which beacons will render. \nThis will only affect new beacons coming into LOD render distance. \nBeacons currently visible in LOD chunks will not be affected.",
"distanthorizons.config.client.advanced.graphics.genericRendering.expandDistantBeacons":
"Expand Distant Beacons",
"distanthorizons.config.client.advanced.graphics.genericRendering.expandDistantBeacons.@tooltip":
"If true LOD beacon beams will be rendered wider at extreme distances, \nmaking them easier to see. \nIf false all LOD beacon beams will only ever be 1 block wide.",
"distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering.@tooltip": "distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering.@tooltip":
"If true LOD beacon beams will be rendered.", "If true LOD beacon beams will be rendered.",
"distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering": "distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering":
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB