diff --git a/src/main/java/com/backsun/lod/renderer/BuildBufferThread.java b/src/main/java/com/backsun/lod/renderer/BuildBufferThread.java index 8721a63f3..43739218c 100644 --- a/src/main/java/com/backsun/lod/renderer/BuildBufferThread.java +++ b/src/main/java/com/backsun/lod/renderer/BuildBufferThread.java @@ -1,83 +1,67 @@ package com.backsun.lod.renderer; import java.awt.Color; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.concurrent.Callable; +import org.lwjgl.opengl.GL11; + import com.backsun.lod.util.enums.FogDistance; -import net.minecraft.client.renderer.GLAllocation; +import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.MathHelper; /** * * * @author James Seibel - * @version 02-13-2021 + * @version 02-21-2021 */ public class BuildBufferThread implements Callable { - public ByteBuffer nearBuffer; - public ByteBuffer farBuffer; + public BufferBuilder nearBuffer; + public BufferBuilder farBuffer; public FogDistance distanceMode; public AxisAlignedBB[][] lods; public Color[][] colors; - private int start = 0; - private int end = -1; - - private int vertexCount = 0; - private VertexFormat vertexFormat = null; - private int vertexFormatIndex = 0; - private VertexFormatElement vertexFormatElement = null; + private int startLodIndex = 0; + private int endLodIndex = -1; BuildBufferThread() { - vertexCount = 0; - vertexFormat = DefaultVertexFormats.POSITION_COLOR; - vertexFormatIndex = 0; - vertexFormatElement = vertexFormat.getElement(vertexFormatIndex); + } - BuildBufferThread(ByteBuffer newNearByteBuffer, ByteBuffer newFarByteBuffer, AxisAlignedBB[][] newLods, Color[][] newColors, FogDistance newDistanceMode, int threadNumber, int totalThreads) + BuildBufferThread(BufferBuilder newNearBufferBuilder, BufferBuilder newFarBufferBuilder, AxisAlignedBB[][] newLods, Color[][] newColors, FogDistance newDistanceMode, int threadNumber, int totalThreads) { - setNewData(newNearByteBuffer, newFarByteBuffer, distanceMode, newLods, newColors, threadNumber, totalThreads); - - vertexCount = 0; - vertexFormat = DefaultVertexFormats.POSITION_COLOR; - vertexFormatIndex = 0; - vertexFormatElement = vertexFormat.getElement(vertexFormatIndex); + setNewData(newNearBufferBuilder, newFarBufferBuilder, distanceMode, newLods, newColors, threadNumber, totalThreads); } - public void setNewData(ByteBuffer newNearByteBuffer, ByteBuffer newFarByteBuffer, FogDistance newDistanceMode, AxisAlignedBB[][] newLods, Color[][] newColors, int threadNumber, int totalThreads) + public void setNewData(BufferBuilder newNearBufferBuilder, BufferBuilder newFarBufferBuilder, FogDistance newDistanceMode, AxisAlignedBB[][] newLods, Color[][] newColors, int threadNumber, int totalThreads) { - vertexCount = 0; - vertexFormatIndex = 0; - - nearBuffer = newNearByteBuffer; - farBuffer = newFarByteBuffer; + nearBuffer = newNearBufferBuilder; + farBuffer = newFarBufferBuilder; distanceMode = newDistanceMode; lods = newLods; colors = newColors; int numbChunksWide = lods.length; int rowsToRender = numbChunksWide / totalThreads; - start = threadNumber * rowsToRender; - end = (threadNumber + 1) * rowsToRender; + startLodIndex = threadNumber * rowsToRender; + endLodIndex = (threadNumber + 1) * rowsToRender; } @Override public NearFarBuffer call() { + nearBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR); + farBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR); + int numbChunksWide = lods.length; - ByteBuffer currentBuffer; + BufferBuilder currentBuffer; AxisAlignedBB bb; int red; int green; @@ -95,7 +79,7 @@ public class BuildBufferThread implements Callable // x axis - for (int i = start; i < end; i++) + for (int i = startLodIndex; i < endLodIndex; i++) { // z axis for (int j = 0; j < numbChunksWide; j++) @@ -197,137 +181,16 @@ public class BuildBufferThread implements Callable } // z axis } // x axis + nearBuffer.finishDrawing(); + farBuffer.finishDrawing(); + return new NearFarBuffer(nearBuffer, farBuffer); } - private void addPosAndColor(ByteBuffer buffer, double x, double y, double z, int red, int green, int blue, int alpha) + private void addPosAndColor(BufferBuilder buffer, double x, double y, double z, int red, int green, int blue, int alpha) { - addPos(buffer, x, y, z); - addColor(buffer, red, green, blue, alpha); - endVertex(); + buffer.pos(x, y, z).color(red, green, blue, alpha).endVertex(); } - private void addPos(ByteBuffer byteBuffer, double x, double y, double z) - { - int i = this.vertexCount * this.vertexFormat.getNextOffset() + this.vertexFormat.getOffset(this.vertexFormatIndex); - - switch (this.vertexFormatElement.getType()) - { - case FLOAT: // This is the one currently used - byteBuffer.putFloat(i, (float)(x)); - byteBuffer.putFloat(i + 4, (float)(y)); - byteBuffer.putFloat(i + 8, (float)(z)); - break; - case UINT: - case INT: - byteBuffer.putInt(i, Float.floatToRawIntBits((float)(x))); - byteBuffer.putInt(i + 4, Float.floatToRawIntBits((float)(y))); - byteBuffer.putInt(i + 8, Float.floatToRawIntBits((float)(z))); - break; - case USHORT: - case SHORT: - byteBuffer.putShort(i, (short)((int)(x))); - byteBuffer.putShort(i + 2, (short)((int)(y))); - byteBuffer.putShort(i + 4, (short)((int)(z))); - break; - case UBYTE: - case BYTE: - byteBuffer.put(i, (byte)((int)(x))); - byteBuffer.put(i + 1, (byte)((int)(y))); - byteBuffer.put(i + 2, (byte)((int)(z))); - } - - nextVertexFormatIndex(); - } - - private void addColor(ByteBuffer byteBuffer, int red, int green, int blue, int alpha) - { - int i = this.vertexCount * this.vertexFormat.getNextOffset() + this.vertexFormat.getOffset(this.vertexFormatIndex); - - switch (this.vertexFormatElement.getType()) - { - case FLOAT: - byteBuffer.putFloat(i, red / 255.0F); - byteBuffer.putFloat(i + 4, green / 255.0F); - byteBuffer.putFloat(i + 8, blue / 255.0F); - byteBuffer.putFloat(i + 12, alpha / 255.0F); - break; - case UINT: - case INT: - byteBuffer.putFloat(i, red); - byteBuffer.putFloat(i + 4, green); - byteBuffer.putFloat(i + 8, blue); - byteBuffer.putFloat(i + 12, alpha); - break; - case USHORT: - case SHORT: - byteBuffer.putShort(i, (short)red); - byteBuffer.putShort(i + 2, (short)green); - byteBuffer.putShort(i + 4, (short)blue); - byteBuffer.putShort(i + 6, (short)alpha); - break; - case UBYTE: - case BYTE: - - if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) - { - // this is the one used currently - byteBuffer.put(i, (byte)red); - byteBuffer.put(i + 1, (byte)green); - byteBuffer.put(i + 2, (byte)blue); - byteBuffer.put(i + 3, (byte)alpha); - } - else - { - byteBuffer.put(i, (byte)alpha); - byteBuffer.put(i + 1, (byte)blue); - byteBuffer.put(i + 2, (byte)green); - byteBuffer.put(i + 3, (byte)red); - } - } - - nextVertexFormatIndex(); - } - - private void nextVertexFormatIndex() - { - ++this.vertexFormatIndex; - this.vertexFormatIndex %= this.vertexFormat.getElementCount(); - this.vertexFormatElement = this.vertexFormat.getElement(this.vertexFormatIndex); - - if (this.vertexFormatElement.getUsage() == VertexFormatElement.EnumUsage.PADDING) - { - this.nextVertexFormatIndex(); - } - } - - private void endVertex() - { - ++this.vertexCount; - growBuffer(this.vertexFormat.getNextOffset()); - } - - private void growBuffer(int p_181670_1_) - { - //if (MathHelper.roundUp(p_181670_1_, 4) / 4 > this.rawIntBuffer.remaining() || this.vertexCount * this.vertexFormat.getNextOffset() + p_181670_1_ > this.byteBuffer.capacity()) - if (this.vertexCount * this.vertexFormat.getNextOffset() + p_181670_1_ > nearBuffer.capacity()) - { - int i = nearBuffer.capacity(); - int j = i + MathHelper.roundUp(p_181670_1_, 2097152); -// int k = this.rawIntBuffer.position(); - ByteBuffer directBytebuffer = GLAllocation.createDirectByteBuffer(j); - nearBuffer.position(0); - directBytebuffer.put(nearBuffer); - directBytebuffer.rewind(); - nearBuffer = directBytebuffer; -// this.rawFloatBuffer = buffer.asFloatBuffer().asReadOnlyBuffer(); -// this.rawIntBuffer = buffer.asIntBuffer(); -// this.rawIntBuffer.position(k); -// this.rawShortBuffer = buffer.asShortBuffer(); -// this.rawShortBuffer.position(k << 1); - } - } - - } \ No newline at end of file diff --git a/src/main/java/com/backsun/lod/renderer/LodRenderer.java b/src/main/java/com/backsun/lod/renderer/LodRenderer.java index 3ad26b079..8db78ef54 100644 --- a/src/main/java/com/backsun/lod/renderer/LodRenderer.java +++ b/src/main/java/com/backsun/lod/renderer/LodRenderer.java @@ -35,7 +35,7 @@ import net.minecraft.util.math.MathHelper; /** * @author James Seibel - * @version 2-13-2021 + * @version 2-21-2021 */ public class LodRenderer { @@ -68,15 +68,14 @@ public class LodRenderer /** How many threads should be used for building the render buffer. */ private int numbBufferThreads = maxNumbThreads; private ArrayList bufferThreads = new ArrayList(); - private volatile ByteBuffer[] nearBuffers = new ByteBuffer[maxNumbThreads]; - private volatile ByteBuffer[] farBuffers = new ByteBuffer[maxNumbThreads]; + private volatile BufferBuilder[] drawableNearBuffers = null; + private volatile BufferBuilder[] drawableFarBuffers = null; + + private volatile BufferBuilder[] buildableNearBuffers = null; + private volatile BufferBuilder[] buildableFarBuffers = null; + private ExecutorService bufferThreadPool = Executors.newFixedThreadPool(maxNumbThreads); - /* - * this is the maximum number of bytes a buffer - * would ever have to hold at once (this prevents the buffer - * from having to resize and thus save performance) - */ - private int bufferMaxCapacity = 0; + private ExecutorService genThread = Executors.newSingleThreadExecutor(); /** This is used to determine if the LODs should be regenerated */ private int previousChunkRenderDistance = 0; @@ -91,6 +90,7 @@ public class LodRenderer private boolean regen = false; private volatile boolean regenerating = false; + private volatile boolean switchBuffers = false; @@ -105,7 +105,6 @@ public class LodRenderer reflectionHandler = new ReflectionHandler(); } - private ExecutorService genThread = Executors.newSingleThreadExecutor(); public void drawLODs(LodDimension newDimension, float partialTicks) @@ -124,7 +123,16 @@ public class LodRenderer return; } - // should the LODs be regenerated? + if (lodDimension == null && newDimension == null) + { + // if there aren't any loaded LodChunks + // don't try drawing anything + return; + } + + + + // should LODs be regenerated? if ((int)Minecraft.getMinecraft().player.posX / LodChunk.WIDTH != prevChunkX || (int)Minecraft.getMinecraft().player.posZ / LodChunk.WIDTH != prevChunkZ || previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks || @@ -146,12 +154,6 @@ public class LodRenderer } lodDimension = newDimension; - if (lodDimension == null) - { - // if there aren't any loaded LodChunks - // don't try drawing anything - return; - } @@ -162,8 +164,6 @@ public class LodRenderer mc.mcProfiler.endSection(); mc.mcProfiler.startSection("LOD"); mc.mcProfiler.startSection("LOD setup"); - @SuppressWarnings("unused") - long startTime = System.nanoTime(); if (LodConfig.drawCheckerBoard) { if (debugging != LodConfig.drawCheckerBoard) @@ -178,18 +178,6 @@ public class LodRenderer } - // color setup - 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); - @SuppressWarnings("unused") - Color invisible = new Color(0,0,0,0); - @SuppressWarnings("unused") - Color error = new Color(255, 0, 225, alpha); // bright pink - - // get the camera location Entity player = mc.player; @@ -208,15 +196,6 @@ public class LodRenderer int totalLength = (int) farPlaneDistance * LOD_CHUNK_DISTANCE_RADIUS * 2; int numbChunksWide = (totalLength / LodChunk.WIDTH); - // this seemingly useless math is required, - // just using (int) camera doesn't work - int playerXChunkOffset = ((int) cameraX / LodChunk.WIDTH) * LodChunk.WIDTH; - int playerZChunkOffset = ((int) cameraZ / LodChunk.WIDTH) * LodChunk.WIDTH; - // this 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; - @@ -226,7 +205,11 @@ public class LodRenderer // create the LODs // //=================// - if (regen && !regenerating) + // only regenerate LODs if: + // 1. we want to regenerate LODs + // 2. we aren't already regenerating LODs + // 3. we aren't waiting for the build and draw buffers to swap + if (regen && !regenerating && !switchBuffers) { mc.mcProfiler.endStartSection("LOD generation"); regenerating = true; @@ -236,95 +219,26 @@ public class LodRenderer // this is where we store the color for each LOD object Color colorArray[][] = new Color[numbChunksWide][numbChunksWide]; - setupBufferThreads(lodArray); + if (numbBufferThreads != bufferThreads.size()) + setupBufferThreads(); - Thread t = new Thread(()-> - { - // 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 (RenderUtil.isCoordinateInLoadedArea(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) - { - // note: for some reason if any color or lod object are set here - // it causes the game to use 100% gpu, all of it undefined in the debug menu - // and drop to ~6 fps. - colorArray[i][j] = null; - lodArray[i][j] = null; - - 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 = getLodHeightPoint(lod.top); - int bottomPoint = getLodHeightPoint(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); - } - } - - generateLodBuffers(lodArray, colorArray, LodConfig.fogDistance); - regenerating = false; - }); -// t.run(); - genThread.execute(t); + if (drawableNearBuffers == null || drawableFarBuffers == null || + previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks) + setupBuffers(lodArray); + + + genThread.execute(createLodGenerationThread(cameraX, cameraZ, lodArray, colorArray)); } + // replace the buffers used to draw and build, + // this is done to keep everything thread safe + if (switchBuffers) + { + swapBuffers(); + switchBuffers = false; + } + + @@ -344,7 +258,6 @@ public class LodRenderer setProjectionMatrix(partialTicks); setupLighting(partialTicks); -// setupBufferThreads(lodArray); @@ -355,30 +268,26 @@ public class LodRenderer // rendering // //===========// -// mc.mcProfiler.endStartSection("LOD build buffer"); -// if (regen) -// generateLodBuffers(lodArray, colorArray, LodConfig.fogDistance); - switch(LodConfig.fogDistance) { case NEAR_AND_FAR: mc.mcProfiler.endStartSection("LOD draw setup"); setupFog(FogDistance.NEAR, reflectionHandler.getFogQuality()); - sendLodsToGpuAndDraw(nearBuffers); + sendLodsToGpuAndDraw(drawableNearBuffers); mc.mcProfiler.endStartSection("LOD draw setup"); setupFog(FogDistance.FAR, reflectionHandler.getFogQuality()); - sendLodsToGpuAndDraw(farBuffers); + sendLodsToGpuAndDraw(drawableFarBuffers); break; case NEAR: mc.mcProfiler.endStartSection("LOD draw setup"); setupFog(FogDistance.NEAR, reflectionHandler.getFogQuality()); - sendLodsToGpuAndDraw(nearBuffers); + sendLodsToGpuAndDraw(drawableNearBuffers); break; case FAR: mc.mcProfiler.endStartSection("LOD draw setup"); setupFog(FogDistance.FAR, reflectionHandler.getFogQuality()); - sendLodsToGpuAndDraw(farBuffers); + sendLodsToGpuAndDraw(drawableFarBuffers); break; } @@ -399,7 +308,7 @@ public class LodRenderer GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); GL11.glEnable(GL11.GL_TEXTURE_2D); - GL11.glDisable(GL11.GL_LIGHT1); + GL11.glDisable(GL11.GL_LIGHT2); GL11.glDisable(GL11.GL_COLOR_MATERIAL); // change the perspective matrix back to prevent incompatibilities @@ -411,18 +320,27 @@ public class LodRenderer previousChunkRenderDistance = mc.gameSettings.renderDistanceChunks; - - // This is about how long this whole process should take - // 16 ms = 60 hz - @SuppressWarnings("unused") - long endTime = System.nanoTime(); - // end of profiler tracking mc.mcProfiler.endSection(); } + + + + + + + + + + + + + + + @@ -437,42 +355,25 @@ public class LodRenderer private void generateLodBuffers(AxisAlignedBB[][] lods, Color[][] colors, FogDistance fogDistance) { List> bufferFutures = new ArrayList<>(); - // TODO this should change based on whether we are using near/far or both fog settings - bufferMaxCapacity = (lods.length * lods.length * (6 * 4 * ((3 * 4) + (4 * 4)))) / numbBufferThreads; for(int i = 0; i < numbBufferThreads; i++) { - if (nearBuffers[i] == null || previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks) - { - nearBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity); - nearBuffers[i].order(ByteOrder.LITTLE_ENDIAN); - - farBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity); - farBuffers[i].order(ByteOrder.LITTLE_ENDIAN); - - clearBytes = new byte[bufferMaxCapacity]; - } - if (regen) { // this is the best way I could find to // overwrite the old data // (which needs to be done otherwise old // LODs may be drawn) - nearBuffers[i].clear(); - nearBuffers[i].put(clearBytes); - nearBuffers[i].clear(); + buildableNearBuffers[i].getByteBuffer().clear(); + buildableNearBuffers[i].getByteBuffer().put(clearBytes); + buildableNearBuffers[i].getByteBuffer().clear(); - farBuffers[i].clear(); - farBuffers[i].put(clearBytes); - farBuffers[i].clear(); + buildableFarBuffers[i].getByteBuffer().clear(); + buildableFarBuffers[i].getByteBuffer().put(clearBytes); + buildableFarBuffers[i].getByteBuffer().clear(); } - - int pos = bufferBuilder.getByteBuffer().position(); - nearBuffers[i].position(pos); - farBuffers[i].position(pos); - - bufferThreads.get(i).setNewData(nearBuffers[i], farBuffers[i], fogDistance, lods, colors, i, numbBufferThreads); + + bufferThreads.get(i).setNewData(buildableNearBuffers[i], buildableFarBuffers[i], fogDistance, lods, colors, i, numbBufferThreads); } try @@ -489,8 +390,8 @@ public class LodRenderer { try { - nearBuffers[i] = bufferFutures.get(i).get().nearBuffer; - farBuffers[i] = bufferFutures.get(i).get().farBuffer; + buildableNearBuffers[i] = bufferFutures.get(i).get().nearBuffer; + buildableFarBuffers[i] = bufferFutures.get(i).get().farBuffer; } catch(CancellationException | ExecutionException| InterruptedException e) { @@ -501,16 +402,19 @@ public class LodRenderer } - private void sendLodsToGpuAndDraw(ByteBuffer[] buffers) + private void sendLodsToGpuAndDraw(BufferBuilder[] buffers) { for(int i = 0; i < numbBufferThreads; i++) { + if (buffers[i] == null || buffers[i].getByteBuffer() == null) + continue; + int pos = bufferBuilder.getByteBuffer().position(); - buffers[i].position(pos); + buffers[i].getByteBuffer().position(pos); bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR); bufferBuilder.getByteBuffer().clear(); - bufferBuilder.putBulkData(buffers[i]); + bufferBuilder.putBulkData(buffers[i].getByteBuffer()); mc.mcProfiler.endStartSection("LOD draw"); tessellator.draw(); @@ -625,40 +529,55 @@ public class LodRenderer ByteBuffer temp = ByteBuffer.allocateDirect(16); temp.order(ByteOrder.nativeOrder()); - GL11.glLight(GL11.GL_LIGHT1, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip()); - GL11.glEnable(GL11.GL_LIGHT1); // Enable the above lighting + GL11.glLight(GL11.GL_LIGHT2, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip()); + GL11.glEnable(GL11.GL_LIGHT2); // Enable the above lighting GlStateManager.enableLighting(); } - private void setupBufferThreads(AxisAlignedBB[][] lods) + private void setupBufferThreads() { - if (numbBufferThreads != bufferThreads.size()) + bufferThreads.clear(); + for(int i = 0; i < numbBufferThreads; i++) + bufferThreads.add(new BuildBufferThread()); + } + + private void setupBuffers(AxisAlignedBB[][] lods) + { + drawableNearBuffers = new BufferBuilder[numbBufferThreads]; + drawableFarBuffers = new BufferBuilder[numbBufferThreads]; + + buildableNearBuffers = new BufferBuilder[numbBufferThreads]; + buildableFarBuffers = new BufferBuilder[numbBufferThreads]; + + + // TODO this should change based on whether we are using near/far or both fog settings + int bufferMaxCapacity = (lods.length * lods.length * (6 * 4 * ((3 * 4) + (4 * 4)))) / numbBufferThreads; + + for(int i = 0; i < numbBufferThreads; i++) { - bufferMaxCapacity = (lods.length * lods.length * (6 * 4 * ((3 * 4) + (4 * 4)))) / numbBufferThreads; - clearBytes = new byte[bufferMaxCapacity]; + drawableNearBuffers[i] = new BufferBuilder(bufferMaxCapacity); + drawableFarBuffers[i] = new BufferBuilder(bufferMaxCapacity); - bufferThreads.clear(); - for(int i = 0; i < numbBufferThreads; i++) - bufferThreads.add(new BuildBufferThread()); - regen = true; - - for(int i = 0; i < maxNumbThreads; i++) - { - nearBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity); - nearBuffers[i].order(ByteOrder.LITTLE_ENDIAN); - - farBuffers[i] = ByteBuffer.allocateDirect(bufferMaxCapacity); - farBuffers[i].order(ByteOrder.LITTLE_ENDIAN); - } + buildableNearBuffers[i] = new BufferBuilder(bufferMaxCapacity); + buildableFarBuffers[i] = new BufferBuilder(bufferMaxCapacity); } + + clearBytes = new byte[bufferMaxCapacity]; } + + + //======================// + // Other Misc Functions // + //======================// + + /** * Returns -1 if there are no valid points */ @@ -675,6 +594,141 @@ public class LodRenderer + private Thread createLodGenerationThread(double cameraX, double cameraZ, + AxisAlignedBB[][] lodArray, Color[][] colorArray) + { + 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); + @SuppressWarnings("unused") + Color invisible = new Color(0,0,0,0); + @SuppressWarnings("unused") + Color error = new Color(255, 0, 225, alpha); // bright pink + + int numbChunksWide = lodArray.length; + // this seemingly useless math is required, + // just using (int) camera doesn't work + int playerXChunkOffset = ((int) cameraX / LodChunk.WIDTH) * LodChunk.WIDTH; + int playerZChunkOffset = ((int) cameraZ / LodChunk.WIDTH) * LodChunk.WIDTH; + // this 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(()-> + { + // 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 (RenderUtil.isCoordinateInLoadedArea(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) + { + // note: for some reason if any color or lod object are set here + // it causes the game to use 100% gpu, all of it undefined in the debug menu + // and drop to ~6 fps. + colorArray[i][j] = null; + lodArray[i][j] = null; + + 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 = getLodHeightPoint(lod.top); + int bottomPoint = getLodHeightPoint(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); + } + } + + generateLodBuffers(lodArray, colorArray, LodConfig.fogDistance); + + regenerating = false; + switchBuffers = true; + }); + return t; + } + + /** + * Swap buildable and drawable buffers. + */ + private void swapBuffers() + { + for(int i = 0; i < buildableNearBuffers.length; i++) + { + try + { + BufferBuilder tmp = buildableNearBuffers[i]; + buildableNearBuffers[i] = drawableNearBuffers[i]; + drawableNearBuffers[i] = tmp; + + tmp = buildableFarBuffers[i]; + buildableFarBuffers[i] = drawableFarBuffers[i]; + drawableFarBuffers[i] = tmp; + } + catch(Exception e) + { + e.printStackTrace(); + } + } + } + } \ No newline at end of file diff --git a/src/main/java/com/backsun/lod/renderer/NearFarBuffer.java b/src/main/java/com/backsun/lod/renderer/NearFarBuffer.java index 1c57da0f8..57b1836d7 100644 --- a/src/main/java/com/backsun/lod/renderer/NearFarBuffer.java +++ b/src/main/java/com/backsun/lod/renderer/NearFarBuffer.java @@ -1,6 +1,6 @@ package com.backsun.lod.renderer; -import java.nio.ByteBuffer; +import net.minecraft.client.renderer.BufferBuilder; /** * This object is just a replacement for an array @@ -8,16 +8,16 @@ import java.nio.ByteBuffer; * and BuildBufferThread. * * @author James Seibel - * @version 02-13-2021 + * @version 02-21-2021 */ public class NearFarBuffer { - public ByteBuffer nearBuffer; + public BufferBuilder nearBuffer; - public ByteBuffer farBuffer; + public BufferBuilder farBuffer; - NearFarBuffer(ByteBuffer newNearBuffer, ByteBuffer newFarBuffer) + NearFarBuffer(BufferBuilder newNearBuffer, BufferBuilder newFarBuffer) { nearBuffer = newNearBuffer; farBuffer = newFarBuffer;