Add overdraw offset

This commit is contained in:
tom lee
2022-03-20 23:18:08 +08:00
parent 467f4a260f
commit 4f2bf9b834
5 changed files with 86 additions and 209 deletions
@@ -31,15 +31,12 @@ package com.seibel.lod.core.enums.config;
*/
public enum VanillaOverdraw
{
/** Never draw LODs where a minecraft chunk could be. */
/** Dont draw LODs where a minecraft chunk could be. Use Overdraw Offset to tweak the border thickness */
NEVER,
/** Draw LODs over the farther minecraft chunks. */
/** Draw LODs over the farther minecraft chunks. Dynamically decides the border thickness */
DYNAMIC,
/** Draw LODs over all minecraft chunks. */
ALWAYS,
/** Draw LODs over border chunks. */
BORDER,
}
@@ -17,6 +17,7 @@ import com.seibel.lod.core.enums.config.VanillaOverdraw;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.enums.rendering.GLProxyContext;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.BoolType;
import com.seibel.lod.core.objects.PosToRenderContainer;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.objects.lod.LodRegion;
@@ -32,6 +33,7 @@ import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.gridList.MovableCenteredGridList;
import com.seibel.lod.core.util.StatsMap;
import com.seibel.lod.core.util.gridList.PosArrayGridList;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
@@ -244,26 +246,6 @@ public class RenderRegion implements AutoCloseable
{ 1, 0},
{ 1, 1}
};
private static MovableCenteredGridList<Boolean> shinkGridEdge(MovableCenteredGridList<Boolean> target) {
MovableCenteredGridList<Boolean> result = new MovableCenteredGridList<Boolean>(
target.gridCentreToEdge-1, target.getCenterX(), target.getCenterY());
int chunkGridMinX = target.getCenterX() - target.gridCentreToEdge;
int chunkGridMinZ = target.getCenterY() - target.gridCentreToEdge;
for (int x=chunkGridMinX+1; x<chunkGridMinX+target.gridSize-2; x++) {
for (int z=chunkGridMinZ+1; z<chunkGridMinZ+target.gridSize-2; z++) {
Boolean b = target.get(x+1, z);
boolean rendered = b!=null && b;
if (!rendered) continue;
for (int[] pos : ADJACENT8) {
Boolean b0 = target.get(x+pos[0], z+pos[1]);
rendered &= b0!=null && b0;
}
if (rendered) result.set(x, z, true);
}
}
return result;
}
private static void makeLodRenderData(LodQuadBuilder quadBuilder, LodRegion region, LodRegion[] adjRegions, int playerX,
int playerZ) {
@@ -276,10 +258,7 @@ public class RenderRegion implements AutoCloseable
// position
PosToRenderContainer posToRender = new PosToRenderContainer(minDetail, region.regionPosX, region.regionPosZ);
region.getPosToRender(posToRender, playerX, playerZ);
MovableCenteredGridList<Boolean> chunkGrid = ClientApi.renderer.vanillaRenderedChunks;
if (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == VanillaOverdraw.BORDER) {
chunkGrid = shinkGridEdge(chunkGrid);
}
PosArrayGridList<BoolType> chunkGrid = ClientApi.renderer.vanillaChunks;
for (int index = 0; index < posToRender.getNumberOfPos(); index++) {
@@ -297,9 +276,8 @@ public class RenderRegion implements AutoCloseable
if (detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL) {
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posX);
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posZ);
Boolean isRendered = chunkGrid.get(chunkX, chunkZ);
// skip any chunks that Minecraft is going to render
if (isRendered != null && isRendered) continue;
if (chunkGrid.get(chunkX, chunkZ) != null) continue;
}
long[] posData = region.getAllData(detailLevel, posX, posZ);
@@ -330,8 +308,7 @@ public class RenderRegion implements AutoCloseable
int zAdj = posZ + lodDirection.getNormal().z;
int chunkXAdj = LevelPosUtil.getChunkPos(detailLevel, xAdj);
int chunkZAdj = LevelPosUtil.getChunkPos(detailLevel, zAdj);
Boolean isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
if (isRenderedAdj!=null && isRenderedAdj) continue;
if (chunkGrid.get(chunkXAdj, chunkZAdj)!=null) continue;
boolean isCrossRegionBoundary = LevelPosUtil.getRegion(detailLevel, xAdj) != region.regionPosX ||
LevelPosUtil.getRegion(detailLevel, zAdj) != region.regionPosZ;
@@ -24,10 +24,10 @@ import java.time.Duration;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import com.seibel.lod.core.objects.BoolType;
import com.seibel.lod.core.objects.Pos2D;
import com.seibel.lod.core.util.*;
import com.seibel.lod.core.util.gridList.MovableCenteredGridList;
import com.seibel.lod.core.util.gridList.MovableGridRingList;
import com.seibel.lod.core.util.gridList.*;
import org.lwjgl.opengl.GL32;
import com.seibel.lod.core.api.ApiShared;
@@ -80,7 +80,6 @@ public class LodRenderer
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
public static final int VANILLA_REFRESH_TIMEOUT = 60;
/**
* If true the LODs colors will be replaced with
* a checkerboard, this can be used for debugging.
@@ -121,10 +120,7 @@ public class LodRenderer
* This HashSet contains every chunk that Vanilla Minecraft
* is going to render
*/
public MovableCenteredGridList<Boolean> vanillaRenderedChunks;
public int vanillaRenderedChunksCenterX;
public int vanillaRenderedChunksCenterZ;
public int vanillaRenderedChunksRefreshTimer;
public PosArrayGridList<BoolType> vanillaChunks = null;
private boolean canVanillaFogBeDisabled = true;
@@ -534,44 +530,32 @@ public class LodRenderer
// returns whether anything changed
private boolean updateVanillaRenderedChunks(LodDimension lodDim) {
int chunkRenderDistance = MC_RENDER.getRenderDistance()+2;
int chunkX = Math.floorDiv(lastUpdatedPos.getX(), 16);
int chunkZ = Math.floorDiv(lastUpdatedPos.getZ(), 16);
// if the player is high enough, draw all LODs
IWorldWrapper world = MC.getWrappedClientWorld();
if (lastUpdatedPos.getY() > world.getHeight()-world.getMinHeight()) {
vanillaRenderedChunks = new MovableCenteredGridList<Boolean>(
chunkRenderDistance, chunkX, chunkZ);
return true;
}
MovableCenteredGridList<Boolean> chunkList;
boolean anyChanged = false;
if (vanillaRenderedChunks == null || vanillaRenderedChunks.gridCentreToEdge != chunkRenderDistance ||
vanillaRenderedChunks.getCenterX()!=chunkX || vanillaRenderedChunks.getCenterY()!=chunkZ) {
chunkList = new MovableCenteredGridList<Boolean>(chunkRenderDistance, chunkX, chunkZ);
anyChanged = true;
} else {
chunkList = vanillaRenderedChunks;
if (vanillaChunks != null) {
vanillaChunks = null;
return true;
}
return false;
}
LagSpikeCatcher getChunks = new LagSpikeCatcher();
Set<AbstractChunkPosWrapper> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, lastUpdatedPos);
getChunks.end("LodDrawSetup:UpdateStatus:UpdateVanillaChunks:getChunks");
for (AbstractChunkPosWrapper pos : chunkPosToSkip)
{
// sometimes we are given chunks that are outside the render distance,
// This prevents index out of bounds exceptions
if (!chunkList.inRange(pos.getX(), pos.getZ())) continue;
Boolean oldBool = chunkList.swap(pos.getX(), pos.getZ(), true);
if (oldBool == null || !oldBool)
{
anyChanged = true;
lodBufferBuilderFactory.setRegionNeedRegen(pos.getRegionX(), pos.getRegionZ());
EdgeDistanceBooleanGrid edgeGrid = LodUtil.readVanillaRenderedChunks(lodDim);
if (edgeGrid == null) {
if (vanillaChunks != null) {
vanillaChunks = null;
return true;
}
return false;
}
if (anyChanged) vanillaRenderedChunks = chunkList;
return anyChanged;
getChunks.end("LodDrawSetup:UpdateStatus:UpdateVanillaChunks:getChunks");
PosArrayGridList<BoolType> grid = new PosArrayGridList<>(edgeGrid.gridSize, edgeGrid.getOffsetX(), edgeGrid.getOffsetY());
int overdrawOffset = LodUtil.computeOverdrawOffset(lodDim);
edgeGrid.flagAllWithDistance(grid, (i) -> (i >= overdrawOffset));
vanillaChunks = grid;
return true;
}
private void updateRegenStatus(LodDimension lodDim, float partialTicks) {
@@ -22,6 +22,7 @@ package com.seibel.lod.core.util;
import java.awt.Color;
import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.config.HorizontalResolution;
@@ -30,10 +31,12 @@ import com.seibel.lod.core.enums.config.VanillaOverdraw;
import com.seibel.lod.core.handlers.IReflectionHandler;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.ParsedIp;
import com.seibel.lod.core.objects.Pos2D;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.objects.lod.RegionPos;
import com.seibel.lod.core.objects.opengl.DefaultLodVertexFormats;
import com.seibel.lod.core.objects.opengl.LodVertexFormat;
import com.seibel.lod.core.util.gridList.EdgeDistanceBooleanGrid;
import com.seibel.lod.core.util.gridList.MovableCenteredGridList;
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
@@ -351,144 +354,49 @@ public class LodUtil
public static int ceilDiv(int value, int divider) {
return -Math.floorDiv(-value, divider);
}
/**
* Get a HashSet of all ChunkPos within the normal render distance
* that should not be rendered.
*/
public static HashSet<AbstractChunkPosWrapper> getNearbyLodChunkPosToSkip(LodDimension lodDim, AbstractBlockPosWrapper blockPosWrapper)
{
int chunkRenderDist = MC_RENDER.getRenderDistance();
int skipRadius;
VanillaOverdraw overdraw = CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw();
HorizontalResolution drawRes = CONFIG.client().graphics().quality().getDrawResolution();
// apply distance based rules for dynamic overdraw
if (overdraw == VanillaOverdraw.DYNAMIC
&& chunkRenderDist <= MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW)
{
// The vanilla render distance isn't far enough
// for partial skipping to make sense...
if (!lodDim.dimension.hasCeiling() && (drawRes == HorizontalResolution.BLOCK))
{
// ...and the dimension is open, so we don't have to worry about
// LODs rendering on top of the player,
// and the user is using a high horizontal resolution,
// so the overdraw shouldn't be noticeable
overdraw = VanillaOverdraw.ALWAYS;
}
else
{
// ...but we are underground, so we don't want
// LODs rendering on top of the player,
// Or the user is using a LOW horizontal resolution
// and overdraw would be very noticeable.
overdraw = VanillaOverdraw.NEVER;
}
}
// determine the skipping type based
// on the overdraw type
switch (overdraw)
{
case ALWAYS:
// don't skip any positions
return new HashSet<>();
case DYNAMIC:
if (chunkRenderDist > MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW
&& chunkRenderDist <= MINIMUM_RENDER_DISTANCE_FOR_FAR_OVERDRAW)
{
// This is a small render distance (but greater than the minimum partial distance)
// skip positions that are greater than 2/3 the render distance
skipRadius = (int) Math.ceil(chunkRenderDist * (2.0/3.0));
}
else
{
// This is a large render distance.
// Skip positions that are greater than 4/5ths the render distance
skipRadius = (int) Math.ceil(chunkRenderDist * (4.0 / 5.0));
}
break;
default:
case BORDER:
case NEVER:
// skip chunks in render distance that are rendered
// by vanilla minecraft
skipRadius = 0;
break;
}
// get the chunks that are going to be rendered by Minecraft
HashSet<AbstractChunkPosWrapper> posToSkip = MC_RENDER.getVanillaRenderedChunks();
// remove everything outside the skipRadius,
// if the skipRadius is being used
if (skipRadius != 0)
{
int centerCX = LevelPosUtil.getChunkPos(BLOCK_DETAIL_LEVEL, blockPosWrapper.getX());
int centerCZ = LevelPosUtil.getChunkPos(BLOCK_DETAIL_LEVEL, blockPosWrapper.getZ());
if (VERSION_CONSTANTS.isVanillaRenderedChunkSquare()) {
int minX = centerCX-skipRadius;
int maxX = centerCX+skipRadius+1;
int minZ = centerCZ-skipRadius;
int maxZ = centerCZ+skipRadius+1;
posToSkip.removeIf((pos) -> {
return (pos.getX() < minX || pos.getX() > maxX || pos.getZ() < minZ || pos.getZ() > maxZ);
});
public static int computeOverdrawOffset(LodDimension lodDim) {
int chunkRenderDist = MC_RENDER.getRenderDistance() + 1;
VanillaOverdraw overdraw = null;//CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw();
if (overdraw == VanillaOverdraw.ALWAYS) return Integer.MAX_VALUE;
int offset;
if (overdraw == VanillaOverdraw.NEVER) {
offset = CONFIG.client().graphics().advancedGraphics().getOverdrawOffset();
} else {
if (chunkRenderDist < MINIMUM_RENDER_DISTANCE_FOR_FAR_OVERDRAW) {
offset = 1;
} else {
int skipRadius2 = skipRadius*skipRadius;
posToSkip.removeIf((pos) -> {
int dx = pos.getX()-centerCX;
int dz = pos.getZ()-centerCZ;
return (dx*dx + dz*dz > skipRadius2);
});
offset = chunkRenderDist / 5;
}
}
return posToSkip;
}
/**
* This method find if a given chunk is a border chunk of the renderable ones
* @param vanillaRenderedChunks matrix of the vanilla rendered chunks
* @param x relative (to the matrix) x chunk to check
* @param z relative (to the matrix) z chunk to check
* @return true if and only if the chunk is a border of the renderable chunks
*/
@Deprecated
public static boolean isBorderChunk(boolean[][] vanillaRenderedChunks, int x, int z)
{
if (x < 0 || z < 0 || x >= vanillaRenderedChunks.length || z >= vanillaRenderedChunks[0].length)
return false;
int tempX;
int tempZ;
for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS)
{
tempX = x + lodDirection.getNormal().x;
tempZ = z + lodDirection.getNormal().z;
if (vanillaRenderedChunks[x][z] || (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length)
&& !vanillaRenderedChunks[tempX][tempZ]))
return true;
if (chunkRenderDist - offset <= 1) {
return Integer.MAX_VALUE;
}
return false;
return offset;
}
public static boolean isBorderChunk(MovableCenteredGridList<Boolean> vanillaRenderedChunks, int chunkX, int chunkZ)
{
for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS)
{
int tempX = chunkX + lodDirection.getNormal().x;
int tempZ = chunkZ + lodDirection.getNormal().z;
Boolean b = vanillaRenderedChunks.get(tempX, tempZ);
if (b == null || !b) return true;
}
return false;
public static EdgeDistanceBooleanGrid readVanillaRenderedChunks(LodDimension lodDim) {
int offset = computeOverdrawOffset(lodDim);
if (offset == Integer.MAX_VALUE) return null;
int renderDist = MC_RENDER.getRenderDistance() + 1;
HashSet<AbstractChunkPosWrapper> posSet = MC_RENDER.getVanillaRenderedChunks();
return new EdgeDistanceBooleanGrid(new Iterator<>() {
final Iterator<AbstractChunkPosWrapper> iter = posSet.iterator();
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Pos2D next() {
AbstractChunkPosWrapper pos = iter.next();
return new Pos2D(pos.getX(), pos.getZ());
}
},
MC.getPlayerChunkPos().getX() - renderDist,
MC.getPlayerChunkPos().getZ() - renderDist, renderDist * 2 + 1);
}
@@ -472,20 +472,31 @@ public interface ILodConfigWrapperSingleton extends IBindable
+ " but may look odd for transparent blocks or in caves. \n"
+ "\n"
+ " " + VanillaOverdraw.NEVER + ": \n"
+ " LODs won't render on top of vanilla chunks. \n"
+ " " + VanillaOverdraw.BORDER + ": \n"
+ " LODs will render only on the border of vanilla chunks, preventing some holes in the world. \n"
+ " LODs won't render on top of vanilla chunks. Use Overdraw offset to change the border offset. \n"
+ " " + VanillaOverdraw.DYNAMIC + ": \n"
+ " LODs will render on top of distant vanilla chunks to hide delayed loading. \n"
+ " More effective on higher render distances. \n"
+ " For vanilla render distances less than or equal to " + LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW + " \n"
+ " " + VanillaOverdraw.NEVER + " or " + VanillaOverdraw.ALWAYS + " will be used depending on the dimension. \n"
+ " Will dynamically decide the border offset based on vanilla render distance. \n"
+ " " + VanillaOverdraw.ALWAYS + ": \n"
+ " LODs will render on all vanilla chunks preventing all holes in the world. \n"
+ "\n"
+ " This setting shouldn't affect performance. \n";
VanillaOverdraw getVanillaOverdraw();
void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw);
MinDefaultMax<Integer> OVERDRAW_OFFSET_MIN_DEFAULT_MAX = new MinDefaultMax<Integer>(-16, 0, 16);
String OVERDRAW_OFFSET_DESC = ""
+ " If on Vanilla Overdraw mode of NEVER, how much should should the border be offset? \n"
+ "\n"
+ " '1': The start of lods will be shifted inwards by 1 chunk, causing 1 chunk of overdraw. \n"
+ " '-1': The start fo lods will be shifted outwards by 1 chunk, causing 1 chunk of gap. \n"
+ "\n"
+ " This setting can be used to deal with gaps due to our vanilla rendered chunk \n"
+ " detection not being perfect. \n";
int getOverdrawOffset();
void setOverdrawOffset(int newOverdrawOffset);
/* Disabled for now due to implementation issues.
MinDefaultMax<Integer> VANILLA_CULLING_RANGE_MIN_DEFAULT_MAX = new MinDefaultMax<Integer>(0, 32, 512);
String VANILLA_CULLING_RANGE_DESC = ""