Compare commits

..

2 Commits

Author SHA1 Message Date
s809 2a4bfef7a6 Add correct descriptions 2026-01-18 22:38:01 +05:00
s809 7be65a2258 Split off server generation into a separate toggle 2026-01-17 01:43:05 +05:00
11 changed files with 100 additions and 537 deletions
@@ -23,7 +23,6 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
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.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
@@ -33,8 +32,8 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.renderer.*;
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.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
@@ -42,6 +41,7 @@ import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.network.session.NetworkSession;
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.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
@@ -55,23 +55,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Vector4f;
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.IOException;
import java.text.NumberFormat;
import java.util.*;
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
@@ -561,11 +551,8 @@ public class ClientApi
* The first fade pass.
* Called after MC finishes rendering the opaque passes.
*/
public void renderFadeOpaque() // TODO this is actually the transparent pass
public void renderFadeOpaque()
{
DepthCalculator.INSTANCE.getMcTransparentDepthTexture();
DepthCalculator.INSTANCE.tryCalculateAsync();
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT
&&
@@ -586,10 +573,8 @@ public class ClientApi
* Called after MC finishes rendering both opaque
* and transparent passes.
*/
public void renderFadeTransparent() // TODO this is actually the opaque pass
public void renderFadeTransparent()
{
DepthCalculator.INSTANCE.getMcOpaqueDepthTexture();
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
{
@@ -618,30 +603,27 @@ public class ClientApi
/** Trigger once on key press, with CLIENT PLAYER. */
public void keyPressedEvent(int glfwKey)
{
//if (!Config.Client.Advanced.Debugging.enableDebugKeybindings.get())
//{
// // keybindings are disabled
// return;
//}
if (!Config.Client.Advanced.Debugging.enableDebugKeybindings.get())
{
// keybindings are disabled
return;
}
if (glfwKey == GLFW.GLFW_KEY_F8)
{
DepthCalculator.INSTANCE.pause = true;
//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());
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)
{
DepthCalculator.INSTANCE.pause = true;
//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());
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)
{
DepthCalculator.INSTANCE.pause = true;
//prefLoggerEnabled = !prefLoggerEnabled;
//MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
prefLoggerEnabled = !prefLoggerEnabled;
MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
}
}
@@ -101,6 +101,7 @@ public class Config
.build();
public static ConfigUiLinkedEntry quickEnableWorldGenerator = new ConfigUiLinkedEntry(Common.WorldGenerator.enableDistantGeneration);
public static ConfigUiLinkedEntry quickEnableServerGeneration = new ConfigUiLinkedEntry(Server.enableServerGeneration);
public static ConfigUiLinkedEntry quickShowWorldGenProgress = new ConfigUiLinkedEntry(Common.WorldGenerator.showGenerationProgress);
@@ -118,20 +119,6 @@ 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
{
// common config links need to have their destination
@@ -1748,6 +1735,15 @@ public class Config
// Generation
public static ConfigEntry<Boolean> enableServerGeneration = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "When enabled, Distant Horizons will attempt to download missing LODs from the server.\n"
+ "\n"
+ "Note: the server must have Distant Generation enabled for it to work."
+ "")
.build();
public static ConfigEntry<Integer> generationRequestRateLimit = new ConfigEntry.Builder<Integer>()
.setChatCommandName("generation.requestRateLimit")
.setMinDefaultMax(1, 20, 100)
@@ -11,6 +11,7 @@ import java.io.Closeable;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class SessionConfig implements INetworkObject
@@ -31,7 +32,23 @@ public class SessionConfig implements INetworkObject
{
// Note: config values are transmitted in the insertion order
registerConfigEntry(Config.Common.WorldGenerator.enableDistantGeneration, Boolean::logicalAnd);
registerConfigEntry(Config.Common.WorldGenerator.enableDistantGeneration.getChatCommandName(), new Entry(
Config.Server.enableServerGeneration::get,
runnable -> new Closeable()
{
private final ConfigChangeListener<Boolean> distantGenerationChanges = new ConfigChangeListener<>(Config.Common.WorldGenerator.enableDistantGeneration, ignored -> runnable.run());
private final ConfigChangeListener<Boolean> serverGenerationChanges = new ConfigChangeListener<>(Config.Server.enableServerGeneration, ignored -> runnable.run());
@Override
public void close()
{
this.serverGenerationChanges.close();
this.distantGenerationChanges.close();
}
},
(Boolean client, Boolean server) -> client && Config.Common.WorldGenerator.enableDistantGeneration.get()
));
registerConfigEntry(Config.Server.maxGenerationRequestDistance, Math::min);
registerConfigEntry(Config.Common.WorldGenerator.generationCenterChunkX, (x, y) -> y);
registerConfigEntry(Config.Common.WorldGenerator.generationCenterChunkZ, (x, y) -> y);
@@ -90,14 +107,24 @@ public class SessionConfig implements INetworkObject
private static <T> void registerConfigEntry(ConfigEntry<T> configEntry, BinaryOperator<T> valueConstrainer)
{
CONFIG_ENTRIES.compute(Objects.requireNonNull(configEntry.getChatCommandName()), (key, existingEntry) -> {
if (existingEntry != null)
{
throw new IllegalArgumentException("Attempted to register config entry with duplicate chatCommandName: " + key);
}
return new Entry(configEntry, valueConstrainer);
});
registerConfigEntry(
Objects.requireNonNull(configEntry.getChatCommandName()),
new Entry(
configEntry::get,
runnable -> new ConfigChangeListener<>(configEntry, ignored -> runnable.run()),
valueConstrainer
)
);
}
private static void registerConfigEntry(String key, Entry entry)
{
if (CONFIG_ENTRIES.containsKey(key))
{
throw new IllegalArgumentException("Attempted to register config entry with duplicate key: " + key);
}
CONFIG_ENTRIES.put(key, entry);
}
@@ -115,7 +142,7 @@ public class SessionConfig implements INetworkObject
T value = (T) this.values.get(name);
if (value == null)
{
value = (T) entry.supplier.get();
value = (T) entry.valueSupplier.get();
}
return (this.constrainingConfig != null
@@ -210,13 +237,15 @@ public class SessionConfig implements INetworkObject
private static class Entry
{
public final ConfigEntry<Object> supplier;
public final Supplier<Object> valueSupplier;
public final Function<Runnable, Closeable> changeListenerFactory;
public final BinaryOperator<Object> valueConstrainer;
@SuppressWarnings("unchecked")
private <T> Entry(ConfigEntry<T> supplier, BinaryOperator<T> valueConstrainer)
private <T> Entry(Supplier<Object> valueSupplier, Function<Runnable, Closeable> changeListenerFactory, BinaryOperator<T> valueConstrainer)
{
this.supplier = (ConfigEntry<Object>) supplier;
this.valueSupplier = valueSupplier;
this.changeListenerFactory = changeListenerFactory;
this.valueConstrainer = (BinaryOperator<Object>) valueConstrainer;
}
@@ -225,23 +254,29 @@ public class SessionConfig implements INetworkObject
/** fires if any config value was changed */
public static class AnyChangeListener implements Closeable
{
private final ArrayList<ConfigChangeListener<?>> changeListeners;
private final ArrayList<Closeable> changeListeners;
public AnyChangeListener(Runnable runnable)
{
this.changeListeners = new ArrayList<>(CONFIG_ENTRIES.size());
for (Entry entry : CONFIG_ENTRIES.values())
{
this.changeListeners.add(new ConfigChangeListener<>(entry.supplier, ignored -> runnable.run()));
this.changeListeners.add(entry.changeListenerFactory.apply(runnable));
}
}
@Override
public void close()
{
for (ConfigChangeListener<?> changeListener : this.changeListeners)
for (Closeable changeListener : this.changeListeners)
{
changeListener.close();
try
{
changeListener.close();
}
catch (Exception ignored)
{
}
}
this.changeListeners.clear();
}
@@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.file.fullDatafile.V2.FullDataSourceProviderV2;
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
import com.seibel.distanthorizons.core.generation.tasks.ERetrievalResultState;
import com.seibel.distanthorizons.core.level.DhClientServerLevel;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -144,6 +145,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null;
Config.Common.WorldGenerator.enableDistantGeneration.addListener(this);
Config.Server.enableServerGeneration.addListener(this);
}
@@ -662,7 +664,9 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
@Override
public void onConfigValueSet()
{
boolean generatorEnabled = Config.Common.WorldGenerator.enableDistantGeneration.get();
boolean generatorEnabled = this.level instanceof DhClientServerLevel
? Config.Common.WorldGenerator.enableDistantGeneration.get()
: Config.Server.enableServerGeneration.get();
if (generatorEnabled)
{
// world gen tasks will need to be re-queued
@@ -920,6 +924,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
DebugRenderer.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
Config.Common.WorldGenerator.enableDistantGeneration.removeListener(this);
Config.Server.enableServerGeneration.removeListener(this);
ThreadPoolExecutor mainCleanupExecutor = ThreadPoolUtil.getCleanupExecutor();
@@ -1,444 +0,0 @@
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());
// Clip Uniform
float dhNearClipDistance = 0.1f;//RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks);
//if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
//{
// // this added value prevents the near clip plane and discard circle from touching, which looks bad
// dhNearClipDistance += 16f;
//}
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks);
if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
// this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f;
}
// 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
if (RenderUtil.getHeightBasedNearClipOverride() != -1)
{
dhNearClipDistance = 1.0f; // TODO does this actually disable anything?
dhNearClipDistance = 1.0f;
}
this.setUniform(this.uClipDistance, dhNearClipDistance);
}
@@ -222,8 +222,6 @@ public class LodRenderer
profiler.popPush("LOD Opaque");
this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ true);
DepthCalculator.INSTANCE.trySetDhDepthTexture();
// custom objects with SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
@@ -19,7 +19,6 @@
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.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
@@ -115,7 +114,7 @@ public class VanillaFadeShader extends AbstractShaderRenderer
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(partialTicks);
// this added value prevents the near clip plane and discard circle from touching, which looks bad
//dhNearClipDistance += 16f;
dhNearClipDistance += 16f;
// measured in blocks
// these multipliers in James' tests should provide a fairly smooth transition
@@ -19,10 +19,8 @@
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.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.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -129,16 +127,13 @@ public class RenderUtil
}
public static float getNearClipPlaneInBlocksForFading(float partialTicks)
{
//float overdraw = getAutoOverdrawPrevention();
//return getNearClipPlaneDistanceInBlocks(partialTicks, overdraw);
return DepthCalculator.INSTANCE.actualMcBlockDistance;
float overdraw = getAutoOverdrawPrevention();
return getNearClipPlaneDistanceInBlocks(partialTicks, overdraw);
}
private static float getNearClipPlaneDistanceInBlocks(float partialTicks, float overdrawPreventionPercent)
{
int chunkRenderDistance = MC_RENDER.getRenderDistance();
//float chunkRenderDistance = ClientApi.actualMcBlockDistance / 16.0f;
float vanillaBlockRenderedDistance = chunkRenderDistance * LodUtil.CHUNK_WIDTH;
int vanillaBlockRenderedDistance = chunkRenderDistance * LodUtil.CHUNK_WIDTH;
float nearClipPlane;
if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
@@ -63,9 +63,7 @@ public interface IMinecraftRenderWrapper extends IBindable
/** @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)
// Iris commit: https://github.com/IrisShaders/Iris/commit/a76a240527e93780bbcba57c09bef377419d47a7#diff-7b9ded0c79bbcdb130010373387756a28ee8d3640d522c0a5b7acd0abbfc20aeR16
/** @return -1 if there was an issue or no texture exists */
int getDepthTextureId();
/** @return -1 if there was an issue or no texture exists */
int getColorTextureId();
int getTargetFramebufferViewportWidth();
int getTargetFramebufferViewportHeight();
@@ -84,13 +84,7 @@
"Show The Options Button",
"distanthorizons.config.client.optionsButton.@tooltip":
"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":
@@ -769,6 +763,11 @@
"distanthorizons.config.server.levelKeyPrefix.@tooltip":
"Prefix of the level keys sent to the clients.\nIf the mod is running behind a proxy, each backend should use a unique value.\nIf this value is empty, level key will be based on the server's seed hash.",
"distanthorizons.config.server.enableServerGeneration":
"Enable Server Generation",
"distanthorizons.config.server.enableServerGeneration.@tooltip":
"When enabled, Distant Horizons will attempt to download missing LODs from the server.\n\nNote: the server must have Distant Generation enabled for it to work.",
"distanthorizons.config.server.generationRequestRateLimit":
"Rate Limit for Generation Requests",
"distanthorizons.config.server.generationRequestRateLimit.@tooltip":