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); }