diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index da1564904..9619ef70e 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -28,7 +28,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.locks.ReentrantLock; -import com.seibel.lod.util.*; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15C; @@ -44,6 +43,11 @@ import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.proxy.GlProxy; import com.seibel.lod.proxy.GlProxy.GlProxyContext; import com.seibel.lod.render.LodRenderer; +import com.seibel.lod.util.DataPointUtil; +import com.seibel.lod.util.DetailDistanceUtil; +import com.seibel.lod.util.LevelPosUtil; +import com.seibel.lod.util.LodThreadFactory; +import com.seibel.lod.util.LodUtil; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.VertexBuffer; @@ -67,70 +71,69 @@ public class LodBufferBuilder * This holds the threads used to generate buffers. */ public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfBufferBuilderThreads.get(), new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - builder")); - + /** * The buffers that are used to create LODs using far fog */ public volatile BufferBuilder[][] buildableBuffers; - + /** * Used when building new VBOs */ public volatile VertexBuffer[][] buildableVbos; - + /** * VBOs that are sent over to the LodNodeRenderer */ public volatile VertexBuffer[][] drawableVbos; - + /** * if this is true the LOD buffers are currently being * regenerated. */ public boolean generatingBuffers = false; - + /** * if this is true new LOD buffers have been generated * and are waiting to be swapped with the drawable buffers */ private boolean switchVbos = false; - + /** * Size of the buffer builders in bytes last time we created them */ public int previousBufferSize = 0; - + /** * Width of the dimension in regions last time we created the buffers */ public int previousRegionWidth = 0; - + /** * this is used to prevent multiple threads creating, destroying, or using the buffers at the same time */ private ReentrantLock bufferLock = new ReentrantLock(); - -// private static final int NUMBER_OF_DIRECTION = 4; + + // private static final int NUMBER_OF_DIRECTION = 4; //in order -x, +x, -z, +z -// private static final int[][] ADJ_VECTOR = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; - + // private static final int[][] ADJ_VECTOR = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + private volatile Box[][] boxCache; private volatile PosToRenderContainer[][] setsToRender; private volatile RegionPos center; - + /** * This is the ChunkPos the player was at the last time the buffers were built. * IE the center of the buffers last time they were built */ private volatile ChunkPos drawableCenterChunkPos = new ChunkPos(0, 0); private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0, 0); - - + public LodBufferBuilder() { - + } - + /** * Create a thread to asynchronously generate LOD buffers * centered around the given camera X and Z. @@ -141,61 +144,61 @@ public class LodBufferBuilder * swapped with the drawable buffers in the LodRenderer to be drawn. */ public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim, - BlockPos playerBlockPos, boolean fullRegen) + BlockPos playerBlockPos, boolean fullRegen) { - + // only allow one generation process to happen at a time if (generatingBuffers) return; - + if (buildableBuffers == null) // setupBuffers hasn't been called yet return; - + generatingBuffers = true; - + // round the player's block position down to the nearest chunk BlockPos ChunkPos playerChunkPos = new ChunkPos(playerBlockPos); BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition(); - + Thread thread = new Thread(() -> { bufferLock.lock(); - + try { long treeStart = System.currentTimeMillis(); long treeEnd = System.currentTimeMillis(); - + long startTime = System.currentTimeMillis(); - + ArrayList> nodeToRenderThreads = new ArrayList<>(lodDim.getWidth() * lodDim.getWidth()); - + startBuffers(fullRegen, lodDim); - + // =====================// // RENDERING PART // // =====================// - + RegionPos playerRegionPos = new RegionPos(playerChunkPos); if (center == null) center = playerRegionPos; - + if (setsToRender == null) setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; - + if (setsToRender.length != lodDim.getWidth()) setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; - + if (boxCache == null) boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; - + if (boxCache.length != lodDim.getWidth()) boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; - + // this will be the center of the VBOs once they have been built buildableCenterChunkPos = playerChunkPos; - + for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++) { for (int zRegion = 0; zRegion < lodDim.getWidth(); zRegion++) @@ -205,11 +208,12 @@ public class LodBufferBuilder RegionPos regionPos = new RegionPos( xRegion + lodDim.getCenterX() - Math.floorDiv(lodDim.getWidth(), 2), zRegion + lodDim.getCenterZ() - Math.floorDiv(lodDim.getWidth(), 2)); - + // local position in the vbo and bufferBuilder arrays BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion]; LodRegion region = lodDim.getRegion(regionPos.x, regionPos.z); - if (region == null) continue; + if (region == null) + continue; byte minDetail = region.getMinDetailLevel(); // make sure the buffers weren't // changed while we were running this method @@ -225,11 +229,12 @@ public class LodBufferBuilder if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) { maxVerticalData = 1; - } else + } + else { maxVerticalData = DetailDistanceUtil.getMaxVerticalData(0); } - + for (Direction direction : Box.ADJ_DIRECTIONS) { if (adjData.containsKey(direction) && LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.MULTI_LOD) @@ -242,20 +247,20 @@ public class LodBufferBuilder { setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z); } - + if (boxCache[xR][zR] == null) { boxCache[xR][zR] = new Box(); } PosToRenderContainer posToRender = setsToRender[xR][zR]; posToRender.clear(minDetail, regionPos.x, regionPos.z); - + lodDim.getDataToRender( posToRender, regionPos, playerBlockPosRounded.getX(), playerBlockPosRounded.getZ()); - + byte detailLevel; int posX; int posZ; @@ -263,12 +268,12 @@ public class LodBufferBuilder int zAdj; int chunkXdist; int chunkZdist; - + // keep a local version so we don't have to worry about indexOutOfBounds Exceptions // if it changes in the LodRenderer while we are working here boolean[][] vanillaRenderedChunks = renderer.vanillaRenderedChunks; short gameChunkRenderDistance = (short) (vanillaRenderedChunks.length / 2 - 1); - + for (int index = 0; index < posToRender.getNumberOfPos(); index++) { detailLevel = posToRender.getNthDetailLevel(index); @@ -278,9 +283,9 @@ public class LodBufferBuilder chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x; chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z; if (gameChunkRenderDistance >= Math.abs(chunkXdist) - && gameChunkRenderDistance >= Math.abs(chunkZdist) - && detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL - && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]) + && gameChunkRenderDistance >= Math.abs(chunkZdist) + && detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL + && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]) { continue; } @@ -296,48 +301,62 @@ public class LodBufferBuilder if (gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist)) { if (!vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] - && posToRender.contains(detailLevel, xAdj, zAdj)) + && posToRender.contains(detailLevel, xAdj, zAdj)) { if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) { adjData.get(direction)[0] = lodDim.getSingleData(detailLevel, xAdj, zAdj); - } else + } + else { - adjData.put(direction, new long[DetailDistanceUtil.getMaxVerticalData(lodDim.getMaxVerticalData(detailLevel,posX,posZ))]); + adjData.put(direction, new long[DetailDistanceUtil.getMaxVerticalData(lodDim.getMaxVerticalData(detailLevel, posX, posZ))]); for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++) adjData.get(direction)[verticalIndex] = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex); } - - } else + + } + else { if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) { adjData.get(direction)[0] = DataPointUtil.createVoidDataPoint(0); - } else + } + else { adjData.put(direction, null); } } - } else + } + else { if (posToRender.contains(detailLevel, xAdj, zAdj)) { if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) { - adjData.get(direction)[0] = lodDim.getSingleData(detailLevel, xAdj, zAdj); - } else + try + { + adjData.get(direction)[0] = lodDim.getSingleData(detailLevel, xAdj, zAdj); + } + catch (NullPointerException e) + { + e.printStackTrace(); + } + } + else { - adjData.put(direction, new long[DetailDistanceUtil.getMaxVerticalData(lodDim.getMaxVerticalData(detailLevel,posX,posZ))]); + adjData.put(direction, new long[DetailDistanceUtil.getMaxVerticalData(lodDim.getMaxVerticalData(detailLevel, posX, posZ))]); for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++) adjData.get(direction)[verticalIndex] = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex); } - } else + } + else { - + if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) { adjData.get(direction)[0] = DataPointUtil.createVoidDataPoint(0); - } else + } + else { adjData.put(direction, null); } @@ -353,8 +372,9 @@ public class LodBufferBuilder LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPosRounded, dataPoint, adjData, detailLevel, posX, posZ, boxCache[xR][zR], renderer.previousDebugMode, renderer.lightMap); } - - } else if (region.getLodQualityMode() == LodQualityMode.MULTI_LOD) + + } + else if (region.getLodQualityMode() == LodQualityMode.MULTI_LOD) { long data; for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++) @@ -366,15 +386,16 @@ public class LodBufferBuilder detailLevel, posX, posZ, boxCache[xR][zR], renderer.previousDebugMode, renderer.lightMap); } } - - } catch (ArrayIndexOutOfBoundsException e) + + } + catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); return false; } - + } // for pos to in list to render - // the thread executed successfully + // the thread executed successfully return true; }; nodeToRenderThreads.add(dataToRenderThread); @@ -395,7 +416,7 @@ public class LodBufferBuilder } } long renderEnd = System.currentTimeMillis(); - + long endTime = System.currentTimeMillis(); @SuppressWarnings("unused") long buildTime = endTime - startTime; @@ -403,43 +424,45 @@ public class LodBufferBuilder long treeTime = treeEnd - treeStart; @SuppressWarnings("unused") long renderingTime = renderEnd - renderStart; - -// ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' + -// "Tree cutting time: " + treeTime + " ms" + '\n' + -// "Rendering time: " + renderingTime + " ms"); - + + // ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' + + // "Tree cutting time: " + treeTime + " ms" + '\n' + + // "Rendering time: " + renderingTime + " ms"); + // mark that the buildable buffers as ready to swap switchVbos = true; - } catch (Exception e) + } + catch (Exception e) { ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: "); e.printStackTrace(); - } finally + } + finally { // regardless of if we successfully created the buffers // we are done generating. generatingBuffers = false; - + // clean up any potentially open resources if (buildableBuffers != null) closeBuffers(fullRegen, lodDim); - + // upload the new buffers uploadBuffers(fullRegen, lodDim); bufferLock.unlock(); } - + }); - + mainGenThread.execute(thread); - + return; } - + //===============================// // BufferBuilder related methods // //===============================// - + /** * Called from the LodRenderer to create the * BufferBuilders.

@@ -449,30 +472,29 @@ public class LodBufferBuilder public void setupBuffers(int numbRegionsWide, int bufferMaxCapacity) { bufferLock.lock(); - + previousRegionWidth = numbRegionsWide; previousBufferSize = bufferMaxCapacity; - + buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide]; - + buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide]; drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide]; - - + for (int x = 0; x < numbRegionsWide; x++) { for (int z = 0; z < numbRegionsWide; z++) { buildableBuffers[x][z] = new BufferBuilder(bufferMaxCapacity); - + buildableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT); drawableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT); } } - + bufferLock.unlock(); } - + /** * sets the buffers and Vbos to null, forcing them to be recreated.

*

@@ -481,14 +503,14 @@ public class LodBufferBuilder public void destroyBuffers() { bufferLock.lock(); - + buildableBuffers = null; buildableVbos = null; drawableVbos = null; - + bufferLock.unlock(); } - + /** * Calls begin on each of the buildable BufferBuilders. */ @@ -505,7 +527,7 @@ public class LodBufferBuilder } } } - + /** * Calls end on each of the buildable BufferBuilders. */ @@ -516,21 +538,20 @@ public class LodBufferBuilder if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building() && (fullRegen || lodDim.getRegenByArrayIndex(x, z))) buildableBuffers[x][z].end(); } - + /** * Upload all buildableBuffers to the GPU. */ private void uploadBuffers(boolean fullRegen, LodDimension lodDim) { GlProxy glProxy = GlProxy.getInstance(); - + try { // make sure we are uploading to a different OpenGL context, // to prevent interference (IE stuttering) with the Minecraft context. glProxy.setGlContext(GlProxyContext.LOD_BUILDER); - - + for (int x = 0; x < buildableVbos.length; x++) { for (int z = 0; z < buildableVbos.length; z++) @@ -543,28 +564,29 @@ public class LodBufferBuilder } } } - - + // make sure all the buffers have been uploaded. // this probably is necessary, but it makes me feel good :) GL11.glFlush(); - } catch (IllegalStateException e) + } + catch (IllegalStateException e) { ClientProxy.LOGGER.error(LodBufferBuilder.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage()); e.printStackTrace(); - } finally + } + finally { // make sure no buffer is bound if (glProxy.getGlContext() == GlProxyContext.LOD_BUILDER) { GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); } - + // make sure the context is disabled glProxy.setGlContext(GlProxyContext.NONE); } } - + /** * Uploads the uploadBuffer into the VBO in GPU memory. */ @@ -575,25 +597,22 @@ public class LodBufferBuilder { // this is how many points will be rendered vbo.vertexCount = (uploadBuffer.remaining() / vbo.format.getVertexSize()); - - + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); - - + // subData only works if the memory is allocated beforehand. GL15C.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer.remaining(), GL15C.GL_DYNAMIC_DRAW); - + // interestingly bufferSubData renders faster than glMapBuffer // even though OpenGLInsights-AsynchronousBufferTransfers says glMapBuffer // is faster for transferring data. They must put the data in different memory // or something. GL15C.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - - + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); } } - + /** * Get the newly created VBOs */ @@ -606,17 +625,17 @@ public class LodBufferBuilder VertexBuffer[][] tmpVbo = drawableVbos; drawableVbos = buildableVbos; buildableVbos = tmpVbo; - + drawableCenterChunkPos = buildableCenterChunkPos; - + // the vbos have been swapped switchVbos = false; bufferLock.unlock(); } - + return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos); } - + /** * A simple container to pass multiple objects back in the getVertexBuffers method. */ @@ -624,14 +643,14 @@ public class LodBufferBuilder { public VertexBuffer[][] vbos; public ChunkPos drawableCenterChunkPos; - + public VertexBuffersAndOffset(VertexBuffer[][] newVbos, ChunkPos newDrawableCenterChunkPos) { vbos = newVbos; drawableCenterChunkPos = newDrawableCenterChunkPos; } } - + /** * If this is true the buildable near and far * buffers have been generated and are ready to be @@ -641,5 +660,5 @@ public class LodBufferBuilder { return switchVbos; } - + }