From 74e4744ff7de69fe6131837db7196d0d8924d309 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Thu, 26 Aug 2021 13:18:18 +0200 Subject: [PATCH] Correctly removed LodDataPoint and optimized the LevelPos use --- .../lod/builders/GenerationRequest.java | 4 +- .../seibel/lod/builders/LodBufferBuilder.java | 731 +++++++++--------- .../com/seibel/lod/builders/LodBuilder.java | 29 +- .../lodTemplates/AbstractLodTemplate.java | 10 +- .../lodTemplates/CubicLodTemplate.java | 34 +- .../lodTemplates/DynamicLodTemplate.java | 8 +- .../lodTemplates/TriangularLodTemplate.java | 8 +- .../worldGeneration/LodWorldGenerator.java | 16 +- .../java/com/seibel/lod/enums/LodDetail.java | 9 +- .../com/seibel/lod/objects/DataPoint.java | 40 + .../objects/LevelPos/ImmutableLevelPos.java | 7 + .../lod/objects/{ => LevelPos}/LevelPos.java | 138 +++- .../lod/objects/LevelPos/MutableLevelPos.java | 14 + .../com/seibel/lod/objects/LodDataPoint.java | 146 ---- .../com/seibel/lod/objects/LodDimension.java | 38 +- .../com/seibel/lod/objects/LodRegion.java | 67 +- .../com/seibel/lod/proxy/ClientProxy.java | 12 +- .../java/com/seibel/lod/util/LodUtil.java | 9 +- 18 files changed, 639 insertions(+), 681 deletions(-) create mode 100644 src/main/java/com/seibel/lod/objects/DataPoint.java create mode 100644 src/main/java/com/seibel/lod/objects/LevelPos/ImmutableLevelPos.java rename src/main/java/com/seibel/lod/objects/{ => LevelPos}/LevelPos.java (66%) create mode 100644 src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodDataPoint.java diff --git a/src/main/java/com/seibel/lod/builders/GenerationRequest.java b/src/main/java/com/seibel/lod/builders/GenerationRequest.java index 1074e4508..b08aa6007 100644 --- a/src/main/java/com/seibel/lod/builders/GenerationRequest.java +++ b/src/main/java/com/seibel/lod/builders/GenerationRequest.java @@ -2,7 +2,7 @@ package com.seibel.lod.builders; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.objects.LevelPos; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.util.LodUtil; import net.minecraft.util.math.ChunkPos; @@ -25,7 +25,7 @@ public class GenerationRequest public ChunkPos getChunkPos() { - LevelPos chunkLevelPos = levelPos.convert(LodUtil.CHUNK_DETAIL_LEVEL); + LevelPos chunkLevelPos = levelPos.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL); return new ChunkPos(chunkLevelPos.posX, chunkLevelPos.posZ); } } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index 363a7aedd..762f43b59 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -27,11 +27,11 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.locks.ReentrantLock; +import net.minecraft.world.chunk.Chunk; import org.lwjgl.opengl.GL11; import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LevelPos; -import com.seibel.lod.objects.LodDataPoint; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.RegionPos; import com.seibel.lod.proxy.ClientProxy; @@ -53,129 +53,141 @@ import net.minecraft.util.math.ChunkPos; */ public class LodBufferBuilder { - /** This holds the thread used to generate new LODs off the main thread. */ - private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main")); - /** This holds the threads used to generate buffers. */ - private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new LodThreadFactory(this.getClass().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(); - - - - - public LodBufferBuilder() - { - - } - - - - - /** - * Create a thread to asynchronously generate LOD buffers - * centered around the given camera X and Z. - *
- * This method will write to the drawable near and far buffers. - *
- * 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, LodDimension lodDim, - BlockPos playerBlockPos, int numbChunksWide) - { - // only allow one generation process to happen at a time - if (generatingBuffers) - return; - - if (buildableBuffers == null) - throw new IllegalStateException("\"generateLodBuffersAsync\" was called before the \"setupBuffers\" method was called."); - - - 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(); + /** + * This holds the thread used to generate new LODs off the main thread. + */ + private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main")); + /** + * This holds the threads used to generate buffers. + */ + private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); - - try - { - long treeStart = System.currentTimeMillis(); - long treeEnd = System.currentTimeMillis(); + /** + * The buffers that are used to create LODs using far fog + */ + public volatile BufferBuilder[][] buildableBuffers; - long startTime = System.currentTimeMillis(); - - - ArrayList> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); - - startBuffers(); + /** + * Used when building new VBOs + */ + public volatile VertexBuffer[][] buildableVbos; - // =====================// - // RENDERING PART // - // =====================// + /** + * VBOs that are sent over to the LodNodeRenderer + */ + public volatile VertexBuffer[][] drawableVbos; - for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++) - { - for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++) - { - RegionPos regionPos = new RegionPos(xRegion + lodDim.getCenterX() - lodDim.getWidth() / 2, zRegion + lodDim.getCenterZ() - lodDim.getWidth() / 2); + /** + * if this is true the LOD buffers are currently being + * regenerated. + */ + public boolean generatingBuffers = false; - // local position in the vbo and bufferBuilder arrays - BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion]; - - // make sure the buffers weren't - // changed while we were running this method - if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building())) - return; + /** + * if this is true new LOD buffers have been generated + * and are waiting to be swapped with the drawable buffers + */ + private boolean switchVbos = false; - byte detailLevel = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; + /** + * Size of the buffer builders in bytes last time we created them + */ + public int previousBufferSize = 0; - Callable bufferBuildingThread = () -> - { - byte detailToRender; - boolean zFix; - Set setOfPosToRender = new HashSet<>(); + /** + * Width of the dimension in regions last time we created the buffers + */ + public int previousRegionWidth = 0; - for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) - { - detailToRender = detail; - zFix = true; + /** + * this is used to prevent multiple threads creating, destroying, or using the buffers at the same time + */ + private ReentrantLock bufferLock = new ReentrantLock(); + + + public LodBufferBuilder() + { + + } + + + /** + * Create a thread to asynchronously generate LOD buffers + * centered around the given camera X and Z. + *
+ * This method will write to the drawable near and far buffers. + *
+ * 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, LodDimension lodDim, + BlockPos playerBlockPos, int numbChunksWide) + { + // only allow one generation process to happen at a time + if (generatingBuffers) + return; + + if (buildableBuffers == null) + throw new IllegalStateException("\"generateLodBuffersAsync\" was called before the \"setupBuffers\" method was called."); + + + 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> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); + + startBuffers(); + + // =====================// + // RENDERING PART // + // =====================// + + for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++) + { + for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++) + { + RegionPos regionPos = new RegionPos(xRegion + lodDim.getCenterX() - lodDim.getWidth() / 2, zRegion + lodDim.getCenterZ() - lodDim.getWidth() / 2); + + // local position in the vbo and bufferBuilder arrays + BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion]; + + // make sure the buffers weren't + // changed while we were running this method + if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building())) + return; + + + byte detailLevel = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; + + Callable bufferBuildingThread = () -> + { + byte detailToRender; + boolean zFix; + Set setOfPosToRender = new HashSet<>(); + + for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) + { + detailToRender = detail; + zFix = true; /* detailToRender = (byte) 4; if(detail > detailToRender){ @@ -185,269 +197,256 @@ public class LodBufferBuilder zFix = true; } */ - setOfPosToRender.addAll(lodDim.getDataToRender( - regionPos, - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - DetailDistanceUtil.getDistanceRendering(detail), - DetailDistanceUtil.getDistanceRendering(detail + 1), - detailToRender, - zFix)); - } + setOfPosToRender.addAll(lodDim.getDataToRender( + regionPos, + playerBlockPosRounded.getX(), + playerBlockPosRounded.getZ(), + DetailDistanceUtil.getDistanceRendering(detail), + DetailDistanceUtil.getDistanceRendering(detail + 1), + detailToRender, + zFix)); + } - for (LevelPos posToRender : setOfPosToRender) - { - LevelPos chunkPos = posToRender.convert(LodUtil.CHUNK_DETAIL_LEVEL); - // skip any chunks that Minecraft is going to render + for (LevelPos posToRender : setOfPosToRender) + { + LevelPos chunkPos = posToRender.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL); + // skip any chunks that Minecraft is going to render - if (renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkPos.posX, chunkPos.posZ))) - { - continue; - } + if (renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkPos.posX, chunkPos.posZ))) + { + continue; + } - if (lodDim.doesDataExist(posToRender)) - { - try - { - LodDataPoint lodData = lodDim.getData(posToRender); + if (lodDim.doesDataExist(posToRender)) + { + try + { + ChunkPos adjChunkPos = new ChunkPos(0, 0); + LevelPos adjPos = new LevelPos(); + if (lodDim.doesDataExist(posToRender)) + { + short[] lodData = lodDim.getData(posToRender); + short[][][] adjData = new short[2][2][]; + for (int x : new int[]{0, 1}) + { + adjPos.changeParameters(posToRender.detailLevel, posToRender.posX + x * 2 - 1, posToRender.posZ); + if (!renderer.vanillaRenderedChunks.contains(adjPos.getChunkPos()) + && setOfPosToRender.contains(adjPos)) + adjData[0][x] = lodDim.getData(adjPos); + } - if (lodData != null) - { - /*We check for adjacent data*/ - LodDataPoint[][] adjData = new LodDataPoint[2][2]; - LevelPos adjPos; - for(int x : new int[]{0,1}){ - adjPos = new LevelPos(posToRender.detailLevel, posToRender.posX + x*2-1, posToRender.posZ); - if (!renderer.vanillaRenderedChunks.contains(new ChunkPos(adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posX, adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posZ))); - adjData[0][x] = lodDim.getData(adjPos); - /* - if(setOfPosToRender.contains(adjPos)){ - System.out.println("yup"); - adjData[0][x] = lodDim.getData(adjPos); - }*/ - } + for (int z : new int[]{0, 1}) + { + adjPos.changeParameters(posToRender.detailLevel, posToRender.posX, posToRender.posZ + z * 2 - 1); + if (!renderer.vanillaRenderedChunks.contains(adjPos.getChunkPos()) + && setOfPosToRender.contains(adjPos)) + adjData[1][z] = lodDim.getData(adjPos); + } - for(int z : new int[]{0,1}){ - adjPos = new LevelPos(posToRender.detailLevel, posToRender.posX, posToRender.posZ + z*2-1); - if (!renderer.vanillaRenderedChunks.contains(new ChunkPos(adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posX, adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posZ))) - adjData[1][z] = lodDim.getData(adjPos); - /* - if(setOfPosToRender.contains(adjPos)){ - System.out.println("yup2"); - adjData[1][z] = lodDim.getData(adjPos); - }*/ - } + LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPos, lodData, adjData, + posToRender, renderer.debugging); + } + } catch (ArrayIndexOutOfBoundsException e) + { + return false; + } + } - LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPos, lodData, adjData, - posToRender, renderer.debugging); - } - } - catch (ArrayIndexOutOfBoundsException e) - { - return false; - } - } + }// for pos to in list to render - }// for pos to in list to render - - // the thread executed successfully - return true; - };// buffer builder worker thread + // the thread executed successfully + return true; + };// buffer builder worker thread - builderThreads.add(bufferBuildingThread); + builderThreads.add(bufferBuildingThread); - }// region z - }// region z + }// region z + }// region z - long renderStart = System.currentTimeMillis(); - // wait for all threads to finish - List> futures = bufferBuilderThreads.invokeAll(builderThreads); - for(Future future : futures) - { - // the future will be false if its thread failed - if (!future.get()) - { - ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over."); - closeBuffers(); - return; - } - } - long renderEnd = System.currentTimeMillis(); - - - // finish the buffer building - closeBuffers(); - - // upload the new buffers - uploadBuffers(); - - - long endTime = System.currentTimeMillis(); - long buildTime = endTime - startTime; + long renderStart = System.currentTimeMillis(); + // wait for all threads to finish + List> futures = bufferBuilderThreads.invokeAll(builderThreads); + for (Future future : futures) + { + // the future will be false if its thread failed + if (!future.get()) + { + ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over."); + closeBuffers(); + return; + } + } + long renderEnd = System.currentTimeMillis(); - long treeTime = treeEnd - treeStart; - long renderingTime = renderEnd - renderStart; + // finish the buffer building + closeBuffers(); + + // upload the new buffers + uploadBuffers(); + + + long endTime = System.currentTimeMillis(); + long buildTime = endTime - startTime; + + long treeTime = treeEnd - treeStart; + + long renderingTime = renderEnd - renderStart; + + 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) + { + ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: "); + e.printStackTrace(); + } 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(); + + bufferLock.unlock(); + } + + }); + + mainGenThread.execute(thread); + + return; + } + + + //===============================// + // BufferBuilder related methods // + //===============================// + + + /** + * Called from the LodRenderer to create the + * BufferBuilders.

+ *

+ * May have to wait for the bufferLock to open. + */ + 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.

+ *

+ * May have to wait for the bufferLock to open. + */ + public void destroyBuffers() + { + bufferLock.lock(); + + buildableBuffers = null; + buildableVbos = null; + drawableVbos = null; + + bufferLock.unlock(); + } + + + /** + * Calls begin on each of the buildable BufferBuilders. + */ + private void startBuffers() + { + for (int x = 0; x < buildableBuffers.length; x++) + for (int z = 0; z < buildableBuffers.length; z++) + buildableBuffers[x][z].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); + } + + /** + * Calls end on each of the buildable BufferBuilders. + */ + private void closeBuffers() + { + for (int x = 0; x < buildableBuffers.length; x++) + for (int z = 0; z < buildableBuffers.length; z++) + if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building()) + buildableBuffers[x][z].end(); + } + + /** + * Called from the LodRenderer to create the + * BufferBuilders at the right size. + */ + private void uploadBuffers() + { + for (int x = 0; x < buildableVbos.length; x++) + { + for (int z = 0; z < buildableVbos.length; z++) + { + buildableVbos[x][z].upload(buildableBuffers[x][z]); + } + } + } + + + /** + * Get the newly created VBOs + */ + public VertexBuffer[][] getVertexBuffers() + { + // don't wait for the lock to open + // since this is called on the main render thread + if (bufferLock.tryLock()) + { + VertexBuffer[][] tmp = drawableVbos; + drawableVbos = buildableVbos; + buildableVbos = tmp; + + // the vbos have been swapped + switchVbos = false; + bufferLock.unlock(); + } + + return drawableVbos; + } + + /** + * 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 switchVbos; + } + - 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) - { - ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: "); - e.printStackTrace(); - } - 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(); - - bufferLock.unlock(); - } - - }); - - mainGenThread.execute(thread); - - return; - } - - - - - //===============================// - // BufferBuilder related methods // - //===============================// - - - /** - * Called from the LodRenderer to create the - * BufferBuilders.

- * - * May have to wait for the bufferLock to open. - */ - 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.

- * - * May have to wait for the bufferLock to open. - */ - public void destroyBuffers() - { - bufferLock.lock(); - - buildableBuffers = null; - buildableVbos = null; - drawableVbos = null; - - bufferLock.unlock(); - } - - - /** - * Calls begin on each of the buildable BufferBuilders. - */ - private void startBuffers() - { - for (int x = 0; x < buildableBuffers.length; x++) - for (int z = 0; z < buildableBuffers.length; z++) - buildableBuffers[x][z].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); - } - - /** - * Calls end on each of the buildable BufferBuilders. - */ - private void closeBuffers() - { - for (int x = 0; x < buildableBuffers.length; x++) - for (int z = 0; z < buildableBuffers.length; z++) - if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building()) - buildableBuffers[x][z].end(); - } - - /** - * Called from the LodRenderer to create the - * BufferBuilders at the right size. - * - */ - private void uploadBuffers() - { - for (int x = 0; x < buildableVbos.length; x++) - { - for (int z = 0; z < buildableVbos.length; z++) - { - buildableVbos[x][z].upload(buildableBuffers[x][z]); - } - } - } - - - /** - * Get the newly created VBOs - */ - public VertexBuffer[][] getVertexBuffers() - { - // don't wait for the lock to open - // since this is called on the main render thread - if (bufferLock.tryLock()) - { - VertexBuffer[][] tmp = drawableVbos; - drawableVbos = buildableVbos; - buildableVbos = tmp; - - // the vbos have been swapped - switchVbos = false; - bufferLock.unlock(); - } - - return drawableVbos; - } - - /** - * 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 switchVbos; - } - - } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/builders/LodBuilder.java b/src/main/java/com/seibel/lod/builders/LodBuilder.java index 0164bc121..5ad4aaa14 100644 --- a/src/main/java/com/seibel/lod/builders/LodBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBuilder.java @@ -24,8 +24,7 @@ import java.util.concurrent.Executors; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.LodDetail; import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LevelPos; -import com.seibel.lod.objects.LodDataPoint; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.LodWorld; import com.seibel.lod.util.LodThreadFactory; @@ -150,11 +149,11 @@ public class LodBuilder int startZ; int endX; int endZ; - Color color; + short[] color; short height; short depth; - LevelPos levelPos; - LodDataPoint data = null; + LevelPos levelPos = new LevelPos(); + short[] data; for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++) { @@ -175,14 +174,14 @@ public class LodBuilder startZ, endX, endZ); depth = 0; } - levelPos = new LevelPos((byte) 0, + levelPos.changeParameters((byte) 0, chunk.getPos().x * 16 + startX, chunk.getPos().z * 16 + startZ); - data = new LodDataPoint(height, depth, color); - lodDim.addData(levelPos.convert(detail.detailLevel), + levelPos.convert(detail.detailLevel); + data = new short[]{height, depth, color[0], color[1], color[2]}; + lodDim.addData(levelPos, data, config.distanceGenerationMode, - false, false); } lodDim.updateData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z)); @@ -332,7 +331,7 @@ public class LodBuilder * otherwise only use the block's * material color */ - private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, + private short[] generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ) { ChunkSection[] chunkSections = chunk.getSections(); @@ -386,11 +385,9 @@ public class LodBuilder colorInt = getColorForBlock(x, z, blockState, biome); } - Color c = LodUtil.intToColor(colorInt); - - red += c.getRed(); - green += c.getGreen(); - blue += c.getBlue(); + red += (colorInt)&0xFF;; + green += (colorInt>>8)&0xFF; + blue += (colorInt>>16)&0xFF; numbOfBlocks++; @@ -410,7 +407,7 @@ public class LodBuilder green /= numbOfBlocks; blue /= numbOfBlocks; - return new Color(red, green, blue); + return new short[]{(short) red, (short) green, (short) blue}; } /** diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java index 264245ed1..665433485 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java @@ -18,12 +18,8 @@ package com.seibel.lod.builders.lodTemplates; import java.awt.Color; -import java.util.Set; -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.objects.LevelPos; -import com.seibel.lod.objects.LodDataPoint; -import com.seibel.lod.objects.LodDimension; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.util.LodUtil; import net.minecraft.client.renderer.BufferBuilder; @@ -38,8 +34,8 @@ import net.minecraft.util.math.BlockPos; */ public abstract class AbstractLodTemplate { - public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData, - LevelPos levelPos, boolean debugging); + public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, + LevelPos levelPos, boolean debugging); /** add the given position and color to the buffer */ protected void addPosAndColor(BufferBuilder buffer, diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java index 2de4b6224..f85955895 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java @@ -21,8 +21,8 @@ import java.awt.Color; import com.seibel.lod.enums.ShadingMode; import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LevelPos; -import com.seibel.lod.objects.LodDataPoint; +import com.seibel.lod.objects.DataPoint; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.util.LodUtil; import net.minecraft.client.renderer.BufferBuilder; @@ -45,23 +45,23 @@ public class CubicLodTemplate extends AbstractLodTemplate } @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, LevelPos levelPos, boolean debugging) { AxisAlignedBB bbox; - int width = (int) Math.pow(2, levelPos.detailLevel); + int width = 1 << levelPos.detailLevel; // add each LOD for the detail level bbox = generateBoundingBox( - data.height, - data.depth, + DataPoint.getHeight(data), + DataPoint.getDepth(data), width, levelPos.posX * width, 0, levelPos.posZ * width); - Color color = data.color; + Color color = new Color(DataPoint.getColor(data)); if (LodConfig.CLIENT.debugMode.get()) { color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel]; @@ -105,7 +105,7 @@ public class CubicLodTemplate extends AbstractLodTemplate return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset); } - private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c, BlockPos playerBlockPos, LodDataPoint[][] adjData) + private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c, BlockPos playerBlockPos, short[][][] adjData) { Color topColor = c; Color northSouthColor = c; @@ -136,7 +136,7 @@ public class CubicLodTemplate extends AbstractLodTemplate bottomColor = applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier); int minY; int maxY; - LodDataPoint data; + short[] data; /**TODO make all of this more automatic if possible*/ if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET) { @@ -168,7 +168,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); } else { - maxY = data.height; + maxY = DataPoint.getHeight(data); if (maxY < bb.maxY) { minY = (int) Math.max(maxY, bb.minY); @@ -177,7 +177,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); addPosAndColor(buffer, bb.minX, minY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); } - minY = data.depth; + minY = DataPoint.getDepth(data); if (minY > bb.minY) { maxY = (int) Math.min(minY, bb.maxX); @@ -201,7 +201,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); } else { - maxY = data.height; + maxY = DataPoint.getHeight(data); if (maxY < bb.maxY) { minY = (int) Math.max(maxY, bb.minY); @@ -210,7 +210,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); addPosAndColor(buffer, bb.maxX, minY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); } - minY = data.depth; + minY = DataPoint.getDepth(data); if (minY > bb.minY) { maxY = (int) Math.min(minY, bb.maxX); @@ -234,7 +234,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); } else { - maxY = data.height; + maxY = DataPoint.getHeight(data); if (maxY < bb.maxY) { minY = (int) Math.max(maxY, bb.minY); @@ -243,7 +243,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); } - minY = data.depth; + minY = DataPoint.getDepth(data); if (minY > bb.minY) { maxY = (int) Math.min(minY, bb.maxX); @@ -267,7 +267,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); } else { - maxY = data.height; + maxY = DataPoint.getHeight(data); if (maxY < bb.maxY) { minY = (int) Math.max(maxY, bb.minY); @@ -276,7 +276,7 @@ public class CubicLodTemplate extends AbstractLodTemplate addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); addPosAndColor(buffer, bb.maxX, minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); } - minY = data.depth; + minY = DataPoint.getDepth(data); if (minY > bb.minY) { maxY = (int) Math.min(minY, bb.maxX); diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java index 5263b5aa1..4c4d0411b 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java @@ -17,15 +17,11 @@ */ package com.seibel.lod.builders.lodTemplates; -import com.seibel.lod.objects.LevelPos; -import com.seibel.lod.objects.LodDataPoint; -import com.seibel.lod.objects.LodDimension; +import com.seibel.lod.objects.LevelPos.LevelPos; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.math.BlockPos; -import java.util.Set; - /** * TODO DynamicLodTemplate * Chunks smoothly transition between @@ -38,7 +34,7 @@ import java.util.Set; public class DynamicLodTemplate extends AbstractLodTemplate { @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, LevelPos levelPos, boolean debugging) { System.err.println("DynamicLodTemplate not implemented!"); diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java index 35f542d72..f581fd340 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java @@ -17,15 +17,11 @@ */ package com.seibel.lod.builders.lodTemplates; -import com.seibel.lod.objects.LevelPos; -import com.seibel.lod.objects.LodDataPoint; -import com.seibel.lod.objects.LodDimension; +import com.seibel.lod.objects.LevelPos.LevelPos; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.math.BlockPos; -import java.util.Set; - /** * TODO #21 TriangularLodTemplate * Builds each LOD chunk as a singular rectangular prism. @@ -36,7 +32,7 @@ import java.util.Set; public class TriangularLodTemplate extends AbstractLodTemplate { @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, LevelPos levelPos, boolean debugging) { System.err.println("DynamicLodTemplate not implemented!"); diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java index 76371bc74..0338e681a 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java @@ -13,7 +13,7 @@ import com.seibel.lod.builders.GenerationRequest; import com.seibel.lod.builders.LodBuilder; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LevelPos; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.render.LodRenderer; import com.seibel.lod.util.DetailDistanceUtil; @@ -131,14 +131,13 @@ public class LodWorldGenerator //=======================================// // start by generating half-region sized blocks... - int farRequesting = maxChunkGenRequests/2; + int farRequesting = maxChunkGenRequests/4; //we firstly make sure that the world is filled with half region wide block - /* + for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++) { if (farRequesting <= 0){ - farRequesting=0; break; } levelPosListToGen = lodDim.getDataToGenerate( @@ -147,20 +146,19 @@ public class LodWorldGenerator DetailDistanceUtil.getDistanceGeneration(detailGen), DetailDistanceUtil.getDistanceGeneration(detailGen + 1), DetailDistanceUtil.getDistanceGenerationMode(detailGen).complexity, - (byte) 7, + (byte) 8, farRequesting); for(LevelPos levelPos : levelPosListToGen){ generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen))); } farRequesting = farRequesting - levelPosListToGen.size(); - }*/ + } // ...then once the world is filled with half-region sized blocks // fill in the rest int t = generationRequestList.size(); - int nearRequesting = maxChunkGenRequests - maxChunkGenRequests/2 + farRequesting; - System.out.println("clear slot " + nearRequesting); + int nearRequesting = maxChunkGenRequests - maxChunkGenRequests/4 + farRequesting; //we then fill the world with the rest of the block for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++) { @@ -177,9 +175,7 @@ public class LodWorldGenerator generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen))); } nearRequesting = nearRequesting - levelPosListToGen.size(); - System.out.println("adding " + levelPosListToGen.size()); } - System.out.println("generating " + generationRequestList.size() + " and " + t); //====================================// diff --git a/src/main/java/com/seibel/lod/enums/LodDetail.java b/src/main/java/com/seibel/lod/enums/LodDetail.java index 0bbeeaf9f..b83b944c3 100644 --- a/src/main/java/com/seibel/lod/enums/LodDetail.java +++ b/src/main/java/com/seibel/lod/enums/LodDetail.java @@ -20,7 +20,6 @@ package com.seibel.lod.enums; import java.util.ArrayList; import java.util.Collections; -import com.seibel.lod.objects.LodDataPoint; import com.seibel.lod.util.LodUtil; /** @@ -63,10 +62,7 @@ public enum LodDetail public final int[] endX; public final int[] endZ; - - /** This is how many pieces of data should be expected - * when creating a LodChunk with this detail level */ - public final int lodChunkStringDelimiterCount; + /** * 1st dimension: LodDetail.detailLevel
@@ -106,9 +102,6 @@ public enum LodDetail } } - - lodChunkStringDelimiterCount = 2 + (dataPointLengthCount * dataPointLengthCount * LodDataPoint.NUMBER_OF_DELIMITERS); - }// constructor diff --git a/src/main/java/com/seibel/lod/objects/DataPoint.java b/src/main/java/com/seibel/lod/objects/DataPoint.java new file mode 100644 index 000000000..1df0e6ae3 --- /dev/null +++ b/src/main/java/com/seibel/lod/objects/DataPoint.java @@ -0,0 +1,40 @@ +package com.seibel.lod.objects; + +public class DataPoint +{ + + public static short[] createDataPoint(int height, int depth, int red, int green, int blue){ + return new short[]{(short) height, (short) depth, (short) red, (short) green, (short) blue}; + } + + public static short getHeight(short[] dataPoint){ + return dataPoint[0]; + } + + public static short getDepth(short[] dataPoint){ + return dataPoint[1]; + } + + public static short getRed(short[] dataPoint){ + return dataPoint[2]; + } + + public static short getGreen(short[] dataPoint){ + return dataPoint[3]; + } + + public static short getBlue(short[] dataPoint){ + return dataPoint[4]; + } + + public static short[] getHeightDepth(short[] dataPoint){ + return new short[]{dataPoint[0], dataPoint[1]}; + } + + public static int getColor(short[] dataPoint){ + int R = (dataPoint[2] << 16) & 0x00FF0000; + int G = (dataPoint[3] << 8) & 0x0000FF00; + int B = dataPoint[4] & 0x000000FF; + return 0xFF000000 | R | G | B; + } +} diff --git a/src/main/java/com/seibel/lod/objects/LevelPos/ImmutableLevelPos.java b/src/main/java/com/seibel/lod/objects/LevelPos/ImmutableLevelPos.java new file mode 100644 index 000000000..1d75dd605 --- /dev/null +++ b/src/main/java/com/seibel/lod/objects/LevelPos/ImmutableLevelPos.java @@ -0,0 +1,7 @@ +package com.seibel.lod.objects.LevelPos; + +public interface ImmutableLevelPos +{ + public LevelPos getConvertedLevelPos(byte newDetailLevel); + public LevelPos getRegionModuleLevelPos(); +} diff --git a/src/main/java/com/seibel/lod/objects/LevelPos.java b/src/main/java/com/seibel/lod/objects/LevelPos/LevelPos.java similarity index 66% rename from src/main/java/com/seibel/lod/objects/LevelPos.java rename to src/main/java/com/seibel/lod/objects/LevelPos/LevelPos.java index 7e28d7db8..dd396a097 100644 --- a/src/main/java/com/seibel/lod/objects/LevelPos.java +++ b/src/main/java/com/seibel/lod/objects/LevelPos/LevelPos.java @@ -1,15 +1,23 @@ -package com.seibel.lod.objects; +package com.seibel.lod.objects.LevelPos; +import com.seibel.lod.objects.RegionPos; import com.seibel.lod.util.LodUtil; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.chunk.Chunk; import java.util.Comparator; import java.util.Map; -public class LevelPos implements Cloneable +public class LevelPos implements Cloneable, ImmutableLevelPos, MutableLevelPos { - public final byte detailLevel; - public final int posX; - public final int posZ; + public byte detailLevel; + public int posX; + public int posZ; + + + public LevelPos() + { + } public LevelPos(byte detailLevel, int posX, int posZ) { @@ -18,46 +26,117 @@ public class LevelPos implements Cloneable this.detailLevel = detailLevel; } - public LevelPos convert(byte newDetailLevel) + /** + * this operation does not change the state + */ + public LevelPos getConvertedLevelPos(byte newDetailLevel) { if (newDetailLevel >= detailLevel) { + int width = 1 << (newDetailLevel - detailLevel); return new LevelPos( newDetailLevel, - Math.floorDiv(posX, (int) Math.pow(2, newDetailLevel - detailLevel)), - Math.floorDiv(posZ, (int) Math.pow(2, newDetailLevel - detailLevel))); + Math.floorDiv(posX, width), + Math.floorDiv(posZ, width)); } else { + int width = 1 << (detailLevel - newDetailLevel); return new LevelPos( newDetailLevel, - posX * (int) Math.pow(2, detailLevel - newDetailLevel), - posZ * (int) Math.pow(2, detailLevel - newDetailLevel)); + posX * width, + posZ * width); } } - public LevelPos regionModule() + /** + * this operation does not change the state + */ + public LevelPos getRegionModuleLevelPos() { + int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); return new LevelPos( detailLevel, - Math.floorMod(posX, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)), - Math.floorMod(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel))); + Math.floorMod(posX, width), + Math.floorMod(posZ, width)); + } + + /** + * this operation changes the state + */ + public void convert(byte newDetailLevel) + { + if (newDetailLevel >= detailLevel) + { + int width = 1 << (newDetailLevel - detailLevel); + detailLevel = newDetailLevel; + posX = Math.floorDiv(posX, width); + posZ = Math.floorDiv(posZ, width); + } else + { + int width = 1 << (detailLevel - newDetailLevel); + detailLevel = newDetailLevel; + posX = posX * width; + posZ = posZ * width; + } + } + + /** + * this operation changes the state + */ + public void performRegionModule() + { + int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); + posX = Math.floorMod(posX, width); + posX = Math.floorMod(posZ, width); + } + + /** + * this operation changes the state + */ + public void applyOffset(int xOffset, int zOffset) + { + posX = posX + xOffset; + posX = posZ + zOffset; + } + + /** + * this operation changes the state + */ + public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ) + { + detailLevel = newDetailLevel; + posX = newPosX; + posX = newPosZ; } public RegionPos getRegionPos() { + int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); return new RegionPos( - Math.floorDiv(posX, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)), - Math.floorDiv(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel))); + Math.floorDiv(posX, width), + Math.floorDiv(posZ, width)); } - - @Override - public LevelPos clone() + public ChunkPos getChunkPos() { - return new LevelPos(detailLevel, posX, posZ); + if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel) + { + int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel); + return new ChunkPos( + Math.floorDiv(posX, width), + Math.floorDiv(posZ, width)); + } else + { + int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL); + return new ChunkPos( + posX * width, + posZ * width); + } } -/**TODO fix the region disappearing for a second*/ + /** + * TODO fix the region disappearing for a second + */ public int maxDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ) { @@ -192,7 +271,7 @@ public class LevelPos implements Cloneable @Override public int compare(Map.Entry first, Map.Entry second) { - Integer compareResult = Integer.compare(first.getKey().detailLevel, second.getKey().detailLevel); + int compareResult = Integer.compare(first.getKey().detailLevel, second.getKey().detailLevel); if (compareResult != 0) { compareResult = Integer.compare(first.getValue(), second.getValue()); @@ -201,6 +280,14 @@ public class LevelPos implements Cloneable } } + + @Override + public LevelPos clone() + { + return new LevelPos(detailLevel, posX, posZ); + } + + @Override public int hashCode() { int hash = 7; @@ -210,11 +297,12 @@ public class LevelPos implements Cloneable return hash; } - public boolean equals(LevelPos other) + @Override + public boolean equals(Object other) { - return (this.detailLevel == other.detailLevel && - this.posX == other.posX && - this.posZ == other.posZ); + return (this.detailLevel == ((LevelPos) other).detailLevel && + this.posX == ((LevelPos) other).posX && + this.posZ == ((LevelPos) other).posZ); } @Override diff --git a/src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java b/src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java new file mode 100644 index 000000000..a57d3aef4 --- /dev/null +++ b/src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java @@ -0,0 +1,14 @@ +package com.seibel.lod.objects.LevelPos; + +import com.seibel.lod.util.LodUtil; + +public interface MutableLevelPos +{ + public void convert(byte newDetailLevel); + + public void performRegionModule(); + + public void applyOffset(int xOffset, int zOffset); + + public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ); +} diff --git a/src/main/java/com/seibel/lod/objects/LodDataPoint.java b/src/main/java/com/seibel/lod/objects/LodDataPoint.java deleted file mode 100644 index c554441fe..000000000 --- a/src/main/java/com/seibel/lod/objects/LodDataPoint.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.objects; - -import java.awt.*; -import java.io.Serializable; -import java.util.Objects; - -import com.seibel.lod.handlers.LodDimensionFileHandler; -import com.seibel.lod.util.LodUtil; - -/** - * This stores the height and color - * for a specific area in a LodChunk. - * - * @author James Seibel - * @version 8-8-2021 - */ -public class LodDataPoint implements Serializable -{ - /** - * This is what separates each piece of data in the toData method - */ - private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER; - - /** - * this is how many pieces of data are exported when toData is called - */ - public static final int NUMBER_OF_DELIMITERS = 5; - - /** - * a empty data point that can be used for comparisons - */ - public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint(); - - - /** - * highest point - */ - public short height; - - /** - * lowest point - */ - public short depth; - - /** - * The average color for the 6 cardinal directions - */ - public Color color; - - - /** - * Creates an empty LodDataPoint - */ - public LodDataPoint() - { - height = -1; - depth = -1; - color = LodUtil.COLOR_INVISIBLE; - } - - - public LodDataPoint(short newHeight, short newDepth, Color newColor) - { - height = newHeight; - depth = newDepth; - color = newColor; - } - - public LodDataPoint(int newHeight, int newDepth, Color newColor) - { - height = (short) newHeight; - depth = (short) newDepth; - color = newColor; - } - - @Override - public int hashCode() - { - return Objects.hash(this.height, this.depth, this.color); - } - - public boolean equals(LodDataPoint other) - { - return (this.height == other.height - && this.depth == other.depth - && this.color == other.color); - } - - public boolean isEmpty() - { - return this.equals(EMPTY_DATA_POINT); - } - - /** - * Outputs all data in a csv format - * with the given delimiter. - *
- * Exports data in the form: - *
- * height, depth, rgb color data - * - *
- * example output: - *
- * 4, 0, 255,255,255, - */ - public String toData() - { - String s = Short.toString(height) + DATA_DELIMITER; - - s += Short.toString(depth) + DATA_DELIMITER; - - s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER; - - return s; - } - - @Override - public String toString() - { - String s = Short.toString(height) + DATA_DELIMITER; - - s += Short.toString(depth) + DATA_DELIMITER; - - s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER; - - return s; - } -} diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java index 4a7491394..0507b3428 100644 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodDimension.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.handlers.LodDimensionFileHandler; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.util.DetailDistanceUtil; import com.seibel.lod.util.LodUtil; @@ -400,7 +401,7 @@ public class LodDimension * stored in the LOD. If an LOD already exists at the given * coordinates it will be overwritten. */ - public synchronized Boolean addData(LevelPos levelPos, LodDataPoint lodDataPoint, DistanceGenerationMode generationMode, boolean update, boolean dontSave) + public synchronized Boolean addData(LevelPos levelPos, short[] lodDataPoint, DistanceGenerationMode generationMode, boolean dontSave) { // don't continue if the region can't be saved @@ -412,7 +413,7 @@ public class LodDimension LodRegion region = getRegion(levelPos); - boolean nodeAdded = region.setData(levelPos, lodDataPoint, generationMode.complexity, true); + boolean nodeAdded = region.setData(levelPos, lodDataPoint, generationMode.complexity); // only save valid LODs to disk if (!dontSave && fileHandler != null) { @@ -432,20 +433,6 @@ public class LodDimension } - /** - * Get the LodNodeData at the given X and Z coordinates - * in this dimension. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public LodDataPoint getData(ChunkPos chunkPos) - { - LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z); - return getData(levelPos); - } - - /** * method to get all the quadtree level that have to be generated based on the position of the player * @@ -474,7 +461,7 @@ public class LodDimension if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) && start <= regionLevelPos.maxDistance(playerPosX, playerPosZ)) { - region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).convert(detailLevel)); + region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel)); listOfData.addAll(region.getDataToGenerate(playerPosX, playerPosZ, start, end, generation, detailLevel, dataNumber)); } }catch (Exception e){ @@ -510,7 +497,7 @@ public class LodDimension if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) && start <= regionLevelPos.maxDistance(playerPosX, playerPosZ)) { - LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).convert(detailLevel)); + LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel)); listOfData.addAll(region.getDataToRender(playerPosX, playerPosZ, start, end, detailLevel,zFix)); } }catch (Exception e){ @@ -522,6 +509,19 @@ public class LodDimension } } + /** + * Get the LodNodeData at the given X and Z coordinates + * in this dimension. + *
+ * Returns null if the LodChunk doesn't exist or + * is outside the loaded area. + */ + public short[] getData(ChunkPos chunkPos) + { + LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z); + return getData(levelPos); + } + /** * Get the data point at the given X and Z coordinates * in this dimension. @@ -529,7 +529,7 @@ public class LodDimension * Returns null if the LodChunk doesn't exist or * is outside the loaded area. */ - public LodDataPoint getData(LevelPos levelPos) + public short[] getData(LevelPos levelPos) { if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL) throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java index d16ec73fa..42fbf6d04 100644 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ b/src/main/java/com/seibel/lod/objects/LodRegion.java @@ -1,16 +1,14 @@ package com.seibel.lod.objects; import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.handlers.LodConfig; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.util.LodUtil; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; -import java.awt.*; import java.io.Serializable; import java.util.*; import java.util.List; -import java.util.stream.Collectors; /** * STANDARD TO FOLLOW @@ -128,12 +126,11 @@ public class LodRegion implements Serializable * @param levelPos * @param dataPoint * @param generationType - * @param update * @return */ - public boolean setData(LevelPos levelPos, LodDataPoint dataPoint, byte generationType, boolean update) + public boolean setData(LevelPos levelPos, short[] dataPoint, byte generationType) { - levelPos = levelPos.regionModule(); + levelPos.performRegionModule(); if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])) { @@ -141,24 +138,13 @@ public class LodRegion implements Serializable if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++; //add the node data - this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint.color.getRed() - 128); - this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (dataPoint.color.getGreen() - 128); - this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (dataPoint.color.getBlue() - 128); - this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.height; - this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.depth; + this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint[0]; + this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint[1]; + this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint[2] - 128); + this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (dataPoint[3] - 128); + this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (dataPoint[4] - 128); this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = generationType; this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true; - - //update could be stopped and a single big update could be done at the end - LevelPos tempLevelPos = levelPos; - if (update) - { - for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) - { - tempLevelPos = tempLevelPos.convert(tempLod); - update(tempLevelPos); - } - } return true; } else { @@ -167,7 +153,7 @@ public class LodRegion implements Serializable } - public LodDataPoint getData(ChunkPos chunkPos) + public short[] getData(ChunkPos chunkPos) { return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z)); } @@ -178,7 +164,7 @@ public class LodRegion implements Serializable * @param lod * @return the data at the relative pos and level */ - public LodDataPoint getData(byte lod, BlockPos blockPos) + public short[] getData(byte lod, BlockPos blockPos) { int posX = Math.floorMod(blockPos.getX(), (int) Math.pow(2, lod)); int posZ = Math.floorMod(blockPos.getZ(), (int) Math.pow(2, lod)); @@ -191,17 +177,15 @@ public class LodRegion implements Serializable * @param levelPos * @return the data at the relative pos and level */ - public LodDataPoint getData(LevelPos levelPos) + public short[] getData(LevelPos levelPos) { - levelPos = levelPos.regionModule(); - return new LodDataPoint( - height[levelPos.detailLevel][levelPos.posX][levelPos.posZ], + levelPos = levelPos.getRegionModuleLevelPos(); + return new short[]{height[levelPos.detailLevel][levelPos.posX][levelPos.posZ], depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ], - new Color(colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128, - colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128, - colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128 - ) - ); + (short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128), + (short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128), + (short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128) + }; } /** @@ -296,8 +280,9 @@ public class LodRegion implements Serializable } } } else + //now we keep exploring the top right child { - childPos = levelPos.convert((byte) (levelPos.detailLevel - 1)); + childPos = levelPos.getConvertedLevelPos((byte) (levelPos.detailLevel - 1)); if (generationType[childPos.detailLevel][childPos.posX][childPos.posZ] < generation) { minDistance = childPos.minDistance(playerPosX,playerPosZ,regionPosX,regionPosZ); @@ -403,7 +388,7 @@ public class LodRegion implements Serializable int startX; int startZ; for(byte bottom = (byte) (minDetailLevel + 1); bottom < levelPos.detailLevel ; bottom++){ - tempLevelPos = levelPos.convert(bottom); + tempLevelPos = levelPos.getConvertedLevelPos(bottom); startX = tempLevelPos.posX; startZ = tempLevelPos.posZ; sizeDiff = (int) Math.pow(2, levelPos.detailLevel - bottom); @@ -415,7 +400,7 @@ public class LodRegion implements Serializable } for (byte tempLod = levelPos.detailLevel; tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) { - tempLevelPos = levelPos.convert(tempLod); + tempLevelPos = levelPos.getConvertedLevelPos(tempLod); update(tempLevelPos); } } @@ -426,7 +411,7 @@ public class LodRegion implements Serializable private void update(LevelPos levelPos) { - levelPos = levelPos.regionModule(); + levelPos = levelPos.getRegionModuleLevelPos(); int numberOfChildren = 0; /**TODO add the ability to change how the heigth and depth are determinated (for example min or max)**/ @@ -480,7 +465,7 @@ public class LodRegion implements Serializable */ private boolean[][] getChildren(LevelPos levelPos) { - levelPos = levelPos.regionModule(); + levelPos = levelPos.getRegionModuleLevelPos(); boolean[][] children = new boolean[2][2]; int numberOfChild = 0; if (minDetailLevel == levelPos.detailLevel) @@ -512,7 +497,7 @@ public class LodRegion implements Serializable */ public boolean doesDataExist(LevelPos levelPos) { - levelPos = levelPos.regionModule(); + levelPos = levelPos.getRegionModuleLevelPos(); return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]; } @@ -522,7 +507,7 @@ public class LodRegion implements Serializable */ public DistanceGenerationMode getGenerationMode(LevelPos levelPos) { - levelPos = levelPos.regionModule(); + levelPos = levelPos.getRegionModuleLevelPos(); DistanceGenerationMode generationMode = DistanceGenerationMode.NONE; switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) { @@ -558,7 +543,7 @@ public class LodRegion implements Serializable */ public boolean hasDataBeenGenerated(LevelPos levelPos) { - levelPos = levelPos.regionModule(); + levelPos = levelPos.getRegionModuleLevelPos(); return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0); } diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index b1454fc61..36d83b816 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -95,9 +95,8 @@ public class ClientProxy { if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) return; - - applyConfigOverrides(); + viewDistanceChangedEvent(); LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); @@ -125,7 +124,9 @@ public class ClientProxy profiler.pop(); // end LOD profiler.push("terrain"); // restart "terrain" - + + + applyConfigOverrides(); // these can't be set until after the buffers are built (in renderer.drawLODs) // otherwise the buffers may be set to the wrong size, or not changed at all @@ -156,10 +157,9 @@ public class ClientProxy // LodConfig.CLIENT.saturationMultiplier.set(1.0); LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); - LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); - LodConfig.CLIENT.lodChunkRenderDistance.set(256); + LodConfig.CLIENT.lodChunkRenderDistance.set(128); LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); - LodConfig.CLIENT.lodQuality.set(1); + LodConfig.CLIENT.lodQuality.set(2); LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors()); diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java index 4c8d2d65f..a6fcc0ba2 100644 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/util/LodUtil.java @@ -21,8 +21,7 @@ import java.awt.Color; import java.io.File; import java.util.HashSet; -import com.seibel.lod.objects.LevelPos; -import com.seibel.lod.objects.LodDataPoint; +import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.RegionPos; @@ -325,11 +324,9 @@ public class LodUtil if (!lodDim.doesDataExist(levelPos)) continue; - LodDataPoint data = lodDim.getData(levelPos); - if (data == null) - continue; + short[] data = lodDim.getData(levelPos); - short lodAverageHeight = data.height; + short lodAverageHeight = data[0]; if (playerPos.getY() <= lodAverageHeight) { // don't draw Lod's that are taller than the player