From d99d34683c22daeb62b2b30a574c1cafc1695a74 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 13 Jun 2023 20:04:44 -0500 Subject: [PATCH] Merge DetailDistanceUtil into LodQuadTree --- .../RenderCacheConfigEventHandler.java | 3 - .../ResetConfigEventHandler.java | 5 - .../lod/core/level/AbstractDhClientLevel.java | 3 +- .../seibel/lod/core/render/LodQuadTree.java | 98 +++++++++++-------- .../lod/core/util/DetailDistanceUtil.java | 98 ------------------- .../core/util/objects/quadTree/QuadTree.java | 2 - .../seibel/lod/core/world/DhClientWorld.java | 4 +- 7 files changed, 61 insertions(+), 152 deletions(-) delete mode 100644 core/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java diff --git a/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java b/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java index 9374426c2..de1ffab9c 100644 --- a/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java +++ b/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java @@ -5,9 +5,7 @@ import com.seibel.lod.api.enums.config.EHorizontalResolution; import com.seibel.lod.api.enums.config.EVerticalQuality; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.config.listeners.IConfigListener; -import com.seibel.lod.core.util.DetailDistanceUtil; -import java.sql.Date; import java.util.Timer; import java.util.TimerTask; @@ -83,7 +81,6 @@ public class RenderCacheConfigEventHandler implements IConfigListener { public void run() { - DetailDistanceUtil.updateSettings(); DhApiMain.Delayed.renderProxy.clearRenderDataCache(); } }; diff --git a/core/src/main/java/com/seibel/lod/core/config/eventHandlers/ResetConfigEventHandler.java b/core/src/main/java/com/seibel/lod/core/config/eventHandlers/ResetConfigEventHandler.java index fed72f0c7..720d56b15 100644 --- a/core/src/main/java/com/seibel/lod/core/config/eventHandlers/ResetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/lod/core/config/eventHandlers/ResetConfigEventHandler.java @@ -1,12 +1,7 @@ package com.seibel.lod.core.config.eventHandlers; -import com.seibel.lod.api.DhApiMain; -import com.seibel.lod.api.enums.config.EHorizontalResolution; -import com.seibel.lod.api.enums.config.EVerticalQuality; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.config.listeners.ConfigChangeListener; -import com.seibel.lod.core.config.listeners.IConfigListener; -import com.seibel.lod.core.util.DetailDistanceUtil; public class ResetConfigEventHandler { diff --git a/core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java b/core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java index 36304c110..fb344f714 100644 --- a/core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java +++ b/core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java @@ -14,7 +14,6 @@ import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.pos.DhBlockPos2D; import com.seibel.lod.core.pos.DhLodPos; import com.seibel.lod.core.pos.DhSectionPos; -import com.seibel.lod.core.render.renderer.DebugRenderer; import com.seibel.lod.core.util.FileScanUtil; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.coreapi.util.math.Mat4f; @@ -86,7 +85,7 @@ public abstract class AbstractDhClientLevel implements IDhClientLevel // TODO this should probably be handled via a config change listener // recreate the RenderState if the render distance changes - if (clientRenderState.quadtree.blockRenderDistance != Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH) + if (clientRenderState.quadtree.blockRenderDistanceRadius != Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH) { if (!this.ClientRenderStateRef.compareAndSet(clientRenderState, null)) { diff --git a/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java index 2160fbcd0..061aa6b33 100644 --- a/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java @@ -9,14 +9,14 @@ import com.seibel.lod.core.pos.DhBlockPos2D; import com.seibel.lod.core.pos.DhSectionPos; import com.seibel.lod.core.file.renderfile.ILodRenderSourceProvider; import com.seibel.lod.core.logging.DhLoggerBuilder; -import com.seibel.lod.core.util.DetailDistanceUtil; +import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.objects.quadTree.QuadNode; import com.seibel.lod.core.util.objects.quadTree.QuadTree; +import com.seibel.lod.coreapi.util.MathUtil; import org.apache.logging.log4j.Logger; import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** @@ -29,7 +29,7 @@ public class LodQuadTree extends QuadTree implements AutoClose private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public final int blockRenderDistance; + public final int blockRenderDistanceRadius; private final ILodRenderSourceProvider renderSourceProvider; /** @@ -37,31 +37,39 @@ public class LodQuadTree extends QuadTree implements AutoClose * This is a {@link ConcurrentLinkedQueue} because new sections can be added to this list via the world generator threads. */ private final ConcurrentLinkedQueue sectionsToReload = new ConcurrentLinkedQueue<>(); - private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference! - private final ConfigChangeListener horizontalScaleChangeListener; + private final ReentrantLock treeReadWriteLock = new ReentrantLock(); - private ReentrantLock treeReadWriteLock = new ReentrantLock(); + /** the lowest detail level number that can be rendered */ + private byte maxRenderDetailLevel; + /** used to calculate when a detail drop will occur */ + private double detailDropOffDistanceUnit; + /** used to calculate when a detail drop will occur */ + private double detailDropOffLogBase; - public LodQuadTree( + //==============// + // constructors // + //==============// + + public LodQuadTree( IDhClientLevel level, int viewDistanceInBlocks, int initialPlayerBlockX, int initialPlayerBlockZ, ILodRenderSourceProvider provider) { super(viewDistanceInBlocks, new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), TREE_LOWEST_DETAIL_LEVEL); - DetailDistanceUtil.updateSettings(); //TODO: Move this to somewhere else this.level = level; this.renderSourceProvider = provider; - this.blockRenderDistance = viewDistanceInBlocks; + this.blockRenderDistanceRadius = viewDistanceInBlocks; this.horizontalScaleChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality, (newHorizontalScale) -> this.onHorizontalQualityChange()); } + //=============// // tick update // //=============// @@ -80,6 +88,10 @@ public class LodQuadTree extends QuadTree implements AutoClose } + + // this shouldn't be updated while the tree is being iterated through + this.updateDetailLevelVariables(); + // don't traverse the tree if it is being modified if (this.treeReadWriteLock.tryLock()) { @@ -286,33 +298,48 @@ public class LodQuadTree extends QuadTree implements AutoClose * @param sectionPos section position * @return detail level of this section pos */ - public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos) + public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos) { return getDetailLevelFromDistance(playerPos.dist(sectionPos.getCenter().getCenterBlockPos())); } + private byte getDetailLevelFromDistance(double distance) { - return DetailDistanceUtil.getDetailLevelFromDistance(playerPos.dist(sectionPos.getCenter().getCenterBlockPos())); + // special case, never drop the quality + if (Config.Client.Advanced.Graphics.Quality.horizontalQuality.get() == EHorizontalQuality.UNLIMITED) + { + return this.maxRenderDetailLevel; + } + + + double maxDetailDistance = this.getDrawDistanceFromDetail(Byte.MAX_VALUE -1); + if (distance > maxDetailDistance) + { + return Byte.MAX_VALUE - 1; + } + + + int detailLevel = (int) (Math.log(distance / this.detailDropOffDistanceUnit) / this.detailDropOffLogBase); + return (byte) MathUtil.clamp(this.maxRenderDetailLevel, detailLevel, Byte.MAX_VALUE - 1); } - /** - * The method will return the highest detail level in a circle around the center - * Override this method if you want to use a different algorithm - * Note: the returned distance should always be the ceiling estimation of the distance - * //TODO: Make this input a bbox or a circle or something.... - * @param distance the circle radius - * @return the highest detail level in the circle - */ - public byte getMaxDetailInRange(double distance) { return DetailDistanceUtil.getDetailLevelFromDistance(distance); } - - /** - * The method will return the furthest distance to the center for the given detail level - * Override this method if you want to use a different algorithm - * Note: the returned distance should always be the ceiling estimation of the distance - * //TODO: Make this return a bbox instead of a distance in circle - * @param detailLevel detail level - * @return the furthest distance to the center, in blocks - */ - public int getFurthestDistance(byte detailLevel) + private double getDrawDistanceFromDetail(int detail) { - return (int)Math.ceil(DetailDistanceUtil.getDrawDistanceFromDetail(detailLevel + 1)); - // +1 because that's the border to the next detail level, and we want to include up to it. + if (detail <= this.maxRenderDetailLevel) + { + return 0; + } + else if (detail >= Byte.MAX_VALUE) + { + return this.blockRenderDistanceRadius * 2; + } + + + double base = Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().quadraticBase; + return Math.pow(base, detail) * this.detailDropOffDistanceUnit; + } + + private void updateDetailLevelVariables() + { + this.maxRenderDetailLevel = Config.Client.Advanced.Graphics.Quality.drawResolution.get().detailLevel; + this.detailDropOffDistanceUnit = Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().distanceUnitInBlocks * LodUtil.CHUNK_WIDTH; + this.detailDropOffLogBase = Math.log(Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().quadraticBase); } @@ -333,13 +360,6 @@ public class LodQuadTree extends QuadTree implements AutoClose { LOGGER.info("Clearing render cache..."); - - // changing this while the tree is being traversed can cause (harmless) errors, - // where the traversal goes deeper into the tree than it should. - // so it should also be inside the tree lock - DetailDistanceUtil.updateSettings(); - - // clear the tree Iterator> nodeIterator = this.nodeIterator(); while (nodeIterator.hasNext()) diff --git a/core/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java b/core/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java deleted file mode 100644 index 6be39f898..000000000 --- a/core/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.lod.core.util; - -import com.seibel.lod.api.enums.config.EHorizontalQuality; -import com.seibel.lod.core.config.Config; -import com.seibel.lod.core.render.LodQuadTree; -import com.seibel.lod.coreapi.util.MathUtil; - -/** - * - * @author Leonardo Amato - * @version ?? - */ -// TODO move this into LodQuadTree? If so then the update method should only be called when the tree isn't being updated -@Deprecated -public class DetailDistanceUtil -{ - /** smallest numerical detail level */ - private static byte maxDetailLevel = Config.Client.Advanced.Graphics.Quality.drawResolution.get().detailLevel; - - /** largest numerical detail level */ - private static final byte minDetailLevel = Byte.MAX_VALUE; - private static final double minDistance = 0; - - // TODO merge with updateSettings() below - private static double distanceUnit = Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().distanceUnitInBlocks * LodUtil.CHUNK_WIDTH; - private static double maxDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH * 2; - private static double logBase = Math.log(Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().quadraticBase); - - - - /** can cause issues if called while the {@link LodQuadTree} is being iterated through */ - public static void updateSettings() - { - maxDetailLevel = Config.Client.Advanced.Graphics.Quality.drawResolution.get().detailLevel; - - distanceUnit = Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().distanceUnitInBlocks * LodUtil.CHUNK_WIDTH; - maxDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH * 2; - logBase = Math.log(Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().quadraticBase); - } - - public static double getDrawDistanceFromDetail(int detail) { return baseDistanceFunction(detail); } - private static double baseDistanceFunction(int detail) - { - if (detail <= maxDetailLevel) - { - return minDistance; - } - else if (detail >= minDetailLevel) - { - return maxDistance; - } - - - double base = Config.Client.Advanced.Graphics.Quality.horizontalQuality.get().quadraticBase; - return Math.pow(base, detail) * distanceUnit; - } - - public static byte getDetailLevelFromDistance(double distance) { return baseInverseFunction(distance); } - private static byte baseInverseFunction(double distance) - { - // special case, never drop the quality - if (Config.Client.Advanced.Graphics.Quality.horizontalQuality.get() == EHorizontalQuality.UNLIMITED) - { - return maxDetailLevel; - } - - - double maxDetailDistance = getDrawDistanceFromDetail(minDetailLevel -1); - if (distance > maxDetailDistance) - { - return minDetailLevel - 1; - } - - - int detailLevel = (int) (Math.log(distance / distanceUnit) / logBase); - return (byte) MathUtil.clamp(maxDetailLevel, detailLevel, minDetailLevel - 1); - } - -} diff --git a/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java b/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java index b33bd8425..ee9e947c5 100644 --- a/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java +++ b/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java @@ -6,7 +6,6 @@ import com.seibel.lod.core.pos.DhLodPos; import com.seibel.lod.core.pos.DhSectionPos; import com.seibel.lod.core.pos.Pos2D; import com.seibel.lod.coreapi.util.BitShiftUtil; -import com.seibel.lod.core.util.DetailDistanceUtil; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.coreapi.util.MathUtil; import com.seibel.lod.core.util.gridList.MovableGridRingList; @@ -47,7 +46,6 @@ public class QuadTree */ public QuadTree(int widthInBlocks, DhBlockPos2D centerBlockPos, byte treeMinDetailLevel) { - DetailDistanceUtil.updateSettings(); //TODO: Move this to somewhere else this.centerBlockPos = centerBlockPos; this.widthInBlocks = widthInBlocks; diff --git a/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java index b4a16547f..556467ab4 100644 --- a/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java @@ -5,7 +5,6 @@ import com.seibel.lod.core.level.IDhLevel; import com.seibel.lod.core.file.structure.ClientOnlySaveStructure; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.level.states.ClientRenderState; -import com.seibel.lod.core.util.DetailDistanceUtil; import com.seibel.lod.core.util.ThreadUtil; import com.seibel.lod.core.util.objects.EventLoop; import com.seibel.lod.core.util.LodUtil; @@ -102,7 +101,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld if (clientRenderState != null && clientRenderState.quadtree != null) { - if (clientRenderState.quadtree.blockRenderDistance != newBlockRenderDistance) + if (clientRenderState.quadtree.blockRenderDistanceRadius != newBlockRenderDistance) { // TODO is this the best way to handle changing the render distance? level.close(); @@ -111,7 +110,6 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld } } - DetailDistanceUtil.updateSettings(); this.levels.values().forEach(DhClientLevel::clientTick); }