From 4f2bf9b834df5f54d8440986bc0603b93a82c710 Mon Sep 17 00:00:00 2001 From: tom lee Date: Sun, 20 Mar 2022 23:18:08 +0800 Subject: [PATCH] Add overdraw offset --- .../core/enums/config/VanillaOverdraw.java | 7 +- .../lod/core/objects/opengl/RenderRegion.java | 33 +--- .../seibel/lod/core/render/LodRenderer.java | 58 +++--- .../com/seibel/lod/core/util/LodUtil.java | 174 +++++------------- .../config/ILodConfigWrapperSingleton.java | 23 ++- 5 files changed, 86 insertions(+), 209 deletions(-) diff --git a/src/main/java/com/seibel/lod/core/enums/config/VanillaOverdraw.java b/src/main/java/com/seibel/lod/core/enums/config/VanillaOverdraw.java index e28552893..dfe72e317 100644 --- a/src/main/java/com/seibel/lod/core/enums/config/VanillaOverdraw.java +++ b/src/main/java/com/seibel/lod/core/enums/config/VanillaOverdraw.java @@ -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, } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java index 0c3376250..8448e2430 100644 --- a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java +++ b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java @@ -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 shinkGridEdge(MovableCenteredGridList target) { - MovableCenteredGridList result = new MovableCenteredGridList( - 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 chunkGrid = ClientApi.renderer.vanillaRenderedChunks; - if (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == VanillaOverdraw.BORDER) { - chunkGrid = shinkGridEdge(chunkGrid); - } + PosArrayGridList 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; diff --git a/src/main/java/com/seibel/lod/core/render/LodRenderer.java b/src/main/java/com/seibel/lod/core/render/LodRenderer.java index b1a127073..b4360dbe9 100644 --- a/src/main/java/com/seibel/lod/core/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/core/render/LodRenderer.java @@ -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 vanillaRenderedChunks; - public int vanillaRenderedChunksCenterX; - public int vanillaRenderedChunksCenterZ; - public int vanillaRenderedChunksRefreshTimer; + public PosArrayGridList 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( - chunkRenderDistance, chunkX, chunkZ); - return true; - } - MovableCenteredGridList chunkList; - - boolean anyChanged = false; - if (vanillaRenderedChunks == null || vanillaRenderedChunks.gridCentreToEdge != chunkRenderDistance || - vanillaRenderedChunks.getCenterX()!=chunkX || vanillaRenderedChunks.getCenterY()!=chunkZ) { - chunkList = new MovableCenteredGridList(chunkRenderDistance, chunkX, chunkZ); - anyChanged = true; - } else { - chunkList = vanillaRenderedChunks; + if (vanillaChunks != null) { + vanillaChunks = null; + return true; + } + return false; } LagSpikeCatcher getChunks = new LagSpikeCatcher(); - Set 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 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) { diff --git a/src/main/java/com/seibel/lod/core/util/LodUtil.java b/src/main/java/com/seibel/lod/core/util/LodUtil.java index 9a0295983..403558b1e 100644 --- a/src/main/java/com/seibel/lod/core/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/core/util/LodUtil.java @@ -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 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 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 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 posSet = MC_RENDER.getVanillaRenderedChunks(); + return new EdgeDistanceBooleanGrid(new Iterator<>() { + final Iterator 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); } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java index 929059899..cc640b5b1 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java @@ -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 OVERDRAW_OFFSET_MIN_DEFAULT_MAX = new MinDefaultMax(-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 VANILLA_CULLING_RANGE_MIN_DEFAULT_MAX = new MinDefaultMax(0, 32, 512); String VANILLA_CULLING_RANGE_DESC = ""