1. Make VerticalQuality overall is higher.
2. Rework how FAR_FIRST works.
3. Clean up some stuff.
This commit is contained in:
tom lee
2022-01-24 21:02:55 +08:00
parent ab3880a5e5
commit c3abb9c46b
9 changed files with 252 additions and 208 deletions
@@ -19,7 +19,9 @@
package com.seibel.lod.core.api;
import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -60,6 +62,22 @@ public class ClientApi
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
private static final EventApi EVENT_API = EventApi.INSTANCE;
public static final boolean ENABLE_LAG_SPIKE_LOGGING = true;
public static final long LAG_SPIKE_THRESOLD_NS = TimeUnit.NANOSECONDS.convert(16, TimeUnit.MILLISECONDS);
public static class LagSpikeCatcher {
long timer = System.nanoTime();
public LagSpikeCatcher() {}
public void end(String source) {
if (!ENABLE_LAG_SPIKE_LOGGING) return;
timer = System.nanoTime() - timer;
if (timer > LAG_SPIKE_THRESOLD_NS) {
ClientApi.LOGGER.info("LagSpikeCatcher: "+source+" took "+Duration.ofNanos(timer)+"!");
}
}
}
/**
* there is some setup that should only happen once,
@@ -81,8 +99,10 @@ public class ClientApi
public void clientChunkLoadEvent(IChunkWrapper chunk, IWorldWrapper world)
{
LagSpikeCatcher clientChunkLoad = new LagSpikeCatcher();
//ClientApi.LOGGER.info("Lod Generating add: "+chunk.getLongChunkPos());
toBeLoaded.add(chunk.getLongChunkPos());
clientChunkLoad.end("clientChunkLoad");
}
//private HashSet<AbstractChunkPosWrapper> lastFrame = new HashSet<AbstractChunkPosWrapper>();
@@ -115,7 +135,8 @@ public class ClientApi
ApiShared.lodBuilder.defaultDimensionWidthInRegions);
ApiShared.lodWorld.addLodDimension(lodDim);
}
LagSpikeCatcher updateToBeLoadedChunk = new LagSpikeCatcher();
for (long pos : toBeLoaded) {
if (generating.size() >= 8) {
//ClientApi.LOGGER.info("Lod Generating Full! Remining: "+toBeLoaded.size());
@@ -139,18 +160,24 @@ public class ClientApi
toBeLoaded.add(pos);
});
}
updateToBeLoadedChunk.end("updateToBeLoadedChunk");
LagSpikeCatcher updateSettings = new LagSpikeCatcher();
DetailDistanceUtil.updateSettings();
EVENT_API.viewDistanceChangedEvent();
updateSettings.end("updateSettings");
LagSpikeCatcher updatePlayerMove = new LagSpikeCatcher();
EVENT_API.playerMoveEvent(lodDim);
updatePlayerMove.end("updatePlayerMove");
LagSpikeCatcher cutAndExpendAsync = new LagSpikeCatcher();
lodDim.cutRegionNodesAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
lodDim.expandOrLoadRegionsAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
cutAndExpendAsync.end("cutAndExpendAsync");
@@ -23,6 +23,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.objects.lod.LodRegion;
@@ -205,7 +206,7 @@ public class LodBuilder
lodDim.regenDimensionBuffers = true;
if (!region.doesDataExist((byte)0, chunk.getMinX(), chunk.getMinZ(), config.distanceGenerationMode))
throw new RuntimeException();
throw new RuntimeException("data at detail 0 is still null after writes to it!");
} catch (Exception e) {
e.printStackTrace();
} finally {
@@ -247,8 +248,8 @@ public class LodBuilder
y = height - 1;
// We search light on above air block
int depth = determineBottomPointFrom(chunk, config, x, y, z,
count < this.config.client().graphics().quality().getVerticalQuality().maxConnectedLods
&& !hasCeiling);
//count < this.config.client().graphics().quality().getVerticalQuality().maxConnectedLods &&
(!hasCeiling || !topBlock));
if (hasCeiling && topBlock)
y = depth;
int light = getLightValue(chunk, x, y, z, hasCeiling, hasSkyLight, topBlock);
@@ -266,6 +267,27 @@ public class LodBuilder
if (result.length != maxVerticalData) throw new ArrayIndexOutOfBoundsException();
System.arraycopy(result, 0, data, dataOffset, maxVerticalData);
}
public static final LodDirection[] DIRECTIONS = new LodDirection[] {
LodDirection.UP,
LodDirection.DOWN,
LodDirection.WEST,
LodDirection.EAST,
LodDirection.NORTH,
LodDirection.SOUTH };
private boolean hasCliffFace(IChunkWrapper chunk, int x, int y, int z) {
for (LodDirection dir : DIRECTIONS) {
int cx = x+dir.getNormal().x;
int cy = y+dir.getNormal().y;
int cz = z+dir.getNormal().z;
if (!chunk.blockPosInsideChunk(cx, cy, cz)) continue;
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(cx, cy, cz);
if (block == null || block.hasNoCollision() || block.isToAvoid() || block.isNonFull() || block.hasNoCollision())
return true;
}
return false;
}
/**
* Find the lowest valid point from the bottom.
@@ -293,9 +315,9 @@ public class LodBuilder
for (int y = yAbs - 1; y >= chunk.getMinBuildHeight(); y--)
{
if (!isLayerValidLodPoint(chunk, xAbs, y, zAbs)
|| (strictEdge && colorOfBlock != chunk.getBlockColorWrapper(xAbs, y, zAbs).getColor()))
|| (strictEdge && hasCliffFace(chunk, xAbs, y, zAbs)
&& colorOfBlock != chunk.getBlockColorWrapper(xAbs, y, zAbs).getColor()))
{
depth = (short) (y + 1);
break;
@@ -29,21 +29,23 @@ package com.seibel.lod.core.enums.config;
public enum VerticalQuality
{
LOW(
new int[] { 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1 },
0
new int[] { 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
4
),
MEDIUM(
new int[] { 4, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1 },
1
new int[] { 8, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1 },
8
),
HIGH(
new int[] { 8, 8, 4, 4, 2, 2, 2, 1, 1, 1, 1 },
2
new int[] { 16, 8, 4, 2, 1, 1, 1, 1, 1, 1, 1 },
16
);
public final int[] maxVerticalData;
@Deprecated // Will find other ways to optimize
public final int maxConnectedLods;
VerticalQuality(int[] maxVerticalData, int maxConnectedLods)
@@ -31,7 +31,6 @@ public class PosToGenerateContainer
{
private final int playerPosX;
private final int playerPosZ;
public final byte farMinDetail;
private int nearSize;
private int farSize;
@@ -39,14 +38,10 @@ public class PosToGenerateContainer
private final int[][] nearPosToGenerate;
private final int[][] farPosToGenerate;
public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int playerPosX, int playerPosZ)
public PosToGenerateContainer(int maxDataToGenerate, int playerPosX, int playerPosZ)
{
this.playerPosX = playerPosX;
this.playerPosZ = playerPosZ;
this.farMinDetail = farMinDetail;
nearSize = 0;
farSize = 0;
nearPosToGenerate = new int[maxDataToGenerate][4];
@@ -56,65 +51,67 @@ public class PosToGenerateContainer
// TODO what is going on in this method?
public void addPosToGenerate(byte detailLevel, int posX, int posZ, boolean sort)
public void addNearPosToGenerate(byte detailLevel, int posX, int posZ, boolean sort)
{
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
int index;
if (detailLevel >= farMinDetail)
{
// We are introducing a position in the far array
index = farSize;
if (index == farPosToGenerate.length) {
if (LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) > 0) {
return;
}
index--;
} else farSize++;
if (sort) {
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
{
farPosToGenerate[index][0] = farPosToGenerate[index - 1][0];
farPosToGenerate[index][1] = farPosToGenerate[index - 1][1];
farPosToGenerate[index][2] = farPosToGenerate[index - 1][2];
farPosToGenerate[index][3] = farPosToGenerate[index - 1][3];
index--;
}
}
farPosToGenerate[index][0] = detailLevel + 1;
farPosToGenerate[index][1] = posX;
farPosToGenerate[index][2] = posZ;
farPosToGenerate[index][3] = distance;
}
else
{
//We are introducing a position in the near array
//We are introducing a position in the near array
index = nearSize;
if (index == nearPosToGenerate.length) {
if (LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) > 0) {
return;
}
index--;
} else nearSize++;
if (sort) {
while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0)
{
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2];
nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3];
index--;
}
index = nearSize;
if (index == nearPosToGenerate.length) {
if (LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) > 0) {
return;
}
index--;
} else nearSize++;
if (sort) {
while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0)
{
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2];
nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3];
index--;
}
nearPosToGenerate[index][0] = detailLevel + 1;
nearPosToGenerate[index][1] = posX;
nearPosToGenerate[index][2] = posZ;
nearPosToGenerate[index][3] = distance;
}
nearPosToGenerate[index][0] = detailLevel + 1;
nearPosToGenerate[index][1] = posX;
nearPosToGenerate[index][2] = posZ;
nearPosToGenerate[index][3] = distance;
}
// TODO what is going on in this method?
public void addFarPosToGenerate(byte detailLevel, int posX, int posZ, boolean sort)
{
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
int index;
// We are introducing a position in the far array
index = farSize;
if (index == farPosToGenerate.length) {
if (LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) > 0) {
return;
}
index--;
} else farSize++;
if (sort) {
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
{
farPosToGenerate[index][0] = farPosToGenerate[index - 1][0];
farPosToGenerate[index][1] = farPosToGenerate[index - 1][1];
farPosToGenerate[index][2] = farPosToGenerate[index - 1][2];
farPosToGenerate[index][3] = farPosToGenerate[index - 1][3];
index--;
}
}
farPosToGenerate[index][0] = detailLevel + 1;
farPosToGenerate[index][1] = posX;
farPosToGenerate[index][2] = posZ;
farPosToGenerate[index][3] = distance;
}
public boolean isFull() {
@@ -398,7 +398,7 @@ public class LodDimension
// and cut it if it is higher then that
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ,
playerPosX, playerPosZ);
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
detail = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
if (region.getMinDetailLevel() < detail) {
if (region.needSaving) return; // FIXME: A crude attempt at lowering chance of race condition!
region.cutTree(detail);
@@ -452,8 +452,8 @@ public class LodDimension
playerPosZ);
maxDistance = LevelPosUtil.maxDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX,
playerPosZ);
minDetail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance);
maxDetail = DetailDistanceUtil.getTreeGenDetailFromDistance(maxDistance);
minDetail = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
maxDetail = DetailDistanceUtil.getDetailLevelFromDistance(maxDistance);
boolean updated = false;
if (region == null) {
@@ -524,7 +524,7 @@ public class LodDimension
GenerationPriority priority, DistanceGenerationMode genMode)
{
PosToGenerateContainer posToGenerate;
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
posToGenerate = new PosToGenerateContainer(maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
// This ensures that we don't spawn way too much regions without finish flushing them first.
@@ -19,7 +19,6 @@
package com.seibel.lod.core.objects.lod;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.enums.config.DropoffQuality;
import com.seibel.lod.core.enums.config.GenerationPriority;
@@ -71,6 +70,15 @@ public class LodRegion {
public volatile int needRegenBuffer = 2;
public volatile boolean needSaving = false;
public volatile int isWriting = 0;
public static byte calculateFarModeSwitch(byte targetLevel) {
if (targetLevel==0) return 0; // Always use detail 0 if it's way too close
double part = targetLevel / (double)LodUtil.REGION_DETAIL_LEVEL;
byte farModeLevel = LodUtil.DETAIL_OPTIONS-(LodUtil.CHUNK_DETAIL_LEVEL+1);
farModeLevel *= part;
farModeLevel += (LodUtil.CHUNK_DETAIL_LEVEL+1);
return (byte)LodUtil.clamp(LodUtil.CHUNK_DETAIL_LEVEL+1, farModeLevel, LodUtil.DETAIL_OPTIONS - 1);
}
public LodRegion(byte minDetailLevel, RegionPos regionPos, VerticalQuality verticalQuality) {
this.minDetailLevel = minDetailLevel;
@@ -145,7 +153,8 @@ public class LodRegion {
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
return false;// this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
if (this.dataContainer[detailLevel].getVerticalSize() != verticalSize) throw new RuntimeException();
if (this.dataContainer[detailLevel].getVerticalSize() != verticalSize)
throw new RuntimeException("Provided data's verticalSize is different from current storage's verticalSize!");
boolean updated = this.dataContainer[detailLevel].addChunkOfData(data, posX, posZ, widthX, widthZ, override);
//ClientApi.LOGGER.info("addChunkOfData(region:{}, level:{}, x:{}, z:{}, wx:{}, wz:{}, override:{}, updated:{})",
@@ -161,7 +170,7 @@ public class LodRegion {
}
if (!doesDataExist(detailLevel, posX, posZ, DistanceGenerationMode.values()[DataPointUtil.getGenerationMode(data[0])])) {
throw new RuntimeException();
throw new RuntimeException("Data still doesn't exist after addChunkOfData!");
}
return updated;
@@ -219,43 +228,44 @@ public class LodRegion {
// calculate what LevelPos are in range to generate
int minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerPosX,
playerPosZ);
// determine this child's levelPos
byte childDetailLevel = (byte) (detailLevel - 1);
int childOffsetPosX = offsetPosX * 2;
int childOffsetPosZ = offsetPosZ * 2;
DistanceGenerationMode testerGenMode = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? DistanceGenerationMode.NONE : genMode;
byte targetDetailLevel = DetailDistanceUtil.getGenerationDetailFromDistance(minDistance);
byte targetDetailLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
byte farModeSwitchLevel = (priority == GenerationPriority.NEAR_FIRST) ? 0 : calculateFarModeSwitch(targetDetailLevel);
if (targetDetailLevel <= detailLevel) {
if (targetDetailLevel == detailLevel) {
if (!doesDataExist(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size, testerGenMode))
posToGenerate.addPosToGenerate(detailLevel, offsetPosX + regionPosX * size,
if (!doesDataExist(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size, testerGenMode)) {
if (detailLevel==farModeSwitchLevel && priority == GenerationPriority.FAR_FIRST)
posToGenerate.addFarPosToGenerate(detailLevel, offsetPosX + regionPosX * size,
offsetPosZ + regionPosZ * size, shouldSort);
} else {
if (priority == GenerationPriority.FAR_FIRST && detailLevel >= posToGenerate.farMinDetail
&& !doesDataExist(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size, testerGenMode)) {
posToGenerate.addPosToGenerate(detailLevel, offsetPosX + regionPosX * size,
else
posToGenerate.addNearPosToGenerate(detailLevel, offsetPosX + regionPosX * size,
offsetPosZ + regionPosZ * size, shouldSort);
} else if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL) {
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getPosToGenerate(posToGenerate, childDetailLevel, childOffsetPosX + x, childOffsetPosZ + z, playerPosX,
playerPosZ, priority, genMode, shouldSort);
} else {
// we want at max one request per chunk (since the world generator creates
// chunks).
// So for lod smaller than a chunk, only recurse down
// the top right child
getPosToGenerate(posToGenerate, childDetailLevel, childOffsetPosX, childOffsetPosZ, playerPosX, playerPosZ,
priority, genMode, shouldSort);
}
} else if (detailLevel == farModeSwitchLevel
&& !doesDataExist(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size, testerGenMode)) {
posToGenerate.addFarPosToGenerate(detailLevel, offsetPosX + regionPosX * size,
offsetPosZ + regionPosZ * size, shouldSort);
} else if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL) {
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getPosToGenerate(posToGenerate, childDetailLevel, childOffsetPosX + x, childOffsetPosZ + z, playerPosX,
playerPosZ, priority, genMode, shouldSort);
} else {
// we want at max one request per chunk (since the world generator creates
// chunks).
// So for lod smaller than a chunk, only recurse down
// the top right child
getPosToGenerate(posToGenerate, childDetailLevel, childOffsetPosX, childOffsetPosZ, playerPosX, playerPosZ,
priority, genMode, shouldSort);
}
}
// we have gone beyond the target Detail level
// we can stop generating
}
/**
@@ -268,12 +278,15 @@ public class LodRegion {
public void getPosToRender(PosToRenderContainer posToRender, int playerPosX, int playerPosZ,
boolean requireCorrectDetailLevel, DropoffQuality dropoffQuality) {
int minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerPosX, playerPosZ);
byte targetLevel = DetailDistanceUtil.getDrawDetailFromDistance(minDistance);
byte targetLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
// FarModeSwitchLevel or above is the level where a giant block of lod is not acceptable even if not all child data exist.
byte farModeSwitchLevel = requireCorrectDetailLevel ? 0 : calculateFarModeSwitch(targetLevel);
if (requireCorrectDetailLevel) farModeSwitchLevel = 0;
if (targetLevel <= dropoffQuality.fastModeSwitch) {
getPosToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ,
requireCorrectDetailLevel);
} else {
getPosToRenderFlat(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, targetLevel, requireCorrectDetailLevel);
getPosToRenderFlat(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, targetLevel, farModeSwitchLevel);
}
}
@@ -290,47 +303,40 @@ public class LodRegion {
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
byte desiredLevel;
int maxDistance;
int minDistance;
int childLevel;
// calculate the LevelPos that are in range
maxDistance = LevelPosUtil.maxDistance(detailLevel, offsetPosX + regionPosX*size, offsetPosZ + regionPosZ*size, playerPosX, playerPosZ);
desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance));
minDistance = LevelPosUtil.minDistance(detailLevel, offsetPosX + regionPosX*size, offsetPosZ + regionPosZ*size, playerPosX, playerPosZ);
childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance));
if (detailLevel == childLevel - 1) {
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
} else
// if (desiredLevel > detailLevel)
// {
// we have gone beyond the target Detail level
// we can stop generating
// } else
if (desiredLevel == detailLevel) {
int minDistance = LevelPosUtil.minDistance(detailLevel, offsetPosX + regionPosX*size, offsetPosZ + regionPosZ*size, playerPosX, playerPosZ);
byte minLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
// FarModeSwitchLevel or above is the level where a giant block of lod is not acceptable even if not all child data exist.
byte farModeSwitchLevel = requireCorrectDetailLevel ? 0 : calculateFarModeSwitch(minLevel);
if (detailLevel == minLevel) {
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
} else // case where (detailLevel > desiredLevel)
{
int childPosX = (offsetPosX + regionPosX*size) * 2;
int childPosZ = (offsetPosZ + regionPosZ*size) * 2;
byte childDetailLevel = (byte) (detailLevel - 1);
int childrenCount = 0;
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, DistanceGenerationMode.NONE)) {
if (!requireCorrectDetailLevel)
childrenCount++;
else
if (detailLevel > farModeSwitchLevel) {
// Giant block is not acceptable. So leave empty void if data doesn't exist.
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, DistanceGenerationMode.NONE)) {
getPosToRender(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, playerPosX,
playerPosZ, requireCorrectDetailLevel);
}
}
}
} else {
// Giant block is acceptable. So use this level lod if not all child data exist.
int childrenCount = 0;
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, DistanceGenerationMode.NONE)) {
childrenCount++;
}
}
}
}
if (!requireCorrectDetailLevel) {
// If all the four children exist go deeper
if (childrenCount == 4) {
for (int x = 0; x <= 1; x++)
@@ -348,36 +354,42 @@ public class LodRegion {
* This method will fill the posToRender array with all levelPos that are
* render-able. But the entire region try use the same detail level.
*/
private void getPosToRenderFlat(PosToRenderContainer posToRender, byte detailLevel, int offsetPosX, int offsetPosZ, byte targetLevel, boolean requireCorrectDetailLevel) {
private void getPosToRenderFlat(PosToRenderContainer posToRender, byte detailLevel, int offsetPosX, int offsetPosZ,
byte targetLevel, byte farModeSwitchLevel) {
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
if (detailLevel == targetLevel) {
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
} else // case where (detailLevel > desiredLevel)
{
int childPosX = (offsetPosX + regionPosX*size) * 2;
int childPosZ = (offsetPosZ + regionPosX*size) * 2;
int childPosZ = (offsetPosZ + regionPosZ*size) * 2;
byte childDetailLevel = (byte) (detailLevel - 1);
int childrenCount = 0;
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, DistanceGenerationMode.NONE)) {
if (!requireCorrectDetailLevel)
childrenCount++;
else
getPosToRenderFlat(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, targetLevel, requireCorrectDetailLevel);
if (detailLevel > farModeSwitchLevel) {
// Giant block is not acceptable. So leave empty void if data doesn't exist.
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, DistanceGenerationMode.NONE)) {
getPosToRenderFlat(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, targetLevel, farModeSwitchLevel);
}
}
}
} else {
// Giant block is acceptable. So use this level lod if not all child data exist.
int childrenCount = 0;
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, DistanceGenerationMode.NONE)) {
childrenCount++;
}
}
}
}
if (!requireCorrectDetailLevel) {
// If all the four children exist go deeper
if (childrenCount == 4) {
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getPosToRenderFlat(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, targetLevel, requireCorrectDetailLevel);
getPosToRenderFlat(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, targetLevel, farModeSwitchLevel);
} else {
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
}
@@ -1177,13 +1177,13 @@ public class VerticalLevelContainer implements LevelContainer
}
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getVerticalSize());
if (!anyDataExist)
throw new RuntimeException();
throw new RuntimeException("Update data called but no child datapoint exist!");
if ((!DataPointUtil.doesItExist(data[0])) && anyDataExist)
throw new RuntimeException();
throw new RuntimeException("Update data called but higher level datapoint doesn't exist even though child data does exist!");
if (DataPointUtil.getGenerationMode(data[0]) != DataPointUtil.getGenerationMode(lowerLevelContainer.getSingleData(posX*2, posZ*2)))
throw new RuntimeException();
throw new RuntimeException("Update data called but higher level datapoint does not have the same GenerationMode as the top left corner child datapoint!");
forceWriteVerticalData(data, posX, posZ);
@@ -34,8 +34,11 @@ public class DetailDistanceUtil
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
@Deprecated
private static final double genMultiplier = 1.0;
@Deprecated
private static final double treeGenMultiplier = 1.0;
@Deprecated
private static final double treeCutMultiplier = 1.0;
private static byte minGenDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel;
private static byte minDrawDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel;
@@ -44,21 +47,6 @@ public class DetailDistanceUtil
private static int minDetailDistance = (int) (MC_RENDER.getRenderDistance()*16 * 1.42f);
private static int maxDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 16 * 2;
@Deprecated
private static final HorizontalResolution[] lodGenDetails = {
HorizontalResolution.BLOCK,
HorizontalResolution.TWO_BLOCKS,
HorizontalResolution.FOUR_BLOCKS,
HorizontalResolution.HALF_CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK };
public static void updateSettings()
{
@@ -93,44 +81,54 @@ public class DetailDistanceUtil
return baseDistanceFunction(detail);
}
public static byte baseInverseFunction(int distance, byte minDetail, boolean useRenderMinDistance)
public static byte baseInverseFunction(int distance, byte minDetail)
{
byte detail;
if (distance == 0
|| (distance < minDetailDistance && useRenderMinDistance)
|| CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality())
return minDetail;
distance -= minDetailDistance;
if (distance < 0 || CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality())
distance = 0;
int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale() * 16;
double scaledDistance = distance;
scaledDistance /= distanceUnit;
if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST)
detail = (byte) (distance / distanceUnit);
detail = (byte) (scaledDistance);
else
{
double base = CONFIG.client().graphics().quality().getHorizontalQuality().quadraticBase;
double logBase = Math.log(base);
//noinspection IntegerDivisionInFloatingPointContext
detail = (byte) (Math.log(distance / distanceUnit) / logBase);
detail = (byte) (Math.log(scaledDistance) / logBase);
}
return (byte) LodUtil.clamp(minDetail, detail, maxDetail - 1);
return (byte) LodUtil.clamp(minDetail, detail+minDetail, maxDetail - 1);
}
public static byte getDetailLevelFromDistance(int distance)
{
return baseInverseFunction(distance, minDrawDetail);
}
@Deprecated //Reason: All merged into `getDetailLevelFromDistance`
public static byte getDrawDetailFromDistance(int distance)
{
return baseInverseFunction(distance, minDrawDetail, false);
return baseInverseFunction(distance, minDrawDetail);
}
@Deprecated //Reason: Same as 'getDrawDetailFromDistance'
public static byte getGenerationDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * genMultiplier), minGenDetail, true);
return baseInverseFunction((int) (distance * genMultiplier), minGenDetail);
}
@Deprecated //Reason: Same as 'getDrawDetailFromDistance'
public static byte getTreeCutDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail, true);
return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail);
}
@Deprecated //Reason: Same as 'getDrawDetailFromDistance'
public static byte getTreeGenDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail, true);
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail);
}
@@ -142,6 +140,7 @@ public class DetailDistanceUtil
return CONFIG.client().worldGenerator().getDistanceGenerationMode();
}*/
@Deprecated
public static byte getLodDrawDetail(byte detail)
{
detail += minDrawDetail;
@@ -150,27 +149,6 @@ public class DetailDistanceUtil
return detail;
}
@Deprecated
public static HorizontalResolution getLodGenDetail(int detail)
{
if (detail < minGenDetail)
return lodGenDetails[minGenDetail];
else
return lodGenDetails[detail];
}
@Deprecated
public static byte getCutLodDetail(int detail)
{
if (detail < minGenDetail)
return lodGenDetails[minGenDetail].detailLevel;
else if (detail == maxDetail)
return LodUtil.REGION_DETAIL_LEVEL;
else
return lodGenDetails[detail].detailLevel;
}
public static int getMaxVerticalData(int detail)
{
return CONFIG.client().graphics().quality().getVerticalQuality().maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)];
@@ -62,4 +62,10 @@ public interface IChunkWrapper
default int getBlockLight(int x, int y, int z) {return -1;}
default int getSkyLight(int x, int y, int z) {return -1;}
default boolean blockPosInsideChunk(int x, int y, int z) {
return (x>=getMinX() && x<=getMaxX()
&& y>=getMinBuildHeight() && y<getMaxBuildHeight()
&& z>=getMinZ() && z<=getMaxZ());
}
}