From 37db05d18fae4b573cfed8b3186249bd63f30c46 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Thu, 30 Sep 2021 01:38:39 +0200 Subject: [PATCH] added a borderChunk finder function --- .../bufferBuilding/LodBufferBuilder.java | 9 +- .../com/seibel/lod/render/LodRenderer.java | 156 ++++---- .../java/com/seibel/lod/util/LodUtil.java | 342 ++++++++++-------- 3 files changed, 279 insertions(+), 228 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java index 95de06517..096efbd57 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java @@ -295,10 +295,12 @@ public class LodBufferBuilder chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x; chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z; + boolean isItBorderPos = LodUtil.isBorderChunk(vanillaRenderedChunks,chunkXdist + gameChunkRenderDistance + 1,chunkZdist + gameChunkRenderDistance + 1); if (gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist) && detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL - && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]) + && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] + && !isItBorderPos) { continue; } @@ -316,7 +318,8 @@ public class LodBufferBuilder && posToRender.contains(detailLevel, xAdj, zAdj) && (gameChunkRenderDistance < Math.abs(chunkXdist) || gameChunkRenderDistance < Math.abs(chunkZdist) - || !vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])) + || !(vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] + && !LodUtil.isBorderChunk(vanillaRenderedChunks,chunkXdist + gameChunkRenderDistance + 1,chunkZdist + gameChunkRenderDistance + 1)))) { if (!adjData.containsKey(direction) || adjData.get(direction) == null) adjData.put(direction, new long[maxVerticalData]); @@ -442,7 +445,7 @@ public class LodBufferBuilder drawableVbos[x][z] = new VertexBuffer[1]; } else { - numberOfBuffers = (int) Math.ceil(memoryRequired / BUFFER_MAX_CAPACITY)+1; + numberOfBuffers = (int) Math.ceil(memoryRequired / BUFFER_MAX_CAPACITY) + 1; System.out.println(numberOfBuffers); memoryRequired = BUFFER_MAX_CAPACITY; bufferSize[x][z] = numberOfBuffers; diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index cf5e30428..879e1d680 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -19,6 +19,8 @@ package com.seibel.lod.render; import java.util.HashSet; +import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box; +import net.minecraft.util.Direction; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15C; @@ -58,6 +60,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Vector3d; +import org.lwjgl.system.CallbackI; /** @@ -74,7 +77,7 @@ public class LodRenderer * it should be something different than what is used by Minecraft */ private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2; - + /** * If true the LODs colors will be replaced with * a checkerboard, this can be used for debugging. @@ -97,15 +100,16 @@ public class LodRenderer */ private VertexBuffer[][][] vbos; public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR; - private ChunkPos vbosCenter = new ChunkPos(0,0); + private ChunkPos vbosCenter = new ChunkPos(0, 0); + /** * This is used to determine if the LODs should be regenerated */ - private int[] previousPos = new int[]{0,0,0}; - + private int[] previousPos = new int[]{0, 0, 0}; + public NativeImage lightMap = null; - + // these variables are used to determine if the buffers should be rebuilt private long prevDayTime = 0; private double prevBrightness = 0; @@ -148,9 +152,9 @@ public class LodRenderer * Besides drawing the LODs this method also starts * the async process of generating the Buffers that hold those LODs. * - * @param lodDim The dimension to draw, if null doesn't replace the current dimension. + * @param lodDim The dimension to draw, if null doesn't replace the current dimension. * @param mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method - * @param partialTicks how far into the current tick this method was called. + * @param partialTicks how far into the current tick this method was called. */ public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, float partialTicks, IProfiler newProfiler) { @@ -168,8 +172,8 @@ public class LodRenderer profiler = newProfiler; profiler.push("LOD setup"); - - + + // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) // starting here... determineIfLodsShouldRegenerate(lodDim); @@ -197,12 +201,11 @@ public class LodRenderer // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) // ...ending here - + if (lodBufferBuilder.newBuffersAvaliable()) { swapBuffers(); } - //===========================// @@ -210,7 +213,7 @@ public class LodRenderer //===========================// // set the required open GL settings - + if (LodConfig.CLIENT.debugging.debugMode.get() == DebugMode.SHOW_DETAIL_WIREFRAME) GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); else @@ -233,7 +236,7 @@ public class LodRenderer // OpenGl outputs their matricies in col,row form instead of row,col // (or maybe vice versa I have no idea :P) mcProjectionMatrix.transpose(); - + Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcMatrixStack, partialTicks); // required for setupFog and setupProjectionMatrix @@ -256,16 +259,16 @@ public class LodRenderer //===========// // rendering // //===========// - + profiler.popPush("LOD draw"); - + if (vbos != null) { ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera(); Vector3d cameraDir = new Vector3d(renderInfo.getLookVector()); - + boolean cullingDisabled = LodConfig.CLIENT.graphics.disableDirectionalCulling.get(); - + // used to determine what type of fog to render int halfWidth = vbos.length / 2; int quarterWidth = vbos.length / 4; @@ -283,7 +286,7 @@ public class LodRenderer setupFog(fogSettings.far.distance, fogSettings.far.quality); - for(int i = 0; i < lodBufferBuilder.bufferSize[x][z]; i++) + for (int i = 0; i < lodBufferBuilder.bufferSize[x][z]; i++) { sendLodsToGpuAndDraw(vbos[x][z][i], modelViewMatrix); } @@ -291,7 +294,7 @@ public class LodRenderer } } } - + //=========// // cleanup // @@ -314,11 +317,11 @@ public class LodRenderer // reset the projection matrix so anything drawn after // the LODs will use the correct projection matrix gameRender.resetProjectionMatrix(mcProjectionMatrix); - + // clear the depth buffer so anything drawn is drawn // over the LODs GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); - + // end of internal LOD profiling profiler.pop(); @@ -332,13 +335,13 @@ public class LodRenderer { if (vbo == null) return; - + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); // 0L is the starting pointer LOD_VERTEX_FORMAT.setupBufferState(0L); - + vbo.draw(modelViewMatrix, GL11.GL_QUADS); - + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); LOD_VERTEX_FORMAT.clearBufferState(); } @@ -369,8 +372,7 @@ public class LodRenderer { // fancy fog (fragment distance based fog) glFogDistanceMode = NVFogDistance.GL_EYE_RADIAL_NV; - } - else + } else { // fast fog (frustum distance based fog) glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV; @@ -388,16 +390,16 @@ public class LodRenderer if (fogQuality == FogQuality.FANCY) { // for more realistic fog when using FAR - if(LodConfig.CLIENT.graphics.fogDistance.get() == FogDistance.NEAR_AND_FAR) + if (LodConfig.CLIENT.graphics.fogDistance.get() == FogDistance.NEAR_AND_FAR) { RenderSystem.fogStart(farPlaneBlockDistance * 0.9f); RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f); - }else{ + } else + { RenderSystem.fogStart(farPlaneBlockDistance * 0.1f); RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f); } - } - else if (fogQuality == FogQuality.FAST) + } else if (fogQuality == FogQuality.FAST) { // for the far fog of the normal chunks // to start right where the LODs' end use: @@ -405,15 +407,13 @@ public class LodRenderer RenderSystem.fogStart(farPlaneBlockDistance * 1.5f); RenderSystem.fogEnd(farPlaneBlockDistance * 2.0f); } - } - else if (fogDistance == FogDistance.NEAR) + } else if (fogDistance == FogDistance.NEAR) { if (fogQuality == FogQuality.FANCY) { RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.41f); RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.6f); - } - else if (fogQuality == FogQuality.FAST) + } else if (fogQuality == FogQuality.FAST) { RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.0f); RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.5f); @@ -461,12 +461,12 @@ public class LodRenderer { // duplicate the last matrix mcMatrixStack.pushPose(); - - + + // get all relevant camera info ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera(); Vector3d projectedView = renderInfo.getPosition(); - + // translate the camera relative to the regions' center // (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher // accuracy vs the model view matrix, which only uses floats) @@ -475,73 +475,75 @@ public class LodRenderer double xDiff = eyePos.x - bufferPos.getX(); double zDiff = eyePos.z - bufferPos.getZ(); mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff); - - + + // get the modified model view matrix - Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose(); + Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose(); // remove the lod ModelViewMatrix mcMatrixStack.popPose(); - + return lodModelViewMatrix; } - /** James added this to test if Vivecraft is not using - * the MC FOV setting or if the problem is deeper */ + /** + * James added this to test if Vivecraft is not using + * the MC FOV setting or if the problem is deeper + */ public enum FovTest { LOD_USE_FOV(true, false), MC_USE_FOV(false, true), NEITHER(false, false), BOTH(true, true); - + boolean lodProjUseFov; boolean defaultMcProjUseFov; - + private FovTest(boolean newLodProjUseFov, boolean newDefaultMcProjUseFov) { lodProjUseFov = newLodProjUseFov; defaultMcProjUseFov = newDefaultMcProjUseFov; } } - + /** * create a new projection matrix and send it over to the GPU - * + * * @param currentProjectionMatrix this is Minecraft's current projection matrix - * @param partialTicks how many ticks into the frame we are + * @param partialTicks how many ticks into the frame we are */ private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float partialTicks) { // create the new projection matrix Matrix4f lodPoj = - Matrix4f.perspective( - getFov(partialTicks, LodConfig.CLIENT.graphics.useFovSetting.get().lodProjUseFov), - (float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(), - mc.getRenderDistance()/2, - farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2 / 4); - + Matrix4f.perspective( + getFov(partialTicks, LodConfig.CLIENT.graphics.useFovSetting.get().lodProjUseFov), + (float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(), + mc.getRenderDistance() / 2, + farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2 / 4); + // get Minecraft's un-edited projection matrix // (this is before it is zoomed, distorted, etc.) Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, LodConfig.CLIENT.graphics.useFovSetting.get().defaultMcProjUseFov); // true here means use "use fov setting" (probably) - - + + // this logic strips away the defaultMcProj matrix so we // can get the distortionMatrix, which represents all // transformations, zooming, distortions, etc. done // to Minecraft's Projection matrix Matrix4f defaultMcProjInv = defaultMcProj.copy(); defaultMcProjInv.invert(); - + Matrix4f distortionMatrix = defaultMcProjInv.copy(); distortionMatrix.multiply(currentProjectionMatrix); - - + + // edit the lod projection to match Minecraft's // (so the LODs line up with the real world) lodPoj.multiply(distortionMatrix); - + // send the projection over to the GPU gameRender.resetProjectionMatrix(lodPoj); } @@ -616,7 +618,7 @@ public class LodRenderer /** * Replace the current Vertex Buffers with the newly * created buffers from the lodBufferBuilder.

- * + *

* For some reason this has to be called after the frame has been rendered, * otherwise visual stuttering/rubber banding may happen. I'm not sure why... */ @@ -758,27 +760,27 @@ public class LodRenderer */ private void determineIfLodsShouldRegenerate(LodDimension lodDim) { + + short chunkRenderDistance = (short) mc.getRenderDistance(); - - int vanillaRenderedChunksWidth = chunkRenderDistance*2+2; - vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; - - - + int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2; + //=============// // full regens // //=============// - + // check if the view distance changed if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() - || mc.getRenderDistance() != prevRenderDistance + || chunkRenderDistance != prevRenderDistance || prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get()) { + + vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; DetailDistanceUtil.updateSettings(); fullRegen = true; previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk); prevFogDistance = LodConfig.CLIENT.graphics.fogDistance.get(); - prevRenderDistance = mc.getRenderDistance(); + prevRenderDistance = chunkRenderDistance; } // did the user change the debug setting? @@ -791,7 +793,7 @@ public class LodRenderer long newTime = System.currentTimeMillis(); - if(LodConfig.CLIENT.graphics.detailDropOff.get() == DetailDropOff.FANCY) + if (LodConfig.CLIENT.graphics.detailDropOff.get() == DetailDropOff.FANCY) { // check if the player has moved if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get()) @@ -800,6 +802,7 @@ public class LodRenderer || mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos) || mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos)) { + vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; fullRegen = true; previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk); } @@ -808,7 +811,6 @@ public class LodRenderer } - //================// // partial regens // //================// @@ -848,8 +850,6 @@ public class LodRenderer } - - //==============// // LOD skipping // //==============// @@ -862,11 +862,11 @@ public class LodRenderer { xIndex = (pos.x - mc.getPlayer().xChunk) + (chunkRenderDistance + 1); zIndex = (pos.z - mc.getPlayer().zChunk) + (chunkRenderDistance + 1); - + // sometimes we are given chunks that are outside the render distance, // This prevents index out of bounds exceptions if (xIndex >= 0 && zIndex >= 0 - && xIndex < vanillaRenderedChunks.length + && xIndex < vanillaRenderedChunks.length && zIndex < vanillaRenderedChunks.length) { if (!vanillaRenderedChunks[xIndex][zIndex]) @@ -877,8 +877,8 @@ public class LodRenderer } } } - - + + // if the player is high enough, draw all LODs if(chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256) { diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java index a97ca2334..7b6e1303b 100644 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/util/LodUtil.java @@ -21,6 +21,7 @@ import java.awt.Color; import java.io.File; import java.util.HashSet; +import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box; import com.seibel.lod.enums.LodTemplate; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.RegionPos; @@ -30,6 +31,7 @@ import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk; import net.minecraft.server.integrated.IntegratedServer; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.DimensionType; @@ -42,26 +44,32 @@ import net.minecraft.world.server.ServerWorld; /** * This class holds methods and constants that may be used in multiple places. - * + * * @author James Seibel * @version 9-7-2021 */ public class LodUtil { private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - - /** alpha used when drawing chunks in debug mode */ + + + /** + * alpha used when drawing chunks in debug mode + */ public static final int DEBUG_ALPHA = 255; // 0 - 255 public static final Color COLOR_DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA); public static final Color COLOR_DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA); - public static final Color COLOR_INVISIBLE = new Color(0,0,0,0); - - /** a gray-purple color */ + public static final Color COLOR_INVISIBLE = new Color(0, 0, 0, 0); + + /** + * a gray-purple color + */ public static final int MYCELIUM_COLOR_INT = LodUtil.colorToInt(Color.decode("#6E6166")); - /** TODO, add a better way to override material colors + /** + * TODO, add a better way to override material colors * and/or add a method to generate colors based on texture - * issue #64 */ + * issue #64 + */ public static final int STONE_COLOR_INT = LodUtil.colorToInt(new Color(150, 150, 150)); public static final int NETHERRACK_COLOR_INT = LodUtil.colorToInt(new Color(95, 38, 38)); public static final int WARPED_NYLIUM_COLOR_INT = LodUtil.colorToInt(new Color(34, 94, 85)); @@ -71,14 +79,20 @@ public class LodUtil * In order of nearest to farthest:
* Red, Orange, Yellow, Green, Cyan, Blue, Magenta, white, gray, black */ - public static final Color DEBUG_DETAIL_LEVEL_COLORS[] = new Color[] { Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.WHITE, Color.GRAY, Color.BLACK }; - - - /** 512 blocks wide */ + public static final Color DEBUG_DETAIL_LEVEL_COLORS[] = new Color[]{Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.WHITE, Color.GRAY, Color.BLACK}; + + + /** + * 512 blocks wide + */ public static final byte REGION_DETAIL_LEVEL = 9; - /** 16 blocks wide */ + /** + * 16 blocks wide + */ public static final byte CHUNK_DETAIL_LEVEL = 4; - /** 1 block wide */ + /** + * 1 block wide + */ public static final byte BLOCK_DETAIL_LEVEL = 0; @@ -86,28 +100,40 @@ public class LodUtil public static final short MAX_VERTICAL_DATA = 4; - /** measured in Blocks
- * detail level 9 */ + /** + * measured in Blocks
+ * detail level 9 + */ public static final short REGION_WIDTH = 512; - /** measured in Blocks
- * detail level 4 */ + /** + * measured in Blocks
+ * detail level 4 + */ public static final short CHUNK_WIDTH = 16; - /** measured in Blocks
- * detail level 0 */ + /** + * measured in Blocks
+ * detail level 0 + */ public static final short BLOCK_WIDTH = 1; - - - /** number of chunks wide */ + + + /** + * number of chunks wide + */ public static final int REGION_WIDTH_IN_CHUNKS = 32; - - - /** If we ever need to use a heightmap for any reason, use this one. */ + + + /** + * If we ever need to use a heightmap for any reason, use this one. + */ public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; - - /** This regex finds any characters that are invalid for use in a windows - * (and by extension mac and linux) file path */ + + /** + * This regex finds any characters that are invalid for use in a windows + * (and by extension mac and linux) file path + */ public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\\\/:*?\\\"<>|]"; - + /** * 64 MB by default is the maximum amount of memory that * can be directly allocated.

@@ -120,33 +146,29 @@ public class LodUtil * https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx */ public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024; - - - - - - + + /** * Gets the first valid ServerWorld. - * + * * @return null if there are no ServerWorlds */ public static ServerWorld getFirstValidServerWorld() { if (mc.hasSingleplayerServer()) return null; - + Iterable worlds = mc.getSingleplayerServer().getAllLevels(); - + for (ServerWorld world : worlds) return world; - + return null; } - + /** * Gets the ServerWorld for the relevant dimension. - * + * * @return null if there is no ServerWorld for the given dimension */ public static ServerWorld getServerWorldFromDimension(DimensionType dimension) @@ -154,42 +176,43 @@ public class LodUtil IntegratedServer server = mc.getSingleplayerServer(); if (server == null) return null; - + Iterable worlds = server.getAllLevels(); ServerWorld returnWorld = null; - + for (ServerWorld world : worlds) { - if(world.dimensionType() == dimension) + if (world.dimensionType() == dimension) { returnWorld = world; break; } } - + return returnWorld; } - + /** - * Convert a 2D absolute position into a quad tree relative position. + * Convert a 2D absolute position into a quad tree relative position. */ public static RegionPos convertGenericPosToRegionPos(int x, int z, int detailLevel) { int relativePosX = Math.floorDiv(x, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)); int relativePosZ = Math.floorDiv(z, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)); - + return new RegionPos(relativePosX, relativePosZ); } - + /** * Convert a 2D absolute position into a quad tree relative position. */ public static int convertLevelPos(int pos, int currectDetailLevel, int targetDetailLevel) { int newPos = Math.floorDiv(pos, (int) Math.pow(2, targetDetailLevel - currectDetailLevel)); - + return newPos; } + /** * Return whether the given chunk * has any data in it. @@ -197,20 +220,19 @@ public class LodUtil public static boolean chunkHasBlockData(IChunk chunk) { ChunkSection[] blockStorage = chunk.getSections(); - - for(ChunkSection section : blockStorage) + + for (ChunkSection section : blockStorage) { - if(section != null && !section.isEmpty()) + if (section != null && !section.isEmpty()) { return true; } } - + return false; } - - - + + /** * If on single player this will return the name of the user's * world, if in multiplayer it will return the server name, IP, @@ -218,28 +240,26 @@ public class LodUtil */ public static String getWorldID(IWorld world) { - if(mc.hasSingleplayerServer()) + if (mc.hasSingleplayerServer()) { // chop off the dimension ID as it is not needed/wanted String dimId = getDimensionIDFromWorld(world); - + // get the world name int saveIndex = dimId.indexOf("saves") + 1 + "saves".length(); int slashIndex = dimId.indexOf(File.separatorChar, saveIndex); dimId = dimId.substring(saveIndex, slashIndex); return dimId; - } - else + } else { return getServerId(); } } - - - + + /** * If on single player this will return the name of the user's - * world and the dimensional save folder, if in multiplayer + * world and the dimensional save folder, if in multiplayer * it will return the server name, ip, game version, and dimension.
*
* This can be used to determine where to save files for a given @@ -247,58 +267,57 @@ public class LodUtil */ public static String getDimensionIDFromWorld(IWorld world) { - if(mc.hasSingleplayerServer()) + if (mc.hasSingleplayerServer()) { // this will return the world save location // and the dimension folder - + ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(world.dimensionType()); - if(serverWorld == null) + if (serverWorld == null) throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerWorld for the dimension " + world.dimensionType().effectsLocation().getPath()); - + ServerChunkProvider provider = serverWorld.getChunkSource(); - if(provider == null) + if (provider == null) throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerChunkProvider for the dimension " + world.dimensionType().effectsLocation().getPath()); - + return provider.dataStorage.dataFolder.toString(); - } - else + } else { return getServerId() + File.separatorChar + "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar; } } - + /** * returns the server name, IP and game version. */ public static String getServerId() { ServerData server = mc.getCurrentServer(); - + String serverName = server.name.replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); String serverIp = server.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); String serverMcVersion = server.version.getString().replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); - - String serverId = serverName + ", IP " + serverIp + ", GameVersion " + serverMcVersion; - - return serverId; + + String serverId = serverName + ", IP " + serverIp + ", GameVersion " + serverMcVersion; + + return serverId; } - - + + /** * Convert a BlockColors int into a Color object. */ public static Color intToColor(int num) { int filter = 0b11111111; - - int red = (num >> 16 ) & filter; - int green = (num >> 8 ) & filter; + + int red = (num >> 16) & filter; + int green = (num >> 8) & filter; int blue = num & filter; - + return new Color(red, green, blue); } - + /** * Convert a Color into a BlockColors object. */ @@ -306,8 +325,8 @@ public class LodUtil { return color.getRGB(); } - - + + /** * Clamps the given value between the min and max values. * May behave strangely if min > max. @@ -316,7 +335,7 @@ public class LodUtil { return Math.min(max, Math.max(value, min)); } - + /** * Clamps the given value between the min and max values. * May behave strangely if min > max. @@ -325,7 +344,7 @@ public class LodUtil { return Math.min(max, Math.max(value, min)); } - + /** * Clamps the given value between the min and max values. * May behave strangely if min > max. @@ -337,8 +356,9 @@ public class LodUtil /** * This methods return the number of lods that are going to be rendered in a region in the worst case + * * @param regionPosX x region position to check - * @param regionPosZ z region position to check + * @param regionPosZ z region position to check * @return number of lods in the region */ public static long regionRenderingMemoryUse(int regionPosX, int regionPosZ, LodTemplate template) @@ -354,8 +374,8 @@ public class LodUtil | | X - X - X */ - int circleCenterX = 256 + 256*xRegionSign; - int circleCenterZ = 256 + 256*zRegionSign; + int circleCenterX = 256 + 256 * xRegionSign; + int circleCenterZ = 256 + 256 * zRegionSign; int innerRadius; @@ -366,7 +386,7 @@ public class LodUtil int maxDistance; long memoryUse = 0; int number = 0; - for(byte detailLevel = BLOCK_DETAIL_LEVEL; detailLevel <= REGION_DETAIL_LEVEL; detailLevel++) + for (byte detailLevel = BLOCK_DETAIL_LEVEL; detailLevel <= REGION_DETAIL_LEVEL; detailLevel++) { //We find now the inner and outer detail of this area innerRadius = DetailDistanceUtil.getDrawDistanceFromDetail(detailLevel); @@ -400,59 +420,59 @@ public class LodUtil } return memoryUse; } - - - /** - * Get a HashSet of all ChunkPos within the normal render distance - * that should not be rendered. - */ - public static HashSet getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos) - { - int chunkRenderDist = mc.getRenderDistance(); - ChunkPos centerChunk = new ChunkPos(playerPos); - - // skip chunks that are already going to be rendered by Minecraft - HashSet posToSkip = getRenderedChunks(); - - // go through each chunk within the normal view distance - for (int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++) - { - for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++) - { - if (!lodDim.doesDataExist(LodUtil.CHUNK_DETAIL_LEVEL, x, z)) - continue; - - long data = lodDim.getSingleData(LodUtil.CHUNK_DETAIL_LEVEL, x, z); - - short lodAverageHeight = DataPointUtil.getHeight(data); - - if (playerPos.getY() <= lodAverageHeight) - { - // don't draw Lod's that are taller than the player - // to prevent LODs being drawn on top of the player - posToSkip.add(new ChunkPos(x, z)); - } - } - } - - return posToSkip; - } /** - * This method returns the ChunkPos of all chunks that Minecraft - * is going to render this frame.

- *

- * Note: This isn't perfect. It will return some chunks that are outside - * the clipping plane. (For example, if you are high above the ground some chunks - * will be incorrectly added, even though they are outside render range). - */ + * Get a HashSet of all ChunkPos within the normal render distance + * that should not be rendered. + */ + public static HashSet getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos) + { + int chunkRenderDist = mc.getRenderDistance(); + ChunkPos centerChunk = new ChunkPos(playerPos); + + // skip chunks that are already going to be rendered by Minecraft + HashSet posToSkip = getRenderedChunks(); + + // go through each chunk within the normal view distance + for (int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++) + { + for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++) + { + if (!lodDim.doesDataExist(LodUtil.CHUNK_DETAIL_LEVEL, x, z)) + continue; + + long data = lodDim.getSingleData(LodUtil.CHUNK_DETAIL_LEVEL, x, z); + + short lodAverageHeight = DataPointUtil.getHeight(data); + + if (playerPos.getY() <= lodAverageHeight) + { + // don't draw Lod's that are taller than the player + // to prevent LODs being drawn on top of the player + posToSkip.add(new ChunkPos(x, z)); + } + } + } + + return posToSkip; + } + + + /** + * This method returns the ChunkPos of all chunks that Minecraft + * is going to render this frame.

+ *

+ * Note: This isn't perfect. It will return some chunks that are outside + * the clipping plane. (For example, if you are high above the ground some chunks + * will be incorrectly added, even though they are outside render range). + */ public static HashSet getRenderedChunks() { HashSet loadedPos = new HashSet<>(); - + // Wow those are some long names! - + // go through every RenderInfo to get the compiled chunks WorldRenderer renderer = mc.getLevelRenderer(); for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : renderer.renderChunks) @@ -462,14 +482,42 @@ public class LodUtil { // add the ChunkPos for every rendered chunk BlockPos bpos = worldrenderer$localrenderinformationcontainer.chunk.getOrigin(); - + loadedPos.add(new ChunkPos(bpos)); } } - - + + return loadedPos; } - - + + /** + * + */ + 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 (Direction direction : Box.ADJ_DIRECTIONS) + { + tempX = x + direction.getNormal().getX(); + tempZ = z + direction.getNormal().getZ(); + if (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length)) + { + if (!vanillaRenderedChunks[tempX][tempZ]) + { + return true; + } + }else + { + if (vanillaRenderedChunks[x][z]) + { + return true; + } + } + } + return false; + } }