diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/config/ELodShading.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/ELodShading.java
new file mode 100644
index 000000000..2c14a8874
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/ELodShading.java
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Distant Horizons mod (formerly the LOD Mod),
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2022 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.api.enums.config;
+
+/**
+ * MINECRAFT
+ * OLD_LIGHTING
+ * NONE
+ */
+public enum ELodShading
+{
+ // Reminder:
+ // when adding items up the API minor version
+ // when removing items up the API major version
+
+ MINECRAFT,
+ OLD_LIGHTING,
+ NONE;
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/config/quickOptions/EThreadPreset.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/quickOptions/EThreadPreset.java
index cf08b58c3..87e9f09ab 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/enums/config/quickOptions/EThreadPreset.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/quickOptions/EThreadPreset.java
@@ -43,6 +43,8 @@ public enum EThreadPreset
LOW_IMPACT,
BALANCED,
AGGRESSIVE,
- I_PAID_FOR_THE_WHOLE_CPU;
+
+ // temporarily removed due to stability concerns
+ //I_PAID_FOR_THE_WHOLE_CPU;
}
\ No newline at end of file
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java
index 1670a7f58..3587be8a7 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java
@@ -586,6 +586,20 @@ public class Config
+ "If set to 0 the mod wont overwrite vanilla's default (which so happens to also be 0)")
.build();
+ public static ConfigEntry lodShading = new ConfigEntry.Builder()
+ .set(ELodShading.MINECRAFT)
+ .comment(""
+ + "How should LODs be shaded? \n"
+ + "\n"
+ + ELodShading.MINECRAFT + ": Uses the same side shading as vanilla Minecraft blocks. \n"
+ + ELodShading.OLD_LIGHTING + ": Simulates Minecraft's block shading for LODs. \n"
+ + " Can be used to force LOD shading when using some shaders. \n"
+ + ELodShading.NONE + ": All LOD sides will be rendered with the same brightness. \n"
+ + "")
+ .setPerformance(EConfigEntryPerformance.NONE)
+ .addListener(RenderCacheConfigEventHandler.INSTANCE)
+ .build();
+
}
}
@@ -844,7 +858,7 @@ public class Config
public static final ConfigEntry numberOfDataTransformerThreads = new ConfigEntry.Builder()
.setMinDefaultMax(1,
- ThreadPresetConfigEventHandler.getDataConverterDefaultThreadCount(),
+ ThreadPresetConfigEventHandler.getDataTransformerDefaultThreadCount(),
Runtime.getRuntime().availableProcessors())
.comment(""
+ "How many threads should be used when converting full ID data to render data? \n"
@@ -859,7 +873,7 @@ public class Config
+ THREAD_NOTE)
.build();
public static final ConfigEntry runTimeRatioForDataTransformerThreads = new ConfigEntry.Builder()
- .setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getDataConverterDefaultRunTimeRatio(), 1.0)
+ .setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getDataTransformerDefaultRunTimeRatio(), 1.0)
.comment(THREAD_RUN_TIME_RATIO_NOTE)
.build();
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java
index a88979cf2..8bbca7f0c 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java
@@ -1,6 +1,7 @@
package com.seibel.distanthorizons.core.config.eventHandlers;
import com.seibel.distanthorizons.api.DhApi;
+import com.seibel.distanthorizons.api.enums.config.ELodShading;
import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution;
import com.seibel.distanthorizons.api.enums.config.EVerticalQuality;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
@@ -22,6 +23,7 @@ public class RenderCacheConfigEventHandler implements IConfigListener
// previous values used to check if a watched setting was actually modified
private EVerticalQuality previousVerticalQualitySetting = null;
private EMaxHorizontalResolution previousHorizontalResolution = null;
+ private ELodShading lodShading = null;
/** how long to wait in milliseconds before applying the config changes */
private static final long TIMEOUT_IN_MS = 400L;
@@ -54,6 +56,13 @@ public class RenderCacheConfigEventHandler implements IConfigListener
refreshRenderData = true;
}
+ ELodShading newLodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
+ if (this.lodShading != newLodShading)
+ {
+ this.lodShading = newLodShading;
+ refreshRenderData = true;
+ }
+
if (refreshRenderData)
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java
index 2cfb8d104..9c21d6233 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java
@@ -12,9 +12,11 @@ import java.util.*;
public abstract class AbstractPresetConfigEventHandler> implements IConfigListener
{
private static final Logger LOGGER = LogManager.getLogger();
-
+ private static final long MS_DELAY_BEFORE_APPLYING_PRESET = 1_000;
protected final ArrayList> configList = new ArrayList<>();
+ /** this timer is used so each preset isn't applied while a user is clicking through the config options */
+ protected Timer presetApplicationTimer;
protected boolean changingPreset = false;
@@ -38,28 +40,42 @@ public abstract class AbstractPresetConfigEventHandler configEntry : this.configList)
{
- configEntry.updateConfigEntry(qualityPreset);
+ configEntry.updateConfigEntry(presetEnum);
}
this.changingPreset = false;
- LOGGER.debug("preset active: "+qualityPreset);
-
+ LOGGER.debug("preset active: "+presetEnum);
}
+
@Override
public void onUiModify() { /* do nothing, we only care about modified config values */ }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java
index 7d4916c2c..1a3f5e295 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java
@@ -18,28 +18,29 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
public static final ThreadPresetConfigEventHandler INSTANCE = new ThreadPresetConfigEventHandler();
private static final Logger LOGGER = LogManager.getLogger();
+ private static final boolean LOW_THREAD_COUNT_CPU = (Runtime.getRuntime().availableProcessors() <= 4);
- public static int getWorldGenDefaultThreadCount() { return getThreadCountByPercent(0.1); }
+ public static int getWorldGenDefaultThreadCount() { return getThreadCountByPercent(0.2); }
private final ConfigEntryWithPresetOptions worldGenThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads,
new HashMap()
{{
this.put(EThreadPreset.MINIMAL_IMPACT, 1);
this.put(EThreadPreset.LOW_IMPACT, getWorldGenDefaultThreadCount());
- this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.2));
- this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.4));
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
+ this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.4));
+ this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.6));
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
}});
- public static double getWorldGenDefaultRunTimeRatio() { return 0.25; }
+ public static double getWorldGenDefaultRunTimeRatio() { return LOW_THREAD_COUNT_CPU ? 0.5 : 1; }
private final ConfigEntryWithPresetOptions worldGenRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads,
new HashMap()
{{
- this.put(EThreadPreset.MINIMAL_IMPACT, 0.1);
+ this.put(EThreadPreset.MINIMAL_IMPACT, LOW_THREAD_COUNT_CPU ? 0.1 : 0.25);
this.put(EThreadPreset.LOW_IMPACT, getWorldGenDefaultRunTimeRatio());
- this.put(EThreadPreset.BALANCED, 0.5);
- this.put(EThreadPreset.AGGRESSIVE, 0.75);
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
+ this.put(EThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.5 : 1.0);
+ this.put(EThreadPreset.AGGRESSIVE, LOW_THREAD_COUNT_CPU ? 0.75 : 1.0);
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
}});
@@ -51,17 +52,17 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.put(EThreadPreset.LOW_IMPACT, getBufferBuilderDefaultThreadCount());
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.2));
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.4));
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
}});
- public static double getBufferBuilderDefaultRunTimeRatio() { return 0.5; }
+ public static double getBufferBuilderDefaultRunTimeRatio() { return LOW_THREAD_COUNT_CPU ? 0.5 : 0.75; }
private final ConfigEntryWithPresetOptions bufferBuilderRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForBufferBuilderThreads,
new HashMap()
{{
this.put(EThreadPreset.MINIMAL_IMPACT, 0.25);
this.put(EThreadPreset.LOW_IMPACT, getBufferBuilderDefaultRunTimeRatio());
- this.put(EThreadPreset.BALANCED, 0.75);
+ this.put(EThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.75 : 1.0);
this.put(EThreadPreset.AGGRESSIVE, 1.0);
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
}});
@@ -73,7 +74,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.put(EThreadPreset.LOW_IMPACT, getFileHandlerDefaultThreadCount());
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.2));
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.2));
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
}});
public static double getFileHandlerDefaultRunTimeRatio() { return 0.5; }
private final ConfigEntryWithPresetOptions fileHandlerRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForFileHandlerThreads,
@@ -83,29 +84,29 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.put(EThreadPreset.LOW_IMPACT, getFileHandlerDefaultRunTimeRatio());
this.put(EThreadPreset.BALANCED, 0.75);
this.put(EThreadPreset.AGGRESSIVE, 1.0);
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
}});
- public static int getDataConverterDefaultThreadCount() { return getThreadCountByPercent(0.1); }
+ public static int getDataTransformerDefaultThreadCount() { return getThreadCountByPercent(0.1); }
private final ConfigEntryWithPresetOptions dataTransformerThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfDataTransformerThreads,
new HashMap()
{{
this.put(EThreadPreset.MINIMAL_IMPACT, 1);
- this.put(EThreadPreset.LOW_IMPACT, getDataConverterDefaultThreadCount());
+ this.put(EThreadPreset.LOW_IMPACT, getDataTransformerDefaultThreadCount());
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.2));
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.2));
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
}});
- public static double getDataConverterDefaultRunTimeRatio() { return 0.25; }
+ public static double getDataTransformerDefaultRunTimeRatio() { return LOW_THREAD_COUNT_CPU ? 0.5 : 1; }
private final ConfigEntryWithPresetOptions dataTransformerRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForDataTransformerThreads,
new HashMap()
{{
- this.put(EThreadPreset.MINIMAL_IMPACT, 0.1);
- this.put(EThreadPreset.LOW_IMPACT, getDataConverterDefaultRunTimeRatio());
- this.put(EThreadPreset.BALANCED, 0.75);
+ this.put(EThreadPreset.MINIMAL_IMPACT, 0.25);
+ this.put(EThreadPreset.LOW_IMPACT, getDataTransformerDefaultRunTimeRatio());
+ this.put(EThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.75 : 1);
this.put(EThreadPreset.AGGRESSIVE, 1.0);
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
}});
@@ -117,17 +118,17 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.put(EThreadPreset.LOW_IMPACT, getChunkLodConverterDefaultThreadCount());
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.2));
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.4));
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
}});
- public static double getChunkLodConverterDefaultRunTimeRatio() { return 0.5; }
+ public static double getChunkLodConverterDefaultRunTimeRatio() { return LOW_THREAD_COUNT_CPU ? 0.5 : 0.75; }
private final ConfigEntryWithPresetOptions chunkLodConverterRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForChunkLodConverterThreads,
new HashMap()
{{
- this.put(EThreadPreset.MINIMAL_IMPACT, 0.25);
- this.put(EThreadPreset.LOW_IMPACT, getDataConverterDefaultRunTimeRatio());
- this.put(EThreadPreset.BALANCED, 0.75);
+ this.put(EThreadPreset.MINIMAL_IMPACT, LOW_THREAD_COUNT_CPU ? 0.1 : 0.25);
+ this.put(EThreadPreset.LOW_IMPACT, getDataTransformerDefaultRunTimeRatio());
+ this.put(EThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.75 : 1);
this.put(EThreadPreset.AGGRESSIVE, 1.0);
- this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
+ //this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
}});
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
index bf3652f13..b1e73a0fb 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
@@ -53,7 +53,9 @@ public class ColumnRenderBufferBuilder
// vbo building //
//==============//
- public static CompletableFuture buildBuffers(IDhClientLevel clientLevel, Reference renderBufferRef, ColumnRenderSource renderSource, ColumnRenderSource[] adjData)
+ public static CompletableFuture buildBuffersAsync(
+ IDhClientLevel clientLevel, Reference renderBufferRef,
+ ColumnRenderSource renderSource, ColumnRenderSource[] adjData)
{
/* if (isBusy())
{
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java
index 61455e81a..75aa3f46e 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java
@@ -278,11 +278,11 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
{
return metaFile; // someone else loaded it already.
}
-
+
try
{
metaFile = RenderMetaDataFile.createFromExistingFile(this, fileToLoad);
- this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel));
+ this.topDetailLevel.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.sectionDetailLevel));
this.filesBySectionPos.put(pos, metaFile);
return metaFile;
}
@@ -317,7 +317,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
return null;
}
- this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel));
+ this.topDetailLevel.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.sectionDetailLevel));
// This is a CAS with expected null value.
RenderMetaDataFile metaFileCas = this.filesBySectionPos.putIfAbsent(pos, metaFile);
return metaFileCas == null ? metaFile : metaFileCas;
@@ -336,7 +336,10 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
RenderMetaDataFile metaFile = this.getLoadOrMakeFile(pos, true);
// On error, (when it returns null,) return an empty render source
- if (metaFile == null) return CompletableFuture.completedFuture(ColumnRenderSource.createEmptyRenderSource(pos));
+ if (metaFile == null)
+ {
+ return CompletableFuture.completedFuture(ColumnRenderSource.createEmptyRenderSource(pos));
+ }
CompletableFuture future = metaFile.loadOrGetCachedDataSourceAsync(this.fileHandlerThreadPool, this.level).handle(
(renderSource, exception) ->
@@ -348,8 +351,10 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
return (renderSource != null) ? renderSource : ColumnRenderSource.createEmptyRenderSource(pos);
});
- synchronized (taskTracker) {
- taskTracker.put(future, TaskType.Read);
+
+ synchronized (this.taskTracker)
+ {
+ this.taskTracker.put(future, TaskType.Read);
}
return future;
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java
index 1a9ce9dcc..28e395df6 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java
@@ -11,12 +11,18 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
/**
- * The position object used to define LOD objects in the quad trees.
+ * The position object used to define LOD objects in the quad trees.
*
* A section contains 64 x 64 LOD columns at a given quality.
* The Section detail level is different from the LOD detail level.
* For the specifics of how they compare can be viewed in the constants {@link #SECTION_BLOCK_DETAIL_LEVEL},
- * {@link #SECTION_CHUNK_DETAIL_LEVEL}, and {@link #SECTION_REGION_DETAIL_LEVEL}).
+ * {@link #SECTION_CHUNK_DETAIL_LEVEL}, and {@link #SECTION_REGION_DETAIL_LEVEL}).
+ *
+ * Why does the smallest render section represent 2x2 MC chunks (section detail level 6)?
+ * A section defines what unit the quad tree works in, because of that we don't want that unit to be too big or too small.
+ * Too small, and we'll have 1,000s of sections running around, all needing individual files and render buffers.
+ * Too big, and the LOD dropoff will be very noticeable.
+ * With those thoughts in mind we decided on a smallest section size of 32 data points square (IE 2x2 chunks).
*
* @author Leetom
* @version 2022-11-6
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java
index 275dfd660..d2636d04b 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java
@@ -237,7 +237,7 @@ public class LodQuadTree extends QuadTree implements AutoClose
{
// FIXME having world generation enabled in a pre-generated world that doesn't have any DH data can cause this to happen
// surprisingly reloadPos() doesn't appear to be the culprit, maybe there is an issue with reloading/changing the full data source?
- LOGGER.warn("Potential QuadTree concurrency issue. All child sections should be enabled and ready to render for pos: "+sectionPos);
+ //LOGGER.warn("Potential QuadTree concurrency issue. All child sections should be enabled and ready to render for pos: "+sectionPos);
}
// this section is now being rendered via its children
@@ -254,17 +254,21 @@ public class LodQuadTree extends QuadTree implements AutoClose
// wait for the parent to disable before enabling this section, so we don't overdraw/overlap render sections
if (!parentRenderSectionIsEnabled && renderSection.canRenderNow())
{
- renderSection.enableRendering();
-
- // delete/disable children, all of them will be a lower detail level than requested
- quadNode.deleteAllChildren((childRenderSection) ->
+ // if rendering is already enabled we don't have to re-enable it
+ if (!renderSection.isRenderingEnabled())
{
- if (childRenderSection != null)
+ renderSection.enableRendering();
+
+ // delete/disable children, all of them will be a lower detail level than requested
+ quadNode.deleteAllChildren((childRenderSection) ->
{
- childRenderSection.disableRendering();
- childRenderSection.disposeRenderData();
- }
- });
+ if (childRenderSection != null)
+ {
+ childRenderSection.disableRendering();
+ childRenderSection.disposeRenderData();
+ }
+ });
+ }
}
return renderSection.canRenderNow();
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
index 4803e0f58..a5c05de82 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
@@ -60,18 +60,25 @@ public class LodRenderSection implements IDebugRenderable
/** a reference is used so the render buffer can be swapped to and from the buffer builder */
public final AtomicReference activeRenderBufferRef = new AtomicReference<>();
- private volatile boolean doDisposeActiveBuffer = false;
+ private volatile boolean disposeActiveBuffer = false;
private final QuadTree parentQuadTree;
- public LodRenderSection(QuadTree parentQuadTree, DhSectionPos pos) {
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public LodRenderSection(QuadTree parentQuadTree, DhSectionPos pos)
+ {
this.pos = pos;
this.parentQuadTree = parentQuadTree;
DebugRenderer.register(this);
}
- public void debugRender(DebugRenderer r)
+ public void debugRender(DebugRenderer debugRenderer)
{
Color color = Color.red;
@@ -86,7 +93,7 @@ public class LodRenderSection implements IDebugRenderable
if (canRenderNow() && isRenderingEnabled) color = Color.green;
}
- r.renderBox(new DebugRenderer.Box(this.pos, 400, 8f, Objects.hashCode(this), 0.1f, color));
+ debugRenderer.renderBox(new DebugRenderer.Box(this.pos, 400, 8f, Objects.hashCode(this), 0.1f, color));
}
@@ -95,32 +102,30 @@ public class LodRenderSection implements IDebugRenderable
// rendering //
//===========//
- public void enableRendering() {
+ public void enableRendering()
+ {
+ // FIXME this is a temporary fix for sections not building the first time,
+ // this may cause LODs to flash when first loading
+ // Problem reproduction steps:
+ // 1. connect to a multiplayer server
+ // 2. enter spectator
+ // 3. fly in one direction until section detail levels 7 and 8 appear
+ // 4. empty LODs should appear
+ if (!this.isRenderingEnabled)
+ {
+ // this only needs to be called when first enabling the section
+ this.markBufferDirty();
+ }
+
this.isRenderingEnabled = true;
}
- public void disableRendering() {
- this.isRenderingEnabled = false;
- }
+ public void disableRendering() { this.isRenderingEnabled = false; }
+
+
//=============//
// render data //
//=============//
-
- private void startLoadRenderSource() {
- this.renderSourceLoadFuture = this.renderSourceProvider.readAsync(this.pos);
- this.renderSourceLoadFuture.whenComplete((renderSource, ex) ->
- {
- this.renderSourceLoadFuture = null;
- this.renderSource = renderSource;
- this.lastNs = -1;
- markBufferDirty();
- if (this.reloadRenderSourceOnceLoaded)
- {
- this.reloadRenderSourceOnceLoaded = false;
- reload(this.renderSourceProvider);
- }
- });
- }
/** does nothing if a render source is already loaded or in the process of loading */
public void loadRenderSource(ILodRenderSourceProvider renderDataProvider, IDhClientLevel level)
@@ -129,6 +134,7 @@ public class LodRenderSection implements IDebugRenderable
this.level = level;
if (this.renderSourceProvider == null)
{
+ LOGGER.warn("LodRenderSection ["+this.pos+"] called loadRenderSource with a empty source provider");
return;
}
// don't re-load or double load the render source
@@ -136,23 +142,31 @@ public class LodRenderSection implements IDebugRenderable
{
return;
}
- startLoadRenderSource();
+
+ this.startLoadRenderSourceAsync();
}
public void reload(ILodRenderSourceProvider renderDataProvider)
{
- if (pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)
+ // debug rendering
+ if (this.pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)
+ {
DebugRenderer.makeParticle(
new DebugRenderer.BoxParticle(
new DebugRenderer.Box(pos, 0, 256f, 0.03f, Color.cyan),
0.5, 512f
)
);
+ }
+
+
this.renderSourceProvider = renderDataProvider;
if (this.renderSourceProvider == null)
{
+ LOGGER.warn("LodRenderSection ["+this.pos+"] called reload with a empty source provider");
return;
}
+
// don't accidentally enable rendering for a disabled section
if (!this.isRenderingEnabled)
{
@@ -161,16 +175,33 @@ public class LodRenderSection implements IDebugRenderable
// wait for the current load future to finish before re-loading
if (this.renderSourceLoadFuture != null)
{
- reloadRenderSourceOnceLoaded = true;
+ this.reloadRenderSourceOnceLoaded = true;
return;
}
- startLoadRenderSource();
+
+ this.startLoadRenderSourceAsync();
}
+ private void startLoadRenderSourceAsync()
+ {
+ this.renderSourceLoadFuture = this.renderSourceProvider.readAsync(this.pos);
+ this.renderSourceLoadFuture.whenComplete((renderSource, ex) ->
+ {
+ this.renderSource = renderSource;
+ this.lastNs = -1;
+ this.markBufferDirty();
+ if (this.reloadRenderSourceOnceLoaded)
+ {
+ this.reloadRenderSourceOnceLoaded = false;
+ this.reload(this.renderSourceProvider);
+ }
+
+ this.renderSourceLoadFuture = null;
+ });
+ }
-
//========================//
// getters and properties //
//========================//
@@ -182,6 +213,12 @@ public class LodRenderSection implements IDebugRenderable
public boolean canRenderNow()
{
+ if (this.renderSourceLoadFuture != null || this.buildRenderBufferFuture != null)
+ {
+ // wait for loading to finish
+ return false;
+ }
+
return this.renderSource != null
&&
(
@@ -210,46 +247,44 @@ public class LodRenderSection implements IDebugRenderable
this.buildRenderBufferFuture = null;
}
}
-
- private boolean isBufferOutdated() {
- //if (this.lastNs == -1) return false;
-/* boolean inTimeout = System.nanoTime() - this.lastNs < SWAP_TIMEOUT_IN_NS;
- if (!inTimeout && ColumnRenderBufferBuilder.isBusy()) {
- this.lastNs += (long) (SWAP_BUSY_COLLISION_TIMEOUT_IN_NS * Math.random());
- return true;
- }*/
- return neighborUpdated || renderSource.localVersion.get() - lastSwapLocalVersion > 0;
- }
-
+
private LodRenderSection[] getNeighbors()
{
- LodRenderSection[] adjacents = new LodRenderSection[EDhDirection.ADJ_DIRECTIONS.length];
- for (EDhDirection direction : EDhDirection.ADJ_DIRECTIONS) {
- try {
- DhSectionPos adjPos = pos.getAdjacentPos(direction);
- LodRenderSection adjRenderSection = parentQuadTree.getValue(adjPos);
+ LodRenderSection[] adjacentRenderSections = new LodRenderSection[EDhDirection.ADJ_DIRECTIONS.length];
+ for (EDhDirection direction : EDhDirection.ADJ_DIRECTIONS)
+ {
+ try
+ {
+ DhSectionPos adjPos = this.pos.getAdjacentPos(direction);
+ LodRenderSection adjRenderSection = this.parentQuadTree.getValue(adjPos);
// adjacent render sources might be null
- adjacents[direction.ordinal() - 2] = adjRenderSection;
- } catch (IndexOutOfBoundsException e) {
+ adjacentRenderSections[direction.ordinal() - 2] = adjRenderSection;
+ }
+ catch (IndexOutOfBoundsException e)
+ {
// adjacent positions can be out of bounds, in that case a null render source will be used
}
}
- return adjacents;
+
+ return adjacentRenderSections;
}
private void tellNeighborsUpdated()
{
- LodRenderSection[] adjacents = getNeighbors();
- for (LodRenderSection adj : adjacents) {
- if (adj != null) {
+ LodRenderSection[] adjacentRenderSections = this.getNeighbors();
+ for (LodRenderSection adj : adjacentRenderSections)
+ {
+ if (adj != null)
+ {
adj.neighborUpdated = true;
}
}
}
/** @return true if this section is loaded and set to render */
- public boolean canBuildBuffer() { return this.renderSource != null && this.buildRenderBufferFuture == null && !this.renderSource.isEmpty() && isBufferOutdated(); }
-
+ public boolean canBuildBuffer() { return this.renderSource != null && this.buildRenderBufferFuture == null && !this.renderSource.isEmpty() && this.isBufferOutdated(); }
+ private boolean isBufferOutdated() { return this.neighborUpdated || (this.renderSource.localVersion.get() - this.lastSwapLocalVersion) > 0; }
+
/** @return true if this section is loaded and set to render */
public boolean canSwapBuffer() { return this.buildRenderBufferFuture != null && this.buildRenderBufferFuture.isDone(); }
@@ -262,49 +297,71 @@ public class LodRenderSection implements IDebugRenderable
*/
public boolean tryBuildAndSwapBuffer()
{
- if (doDisposeActiveBuffer && this.activeRenderBufferRef.get() != null) {
- doDisposeActiveBuffer = false;
+ // delete the existing buffer if it should be disposed
+ if (this.disposeActiveBuffer && this.activeRenderBufferRef.get() != null)
+ {
+ this.disposeActiveBuffer = false;
this.activeRenderBufferRef.getAndSet(null).close();
return false;
}
+
+
+ // attempt to build the buffer
boolean didSwapped = false;
- if (canBuildBuffer()) {
- //if (false)
- if (pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)
+ if (this.canBuildBuffer())
+ {
+ // debug
+ if (this.pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)
+ {
DebugRenderer.makeParticle(
- new DebugRenderer.BoxParticle(
- new DebugRenderer.Box(pos, 32f, 64f, 0.2f, Color.yellow),
- 0.5, 16f
- )
+ new DebugRenderer.BoxParticle(
+ new DebugRenderer.Box(this.pos, 32f, 64f, 0.2f, Color.yellow),
+ 0.5, 16f
+ )
);
- neighborUpdated = false;
- long newVs = renderSource.localVersion.get();
- if (lastSwapLocalVersion != newVs) {
- lastSwapLocalVersion = newVs;
- tellNeighborsUpdated();
}
- LodRenderSection[] adjacents = getNeighbors();
+
+
+ this.neighborUpdated = false;
+ long newVersion = this.renderSource.localVersion.get();
+ if (this.lastSwapLocalVersion != newVersion)
+ {
+ this.lastSwapLocalVersion = newVersion;
+ this.tellNeighborsUpdated();
+ }
+
+
+ LodRenderSection[] adjacentRenderSections = this.getNeighbors();
ColumnRenderSource[] adjacentSources = new ColumnRenderSource[EDhDirection.ADJ_DIRECTIONS.length];
- for (int i = 0; i < EDhDirection.ADJ_DIRECTIONS.length; i++) {
- LodRenderSection adj = adjacents[i];
- if (adj != null) {
+ for (int i = 0; i < EDhDirection.ADJ_DIRECTIONS.length; i++)
+ {
+ LodRenderSection adj = adjacentRenderSections[i];
+ if (adj != null)
+ {
adjacentSources[i] = adj.getRenderSource();
}
}
- this.buildRenderBufferFuture = ColumnRenderBufferBuilder.buildBuffers(level, this.inactiveRenderBufferRef, renderSource, adjacentSources);
+
+ this.buildRenderBufferFuture = ColumnRenderBufferBuilder.buildBuffersAsync(this.level, this.inactiveRenderBufferRef, this.renderSource, adjacentSources);
}
- if (canSwapBuffer()) {
+
+
+ // attempt to swap in the buffer
+ if (this.canSwapBuffer())
+ {
this.lastNs = System.nanoTime();
ColumnRenderBuffer newBuffer;
- try {
+ try
+ {
newBuffer = this.buildRenderBufferFuture.getNow(null);
- this.buildRenderBufferFuture = null;
- if (newBuffer == null) {
+ if (newBuffer == null)
+ {
// failed.
- markBufferDirty();
+ this.markBufferDirty();
return false;
}
- LodUtil.assertTrue(newBuffer.buffersUploaded, "The buffer future for "+pos+" returned an un-built buffer.");
+
+ LodUtil.assertTrue(newBuffer.buffersUploaded, "The buffer future for "+this.pos+" returned an un-built buffer.");
ColumnRenderBuffer oldBuffer = this.activeRenderBufferRef.getAndSet(newBuffer);
if (oldBuffer != null)
{
@@ -315,23 +372,33 @@ public class LodRenderSection implements IDebugRenderable
didSwapped = true;
LodUtil.assertTrue(swapped == null);
}
- catch (CancellationException e1) {
+ catch (CancellationException e1)
+ {
// ignore.
this.buildRenderBufferFuture = null;
}
- catch (CompletionException e) {
- LOGGER.error("Unable to get render buffer for "+pos+".", e);
+ catch (CompletionException e)
+ {
+ LOGGER.error("Unable to get render buffer for " + pos + ".", e);
+ this.buildRenderBufferFuture = null;
+ }
+ finally
+ {
this.buildRenderBufferFuture = null;
}
}
+
return didSwapped;
}
-
+
+
+
//==============//
// base methods //
//==============//
- public String toString() {
+ public String toString()
+ {
return "LodRenderSection{" +
"pos=" + this.pos +
", lodRenderSource=" + this.renderSource +
@@ -340,17 +407,19 @@ public class LodRenderSection implements IDebugRenderable
'}';
}
- public void dispose() {
- disposeRenderData();
+ public void dispose()
+ {
+ this.disposeRenderData();
DebugRenderer.unregister(this);
- if (doDisposeActiveBuffer && this.activeRenderBufferRef.get() != null) {
+ if (this.disposeActiveBuffer && this.activeRenderBufferRef.get() != null)
+ {
this.activeRenderBufferRef.get().close();
}
}
public synchronized void disposeRenderData() // synchronized is a band-aid solution to prevent a rare bug where the future isn't canceled in the right order
{
- disposeRenderBuffer();
+ this.disposeRenderBuffer();
this.renderSource = null;
if (this.renderSourceLoadFuture != null)
{
@@ -361,11 +430,10 @@ public class LodRenderSection implements IDebugRenderable
public void disposeRenderBuffer()
{
- cancelBuildBuffer();
- doDisposeActiveBuffer = true;
+ this.cancelBuildBuffer();
+ this.disposeActiveBuffer = true;
}
- public void markBufferDirty() {
- lastSwapLocalVersion = -1;
- }
+ public void markBufferDirty() { this.lastSwapLocalVersion = -1; }
+
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java
index eed83d04f..6a4f063dd 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java
@@ -156,17 +156,22 @@ public class RenderBufferHandler
DhSectionPos sectionPos = node.sectionPos;
LodRenderSection renderSection = node.value;
- try {
+ try
+ {
- if (renderSection != null) {
- if (rebuildAllBuffers) {
+ if (renderSection != null)
+ {
+ if (rebuildAllBuffers)
+ {
renderSection.markBufferDirty();
}
renderSection.tryBuildAndSwapBuffer();
- if (renderSection.isRenderingEnabled()) {
+ if (renderSection.isRenderingEnabled())
+ {
AbstractRenderBuffer buffer = renderSection.activeRenderBufferRef.get();
- if (buffer != null) {
+ if (buffer != null)
+ {
this.loadedNearToFarBuffers.add(new LoadedRenderBuffer(buffer, sectionPos));
}
}
diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json
index 762c0e159..36bd5c660 100644
--- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json
+++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json
@@ -261,7 +261,11 @@
"distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias":
"LOD Bias §6(Affects vanilla terrain)§r",
"distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias.@tooltip":
- "Sets vanilla's lod bias value\nPlease press F3+T to reload the texture packs and apply this affect",
+ "Sets vanilla's lod bias value\nPlease press F3+T to reload the texture packs and apply this affect.",
+ "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodShading":
+ "LOD Shading",
+ "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodShading.@tooltip":
+ "Defines how LODs should be shaded. \nCan be used to improve shader compatibility.",
"distanthorizons.config.client.advanced.worldGenerator":
@@ -726,5 +730,13 @@
"distanthorizons.config.enum.EBufferRebuildTimes.NORMAL":
"Normal",
"distanthorizons.config.enum.EBufferRebuildTimes.RARE":
- "Rare"
+ "Rare",
+
+ "distanthorizons.config.enum.ELodShading.MINECRAFT":
+ "Minecraft",
+ "distanthorizons.config.enum.ELodShading.OLD_LIGHTING":
+ "Old Lighting",
+ "distanthorizons.config.enum.ELodShading.NONE":
+ "None"
+
}