From eab16ff20a8159c58e60d75c87b911c1a1e2d0db Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 25 Mar 2021 23:04:48 -0500 Subject: [PATCH] Move all buffer building into the LodBufferBuilder and improve chunk generation logic Chunks generation requests should no longer stack exponentially (before whenever one chunk was generated the LODs would be regenerated, causing more chunks to generate so if more than one chunk was ever generated at a time they would stack). --- .../lod/builders/LodBufferBuilder.java | 381 ++++++++++++++++-- .../lod/builders/LodChunkGenWorker.java | 8 +- .../backsun/lod/objects/NearFarBuffer.java | 7 +- .../com/backsun/lod/renderer/LodRenderer.java | 350 +++------------- 4 files changed, 413 insertions(+), 333 deletions(-) diff --git a/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java b/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java index a45cf80ea..724a5cabb 100644 --- a/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java @@ -1,52 +1,287 @@ package com.backsun.lod.builders; import java.awt.Color; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.lwjgl.opengl.GL11; -import com.backsun.lod.enums.FogDistance; +import com.backsun.lod.enums.ColorDirection; +import com.backsun.lod.enums.LodCorner; +import com.backsun.lod.objects.LodChunk; import com.backsun.lod.objects.NearFarBuffer; import com.backsun.lod.renderer.LodRenderer; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.ChunkPos; +import net.minecraftforge.common.WorldWorkerManager; /** * This object is used to create NearFarBuffer objects. * * @author James Seibel - * @version 02-27-2021 + * @version 03-25-2021 */ public class LodBufferBuilder { - public BufferBuilder nearBuffer; - public BufferBuilder farBuffer; - public FogDistance distanceMode; - public AxisAlignedBB[][] lods; - public Color[][] colors; + private Minecraft mc; + + /** This holds the thread used to generate new LODs off the main thread. */ + private ExecutorService genThread = Executors.newSingleThreadExecutor(); + + private LodBuilder lodBuilder; + + /** The buffers that are used to create LODs using near fog */ + public volatile BufferBuilder buildableNearBuffer; + /** The buffers that are used to create LODs using far fog */ + public volatile BufferBuilder buildableFarBuffer; + + /** if this is true the LOD buffers are currently being + * regenerated. */ + public volatile boolean generatingBuffers = false; + + /** if this is true new LOD buffers have been generated + * and are waiting to be swapped with the drawable buffers*/ + private volatile boolean switchBuffers = false; + + /** If this is greater than 0 no new chunk generation requests will be made + * this is to prevent chunks from being generated for a long time in an area + * the player is no longer in. */ + public int numChunksWaitingToGen = 0; + + /** how many chunks to generate outside of the player's + * view distance at one time. (or more specifically how + * many requests to make at one time) */ + public int maxChunkGenRequests = 8; - - public LodBufferBuilder() + public LodBufferBuilder(LodBuilder newLodBuilder) { - + mc = Minecraft.getInstance(); + lodBuilder = newLodBuilder; } - public NearFarBuffer createBuffers( - BufferBuilder newNearBufferBuilder, BufferBuilder newFarBufferBuilder, - FogDistance newDistanceMode, - AxisAlignedBB[][] newLods, Color[][] newColors) + + + /** + * Create a thread to asynchronously generate LOD buffers + * centered around the given camera X and Z. + *
+ * This method will write to the drawableNearBuffers and drawableFarBuffers. + *
+ * After the buildable buffers have been generated they must be + * swapped with the drawable buffers in the LodRenderer to be drawn. + */ + public void generateLodBuffersAsync(LodRenderer renderer, + double playerX, double playerZ, int numbChunksWide) { - nearBuffer = newNearBufferBuilder; - farBuffer = newFarBufferBuilder; - distanceMode = newDistanceMode; - lods = newLods; - colors = newColors; + // only allow one generation process to happen at a time + if (generatingBuffers) + return; + + if (buildableNearBuffer == null || buildableFarBuffer == null) + throw new IllegalStateException("generateLodBuffersAsync was called before the buildableNearBuffer and buildableFarBuffer were created."); - nearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); - farBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); + + generatingBuffers = true; + + // this is where we store the points for each LOD object + AxisAlignedBB lodArray[][] = new AxisAlignedBB[numbChunksWide][numbChunksWide]; + // this is where we store the color for each LOD object + Color colorArray[][] = new Color[numbChunksWide][numbChunksWide]; + + int alpha = 255; // 0 - 255 + Color red = new Color(255, 0, 0, alpha); + Color black = new Color(0, 0, 0, alpha); + Color white = new Color(255, 255, 255, alpha); + + // this seemingly useless math is required, + // just using (int) playerX/Z doesn't work + int playerXChunkOffset = ((int) playerX / LodChunk.WIDTH) * LodChunk.WIDTH; + int playerZChunkOffset = ((int) playerZ / LodChunk.WIDTH) * LodChunk.WIDTH; + // this is where we will start drawing squares + // (exactly half the total width) + int startX = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerXChunkOffset; + int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset; + + + + Thread t = new Thread(()-> + { + // index of the chunk currently being added to the + // generation list + int chunkGenIndex = 0; + + ChunkPos[] chunksToGen = new ChunkPos[maxChunkGenRequests]; + int minChunkDist = Integer.MAX_VALUE; + ChunkPos playerChunkPos = new ChunkPos((int)playerX / LodChunk.WIDTH, (int)playerZ / LodChunk.WIDTH); + + + + // x axis + for (int i = 0; i < numbChunksWide; i++) + { + // z axis + for (int j = 0; j < numbChunksWide; j++) + { + // skip the middle + // (As the player moves some chunks will overlap or be missing, + // this is just how chunk loading/unloading works. This can hopefully + // be hidden with careful use of fog) + int middle = (numbChunksWide / 2); + if (isCoordInCenterArea(i, j, middle)) + { + continue; + } + + + // set where this square will be drawn in the world + double xOffset = (LodChunk.WIDTH * i) + // offset by the number of LOD blocks + startX; // offset so the center LOD block is centered underneath the player + double yOffset = 0; + double zOffset = (LodChunk.WIDTH * j) + startZ; + + int chunkX = i + (startX / LodChunk.WIDTH); + int chunkZ = j + (startZ / LodChunk.WIDTH); + + LodChunk lod = renderer.lodDimension.getLodFromCoordinates(chunkX, chunkZ); + if (lod == null || lod.isLodEmpty()) + { + // note: for some reason if any color or lod objects are set here + // it causes the game to use 100% gpu; + // undefined in the debug menu + // and drop to ~6 fps. + colorArray[i][j] = null; + lodArray[i][j] = null; + + + // only generate a new chunk if no chunk currently exists + // and we aren't waiting on any other chunks to generate + if (lod == null && numChunksWaitingToGen == 0) + { + ChunkPos pos = new ChunkPos(chunkX, chunkZ); + + // determine if this position is closer to the player + // than the previous + int newDistance = playerChunkPos.getChessboardDistance(pos); + + if (newDistance < minChunkDist) + { + // this chunk is closer, clear any previous + // positions and update the new minimum distance + minChunkDist = newDistance; + + chunkGenIndex = 0; + chunksToGen = new ChunkPos[maxChunkGenRequests]; + chunksToGen[chunkGenIndex] = pos; + chunkGenIndex++; + } + else if (newDistance <= minChunkDist) + { + // this chunk position is as close or closers than the + // minimum distance + if(chunkGenIndex < maxChunkGenRequests) + { + // we are still under the number of chunks to generate + // add this pos to the list + chunksToGen[chunkGenIndex] = pos; + chunkGenIndex++; + } + } + } + + continue; + } + + + Color c = new Color( + (lod.colors[ColorDirection.TOP.value].getRed()), + (lod.colors[ColorDirection.TOP.value].getGreen()), + (lod.colors[ColorDirection.TOP.value].getBlue()), + lod.colors[ColorDirection.TOP.value].getAlpha()); + + if (!renderer.debugging) + { + // add the color to the array + colorArray[i][j] = c; + } + else + { + // if debugging draw the squares as a black and white checker board + if ((chunkX + chunkZ) % 2 == 0) + c = white; + else + c = black; + // draw the first square as red + if (i == 0 && j == 0) + c = red; + + colorArray[i][j] = c; + } + + + // add the new box to the array + int topPoint = getValidHeightPoint(lod.top); + int bottomPoint = getValidHeightPoint(lod.bottom); + + // don't draw an LOD if it is empty + if (topPoint == -1 && bottomPoint == -1) + continue; + + lodArray[i][j] = new AxisAlignedBB(0, bottomPoint, 0, LodChunk.WIDTH, topPoint, LodChunk.WIDTH).offset(xOffset, yOffset, zOffset); + } + } + + // start chunk generation + for(ChunkPos chunkPos : chunksToGen) + { + if(chunkPos == null) + break; + + // add a placeholder chunk to prevent this chunk from + // being generated again + LodChunk placeholder = new LodChunk(); + placeholder.x = chunkPos.x; + placeholder.z = chunkPos.z; + renderer.lodDimension.addLod(placeholder); + + numChunksWaitingToGen++; + + LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, renderer, lodBuilder, this, renderer.lodDimension); + WorldWorkerManager.addWorker(genWorker); + } + + + + + // generate our new buildable buffers + buildBuffersFromAABB(lodArray, colorArray); + + // mark that the buildable buffers as ready to swap + generatingBuffers = false; + switchBuffers = true; + }); + + genThread.execute(t); + + return; + } + + + /** + * Build the buildable near and far buffers. + * + * @param lods + * @param colors + */ + private void buildBuffersFromAABB(AxisAlignedBB[][] lods, Color[][] colors) + { + buildableNearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); + buildableFarBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); int numbChunksWide = lods.length; @@ -77,9 +312,9 @@ public class LodBufferBuilder if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2)) - currentBuffer = nearBuffer; + currentBuffer = buildableNearBuffer; else - currentBuffer = farBuffer; + currentBuffer = buildableFarBuffer; if (bb.minY != bb.maxY) @@ -158,10 +393,8 @@ public class LodBufferBuilder } // z axis } // x axis - nearBuffer.finishDrawing(); - farBuffer.finishDrawing(); - - return new NearFarBuffer(nearBuffer, farBuffer); + buildableNearBuffer.finishDrawing(); + buildableFarBuffer.finishDrawing(); } private void addPosAndColor(BufferBuilder buffer, double x, double y, double z, int red, int green, int blue, int alpha) @@ -171,6 +404,42 @@ public class LodBufferBuilder + + + + //====================// + // generation helpers // + //====================// + + /** + * Returns if the given coordinate is in the loaded area of the world. + * @param centerCoordinate the center of the loaded world + */ + private boolean isCoordInCenterArea(int i, int j, int centerCoordinate) + { + return (i >= centerCoordinate - mc.gameSettings.renderDistanceChunks + && i <= centerCoordinate + mc.gameSettings.renderDistanceChunks) + && + (j >= centerCoordinate - mc.gameSettings.renderDistanceChunks + && j <= centerCoordinate + mc.gameSettings.renderDistanceChunks); + } + + + /** + * @Returns -1 if there are no valid points + */ + private int getValidHeightPoint(short[] heightPoints) + { + if (heightPoints[LodCorner.NE.value] != -1) + return heightPoints[LodCorner.NE.value]; + if (heightPoints[LodCorner.NW.value] != -1) + return heightPoints[LodCorner.NW.value]; + if (heightPoints[LodCorner.SE.value] != -1) + return heightPoints[LodCorner.NE.value]; + return heightPoints[LodCorner.NE.value]; + } + + /** * Find the coordinates that are in the center half of the given * 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius). @@ -186,4 +455,62 @@ public class LodBufferBuilder && chunkZ <= lodRadius + halfRadius); } + + + + + //===============================// + // BufferBuilder related methods // + //===============================// + + + /** + * Called from the LodRenderer to create the + * BufferBuilders at the right size. + * + * @param bufferMaxCapacity + */ + public void setupBuffers(int bufferMaxCapacity) + { + buildableNearBuffer = new BufferBuilder(bufferMaxCapacity); + buildableFarBuffer = new BufferBuilder(bufferMaxCapacity); + } + + /** + * Swap the drawable and buildable buffers and return + * the old drawable buffers. + * @param drawableNearBuffer + * @param drawableFarBuffer + */ + public NearFarBuffer swapBuffers(BufferBuilder drawableNearBuffer, BufferBuilder drawableFarBuffer) + { + // swap the BufferBuilders + BufferBuilder tmp = buildableNearBuffer; + buildableNearBuffer = drawableNearBuffer; + drawableNearBuffer = tmp; + + tmp = buildableFarBuffer; + buildableFarBuffer = drawableFarBuffer; + drawableFarBuffer = tmp; + + + // the buffers have been swapped + switchBuffers = false; + + return new NearFarBuffer(drawableNearBuffer, drawableFarBuffer); + } + + /** + * If this is true the buildable near and far + * buffers have been generated and are ready to be + * sent to the LodRenderer. + */ + public boolean newBuffersAvaliable() + { + return switchBuffers; + } + + + + } \ No newline at end of file diff --git a/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java b/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java index c21017df1..ffc20cdeb 100644 --- a/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java +++ b/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java @@ -34,7 +34,7 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker; * This is used to generate a LodChunk at a given ChunkPos. * * @author James Seibel - * @version 03-24-2021 + * @version 03-25-2021 */ public class LodChunkGenWorker implements IWorker { @@ -42,14 +42,16 @@ public class LodChunkGenWorker implements IWorker private ChunkPos pos; private LodDimension lodDim; private LodBuilder lodBuilder; + private LodBufferBuilder lodBufferBuilder; private LodRenderer lodRenderer; - public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer, LodBuilder newLodBuilder, LodDimension newLodDimension) + public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer, LodBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder, LodDimension newLodDimension) { serverWorld = LodUtils.getServerWorldFromDimension(newLodDimension.dimension); pos = newPos; lodDim = newLodDimension; lodBuilder = newLodBuilder; + lodBufferBuilder = newLodBufferBuilder; lodRenderer = newLodRenderer; } @@ -91,6 +93,8 @@ public class LodChunkGenWorker implements IWorker // System.out.println("Out of range " + x + " " + z); //} + lodBufferBuilder.numChunksWaitingToGen--; + pos = null; } diff --git a/src/main/java/com/backsun/lod/objects/NearFarBuffer.java b/src/main/java/com/backsun/lod/objects/NearFarBuffer.java index 060a53bb5..ab1773ad3 100644 --- a/src/main/java/com/backsun/lod/objects/NearFarBuffer.java +++ b/src/main/java/com/backsun/lod/objects/NearFarBuffer.java @@ -8,7 +8,7 @@ import net.minecraft.client.renderer.BufferBuilder; * and BuildBufferThread. * * @author James Seibel - * @version 02-21-2021 + * @version 03-25-2021 */ public class NearFarBuffer { @@ -16,7 +16,10 @@ public class NearFarBuffer public BufferBuilder farBuffer; - + /** + * @param newNearBuffer + * @param newFarBuffer + */ public NearFarBuffer(BufferBuilder newNearBuffer, BufferBuilder newFarBuffer) { nearBuffer = newNearBuffer; diff --git a/src/main/java/com/backsun/lod/renderer/LodRenderer.java b/src/main/java/com/backsun/lod/renderer/LodRenderer.java index 4395f6e2c..49f72970b 100644 --- a/src/main/java/com/backsun/lod/renderer/LodRenderer.java +++ b/src/main/java/com/backsun/lod/renderer/LodRenderer.java @@ -1,21 +1,15 @@ package com.backsun.lod.renderer; -import java.awt.Color; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import org.lwjgl.opengl.GL11; import com.backsun.lod.builders.LodBufferBuilder; import com.backsun.lod.builders.LodBuilder; -import com.backsun.lod.builders.LodChunkGenWorker; -import com.backsun.lod.enums.ColorDirection; import com.backsun.lod.enums.FogDistance; import com.backsun.lod.enums.FogQuality; -import com.backsun.lod.enums.LodCorner; import com.backsun.lod.handlers.ReflectionHandler; import com.backsun.lod.objects.LodChunk; import com.backsun.lod.objects.LodDimension; @@ -37,13 +31,10 @@ import net.minecraft.client.renderer.vertex.VertexBuffer; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.potion.Effects; import net.minecraft.profiler.IProfiler; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3f; -import net.minecraftforge.common.WorldWorkerManager; /** @@ -51,7 +42,7 @@ import net.minecraftforge.common.WorldWorkerManager; * This is where LODs are draw to the world. * * @author James Seibel - * @version 03-19-2021 + * @version 03-25-2021 */ public class LodRenderer { @@ -72,31 +63,23 @@ public class LodRenderer private ReflectionHandler reflectionHandler; - public LodDimension lodDimension = null; + public LodDimension lodDimension; /** This is used to generate the buildable buffers */ - private LodBufferBuilder lodBufferBuilder = null; + private LodBufferBuilder lodBufferBuilder; /** The buffers that are used to draw LODs using near fog */ - private volatile BufferBuilder drawableNearBuffer = null; + private volatile BufferBuilder drawableNearBuffer; /** The buffers that are used to draw LODs using far fog */ - private volatile BufferBuilder drawableFarBuffer = null; - - /** The buffers that are used to create LODs using near fog */ - private volatile BufferBuilder buildableNearBuffer = null; - /** The buffers that are used to create LODs using far fog */ - private volatile BufferBuilder buildableFarBuffer = null; + private volatile BufferBuilder drawableFarBuffer; /** This is the VertexBuffer used to draw any LODs that use near fog */ - private volatile VertexBuffer nearVbo = null; + private volatile VertexBuffer nearVbo; /** This is the VertexBuffer used to draw any LODs that use far fog */ - private volatile VertexBuffer farVbo = null; + private volatile VertexBuffer farVbo; public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR; - /** This holds the thread used to generate new LODs off the main thread. */ - private ExecutorService genThread = Executors.newSingleThreadExecutor(); - /** This is used to determine if the LODs should be regenerated */ private int previousChunkRenderDistance = 0; /** This is used to determine if the LODs should be regenerated */ @@ -109,14 +92,7 @@ public class LodRenderer /** if this is true the LOD buffers should be regenerated, * provided they aren't already being regenerated. */ private boolean regen = false; - /** if this is true the LOD buffers are currently being - * regenerated. */ - private volatile boolean regenerating = false; - /** if this is true new LOD buffers have been generated - * and are waiting to be swapped with the drawable buffers*/ - private volatile boolean switchBuffers = false; - private LodBuilder lodBuilder; @@ -127,7 +103,7 @@ public class LodRenderer gameRender = mc.gameRenderer; reflectionHandler = new ReflectionHandler(); - lodBuilder = newLodBuilder; + lodBufferBuilder = new LodBufferBuilder(newLodBuilder); } @@ -230,32 +206,28 @@ public class LodRenderer // 2. we aren't already regenerating the LODs // 3. we aren't waiting for the build and draw buffers to swap // (this is to prevent thread conflicts) - if (regen && !regenerating && !switchBuffers) + if (regen && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvaliable()) { - regenerating = true; - - if (lodBufferBuilder == null) - lodBufferBuilder = new LodBufferBuilder(); - // this will mainly happen when the view distance is changed if (drawableNearBuffer == null || drawableFarBuffer == null || previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks) setupBuffers(numbChunksWide); // generate the LODs on a separate thread to prevent stuttering or freezing - generateLodBuffersAsync(player.getPosX(), player.getPosZ(), numbChunksWide); + lodBufferBuilder.generateLodBuffersAsync(this, player.getPosX(), player.getPosZ(), numbChunksWide); - // the regen process has been started + // the regen process has been started, + // it will be done when lodBufferBuilder.newBuffersAvaliable + // is true regen = false; } // replace the buffers used to draw and build, // this is only done when the createLodBufferGenerationThread // has finished executing on a parallel thread. - if (switchBuffers) + if (lodBufferBuilder.newBuffersAvaliable()) { swapBuffers(); - switchBuffers = false; } @@ -325,28 +297,6 @@ public class LodRenderer } - /** - * Create the model view matrix to move the LODs - * from object space into world space. - */ - private Matrix4f generateModelViewMatrix(float partialTicks) - { - // get all relevant camera info - ActiveRenderInfo renderInfo = mc.gameRenderer.getActiveRenderInfo(); - Vector3d projectedView = renderInfo.getProjectedView(); - - - // generate the model view matrix - MatrixStack matrixStack = new MatrixStack(); - matrixStack.push(); - // translate and rotate to the current camera location - matrixStack.rotate(Vector3f.XP.rotationDegrees(renderInfo.getPitch())); - matrixStack.rotate(Vector3f.YP.rotationDegrees(renderInfo.getYaw() + 180)); - matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z); - - return matrixStack.getLast().getMatrix(); - } - /** * This is where the actual drawing happens. @@ -440,6 +390,29 @@ public class LodRenderer } + /** + * Create the model view matrix to move the LODs + * from object space into world space. + */ + private Matrix4f generateModelViewMatrix(float partialTicks) + { + // get all relevant camera info + ActiveRenderInfo renderInfo = mc.gameRenderer.getActiveRenderInfo(); + Vector3d projectedView = renderInfo.getProjectedView(); + + + // generate the model view matrix + MatrixStack matrixStack = new MatrixStack(); + matrixStack.push(); + // translate and rotate to the current camera location + matrixStack.rotate(Vector3f.XP.rotationDegrees(renderInfo.getPitch())); + matrixStack.rotate(Vector3f.YP.rotationDegrees(renderInfo.getYaw() + 180)); + matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z); + + return matrixStack.getLast().getMatrix(); + } + + /** * create a new projection matrix and send it over to the GPU *

@@ -517,6 +490,7 @@ public class LodRenderer RenderSystem.enableLighting(); } + /** * Create all buffers that will be used. */ @@ -533,11 +507,11 @@ public class LodRenderer drawableNearBuffer = new BufferBuilder(bufferMaxCapacity); drawableFarBuffer = new BufferBuilder(bufferMaxCapacity); - buildableNearBuffer = new BufferBuilder(bufferMaxCapacity); - buildableFarBuffer = new BufferBuilder(bufferMaxCapacity); + lodBufferBuilder.setupBuffers(bufferMaxCapacity); } + @@ -556,232 +530,18 @@ public class LodRenderer regen = true; } - /** - * @Returns -1 if there are no valid points - */ - private int getValidHeightPoint(short[] heightPoints) - { - if (heightPoints[LodCorner.NE.value] != -1) - return heightPoints[LodCorner.NE.value]; - if (heightPoints[LodCorner.NW.value] != -1) - return heightPoints[LodCorner.NW.value]; - if (heightPoints[LodCorner.SE.value] != -1) - return heightPoints[LodCorner.NE.value]; - return heightPoints[LodCorner.NE.value]; - } - /** - * Create a thread to asynchronously generate LOD buffers - * centered around the given camera X and Z. - *
- * This thread will write to the drawableNearBuffers and drawableFarBuffers. - *
- * After the buildable buffers have been generated they must be - * swapped with the drawable buffers to be drawn. - */ - private void generateLodBuffersAsync(double playerX, double playerZ, - int numbChunksWide) - { - // this is where we store the points for each LOD object - AxisAlignedBB lodArray[][] = new AxisAlignedBB[numbChunksWide][numbChunksWide]; - // this is where we store the color for each LOD object - Color colorArray[][] = new Color[numbChunksWide][numbChunksWide]; - - int alpha = 255; // 0 - 255 - Color red = new Color(255, 0, 0, alpha); - Color black = new Color(0, 0, 0, alpha); - Color white = new Color(255, 255, 255, alpha); - - // this seemingly useless math is required, - // just using (int) playerX/Z doesn't work - int playerXChunkOffset = ((int) playerX / LodChunk.WIDTH) * LodChunk.WIDTH; - int playerZChunkOffset = ((int) playerZ / LodChunk.WIDTH) * LodChunk.WIDTH; - // this is where we will start drawing squares - // (exactly half the total width) - int startX = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerXChunkOffset; - int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset; - - Thread t = new Thread(()-> - { - // how many chunks to generate outside of the player's - // view distance - int maxNumbToGen = 8; - int chunkGenIndex = 0; - - ChunkPos[] chunksToGen = new ChunkPos[maxNumbToGen]; - int minChunkDist = Integer.MAX_VALUE; - ChunkPos playerChunkPos = new ChunkPos((int)playerX / LodChunk.WIDTH, (int)playerZ / LodChunk.WIDTH); - - - - // x axis - for (int i = 0; i < numbChunksWide; i++) - { - // z axis - for (int j = 0; j < numbChunksWide; j++) - { - // skip the middle - // (As the player moves some chunks will overlap or be missing, - // this is just how chunk loading/unloading works. This can hopefully - // be hidden with careful use of fog) - int middle = (numbChunksWide / 2); - if (isCoordInCenterArea(i, j, middle)) - { - continue; - } - - - // set where this square will be drawn in the world - double xOffset = (LodChunk.WIDTH * i) + // offset by the number of LOD blocks - startX; // offset so the center LOD block is centered underneath the player - double yOffset = 0; - double zOffset = (LodChunk.WIDTH * j) + startZ; - - int chunkX = i + (startX / LodChunk.WIDTH); - int chunkZ = j + (startZ / LodChunk.WIDTH); - - LodChunk lod = lodDimension.getLodFromCoordinates(chunkX, chunkZ); - if (lod == null || lod.isLodEmpty()) - { - // note: for some reason if any color or lod objects are set here - // it causes the game to use 100% gpu; - // undefined in the debug menu - // and drop to ~6 fps. - colorArray[i][j] = null; - lodArray[i][j] = null; - - - if (lod == null) - { - ChunkPos pos = new ChunkPos(chunkX, chunkZ); - - // determine if this position is closer to the player - // than the previous - int newDistance = playerChunkPos.getChessboardDistance(pos); - - if (newDistance < minChunkDist) - { - // this chunk is closer, clear any previous - // positions and update the new minimum distance - minChunkDist = newDistance; - - chunkGenIndex = 0; - chunksToGen = new ChunkPos[maxNumbToGen]; - chunksToGen[chunkGenIndex] = pos; - chunkGenIndex++; - } - else if (newDistance <= minChunkDist) - { - // this chunk position is as close or closers than the - // minimum distance - if(chunkGenIndex < maxNumbToGen) - { - // we are still under the number of chunks to generate - // add this pos to the list - chunksToGen[chunkGenIndex] = pos; - chunkGenIndex++; - } - } - } - - continue; - } - - - Color c = new Color( - (lod.colors[ColorDirection.TOP.value].getRed()), - (lod.colors[ColorDirection.TOP.value].getGreen()), - (lod.colors[ColorDirection.TOP.value].getBlue()), - lod.colors[ColorDirection.TOP.value].getAlpha()); - - if (!debugging) - { - // add the color to the array - colorArray[i][j] = c; - } - else - { - // if debugging draw the squares as a black and white checker board - if ((chunkX + chunkZ) % 2 == 0) - c = white; - else - c = black; - // draw the first square as red - if (i == 0 && j == 0) - c = red; - - colorArray[i][j] = c; - } - - - // add the new box to the array - int topPoint = getValidHeightPoint(lod.top); - int bottomPoint = getValidHeightPoint(lod.bottom); - - // don't draw an LOD if it is empty - if (topPoint == -1 && bottomPoint == -1) - continue; - - lodArray[i][j] = new AxisAlignedBB(0, bottomPoint, 0, LodChunk.WIDTH, topPoint, LodChunk.WIDTH).offset(xOffset, yOffset, zOffset); - } - } - - // start chunk generation - for(ChunkPos chunkPos : chunksToGen) - { - if(chunkPos == null) - break; - - // add a placeholder chunk to prevent this chunk from - // being generated again - LodChunk placeholder = new LodChunk(); - placeholder.x = chunkPos.x; - placeholder.z = chunkPos.z; - lodDimension.addLod(placeholder); - - LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, this, lodBuilder, lodDimension); - WorldWorkerManager.addWorker(genWorker); - } - - - - - - // generate our new buildable buffers - NearFarBuffer nearFarBuffers = lodBufferBuilder.createBuffers( - buildableNearBuffer, buildableFarBuffer, - LodConfig.CLIENT.fogDistance.get(), lodArray, colorArray); - - // update our buffers - buildableNearBuffer = nearFarBuffers.nearBuffer; - buildableFarBuffer = nearFarBuffers.farBuffer; - - // mark that the buildable buffers as ready to swap - regenerating = false; - switchBuffers = true; - }); - - genThread.execute(t); - - return; - } - - - - /** - * Swap buildable and drawable buffers. + * Replace the current drawable buffers with the newly + * created buffers from the lodBufferBuilder. */ private void swapBuffers() { - // swap the BufferBuilders - BufferBuilder tmp = buildableNearBuffer; - buildableNearBuffer = drawableNearBuffer; - drawableNearBuffer = tmp; - - tmp = buildableFarBuffer; - buildableFarBuffer = drawableFarBuffer; - drawableFarBuffer = tmp; + // replace the drawable buffers with + // the newly created buffers from the lodBufferBuilder + NearFarBuffer newBuffers = lodBufferBuilder.swapBuffers(drawableNearBuffer, drawableFarBuffer); + drawableNearBuffer = newBuffers.nearBuffer; + drawableFarBuffer = newBuffers.farBuffer; // bind the buffers with their respective VBOs @@ -800,21 +560,7 @@ public class LodRenderer } - /** - * Returns if the given coordinate is in the loaded area of the world. - * @param centerCoordinate the center of the loaded world - */ - private boolean isCoordInCenterArea(int i, int j, int centerCoordinate) - { - return (i >= centerCoordinate - mc.gameSettings.renderDistanceChunks - && i <= centerCoordinate + mc.gameSettings.renderDistanceChunks) - && - (j >= centerCoordinate - mc.gameSettings.renderDistanceChunks - && j <= centerCoordinate + mc.gameSettings.renderDistanceChunks); - } - - - public double getFov(float partialTicks, boolean useFovSetting) + private double getFov(float partialTicks, boolean useFovSetting) { return mc.gameRenderer.getFOVModifier(mc.gameRenderer.getActiveRenderInfo(), partialTicks, useFovSetting); }