Merge DetailDistanceUtil into LodQuadTree

This commit is contained in:
James Seibel
2023-06-13 20:04:44 -05:00
parent 500d3cde57
commit d99d34683c
7 changed files with 61 additions and 152 deletions
@@ -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();
}
};
@@ -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
{
@@ -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))
{
@@ -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<LodRenderSection> 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<LodRenderSection> implements AutoClose
* This is a {@link ConcurrentLinkedQueue} because new sections can be added to this list via the world generator threads.
*/
private final ConcurrentLinkedQueue<DhSectionPos> sectionsToReload = new ConcurrentLinkedQueue<>();
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
private final ConfigChangeListener<EHorizontalQuality> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
while (nodeIterator.hasNext())
@@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}
@@ -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<T>
*/
public QuadTree(int widthInBlocks, DhBlockPos2D centerBlockPos, byte treeMinDetailLevel)
{
DetailDistanceUtil.updateSettings(); //TODO: Move this to somewhere else
this.centerBlockPos = centerBlockPos;
this.widthInBlocks = widthInBlocks;
@@ -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);
}