From db2410e7fec5a08d69ead5637ff4f562ae93bfc3 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Fri, 27 Aug 2021 14:34:43 +0200 Subject: [PATCH] changed indent --- .../lod/builders/GenerationRequest.java | 28 +- .../seibel/lod/builders/LodBufferBuilder.java | 840 +++++----- .../com/seibel/lod/builders/LodBuilder.java | 982 ++++++------ .../seibel/lod/builders/LodBuilderConfig.java | 150 +- .../lodTemplates/AbstractLodTemplate.java | 16 +- .../lodTemplates/CubicLodTemplate.java | 546 +++---- .../lodTemplates/DynamicLodTemplate.java | 4 +- .../lodTemplates/TriangularLodTemplate.java | 4 +- .../worldGeneration/LodServerWorld.java | 207 +-- .../worldGeneration/LodWorldGenerator.java | 133 +- .../lod/enums/DistanceCalculatorType.java | 18 +- .../lod/enums/DistanceGenerationMode.java | 58 +- .../com/seibel/lod/handlers/LodConfig.java | 450 +++--- .../lod/handlers/LodDimensionFileHandler.java | 185 ++- .../lod/handlers/ReflectionHandler.java | 42 +- .../com/seibel/lod/objects/DataPoint.java | 62 +- .../seibel/lod/objects/LevelContainer.java | 164 +- .../lod/objects/LevelPos/MutableLevelPos.java | 8 +- .../com/seibel/lod/objects/LodDimension.java | 1428 ++++++++--------- .../com/seibel/lod/objects/LodRegion.java | 1342 ++++++++-------- .../java/com/seibel/lod/objects/LodWorld.java | 196 +-- .../com/seibel/lod/objects/RegionPos.java | 35 +- .../com/seibel/lod/proxy/ClientProxy.java | 408 ++--- .../java/com/seibel/lod/util/ColorUtil.java | 100 +- .../seibel/lod/util/DetailDistanceUtil.java | 241 +-- 25 files changed, 3853 insertions(+), 3794 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/GenerationRequest.java b/src/main/java/com/seibel/lod/builders/GenerationRequest.java index b08aa6007..fc88e00e3 100644 --- a/src/main/java/com/seibel/lod/builders/GenerationRequest.java +++ b/src/main/java/com/seibel/lod/builders/GenerationRequest.java @@ -12,20 +12,20 @@ import net.minecraft.util.math.ChunkPos; */ public class GenerationRequest { - public final LevelPos levelPos; - public final DistanceGenerationMode generationMode; - public final LodDetail detail; + public final LevelPos levelPos; + public final DistanceGenerationMode generationMode; + public final LodDetail detail; - public GenerationRequest(LevelPos levelPos, DistanceGenerationMode generationMode, LodDetail detail) - { - this.levelPos = levelPos; - this.generationMode = generationMode; - this.detail = detail; - } + public GenerationRequest(LevelPos levelPos, DistanceGenerationMode generationMode, LodDetail detail) + { + this.levelPos = levelPos; + this.generationMode = generationMode; + this.detail = detail; + } - public ChunkPos getChunkPos() - { - LevelPos chunkLevelPos = levelPos.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL); - return new ChunkPos(chunkLevelPos.posX, chunkLevelPos.posZ); - } + public ChunkPos getChunkPos() + { + 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 59afd9853..58abd84b1 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -53,431 +53,431 @@ import javax.xml.crypto.Data; */ 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(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); + /** + * 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(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); - /** - * The buffers that are used to create LODs using far fog - */ - public volatile BufferBuilder[][] buildableBuffers; + /** + * The buffers that are used to create LODs using far fog + */ + public volatile BufferBuilder[][] buildableBuffers; - /** - * Used when building new VBOs - */ - public volatile VertexBuffer[][] buildableVbos; + /** + * Used when building new VBOs + */ + public volatile VertexBuffer[][] buildableVbos; - /** - * VBOs that are sent over to the LodNodeRenderer - */ - public volatile VertexBuffer[][] drawableVbos; + /** + * 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 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(); - - - try - { - long treeStart = System.currentTimeMillis(); - long treeEnd = System.currentTimeMillis(); - - long startTime = System.currentTimeMillis(); - - - ArrayList> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); - ArrayList> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); - - startBuffers(); - - // =====================// - // RENDERING PART // - // =====================// - Object[][] nodeToRenderMatrix = new Object[lodDim.regions.length][lodDim.regions.length]; - - for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++) - { - for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++) - { - nodeToRenderMatrix[xRegion][zRegion] = new ConcurrentSkipListMap<>(LevelPos.getComparator()); - 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; - final int xR = xRegion; - final int zR = zRegion; - Callable dataToRenderThread = () -> - { - byte detailToRender; - boolean zFix; - - for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) - { - detailToRender = detail; - lodDim.getDataToRender( - (ConcurrentSkipListMap>) nodeToRenderMatrix[xR][zR], - regionPos, - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - DetailDistanceUtil.getDistanceRendering(detail), - DetailDistanceUtil.getDistanceRendering(detail + 1), - detailToRender, - true); - } - // the thread executed successfully - return true; - };// buffer builder worker thread - - - nodeToRenderThreads.add(dataToRenderThread); - - }// region z - }// region z - - long renderStart = System.currentTimeMillis(); - // wait for all threads to finish - List> futures = bufferBuilderThreads.invokeAll(nodeToRenderThreads); - 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(); - - 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; - - final int xR = xRegion; - final int zR = zRegion; - Callable bufferBuildingThread = () -> - { - LevelPos adjPos = new LevelPos(); - for (LevelPos posToRender : ((ConcurrentSkipListMap>) nodeToRenderMatrix[xR][zR]).keySet()) - { - 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; - } - - try - { - if (lodDim.doesDataExist(posToRender.clone())) - { - 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())) - 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())) - adjData[1][z] = lodDim.getData(adjPos); - } - - 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 - - // the thread executed successfully - return true; - };// buffer builder worker thread - - - builderThreads.add(bufferBuildingThread); - - }// region z - }// region z - - long renderBufferStart = System.currentTimeMillis(); - // wait for all threads to finish - List> futuresBuffer = bufferBuilderThreads.invokeAll(builderThreads); - for (Future future : futuresBuffer) - { - // 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 renderBufferEnd = System.currentTimeMillis(); - - - // 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; - } + /** + * 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(); + + + try + { + long treeStart = System.currentTimeMillis(); + long treeEnd = System.currentTimeMillis(); + + long startTime = System.currentTimeMillis(); + + + ArrayList> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); + ArrayList> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); + + startBuffers(); + + // =====================// + // RENDERING PART // + // =====================// + Object[][] nodeToRenderMatrix = new Object[lodDim.regions.length][lodDim.regions.length]; + + for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++) + { + for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++) + { + nodeToRenderMatrix[xRegion][zRegion] = new ConcurrentSkipListMap<>(LevelPos.getComparator()); + 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; + final int xR = xRegion; + final int zR = zRegion; + Callable dataToRenderThread = () -> + { + byte detailToRender; + boolean zFix; + + for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) + { + detailToRender = detail; + lodDim.getDataToRender( + (ConcurrentSkipListMap>) nodeToRenderMatrix[xR][zR], + regionPos, + playerBlockPosRounded.getX(), + playerBlockPosRounded.getZ(), + DetailDistanceUtil.getDistanceRendering(detail), + DetailDistanceUtil.getDistanceRendering(detail + 1), + detailToRender, + true); + } + // the thread executed successfully + return true; + };// buffer builder worker thread + + + nodeToRenderThreads.add(dataToRenderThread); + + }// region z + }// region z + + long renderStart = System.currentTimeMillis(); + // wait for all threads to finish + List> futures = bufferBuilderThreads.invokeAll(nodeToRenderThreads); + 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(); + + 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; + + final int xR = xRegion; + final int zR = zRegion; + Callable bufferBuildingThread = () -> + { + LevelPos adjPos = new LevelPos(); + for (LevelPos posToRender : ((ConcurrentSkipListMap>) nodeToRenderMatrix[xR][zR]).keySet()) + { + 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; + } + + try + { + if (lodDim.doesDataExist(posToRender.clone())) + { + 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())) + 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())) + adjData[1][z] = lodDim.getData(adjPos); + } + + 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 + + // the thread executed successfully + return true; + };// buffer builder worker thread + + + builderThreads.add(bufferBuildingThread); + + }// region z + }// region z + + long renderBufferStart = System.currentTimeMillis(); + // wait for all threads to finish + List> futuresBuffer = bufferBuilderThreads.invokeAll(builderThreads); + for (Future future : futuresBuffer) + { + // 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 renderBufferEnd = System.currentTimeMillis(); + + + // 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; + } } diff --git a/src/main/java/com/seibel/lod/builders/LodBuilder.java b/src/main/java/com/seibel/lod/builders/LodBuilder.java index b75ea7e8e..0ef114ea1 100644 --- a/src/main/java/com/seibel/lod/builders/LodBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBuilder.java @@ -57,496 +57,500 @@ import net.minecraft.world.gen.Heightmap; */ public class LodBuilder { - private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); + private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); - public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH; - public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH; - public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; + public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH; + public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH; + public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; - /** If no blocks are found in the area in determineBottomPointForArea return this */ - public static final short DEFAULT_DEPTH = -1; - /** If no blocks are found in the area in determineHeightPointForArea return this */ + /** + * If no blocks are found in the area in determineBottomPointForArea return this + */ + public static final short DEFAULT_DEPTH = -1; + /** + * If no blocks are found in the area in determineHeightPointForArea return this + */ public static final short DEFAULT_HEIGHT = -1; - - /** - * How wide LodDimensions should be in regions - */ - public int defaultDimensionWidthInRegions = 5; - - public LodBuilder() - { - - } - - public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world) - { - generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER); - } - - - public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode) - { - if (lodWorld == null || !lodWorld.getIsWorldLoaded()) - return; - - // don't try to create an LOD object - // if for some reason we aren't - // given a valid chunk object - if (chunk == null) - return; - - Thread thread = new Thread(() -> - { - try - { - DimensionType dim = world.dimensionType(); - - - LodDimension lodDim; - - if (lodWorld.getLodDimension(dim) == null) - { - lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions); - lodWorld.addLodDimension(lodDim); - } else - { - lodDim = lodWorld.getLodDimension(dim); - } - - generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode), LodConfig.CLIENT.maxGenerationDetail.get()); - } catch (IllegalArgumentException | NullPointerException e) - { - System.out.println("Chunk pos " + chunk.getPos()); - e.printStackTrace(); - // if the world changes while LODs are being generated - // they will throw errors as they try to access things that no longer - // exist. - } - }); - lodGenThreadPool.execute(thread); - - return; - } - - /** - * Creates a LodChunk for a chunk in the given world. - * - * @throws IllegalArgumentException thrown if either the chunk or world is null. - */ - public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodDetail detailLevel) throws IllegalArgumentException - { - generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(), detailLevel); - } - - /** - * Creates a LodChunk for a chunk in the given world. - * - * @throws IllegalArgumentException thrown if either the chunk or world is null. - */ - public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config, LodDetail detail) - throws IllegalArgumentException - { - - if (chunk == null) - throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); - - boolean check = false; - - int startX; - int startZ; - int endX; - int endZ; - short[] color; - short height; - short depth; - LevelPos levelPos = new LevelPos((byte) 0, 0, 0); - short[] data; - try - { - for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++) - { - startX = detail.startX[i]; - startZ = detail.startZ[i]; - endX = detail.endX[i]; - endZ = detail.endZ[i]; - - color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ); - - if (!config.useHeightmap) - { - height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ); - depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ); - } else - { - height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP), startX, - startZ, endX, endZ); - depth = 0; - } - levelPos.changeParameters((byte) 0, - chunk.getPos().x * 16 + startX, - chunk.getPos().z * 16 + startZ); - levelPos.convert(detail.detailLevel); - data = DataPoint.createDataPoint(height, depth, color[0], color[1], color[2]); - lodDim.addData(levelPos, - data, - config.distanceGenerationMode, - false); - } - //levelPos.changeParameters(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z); - - lodDim.updateData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z)); - } catch (NullPointerException e) - { - e.printStackTrace(); - } catch (ArrayIndexOutOfBoundsException e) - { - e.printStackTrace(); - } - } - - - // =====================// - // constructor helpers // - // =====================// - - /** - * Find the lowest valid point from the bottom. - */ - private short determineBottomPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ) - { - int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2); - - // search from the bottom up - for (int section = 0; section < CHUNK_DATA_WIDTH; section++) - { - for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++) - { - int numberOfBlocksFound = 0; - - for (int x = startX; x < endX; x++) - { - for (int z = startZ; z < endZ; z++) - { - if (isLayerValidLodPoint(chunkSections, section, y, x, z)) - { - numberOfBlocksFound++; - - if (numberOfBlocksFound >= numberOfBlocksRequired) - { - // we found - // enough blocks in this - // layer to count as an - // LOD point - return (short) (y + (section * CHUNK_SECTION_HEIGHT)); - } - } - } - } - } - } - - // we never found a valid LOD point - return DEFAULT_DEPTH; - } - - - /** - * Find the lowest valid point from the bottom. - */ - @SuppressWarnings("unused") - private short determineBottomPoint(Heightmap heightmap) - { - // the heightmap only shows how high the blocks go, it - // doesn't have any info about how low they go - return 0; - } - - /** - * Find the highest valid point from the Top - */ - private short determineHeightPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ) - { - int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2); - // search from the top down - for (int section = chunkSections.length - 1; section >= 0; section--) - { - for (int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--) - { - int numberOfBlocksFound = 0; - - for (int x = startX; x < endX; x++) - { - for (int z = startZ; z < endZ; z++) - { - if (isLayerValidLodPoint(chunkSections, section, y, x, z)) - { - numberOfBlocksFound++; - - if (numberOfBlocksFound >= numberOfBlocksRequired) - { - // we found - // enough blocks in this - // layer to count as an - // LOD point - return (short) (y + 1 + (section * CHUNK_SECTION_HEIGHT)); - } - } - } - } - } - } - - // we never found a valid LOD point - return DEFAULT_HEIGHT; - } - - - /** - * Find the highest point from the Top - */ - private short determineHeightPoint(Heightmap heightmap, int startX, int startZ, int endX, int endZ) - { - short highest = 0; - for (int x = startX; x < endX; x++) - { - for (int z = startZ; z < endZ; z++) - { - short newHeight = (short) heightmap.getFirstAvailable(x, z); - if (newHeight > highest) - highest = newHeight; - } - } - - return highest; - } - - /** - * Generate the color for the given chunk using biome water color, foliage - * color, and grass color. - * - * @param config_useSolidBlocksInColorGen
- * If true we look down from the top of - * the
- * chunk until we find a non-invisible - * block, and then use
- * its color. If false we generate the - * color immediately for
- * each x and z. - * @param config_useBiomeColors
- * If true use biome foliage, water, and - * grass colors,
- * otherwise only use the block's - * material color - */ - private short[] generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, - int endZ) - { - ChunkSection[] chunkSections = chunk.getSections(); - - int numbOfBlocks = 0; - int red = 0; - int green = 0; - int blue = 0; - - for (int x = startX; x < endX; x++) - { - for (int z = startZ; z < endZ; z++) - { - boolean foundBlock = false; - - // go top down - for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--) - { - if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen)) - { - for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--) - { - int colorInt = 0; - BlockState blockState = null; - - if (chunkSections[i] != null) - { - blockState = chunkSections[i].getBlockState(x, y, z); - colorInt = blockState.materialColor.col; - } - - if (colorInt == 0 && config.useSolidBlocksInColorGen) - { - // skip air or invisible blocks - continue; - } - - if (config.useBiomeColors) - { - // I have no idea why I need to bit shift to the right, but - // if I don't the biomes don't show up correctly. - Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2, - z >> 2); - colorInt = getColorForBiome(x, z, biome); - } else - { - - // the bit shift is equivalent to dividing by 4 - Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2, - z >> 2); - colorInt = getColorForBlock(x, z, blockState, biome); - } - - red += ColorUtil.getRed(colorInt); - green += ColorUtil.getGreen(colorInt); - blue += ColorUtil.getBlue(colorInt); - - numbOfBlocks++; - - // we found a valid block, skip to the - // next x and z - foundBlock = true; - } - } - } - } - } - - if (numbOfBlocks == 0) - numbOfBlocks = 1; - - red /= numbOfBlocks; - green /= numbOfBlocks; - blue /= numbOfBlocks; - - return new short[]{(short) red, (short) green, (short) blue}; - } - - /** - * Returns a color int for a given block. - */ - private int getColorForBlock(int x, int z, BlockState blockState, Biome biome) - { - int colorInt = 0; - - // block special cases - if (blockState == Blocks.AIR.defaultBlockState() || blockState == Blocks.CAVE_AIR.defaultBlockState()) - { - Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); - tmp = tmp.darker(); - colorInt = LodUtil.colorToInt(tmp); - } else if (blockState == Blocks.MYCELIUM.defaultBlockState()) - { - colorInt = LodUtil.MYCELIUM_COLOR_INT; - } - - // plant life - else if (blockState.getBlock() instanceof LeavesBlock || blockState.getBlock() == Blocks.VINE) - { - Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker(); - leafColor = leafColor.darker(); - colorInt = LodUtil.colorToInt(leafColor); - } else if ((blockState.getBlock() instanceof GrassBlock || blockState.getBlock() instanceof AbstractPlantBlock - || blockState.getBlock() instanceof BushBlock || blockState.getBlock() instanceof IGrowable) - && !(blockState.getBlock() == Blocks.BROWN_MUSHROOM || blockState.getBlock() == Blocks.RED_MUSHROOM)) - { - Color plantColor = LodUtil.intToColor(biome.getGrassColor(x, z)); - plantColor = plantColor.darker(); - colorInt = LodUtil.colorToInt(plantColor); - } - - // water - else if (blockState.getBlock() == Blocks.WATER) - { - colorInt = biome.getWaterColor(); - } - - // everything else - else - { - colorInt = blockState.materialColor.col; - } - - return colorInt; - } - - /** - * Returns a color int for the given biome. - */ - private int getColorForBiome(int x, int z, Biome biome) - { - int colorInt = 0; - - switch (biome.getBiomeCategory()) - { - - case NETHER: - colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col; - break; - - case THEEND: - colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col; - break; - - case BEACH: - case DESERT: - colorInt = Blocks.SAND.defaultBlockState().materialColor.col; - break; - - case EXTREME_HILLS: - colorInt = Blocks.STONE.defaultMaterialColor().col; - break; - - case MUSHROOM: - colorInt = MaterialColor.COLOR_LIGHT_GRAY.col; - break; - - case ICY: - colorInt = Blocks.SNOW.defaultMaterialColor().col; - break; - - case MESA: - colorInt = Blocks.RED_SAND.defaultMaterialColor().col; - break; - - case OCEAN: - case RIVER: - colorInt = biome.getWaterColor(); - break; - - case NONE: - case FOREST: - case TAIGA: - case JUNGLE: - case PLAINS: - case SAVANNA: - case SWAMP: - default: - Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); - tmp = tmp.darker(); - colorInt = LodUtil.colorToInt(tmp); - break; - - } - - return colorInt; - } - - /** - * Is the layer between the given X, Z, and dataIndex values a valid LOD point? - */ - private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z) - { - if (chunkSections[sectionIndex] == null) - { - // this section doesn't have any blocks, - // it is not a valid section - return false; - } else - { - if (chunkSections[sectionIndex].getBlockState(x, y, z) != null - && chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR - && chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.CAVE_AIR) - { - return true; - } - } - - return false; - } + + /** + * How wide LodDimensions should be in regions + */ + public int defaultDimensionWidthInRegions = 5; + + public LodBuilder() + { + + } + + public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world) + { + generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER); + } + + + public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode) + { + if (lodWorld == null || !lodWorld.getIsWorldLoaded()) + return; + + // don't try to create an LOD object + // if for some reason we aren't + // given a valid chunk object + if (chunk == null) + return; + + Thread thread = new Thread(() -> + { + try + { + DimensionType dim = world.dimensionType(); + + + LodDimension lodDim; + + if (lodWorld.getLodDimension(dim) == null) + { + lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions); + lodWorld.addLodDimension(lodDim); + } else + { + lodDim = lodWorld.getLodDimension(dim); + } + + generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode), LodConfig.CLIENT.maxGenerationDetail.get()); + } catch (IllegalArgumentException | NullPointerException e) + { + System.out.println("Chunk pos " + chunk.getPos()); + e.printStackTrace(); + // if the world changes while LODs are being generated + // they will throw errors as they try to access things that no longer + // exist. + } + }); + lodGenThreadPool.execute(thread); + + return; + } + + /** + * Creates a LodChunk for a chunk in the given world. + * + * @throws IllegalArgumentException thrown if either the chunk or world is null. + */ + public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodDetail detailLevel) throws IllegalArgumentException + { + generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(), detailLevel); + } + + /** + * Creates a LodChunk for a chunk in the given world. + * + * @throws IllegalArgumentException thrown if either the chunk or world is null. + */ + public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config, LodDetail detail) + throws IllegalArgumentException + { + + if (chunk == null) + throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); + + boolean check = false; + + int startX; + int startZ; + int endX; + int endZ; + short[] color; + short height; + short depth; + LevelPos levelPos = new LevelPos((byte) 0, 0, 0); + short[] data; + try + { + for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++) + { + startX = detail.startX[i]; + startZ = detail.startZ[i]; + endX = detail.endX[i]; + endZ = detail.endZ[i]; + + color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ); + + if (!config.useHeightmap) + { + height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ); + depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ); + } else + { + height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP), startX, + startZ, endX, endZ); + depth = 0; + } + levelPos.changeParameters((byte) 0, + chunk.getPos().x * 16 + startX, + chunk.getPos().z * 16 + startZ); + levelPos.convert(detail.detailLevel); + data = DataPoint.createDataPoint(height, depth, color[0], color[1], color[2]); + lodDim.addData(levelPos, + data, + config.distanceGenerationMode, + false); + } + //levelPos.changeParameters(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z); + + lodDim.updateData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z)); + } catch (NullPointerException e) + { + e.printStackTrace(); + } catch (ArrayIndexOutOfBoundsException e) + { + e.printStackTrace(); + } + } + + + // =====================// + // constructor helpers // + // =====================// + + /** + * Find the lowest valid point from the bottom. + */ + private short determineBottomPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ) + { + int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2); + + // search from the bottom up + for (int section = 0; section < CHUNK_DATA_WIDTH; section++) + { + for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++) + { + int numberOfBlocksFound = 0; + + for (int x = startX; x < endX; x++) + { + for (int z = startZ; z < endZ; z++) + { + if (isLayerValidLodPoint(chunkSections, section, y, x, z)) + { + numberOfBlocksFound++; + + if (numberOfBlocksFound >= numberOfBlocksRequired) + { + // we found + // enough blocks in this + // layer to count as an + // LOD point + return (short) (y + (section * CHUNK_SECTION_HEIGHT)); + } + } + } + } + } + } + + // we never found a valid LOD point + return DEFAULT_DEPTH; + } + + + /** + * Find the lowest valid point from the bottom. + */ + @SuppressWarnings("unused") + private short determineBottomPoint(Heightmap heightmap) + { + // the heightmap only shows how high the blocks go, it + // doesn't have any info about how low they go + return 0; + } + + /** + * Find the highest valid point from the Top + */ + private short determineHeightPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ) + { + int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2); + // search from the top down + for (int section = chunkSections.length - 1; section >= 0; section--) + { + for (int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--) + { + int numberOfBlocksFound = 0; + + for (int x = startX; x < endX; x++) + { + for (int z = startZ; z < endZ; z++) + { + if (isLayerValidLodPoint(chunkSections, section, y, x, z)) + { + numberOfBlocksFound++; + + if (numberOfBlocksFound >= numberOfBlocksRequired) + { + // we found + // enough blocks in this + // layer to count as an + // LOD point + return (short) (y + 1 + (section * CHUNK_SECTION_HEIGHT)); + } + } + } + } + } + } + + // we never found a valid LOD point + return DEFAULT_HEIGHT; + } + + + /** + * Find the highest point from the Top + */ + private short determineHeightPoint(Heightmap heightmap, int startX, int startZ, int endX, int endZ) + { + short highest = 0; + for (int x = startX; x < endX; x++) + { + for (int z = startZ; z < endZ; z++) + { + short newHeight = (short) heightmap.getFirstAvailable(x, z); + if (newHeight > highest) + highest = newHeight; + } + } + + return highest; + } + + /** + * Generate the color for the given chunk using biome water color, foliage + * color, and grass color. + * + * @param config_useSolidBlocksInColorGen
+ * If true we look down from the top of + * the
+ * chunk until we find a non-invisible + * block, and then use
+ * its color. If false we generate the + * color immediately for
+ * each x and z. + * @param config_useBiomeColors
+ * If true use biome foliage, water, and + * grass colors,
+ * otherwise only use the block's + * material color + */ + private short[] generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, + int endZ) + { + ChunkSection[] chunkSections = chunk.getSections(); + + int numbOfBlocks = 0; + int red = 0; + int green = 0; + int blue = 0; + + for (int x = startX; x < endX; x++) + { + for (int z = startZ; z < endZ; z++) + { + boolean foundBlock = false; + + // go top down + for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--) + { + if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen)) + { + for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--) + { + int colorInt = 0; + BlockState blockState = null; + + if (chunkSections[i] != null) + { + blockState = chunkSections[i].getBlockState(x, y, z); + colorInt = blockState.materialColor.col; + } + + if (colorInt == 0 && config.useSolidBlocksInColorGen) + { + // skip air or invisible blocks + continue; + } + + if (config.useBiomeColors) + { + // I have no idea why I need to bit shift to the right, but + // if I don't the biomes don't show up correctly. + Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2, + z >> 2); + colorInt = getColorForBiome(x, z, biome); + } else + { + + // the bit shift is equivalent to dividing by 4 + Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2, + z >> 2); + colorInt = getColorForBlock(x, z, blockState, biome); + } + + red += ColorUtil.getRed(colorInt); + green += ColorUtil.getGreen(colorInt); + blue += ColorUtil.getBlue(colorInt); + + numbOfBlocks++; + + // we found a valid block, skip to the + // next x and z + foundBlock = true; + } + } + } + } + } + + if (numbOfBlocks == 0) + numbOfBlocks = 1; + + red /= numbOfBlocks; + green /= numbOfBlocks; + blue /= numbOfBlocks; + + return new short[]{(short) red, (short) green, (short) blue}; + } + + /** + * Returns a color int for a given block. + */ + private int getColorForBlock(int x, int z, BlockState blockState, Biome biome) + { + int colorInt = 0; + + // block special cases + if (blockState == Blocks.AIR.defaultBlockState() || blockState == Blocks.CAVE_AIR.defaultBlockState()) + { + Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); + tmp = tmp.darker(); + colorInt = LodUtil.colorToInt(tmp); + } else if (blockState == Blocks.MYCELIUM.defaultBlockState()) + { + colorInt = LodUtil.MYCELIUM_COLOR_INT; + } + + // plant life + else if (blockState.getBlock() instanceof LeavesBlock || blockState.getBlock() == Blocks.VINE) + { + Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker(); + leafColor = leafColor.darker(); + colorInt = LodUtil.colorToInt(leafColor); + } else if ((blockState.getBlock() instanceof GrassBlock || blockState.getBlock() instanceof AbstractPlantBlock + || blockState.getBlock() instanceof BushBlock || blockState.getBlock() instanceof IGrowable) + && !(blockState.getBlock() == Blocks.BROWN_MUSHROOM || blockState.getBlock() == Blocks.RED_MUSHROOM)) + { + Color plantColor = LodUtil.intToColor(biome.getGrassColor(x, z)); + plantColor = plantColor.darker(); + colorInt = LodUtil.colorToInt(plantColor); + } + + // water + else if (blockState.getBlock() == Blocks.WATER) + { + colorInt = biome.getWaterColor(); + } + + // everything else + else + { + colorInt = blockState.materialColor.col; + } + + return colorInt; + } + + /** + * Returns a color int for the given biome. + */ + private int getColorForBiome(int x, int z, Biome biome) + { + int colorInt = 0; + + switch (biome.getBiomeCategory()) + { + + case NETHER: + colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col; + break; + + case THEEND: + colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col; + break; + + case BEACH: + case DESERT: + colorInt = Blocks.SAND.defaultBlockState().materialColor.col; + break; + + case EXTREME_HILLS: + colorInt = Blocks.STONE.defaultMaterialColor().col; + break; + + case MUSHROOM: + colorInt = MaterialColor.COLOR_LIGHT_GRAY.col; + break; + + case ICY: + colorInt = Blocks.SNOW.defaultMaterialColor().col; + break; + + case MESA: + colorInt = Blocks.RED_SAND.defaultMaterialColor().col; + break; + + case OCEAN: + case RIVER: + colorInt = biome.getWaterColor(); + break; + + case NONE: + case FOREST: + case TAIGA: + case JUNGLE: + case PLAINS: + case SAVANNA: + case SWAMP: + default: + Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); + tmp = tmp.darker(); + colorInt = LodUtil.colorToInt(tmp); + break; + + } + + return colorInt; + } + + /** + * Is the layer between the given X, Z, and dataIndex values a valid LOD point? + */ + private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z) + { + if (chunkSections[sectionIndex] == null) + { + // this section doesn't have any blocks, + // it is not a valid section + return false; + } else + { + if (chunkSections[sectionIndex].getBlockState(x, y, z) != null + && chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR + && chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.CAVE_AIR) + { + return true; + } + } + + return false; + } } diff --git a/src/main/java/com/seibel/lod/builders/LodBuilderConfig.java b/src/main/java/com/seibel/lod/builders/LodBuilderConfig.java index 2fd48ee31..60454948a 100644 --- a/src/main/java/com/seibel/lod/builders/LodBuilderConfig.java +++ b/src/main/java/com/seibel/lod/builders/LodBuilderConfig.java @@ -30,83 +30,83 @@ import com.seibel.lod.enums.DistanceGenerationMode; */ public class LodBuilderConfig { - /** - * default false - */ - public boolean useHeightmap; - /** - * default false - */ - public boolean useBiomeColors; - /** - * default true - */ - public boolean useSolidBlocksInColorGen; - /** - * default server - */ - public DistanceGenerationMode distanceGenerationMode; + /** + * default false + */ + public boolean useHeightmap; + /** + * default false + */ + public boolean useBiomeColors; + /** + * default true + */ + public boolean useSolidBlocksInColorGen; + /** + * default server + */ + public DistanceGenerationMode distanceGenerationMode; - /** - * default settings for a normal chunk
- * useHeightmap = false
- * useBiomeColors = false
- * useSolidBlocksInColorGen = true
- * generationMode = Server
- */ - public LodBuilderConfig() - { - useHeightmap = false; - useBiomeColors = false; - useSolidBlocksInColorGen = true; - distanceGenerationMode = DistanceGenerationMode.SERVER; - } + /** + * default settings for a normal chunk
+ * useHeightmap = false
+ * useBiomeColors = false
+ * useSolidBlocksInColorGen = true
+ * generationMode = Server
+ */ + public LodBuilderConfig() + { + useHeightmap = false; + useBiomeColors = false; + useSolidBlocksInColorGen = true; + distanceGenerationMode = DistanceGenerationMode.SERVER; + } - /** - * @param newUseHeightmap default = false - * @param newUseBiomeColors default = false - * @param newUseSolidBlocksInBiomeColor default = true - * @param newDistanceGenerationMode default = Server - */ - public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, - boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode) - { - useHeightmap = newUseHeightmap; - useBiomeColors = newUseBiomeColors; - useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor; - distanceGenerationMode = newDistanceGenerationMode; - } + /** + * @param newUseHeightmap default = false + * @param newUseBiomeColors default = false + * @param newUseSolidBlocksInBiomeColor default = true + * @param newDistanceGenerationMode default = Server + */ + public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, + boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode) + { + useHeightmap = newUseHeightmap; + useBiomeColors = newUseBiomeColors; + useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor; + distanceGenerationMode = newDistanceGenerationMode; + } - /** - * @param newUseHeightmap default = false - * @param newUseBiomeColors default = false - * @param newUseSolidBlocksInBiomeColor default = true - * @param newDistanceGenerationMode default = Server - */ - public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor) - { - this(); - useHeightmap = newUseHeightmap; - useBiomeColors = newUseBiomeColors; - useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor; - if (newUseHeightmap) - { - distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; - } else - { - distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY; - } - } + /** + * @param newUseHeightmap default = false + * @param newUseBiomeColors default = false + * @param newUseSolidBlocksInBiomeColor default = true + * @param newDistanceGenerationMode default = Server + */ + public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor) + { + this(); + useHeightmap = newUseHeightmap; + useBiomeColors = newUseBiomeColors; + useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor; + if (newUseHeightmap) + { + distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; + } else + { + distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY; + } + } - /** - * @param newUseHeightmap default = false - * @param newUseBiomeColors default = false - * @param newUseSolidBlocksInBiomeColor default = true - * @param newDistanceGenerationMode default = Server - */ - public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode) - { - this(); - distanceGenerationMode = newDistanceGenerationMode; - } + /** + * @param newUseHeightmap default = false + * @param newUseBiomeColors default = false + * @param newUseSolidBlocksInBiomeColor default = true + * @param newDistanceGenerationMode default = Server + */ + public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode) + { + this(); + distanceGenerationMode = newDistanceGenerationMode; + } } \ No newline at end of file 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 aa656f40b..c44036940 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java @@ -35,17 +35,21 @@ import net.minecraft.util.math.BlockPos; public abstract class AbstractLodTemplate { public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, - LevelPos levelPos, boolean debugging); + LevelPos levelPos, boolean debugging); - /** add the given position and color to the buffer */ + /** + * add the given position and color to the buffer + */ protected void addPosAndColor(BufferBuilder buffer, - double x, double y, double z, - int red, int green, int blue, int alpha) + double x, double y, double z, + int red, int green, int blue, int alpha) { buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex(); } - /** Returns in bytes how much buffer memory is required - * for one LOD object */ + /** + * Returns in bytes how much buffer memory is required + * for one LOD object + */ public abstract int getBufferMemoryForSingleNode(int level); } 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 f57af98f5..0eddc9508 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java @@ -38,89 +38,89 @@ import net.minecraft.util.math.BlockPos; */ public class CubicLodTemplate extends AbstractLodTemplate { - private final int CULL_OFFSET = 16; + private final int CULL_OFFSET = 16; - public CubicLodTemplate() - { + public CubicLodTemplate() + { - } + } - @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, - LevelPos levelPos, boolean debugging) - { - AxisAlignedBB bbox; + @Override + public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, + LevelPos levelPos, boolean debugging) + { + AxisAlignedBB bbox; - int width = 1 << levelPos.detailLevel; + int width = 1 << levelPos.detailLevel; - // add each LOD for the detail level - bbox = generateBoundingBox( - DataPoint.getHeight(data), - DataPoint.getDepth(data), - width, - levelPos.posX * width, - 0, - levelPos.posZ * width); + // add each LOD for the detail level + bbox = generateBoundingBox( + DataPoint.getHeight(data), + DataPoint.getDepth(data), + width, + levelPos.posX * width, + 0, + levelPos.posZ * width); - int color = DataPoint.getColor(data); - if (LodConfig.CLIENT.debugMode.get()) - { - color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel].getRGB(); - } + int color = DataPoint.getColor(data); + if (LodConfig.CLIENT.debugMode.get()) + { + color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel].getRGB(); + } - if (bbox != null) - { - addBoundingBoxToBuffer(buffer, bbox, color, playerBlockPos, adjData); - } + if (bbox != null) + { + addBoundingBoxToBuffer(buffer, bbox, color, playerBlockPos, adjData); + } - } + } - /* - * @Override public void addLodToBuffer(BufferBuilder buffer, - * LodQuadTreeDimension lodDim, LodQuadTreeNode lod, double xOffset, double - * yOffset, double zOffset, boolean debugging) { AxisAlignedBB bbox; - * - * bbox = generateBoundingBox( lod.getLodDataPoint().height, - * lod.getLodDataPoint().depth, lod.width, xOffset, yOffset, zOffset); - * - * Color color = lod.getLodDataPoint().color; - * - * if (bbox != null) { addBoundingBoxToBuffer(buffer, bbox, color); } - * - * } - */ + /* + * @Override public void addLodToBuffer(BufferBuilder buffer, + * LodQuadTreeDimension lodDim, LodQuadTreeNode lod, double xOffset, double + * yOffset, double zOffset, boolean debugging) { AxisAlignedBB bbox; + * + * bbox = generateBoundingBox( lod.getLodDataPoint().height, + * lod.getLodDataPoint().depth, lod.width, xOffset, yOffset, zOffset); + * + * Color color = lod.getLodDataPoint().color; + * + * if (bbox != null) { addBoundingBoxToBuffer(buffer, bbox, color); } + * + * } + */ - private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset) - { - // don't add an LOD if it is empty - if (height == -1 && depth == -1) - return null; + private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset) + { + // don't add an LOD if it is empty + if (height == -1 && depth == -1) + return null; - if (depth == height) - { - // if the top and bottom points are at the same height - // render this LOD as 1 block thick - height++; - } + if (depth == height) + { + // if the top and bottom points are at the same height + // render this LOD as 1 block thick + height++; + } - return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset); - } + return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset); + } - private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, int c, BlockPos playerBlockPos, short[][][] adjData) - { - int topColor = c; - int bottomColor = c; - int northColor = c; - int southColor = c; - int westColor = c; - int eastColor = c; + private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, int c, BlockPos playerBlockPos, short[][][] adjData) + { + int topColor = c; + int bottomColor = c; + int northColor = c; + int southColor = c; + int westColor = c; + int eastColor = c; - // darken the bottom and side colors if requested - if (LodConfig.CLIENT.shadingMode.get() == ShadingMode.DARKEN_SIDES) - { - // the side colors are different because - // when using fast lighting in Minecraft the north/south - // and east/west sides are different in a similar way + // darken the bottom and side colors if requested + if (LodConfig.CLIENT.shadingMode.get() == ShadingMode.DARKEN_SIDES) + { + // the side colors are different because + // when using fast lighting in Minecraft the north/south + // and east/west sides are different in a similar way /* int northSouthDarkenAmount = -25; int eastWestDarkenAmount = -50; @@ -133,217 +133,217 @@ public class CubicLodTemplate extends AbstractLodTemplate float northSouthDarkenAmount = 0.80f; float eastWestDarkenAmount = 0.60f; float bottomDarkenAmount = 0.40f;*/ - /**TODO OPTIMIZE THIS STEP*/ - topColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.UP,true)); - bottomColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.DOWN,true)); - northColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.NORTH,true)); - southColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.SOUTH,true)); - westColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.WEST,true)); - eastColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.EAST,true)); - } + /**TODO OPTIMIZE THIS STEP*/ + topColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.UP, true)); + bottomColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.DOWN, true)); + northColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.NORTH, true)); + southColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.SOUTH, true)); + westColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.WEST, true)); + eastColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.EAST, true)); + } - // apply the user specified saturation and brightness - float saturationMultiplier = LodConfig.CLIENT.saturationMultiplier.get().floatValue(); - float brightnessMultiplier = LodConfig.CLIENT.brightnessMultiplier.get().floatValue(); + // apply the user specified saturation and brightness + float saturationMultiplier = LodConfig.CLIENT.saturationMultiplier.get().floatValue(); + float brightnessMultiplier = LodConfig.CLIENT.brightnessMultiplier.get().floatValue(); - if(saturationMultiplier != 1 || brightnessMultiplier != 1) - { - topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier); - bottomColor = ColorUtil.applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier); - northColor = ColorUtil.applySaturationAndBrightnessMultipliers(northColor, saturationMultiplier, brightnessMultiplier); - southColor = ColorUtil.applySaturationAndBrightnessMultipliers(southColor, saturationMultiplier, brightnessMultiplier); - westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier); - eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier); - } - int minY; - int maxY; - short[] data; + if (saturationMultiplier != 1 || brightnessMultiplier != 1) + { + topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier); + bottomColor = ColorUtil.applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier); + northColor = ColorUtil.applySaturationAndBrightnessMultipliers(northColor, saturationMultiplier, brightnessMultiplier); + southColor = ColorUtil.applySaturationAndBrightnessMultipliers(southColor, saturationMultiplier, brightnessMultiplier); + westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier); + eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier); + } + int minY; + int maxY; + short[] data; - int red; - int green; - int blue; - int alpha; - /**TODO make all of this more automatic if possible*/ - if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET) - { - red = ColorUtil.getRed(topColor); - green = ColorUtil.getGreen(topColor); - blue = ColorUtil.getBlue(topColor); - alpha = ColorUtil.getAlpha(topColor); - // top (facing up) - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); - } - if (playerBlockPos.getY() < bb.minY + CULL_OFFSET) - { - red = ColorUtil.getRed(bottomColor); - green = ColorUtil.getGreen(bottomColor); - blue = ColorUtil.getBlue(bottomColor); - alpha = ColorUtil.getAlpha(bottomColor); - // bottom (facing down) - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); - } + int red; + int green; + int blue; + int alpha; + /**TODO make all of this more automatic if possible*/ + if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET) + { + red = ColorUtil.getRed(topColor); + green = ColorUtil.getGreen(topColor); + blue = ColorUtil.getBlue(topColor); + alpha = ColorUtil.getAlpha(topColor); + // top (facing up) + addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); + } + if (playerBlockPos.getY() < bb.minY + CULL_OFFSET) + { + red = ColorUtil.getRed(bottomColor); + green = ColorUtil.getGreen(bottomColor); + blue = ColorUtil.getBlue(bottomColor); + alpha = ColorUtil.getAlpha(bottomColor); + // bottom (facing down) + addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); + } - if (playerBlockPos.getZ() > bb.minZ - CULL_OFFSET) - { - red = ColorUtil.getRed(northColor); - green = ColorUtil.getGreen(northColor); - blue = ColorUtil.getBlue(northColor); - alpha = ColorUtil.getAlpha(northColor); - // south (facing -Z) - data = adjData[1][1]; - if (data == null) - { - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); - } else - { - maxY = DataPoint.getHeight(data); - if (maxY < bb.maxY) - { - minY = (int) Math.max(maxY, bb.minY); - addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha); - } - minY = DataPoint.getDepth(data); - if (minY > bb.minY) - { - maxY = (int) Math.min(minY, bb.maxY); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); - } - } - } + if (playerBlockPos.getZ() > bb.minZ - CULL_OFFSET) + { + red = ColorUtil.getRed(northColor); + green = ColorUtil.getGreen(northColor); + blue = ColorUtil.getBlue(northColor); + alpha = ColorUtil.getAlpha(northColor); + // south (facing -Z) + data = adjData[1][1]; + if (data == null) + { + addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); + } else + { + maxY = DataPoint.getHeight(data); + if (maxY < bb.maxY) + { + minY = (int) Math.max(maxY, bb.minY); + addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha); + } + minY = DataPoint.getDepth(data); + if (minY > bb.minY) + { + maxY = (int) Math.min(minY, bb.maxY); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); + } + } + } - if (playerBlockPos.getZ() < bb.maxZ + CULL_OFFSET) - { - red = ColorUtil.getRed(southColor); - green = ColorUtil.getGreen(southColor); - blue = ColorUtil.getBlue(southColor); - alpha = ColorUtil.getAlpha(southColor); - data = adjData[1][0]; - // north (facing +Z) - if (data == null) - { - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); - } else - { - maxY = DataPoint.getHeight(data); - if (maxY < bb.maxY) - { - minY = (int) Math.max(maxY, bb.minY); - addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha); - } - minY = DataPoint.getDepth(data); - if (minY > bb.minY) - { - maxY = (int) Math.min(minY, bb.maxY); - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); - } - } - } + if (playerBlockPos.getZ() < bb.maxZ + CULL_OFFSET) + { + red = ColorUtil.getRed(southColor); + green = ColorUtil.getGreen(southColor); + blue = ColorUtil.getBlue(southColor); + alpha = ColorUtil.getAlpha(southColor); + data = adjData[1][0]; + // north (facing +Z) + if (data == null) + { + addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); + } else + { + maxY = DataPoint.getHeight(data); + if (maxY < bb.maxY) + { + minY = (int) Math.max(maxY, bb.minY); + addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha); + } + minY = DataPoint.getDepth(data); + if (minY > bb.minY) + { + maxY = (int) Math.min(minY, bb.maxY); + addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); + } + } + } - if (playerBlockPos.getX() < bb.maxX + CULL_OFFSET) - { - red = ColorUtil.getRed(westColor); - green = ColorUtil.getGreen(westColor); - blue = ColorUtil.getBlue(westColor); - alpha = ColorUtil.getAlpha(westColor); - // west (facing -X) - data = adjData[0][0]; - if (data == null) - { - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); - } else - { - maxY = DataPoint.getHeight(data); - if (maxY < bb.maxY) - { - minY = (int) Math.max(maxY, bb.minY); - addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); - } - minY = DataPoint.getDepth(data); - if (minY > bb.minY) - { - maxY = (int) Math.min(minY, bb.maxY); - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha); - } - } - } + if (playerBlockPos.getX() < bb.maxX + CULL_OFFSET) + { + red = ColorUtil.getRed(westColor); + green = ColorUtil.getGreen(westColor); + blue = ColorUtil.getBlue(westColor); + alpha = ColorUtil.getAlpha(westColor); + // west (facing -X) + data = adjData[0][0]; + if (data == null) + { + addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); + } else + { + maxY = DataPoint.getHeight(data); + if (maxY < bb.maxY) + { + minY = (int) Math.max(maxY, bb.minY); + addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha); + } + minY = DataPoint.getDepth(data); + if (minY > bb.minY) + { + maxY = (int) Math.min(minY, bb.maxY); + addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha); + } + } + } - if (playerBlockPos.getX() > bb.minX - CULL_OFFSET) - { - red = ColorUtil.getRed(eastColor); - green = ColorUtil.getGreen(eastColor); - blue = ColorUtil.getBlue(eastColor); - alpha = ColorUtil.getAlpha(eastColor); - // east (facing +X) - data = adjData[0][1]; - if (data == null) - { - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); - } else - { - maxY = DataPoint.getHeight(data); - if (maxY < bb.maxY) - { - minY = (int) Math.max(maxY, bb.minY); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha); - } - minY = DataPoint.getDepth(data); - if (minY > bb.minY) - { - maxY = (int) Math.min(minY, bb.maxY); - addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); - } - } - } - } + if (playerBlockPos.getX() > bb.minX - CULL_OFFSET) + { + red = ColorUtil.getRed(eastColor); + green = ColorUtil.getGreen(eastColor); + blue = ColorUtil.getBlue(eastColor); + alpha = ColorUtil.getAlpha(eastColor); + // east (facing +X) + data = adjData[0][1]; + if (data == null) + { + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); + } else + { + maxY = DataPoint.getHeight(data); + if (maxY < bb.maxY) + { + minY = (int) Math.max(maxY, bb.minY); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha); + } + minY = DataPoint.getDepth(data); + if (minY > bb.minY) + { + maxY = (int) Math.min(minY, bb.maxY); + addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha); + addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha); + } + } + } + } - @Override - public int getBufferMemoryForSingleNode(int detailLevel) - { - // (sidesOnACube * pointsInASquare * (positionPoints + colorPoints))) * - // howManyPointsPerLodChunk - return (6 * 4 * (3 + 4)); - } + @Override + public int getBufferMemoryForSingleNode(int detailLevel) + { + // (sidesOnACube * pointsInASquare * (positionPoints + colorPoints))) * + // howManyPointsPerLodChunk + return (6 * 4 * (3 + 4)); + } } 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 4c4d0411b..dcaf81f25 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java @@ -27,7 +27,7 @@ import net.minecraft.util.math.BlockPos; * Chunks smoothly transition between * each other, unless a neighboring chunk * is at a significantly different height. - * + * * @author James Seibel * @version 06-16-2021 */ @@ -35,7 +35,7 @@ public class DynamicLodTemplate extends AbstractLodTemplate { @Override public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, - LevelPos levelPos, boolean debugging) + 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 f581fd340..a3cf999ee 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java @@ -25,7 +25,7 @@ import net.minecraft.util.math.BlockPos; /** * TODO #21 TriangularLodTemplate * Builds each LOD chunk as a singular rectangular prism. - * + * * @author James Seibel * @version 06-16-2021 */ @@ -33,7 +33,7 @@ public class TriangularLodTemplate extends AbstractLodTemplate { @Override public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData, - LevelPos levelPos, boolean debugging) + LevelPos levelPos, boolean debugging) { System.err.println("DynamicLodTemplate not implemented!"); } diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodServerWorld.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodServerWorld.java index e6245ce73..b95013703 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodServerWorld.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodServerWorld.java @@ -65,259 +65,266 @@ import net.minecraft.world.storage.IWorldInfo; * This allows us to keep each LodChunk generation independent * of the actual ServerWorld, allowing us * to multithread generation. - * + * * @author James Seibel * @version 7-26-2021 */ -public class LodServerWorld implements ISeedReader { - +public class LodServerWorld implements ISeedReader +{ + public HashMap heightmaps = new HashMap<>(); - + public IChunk chunk; - + public ServerWorld serverWorld; - + public LodServerWorld(ServerWorld newServerWorld, IChunk newChunk) { chunk = newChunk; serverWorld = newServerWorld; } - - - + + @Override public int getHeight(Type heightmapType, int x, int z) { // make sure the block position is set relative to the chunk x = x % LodUtil.CHUNK_WIDTH; x = (x < 0) ? x + 16 : x; - + z = z % LodUtil.CHUNK_WIDTH; z = (z < 0) ? z + 16 : z; - + return chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z); } - + @Override public Biome getBiome(BlockPos pos) { return chunk.getBiomes().getNoiseBiome(pos.getX() >> 2, pos.getY() >> 2, pos.getZ() >> 2); } - + @Override public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) { return chunk.setBlockState(pos, state, false) == state; } - + @Override public BlockState getBlockState(BlockPos pos) { return chunk.getBlockState(pos); } - + @Override public FluidState getFluidState(BlockPos pos) { return chunk.getFluidState(pos); } - - - + + @Override public boolean isStateAtPosition(BlockPos pos, Predicate state) { return state.test(chunk.getBlockState(pos)); } - + @Override public ITickList getBlockTicks() { return EmptyTickList.empty(); } - + @Override public IChunk getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull) { return chunk; } - + @Override public Stream> startsForFeature(SectionPos p_241827_1_, Structure p_241827_2_) { return serverWorld.startsForFeature(p_241827_1_, p_241827_2_); } - + @Override public ITickList getLiquidTicks() { return EmptyTickList.empty(); } - + @Override public WorldLightManager getLightEngine() { return new WorldLightManager(null, false, false); } - + @Override public long getSeed() { return serverWorld.getSeed(); } - + @Override public DynamicRegistries registryAccess() { return serverWorld.registryAccess(); } - - - - + + /** - * * All methods below shouldn't be needed - * and thus have been left unimplemented. - * + * and thus have been left unimplemented. */ - - - + + @Override - public Random getRandom() { + public Random getRandom() + { throw new UnsupportedOperationException("Not Implemented"); } - + @Override public void playSound(PlayerEntity player, BlockPos pos, SoundEvent soundIn, SoundCategory category, float volume, - float pitch) { + float pitch) + { throw new UnsupportedOperationException("Not Implemented"); } - + @Override public void addParticle(IParticleData particleData, double x, double y, double z, double xSpeed, double ySpeed, - double zSpeed) { - throw new UnsupportedOperationException("Not Implemented"); - } - @Override - public BiomeManager getBiomeManager() { - throw new UnsupportedOperationException("Not Implemented"); - } - @Override - public int getSeaLevel() { - throw new UnsupportedOperationException("Not Implemented"); - } - - @Override - public float getShade(Direction p_230487_1_, boolean p_230487_2_) { - throw new UnsupportedOperationException("Not Implemented"); - } - - @Override - public WorldBorder getWorldBorder() { - throw new UnsupportedOperationException("Not Implemented"); - } - - @Override - public boolean removeBlock(BlockPos pos, boolean isMoving) { - throw new UnsupportedOperationException("Not Implemented"); - } - - @Override - public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft) { + double zSpeed) + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public ServerWorld getLevel() { + public BiomeManager getBiomeManager() + { throw new UnsupportedOperationException("Not Implemented"); } - + @Override + public int getSeaLevel() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public float getShade(Direction p_230487_1_, boolean p_230487_2_) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public WorldBorder getWorldBorder() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean removeBlock(BlockPos pos, boolean isMoving) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft) + { + throw new UnsupportedOperationException("Not Implemented"); + } @Override - public AbstractChunkProvider getChunkSource() { + public ServerWorld getLevel() + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0) { + public AbstractChunkProvider getChunkSource() + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public IWorldInfo getLevelData() { + public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0) + { throw new UnsupportedOperationException("Not Implemented"); } - - @Override - public void levelEvent(PlayerEntity arg0, int arg1, BlockPos arg2, int arg3) { - throw new UnsupportedOperationException("Not Implemented"); - - } - - @Override - public List getEntities(Entity arg0, AxisAlignedBB arg1, Predicate arg2) { + public IWorldInfo getLevelData() + { throw new UnsupportedOperationException("Not Implemented"); } + @Override + public void levelEvent(PlayerEntity arg0, int arg1, BlockPos arg2, int arg3) + { + throw new UnsupportedOperationException("Not Implemented"); + + } + + + @Override + public List getEntities(Entity arg0, AxisAlignedBB arg1, Predicate arg2) + { + throw new UnsupportedOperationException("Not Implemented"); + } + @Override public List getEntitiesOfClass(Class arg0, AxisAlignedBB arg1, - Predicate arg2) { + Predicate arg2) + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public List players() { + public List players() + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public int getSkyDarken() { + public int getSkyDarken() + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) { + public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public boolean isClientSide() { + public boolean isClientSide() + { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public DimensionType dimensionType() { + public DimensionType dimensionType() + { throw new UnsupportedOperationException("Not Implemented"); } - - + + @Override - public TileEntity getBlockEntity(BlockPos p_175625_1_) { + public TileEntity getBlockEntity(BlockPos p_175625_1_) + { throw new UnsupportedOperationException("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 fa006bbac..a7f523b23 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java @@ -28,20 +28,24 @@ import net.minecraftforge.common.WorldWorkerManager; /** * A singleton that handles all long distance LOD world generation. - * + * * @author James Seibel * @version 8-24-2021 */ public class LodWorldGenerator { public Minecraft mc = Minecraft.getInstance(); - - /** This holds the thread used to generate new LODs off the main thread. */ + + /** + * This holds the thread used to generate new LODs off the main thread. + */ private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator")); - - /** we only want to queue up one generator thread at a time */ + + /** + * we only want to queue up one generator thread at a time + */ private boolean generatorThreadRunning = false; - + /** * how many chunks to generate outside of the player's view distance at one * time. (or more specifically how many requests to make at one time). I @@ -50,42 +54,44 @@ public class LodWorldGenerator * possible. */ public int maxChunkGenRequests; - + /** * This keeps track of how many chunk generation requests are on going. This is * to limit how many chunks are queued at once. To prevent chunks from being * generated for a long time in an area the player is no longer in. */ public AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); - + public Set positionWaitingToBeGenerated = new HashSet<>(); - - /** Singleton copy of this object */ + + /** + * Singleton copy of this object + */ public static final LodWorldGenerator INSTANCE = new LodWorldGenerator(); - + private LodWorldGenerator() { - + } - + /** * Queues up LodNodeGenWorkers for the given lodDimension. - * + * * @param renderer needed so the LodNodeGenWorkers can flag that the - * buffers need to be rebuilt. + * buffers need to be rebuilt. */ public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder) { - if (LodConfig.CLIENT.distanceGenerationMode.get() != DistanceGenerationMode.NONE - && !generatorThreadRunning - && mc.hasSingleplayerServer()) + if (LodConfig.CLIENT.distanceGenerationMode.get() != DistanceGenerationMode.NONE + && !generatorThreadRunning + && mc.hasSingleplayerServer()) { // the thread is now running, don't queue up another thread generatorThreadRunning = true; - + // just in case the config changed maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8; - + Thread generatorThread = new Thread(() -> { try @@ -93,23 +99,23 @@ public class LodWorldGenerator // round the player's block position down to the nearest chunk BlockPos ChunkPos playerChunkPos = new ChunkPos(mc.player.blockPosition()); BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition(); - + // used when determining which chunks are closer when queuing distance // generation int minChunkDist = Integer.MAX_VALUE; - + List levelPosListToGen; List generationRequestList = new ArrayList<>(); - + ArrayList chunksToGen = new ArrayList<>(maxChunkGenRequests); // if we don't have a full number of chunks to generate in chunksToGen // we can top it off from this reserve ArrayList chunksToGenReserve = new ArrayList<>(maxChunkGenRequests); - + // how many level positions to int requesting = maxChunkGenRequests; - - + + /** TODO can give a totally different generation */ /* * for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <= @@ -122,20 +128,21 @@ public class LodWorldGenerator * (byte) distancesGenerators[detailGen].complexity, detail, 16)); * System.out.println("HERE"); } } */ - - + + //=======================================// // create the generation Request objects // //=======================================// - + // start by generating half-region sized blocks... - int farRequesting = maxChunkGenRequests/4; + 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){ + if (farRequesting <= 0) + { break; } levelPosListToGen = lodDim.getDataToGenerate( @@ -146,17 +153,18 @@ public class LodWorldGenerator DetailDistanceUtil.getDistanceGenerationMode(detailGen).complexity, (byte) 8, farRequesting); - for(LevelPos levelPos : levelPosListToGen){ - generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen))); + 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/4 + farRequesting; + 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++) { @@ -169,23 +177,24 @@ public class LodWorldGenerator DetailDistanceUtil.getDistanceGenerationMode(detailGen).complexity, DetailDistanceUtil.getLodDetail(detailGen).detailLevel, nearRequesting); - for(LevelPos levelPos : levelPosListToGen){ - generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen))); + for (LevelPos levelPos : levelPosListToGen) + { + generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen))); } nearRequesting = nearRequesting - levelPosListToGen.size(); } - - + + //====================================// // get the closet generation requests // //====================================// - + // determine which points in the posListToGenerate // should actually be queued to generate for (GenerationRequest generationRequest : generationRequestList) { ChunkPos chunkPos = generationRequest.getChunkPos(); - + if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests) { // prevent generating the same chunk multiple times @@ -194,17 +203,17 @@ public class LodWorldGenerator // ClientProxy.LOGGER.debug(pos + " asked to be generated again."); continue; } - + // determine if this position is closer to the player // than the previous int newDistance = playerChunkPos.getChessboardDistance(chunkPos); - + if (newDistance < minChunkDist) { // this chunk is closer, clear any previous // positions and update the new minimum distance minChunkDist = newDistance; - + // move all the old chunks into the reserve ArrayList oldReserve = new ArrayList<>(chunksToGenReserve); chunksToGenReserve.clear(); @@ -217,11 +226,10 @@ public class LodWorldGenerator else break; } - + chunksToGen.clear(); chunksToGen.add(generationRequest); - } - else if (newDistance == minChunkDist) + } else if (newDistance == minChunkDist) { // this chunk position as close as the minimum distance if (chunksToGen.size() < maxChunkGenRequests) @@ -230,17 +238,16 @@ public class LodWorldGenerator // add this position to the list chunksToGen.add(generationRequest); } - } - else + } else { // this chunk is farther away than the minimum distance, // add it to the reserve to make sure we always have a full reserve chunksToGenReserve.add(generationRequest); } - + } // lod null and can generate more chunks } // positions to generate - + // fill up chunksToGen from the reserve if it isn't full // already if (chunksToGen.size() < maxChunkGenRequests) @@ -251,18 +258,16 @@ public class LodWorldGenerator chunksToGen.add(reserveIterator.next()); } } - - - - + + //=============================// // start the LodNodeGenWorkers // //=============================// - + // issue #19 // TODO add a way for a server side mod to generate chunks requested here ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension); - + // start chunk generation for (GenerationRequest generationRequest : generationRequestList) { @@ -271,27 +276,25 @@ public class LodWorldGenerator ChunkPos chunkPos = generationRequest.getChunkPos(); if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) continue; - + positionWaitingToBeGenerated.add(chunkPos); numberOfChunksWaitingToGenerate.addAndGet(1); LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, generationRequest.generationMode, generationRequest.detail, renderer, lodBuilder, lodDim, serverWorld); WorldWorkerManager.addWorker(genWorker); } - - } - catch (Exception e) + + } catch (Exception e) { // this shouldn't ever happen, but just in case e.printStackTrace(); - } - finally + } finally { generatorThreadRunning = false; } }); - + mainGenThread.execute(generatorThread); } // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning } - + } diff --git a/src/main/java/com/seibel/lod/enums/DistanceCalculatorType.java b/src/main/java/com/seibel/lod/enums/DistanceCalculatorType.java index ae325f518..aa9df0682 100644 --- a/src/main/java/com/seibel/lod/enums/DistanceCalculatorType.java +++ b/src/main/java/com/seibel/lod/enums/DistanceCalculatorType.java @@ -6,12 +6,18 @@ package com.seibel.lod.enums; */ public enum DistanceCalculatorType { - /** different Lod detail render and generate linearly to the distance */ - LINEAR, + /** + * different Lod detail render and generate linearly to the distance + */ + LINEAR, - /** different Lod detail render and generate quadratically to the distance */ - QUADRATIC, + /** + * different Lod detail render and generate quadratically to the distance + */ + QUADRATIC, - /** we calculate the distance based on game render distance and mod render distance*/ - RENDER_DEPENDANT; + /** + * we calculate the distance based on game render distance and mod render distance + */ + RENDER_DEPENDANT; } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java b/src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java index fb89e5edb..fb88826dc 100644 --- a/src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java +++ b/src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java @@ -24,25 +24,29 @@ package com.seibel.lod.enums; * SURFACE
* FEATURES
* SERVER

- * + *

* In order of fastest to slowest. - * + * * @author James Seibel * @author Leonardo Amato * @version 8-7-2021 */ public enum DistanceGenerationMode { - /** Don't generate anything */ + /** + * Don't generate anything + */ NONE((byte) 0), - - /** Only generate the biomes and use biome + + /** + * Only generate the biomes and use biome * grass/foliage color, water color, or ice color * to generate the color. - * Doesn't generate height, everything is shown at sea level. - * Multithreaded - Fastest (2-5 ms) */ + * Doesn't generate height, everything is shown at sea level. + * Multithreaded - Fastest (2-5 ms) + */ BIOME_ONLY((byte) 1), - + /** * Same as BIOME_ONLY, except instead * of always using sea level as the LOD height @@ -50,30 +54,38 @@ public enum DistanceGenerationMode * use predetermined heights to simulate having height data. */ BIOME_ONLY_SIMULATE_HEIGHT((byte) 2), - - /** Generate the world surface, + + /** + * Generate the world surface, * this does NOT include caves, trees, - * or structures. - * Multithreaded - Faster (10-20 ms) */ + * or structures. + * Multithreaded - Faster (10-20 ms) + */ SURFACE((byte) 3), - - /** Generate everything except structures. + + /** + * Generate everything except structures. * NOTE: This may cause world generation bugs or instability, * since some features cause concurrentModification exceptions. - * Multithreaded - Fast (15-20 ms) */ + * Multithreaded - Fast (15-20 ms) + */ FEATURES((byte) 4), - - /** Ask the server to generate/load each chunk. + + /** + * Ask the server to generate/load each chunk. * This is the most compatible, but causes server/simulation lag. * This will also show player made structures if you - * are adding the mod to a pre-existing world. - * Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) */ + * are adding the mod to a pre-existing world. + * Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) + */ SERVER((byte) 5); - - - /** The higher the number the more complete the generation is. */ + + + /** + * The higher the number the more complete the generation is. + */ public final byte complexity; - + DistanceGenerationMode(byte complexity) { this.complexity = complexity; diff --git a/src/main/java/com/seibel/lod/handlers/LodConfig.java b/src/main/java/com/seibel/lod/handlers/LodConfig.java index fa9055231..81113197c 100644 --- a/src/main/java/com/seibel/lod/handlers/LodConfig.java +++ b/src/main/java/com/seibel/lod/handlers/LodConfig.java @@ -42,281 +42,281 @@ import net.minecraftforge.fml.config.ModConfig; @Mod.EventBusSubscriber public class LodConfig { - public static class Client - { - public ForgeConfigSpec.BooleanValue drawLODs; + public static class Client + { + public ForgeConfigSpec.BooleanValue drawLODs; - public ForgeConfigSpec.EnumValue fogDistance; + public ForgeConfigSpec.EnumValue fogDistance; - public ForgeConfigSpec.EnumValue fogDrawOverride; + public ForgeConfigSpec.EnumValue fogDrawOverride; - public ForgeConfigSpec.BooleanValue debugMode; + public ForgeConfigSpec.BooleanValue debugMode; - public ForgeConfigSpec.EnumValue lodTemplate; + public ForgeConfigSpec.EnumValue lodTemplate; - public ForgeConfigSpec.EnumValue maxDrawDetail; + public ForgeConfigSpec.EnumValue maxDrawDetail; - public ForgeConfigSpec.EnumValue maxGenerationDetail; + public ForgeConfigSpec.EnumValue maxGenerationDetail; - public ForgeConfigSpec.EnumValue distanceGenerationMode; + public ForgeConfigSpec.EnumValue distanceGenerationMode; - public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; + public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; - public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads; + public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads; - public ForgeConfigSpec.EnumValue shadingMode; + public ForgeConfigSpec.EnumValue shadingMode; - public ForgeConfigSpec.EnumValue lodDistanceCalculatorType; + public ForgeConfigSpec.EnumValue lodDistanceCalculatorType; - public ForgeConfigSpec.IntValue lodQuality; + public ForgeConfigSpec.IntValue lodQuality; - public ForgeConfigSpec.IntValue lodChunkRenderDistance; + public ForgeConfigSpec.IntValue lodChunkRenderDistance; - public ForgeConfigSpec.DoubleValue brightnessMultiplier; + public ForgeConfigSpec.DoubleValue brightnessMultiplier; - public ForgeConfigSpec.DoubleValue saturationMultiplier; + public ForgeConfigSpec.DoubleValue saturationMultiplier; - Client(ForgeConfigSpec.Builder builder) - { - builder.comment(ModInfo.MODNAME + " configuration settings").push("client"); + Client(ForgeConfigSpec.Builder builder) + { + builder.comment(ModInfo.MODNAME + " configuration settings").push("client"); - drawLODs = builder - .comment("\n\n" - + " If false LODs will not be drawn, \n" - + " however they will still be generated \n" - + " and saved to file for later use. \n") - .define("drawLODs", true); + drawLODs = builder + .comment("\n\n" + + " If false LODs will not be drawn, \n" + + " however they will still be generated \n" + + " and saved to file for later use. \n") + .define("drawLODs", true); - fogDistance = builder - .comment("\n\n" - + " At what distance should Fog be drawn on the LODs? \n" - + " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n" - + " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n") - .defineEnum("fogDistance", FogDistance.FAR); + fogDistance = builder + .comment("\n\n" + + " At what distance should Fog be drawn on the LODs? \n" + + " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n" + + " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n") + .defineEnum("fogDistance", FogDistance.FAR); - fogDrawOverride = builder - .comment("\n\n" - + " When should fog be drawn? \n" - + " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n" - + " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n" - + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n" - + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n") - .defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING); + fogDrawOverride = builder + .comment("\n\n" + + " When should fog be drawn? \n" + + " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n" + + " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n" + + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n" + + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n") + .defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING); - debugMode = builder - .comment("\n\n" - + " If false the LODs will draw with their normal colors. \n" - + " If true LODs colors will be based on their detail \n" - + " level. \n") - .define("debugMode", false); + debugMode = builder + .comment("\n\n" + + " If false the LODs will draw with their normal colors. \n" + + " If true LODs colors will be based on their detail \n" + + " level. \n") + .define("debugMode", false); - lodTemplate = builder - .comment("\n\n" - + " How should the LODs be drawn? \n" - + " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n" - + " \n" - + " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n" - + " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n" - + " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n" - + " " + " unless a neighboring chunk is at a significantly different height. \n") - .defineEnum("lodTemplate", LodTemplate.CUBIC); + lodTemplate = builder + .comment("\n\n" + + " How should the LODs be drawn? \n" + + " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n" + + " \n" + + " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n" + + " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n" + + " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n" + + " " + " unless a neighboring chunk is at a significantly different height. \n") + .defineEnum("lodTemplate", LodTemplate.CUBIC); - maxDrawDetail = builder - .comment("\n\n" - + " What is the maximum detail level that LODs should be drawn at? \n" - + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" - + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" - + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" - + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" - + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") - .defineEnum("lodDrawQuality", LodDetail.FULL); + maxDrawDetail = builder + .comment("\n\n" + + " What is the maximum detail level that LODs should be drawn at? \n" + + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" + + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" + + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" + + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" + + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") + .defineEnum("lodDrawQuality", LodDetail.FULL); - maxGenerationDetail = builder - .comment("\n\n" - + " What is the maximum detail level that LODs should be generated at? \n" - + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" - + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" - + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" - + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" - + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") - .defineEnum("lodGenerationQuality", LodDetail.FULL); + maxGenerationDetail = builder + .comment("\n\n" + + " What is the maximum detail level that LODs should be generated at? \n" + + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" + + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" + + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" + + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" + + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") + .defineEnum("lodGenerationQuality", LodDetail.FULL); - lodDistanceCalculatorType = builder - .comment("\n\n" - + " " + DistanceCalculatorType.LINEAR + " \n" - + " with LINEAR calculator the quality of block decrease \n" - + " linearly to the distance of the player \n" + lodDistanceCalculatorType = builder + .comment("\n\n" + + " " + DistanceCalculatorType.LINEAR + " \n" + + " with LINEAR calculator the quality of block decrease \n" + + " linearly to the distance of the player \n" - + "\n" - + " " + DistanceCalculatorType.QUADRATIC + " \n" - + " with LINEAR calculator the quality of block decrease \n" - + " quadratically to the distance of the player \n" + + "\n" + + " " + DistanceCalculatorType.QUADRATIC + " \n" + + " with LINEAR calculator the quality of block decrease \n" + + " quadratically to the distance of the player \n" - + "\n" - + " " + DistanceCalculatorType.RENDER_DEPENDANT + " \n" - + " with LINEAR calculator the quality of block decrease \n" - + " quadratically to the distance of the player \n") - .defineEnum("lodDistanceComputation", DistanceCalculatorType.LINEAR); + + "\n" + + " " + DistanceCalculatorType.RENDER_DEPENDANT + " \n" + + " with LINEAR calculator the quality of block decrease \n" + + " quadratically to the distance of the player \n") + .defineEnum("lodDistanceComputation", DistanceCalculatorType.LINEAR); - lodQuality = builder - .comment("\n\n" - + " this value is multiplied by 128 and determine \n" - + " how much the quality decrease over distance \n") - .defineInRange("lodQuality", 1, 1, 4); + lodQuality = builder + .comment("\n\n" + + " this value is multiplied by 128 and determine \n" + + " how much the quality decrease over distance \n") + .defineInRange("lodQuality", 1, 1, 4); - lodChunkRenderDistance = builder - .comment("\n\n" - + " This is the render distance of the mod \n") - .defineInRange("lodChunkRenderDistane", 128, 32, 256); + lodChunkRenderDistance = builder + .comment("\n\n" + + " This is the render distance of the mod \n") + .defineInRange("lodChunkRenderDistane", 128, 32, 256); - distanceGenerationMode = builder - .comment("\n\n" - + " Note: The times listed here are the amount of time it took \n" - + " the developer's PC to generate 1 chunk, \n" - + " and are included so you can compare the \n" - + " different generation options. Your mileage may vary. \n" - + "\n" + distanceGenerationMode = builder + .comment("\n\n" + + " Note: The times listed here are the amount of time it took \n" + + " the developer's PC to generate 1 chunk, \n" + + " and are included so you can compare the \n" + + " different generation options. Your mileage may vary. \n" + + "\n" - + " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n" - + " Only generate the biomes and use biome \n" - + " grass/foliage color, water color, or snow color \n" - + " to generate the color. \n" - + " Doesn't generate height, everything is shown at sea level. \n" - + " Multithreaded - Fastest (2-5 ms) \n" + + " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n" + + " Only generate the biomes and use biome \n" + + " grass/foliage color, water color, or snow color \n" + + " to generate the color. \n" + + " Doesn't generate height, everything is shown at sea level. \n" + + " Multithreaded - Fastest (2-5 ms) \n" - + "\n" - + " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n" - + " Same as BIOME_ONLY, except instead \n" - + " of always using sea level as the LOD height \n" - + " different biome types (mountain, ocean, forest, etc.) \n" - + " use predetermined heights to simulate having height data. \n" - + " Multithreaded - Fastest (2-5 ms) \n" + + "\n" + + " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n" + + " Same as BIOME_ONLY, except instead \n" + + " of always using sea level as the LOD height \n" + + " different biome types (mountain, ocean, forest, etc.) \n" + + " use predetermined heights to simulate having height data. \n" + + " Multithreaded - Fastest (2-5 ms) \n" - + "\n" - + " " + DistanceGenerationMode.SURFACE.toString() + " \n" - + " Generate the world surface, \n" - + " this does NOT include caves, trees, \n" - + " or structures. \n" - + " Multithreaded - Faster (10-20 ms) \n" + + "\n" + + " " + DistanceGenerationMode.SURFACE.toString() + " \n" + + " Generate the world surface, \n" + + " this does NOT include caves, trees, \n" + + " or structures. \n" + + " Multithreaded - Faster (10-20 ms) \n" - + "\n" - + " " + DistanceGenerationMode.FEATURES.toString() + " \n" - + " Generate everything except structures. \n" - + " WARNING: This may cause world generation bugs or instability! \n" - + " Multithreaded - Fast (15-20 ms) \n" + + "\n" + + " " + DistanceGenerationMode.FEATURES.toString() + " \n" + + " Generate everything except structures. \n" + + " WARNING: This may cause world generation bugs or instability! \n" + + " Multithreaded - Fast (15-20 ms) \n" - + "\n" - + " " + DistanceGenerationMode.SERVER.toString() + " \n" - + " Ask the server to generate/load each chunk. \n" - + " This is the most compatible, but causes server/simulation lag. \n" - + " This will also show player made structures if you \n" - + " are adding the mod to a pre-existing world. \n" - + " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n") - .defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE); + + "\n" + + " " + DistanceGenerationMode.SERVER.toString() + " \n" + + " Ask the server to generate/load each chunk. \n" + + " This is the most compatible, but causes server/simulation lag. \n" + + " This will also show player made structures if you \n" + + " are adding the mod to a pre-existing world. \n" + + " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n") + .defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE); - allowUnstableFeatureGeneration = builder - .comment("\n\n" - + " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n" - + " some features may not be thread safe, which could \n" - + " cause instability and crashes. \n" - + " By default (false) those features are skipped, \n" - + " improving stability, but decreasing how many features are \n" - + " actually generated. \n" - + " (for example: some tree generation is unstable, \n" - + " so some trees may not be generated.) \n" - + " By setting this to true, all features will be generated, \n" - + " but your game will be more unstable and crashes may occur. \n" - + " \n" - + " I would love to remove this option and always generate everything, \n" - + " but I'm not sure how to do that. \n" - + " If you are a Java wizard, check out the git issue here: \n" - + " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n") - .define("allowUnstableFeatureGeneration", false); + allowUnstableFeatureGeneration = builder + .comment("\n\n" + + " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n" + + " some features may not be thread safe, which could \n" + + " cause instability and crashes. \n" + + " By default (false) those features are skipped, \n" + + " improving stability, but decreasing how many features are \n" + + " actually generated. \n" + + " (for example: some tree generation is unstable, \n" + + " so some trees may not be generated.) \n" + + " By setting this to true, all features will be generated, \n" + + " but your game will be more unstable and crashes may occur. \n" + + " \n" + + " I would love to remove this option and always generate everything, \n" + + " but I'm not sure how to do that. \n" + + " If you are a Java wizard, check out the git issue here: \n" + + " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n") + .define("allowUnstableFeatureGeneration", false); - numberOfWorldGenerationThreads = builder - .comment("\n\n" - + " This is how many threads are used when generating terrain. \n" - + " If you experience stuttering when generating terrain, decrease \n" - + " this number. If you want to increase LOD generation speed, \n" - + " increase the number. \n" - + " The max is the number of processors on your CPU. \n" - + "\n" - + " Requires a restart to take effect. \n" - ) - .defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors()); + numberOfWorldGenerationThreads = builder + .comment("\n\n" + + " This is how many threads are used when generating terrain. \n" + + " If you experience stuttering when generating terrain, decrease \n" + + " this number. If you want to increase LOD generation speed, \n" + + " increase the number. \n" + + " The max is the number of processors on your CPU. \n" + + "\n" + + " Requires a restart to take effect. \n" + ) + .defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors()); - shadingMode = builder - .comment("\n\n" - + " What kind of shading should LODs have? \n" - + " " + ShadingMode.NONE.toString() + " \n" - + " " + "LODs will have the same lighting on every side. \n" - + " " + "Can make large similarly colored areas hard to differentiate. \n" - + " " + "Fastest" - + "/n" - + " " + ShadingMode.DARKEN_SIDES.toString() + " \n" - + " " + "LODs will have darker sides and bottoms to simulate top down lighting." - + " " + "Fastest /n") - .defineEnum("lightingMode", ShadingMode.DARKEN_SIDES); + shadingMode = builder + .comment("\n\n" + + " What kind of shading should LODs have? \n" + + " " + ShadingMode.NONE.toString() + " \n" + + " " + "LODs will have the same lighting on every side. \n" + + " " + "Can make large similarly colored areas hard to differentiate. \n" + + " " + "Fastest" + + "/n" + + " " + ShadingMode.DARKEN_SIDES.toString() + " \n" + + " " + "LODs will have darker sides and bottoms to simulate top down lighting." + + " " + "Fastest /n") + .defineEnum("lightingMode", ShadingMode.DARKEN_SIDES); - brightnessMultiplier = builder - .comment("\n\n" - + " Change how bright LOD colors are. \n" - + " 0 = black \n" - + " 1 = normal color value \n" - + " 2 = washed out colors \n") - .defineInRange("brightnessMultiplier", 1.0, 0, 2); + brightnessMultiplier = builder + .comment("\n\n" + + " Change how bright LOD colors are. \n" + + " 0 = black \n" + + " 1 = normal color value \n" + + " 2 = washed out colors \n") + .defineInRange("brightnessMultiplier", 1.0, 0, 2); - saturationMultiplier = builder - .comment("\n\n" - + " Change how saturated LOD colors are. \n" - + " 0 = black and white \n" - + " 1 = normal saturation \n" - + " 2 = very saturated \n") - .defineInRange("saturationMultiplier", 1.0, 0, 2); + saturationMultiplier = builder + .comment("\n\n" + + " Change how saturated LOD colors are. \n" + + " 0 = black and white \n" + + " 1 = normal saturation \n" + + " 2 = very saturated \n") + .defineInRange("saturationMultiplier", 1.0, 0, 2); - builder.pop(); - } - } + builder.pop(); + } + } - /** - * {@link Path} to the configuration file of this mod - */ - private static final Path CONFIG_PATH = - Paths.get("config", ModInfo.MODID + ".toml"); + /** + * {@link Path} to the configuration file of this mod + */ + private static final Path CONFIG_PATH = + Paths.get("config", ModInfo.MODID + ".toml"); - public static final ForgeConfigSpec clientSpec; - public static final Client CLIENT; + public static final ForgeConfigSpec clientSpec; + public static final Client CLIENT; - static - { - final Pair specPair = new ForgeConfigSpec.Builder().configure(Client::new); - clientSpec = specPair.getRight(); - CLIENT = specPair.getLeft(); + static + { + final Pair specPair = new ForgeConfigSpec.Builder().configure(Client::new); + clientSpec = specPair.getRight(); + CLIENT = specPair.getLeft(); - // setup the config file - CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH) - .writingMode(WritingMode.REPLACE) - .build(); - config.load(); - config.save(); - clientSpec.setConfig(config); - } + // setup the config file + CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH) + .writingMode(WritingMode.REPLACE) + .build(); + config.load(); + config.save(); + clientSpec.setConfig(config); + } - @SubscribeEvent - public static void onLoad(final ModConfig.Loading configEvent) - { - LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName()); - } + @SubscribeEvent + public static void onLoad(final ModConfig.Loading configEvent) + { + LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName()); + } - @SubscribeEvent - public static void onFileChange(final ModConfig.Reloading configEvent) - { - LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!"); - } + @SubscribeEvent + public static void onFileChange(final ModConfig.Reloading configEvent) + { + LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!"); + } } diff --git a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java index 5cc751bd8..51e2032df 100644 --- a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java @@ -45,73 +45,81 @@ import com.seibel.lod.util.LodUtil; */ public class LodDimensionFileHandler { - /** - * This is what separates each piece of data - */ - public static final char DATA_DELIMITER = ','; + /** + * This is what separates each piece of data + */ + public static final char DATA_DELIMITER = ','; - private LodDimension loadedDimension = null; - public long regionLastWriteTime[][]; + private LodDimension loadedDimension = null; + public long regionLastWriteTime[][]; - private File dimensionDataSaveFolder; + private File dimensionDataSaveFolder; - /** lod */ - private static final String FILE_NAME_PREFIX = "lod"; - /** .txt */ - private static final String FILE_EXTENSION = ".txt"; - /** lod/ */ - private static final String LOD_FOLDER_NAME = "lod"; - /** detail-# */ - private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-"; - - /** - * .tmp
- * Added to the end of the file path when saving to prevent - * nulling a currently existing file.
- * After the file finishes saving it will end with - * FILE_EXTENSION. - */ - private static final String TMP_FILE_EXTENSION = ".tmp"; + /** + * lod + */ + private static final String FILE_NAME_PREFIX = "lod"; + /** + * .txt + */ + private static final String FILE_EXTENSION = ".txt"; + /** + * lod/ + */ + private static final String LOD_FOLDER_NAME = "lod"; + /** + * detail-# + */ + private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-"; - /** - * This is the file version currently accepted by this - * file handler, older versions (smaller numbers) will be deleted and overwritten, - * newer versions (larger numbers) will be ignored and won't be read. - */ - public static final int LOD_SAVE_FILE_VERSION = 4; + /** + * .tmp
+ * Added to the end of the file path when saving to prevent + * nulling a currently existing file.
+ * After the file finishes saving it will end with + * FILE_EXTENSION. + */ + private static final String TMP_FILE_EXTENSION = ".tmp"; - /** - * This is the string written before the file version - */ - private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version"; + /** + * This is the file version currently accepted by this + * file handler, older versions (smaller numbers) will be deleted and overwritten, + * newer versions (larger numbers) will be ignored and won't be read. + */ + public static final int LOD_SAVE_FILE_VERSION = 4; - /** - * Allow saving asynchronously, but never try to save multiple regions - * at a time - */ - private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); + /** + * This is the string written before the file version + */ + private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version"; + + /** + * Allow saving asynchronously, but never try to save multiple regions + * at a time + */ + private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); - public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension) - { - if (newSaveFolder == null) - throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to."); + public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension) + { + if (newSaveFolder == null) + throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to."); - dimensionDataSaveFolder = newSaveFolder; + dimensionDataSaveFolder = newSaveFolder; - loadedDimension = newLoadedDimension; - // these two variable are used in sync with the LodDimension - regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()]; - for (int i = 0; i < loadedDimension.getWidth(); i++) - for (int j = 0; j < loadedDimension.getWidth(); j++) - regionLastWriteTime[i][j] = -1; - } + loadedDimension = newLoadedDimension; + // these two variable are used in sync with the LodDimension + regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()]; + for (int i = 0; i < loadedDimension.getWidth(); i++) + for (int j = 0; j < loadedDimension.getWidth(); j++) + regionLastWriteTime[i][j] = -1; + } - //================// - // read from file // - //================// + //================// + // read from file // + //================// /** * Return the LodRegion region at the given coordinates. @@ -168,8 +176,8 @@ public class LodDimensionFileHandler bufferedReader.close(); f.delete(); ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion + - ", version requested: " + LOD_SAVE_FILE_VERSION + - " File was been deleted."); + ", version requested: " + LOD_SAVE_FILE_VERSION + + " File was been deleted."); continue; } else if (fileVersion > LOD_SAVE_FILE_VERSION) @@ -179,8 +187,8 @@ public class LodDimensionFileHandler // want to accidently delete anything the user may want. bufferedReader.close(); ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion + - ", version requested: " + LOD_SAVE_FILE_VERSION + - " this region will not be written to in order to protect the newer file."); + ", version requested: " + LOD_SAVE_FILE_VERSION + + " this region will not be written to in order to protect the newer file."); continue; } @@ -349,38 +357,37 @@ public class LodDimensionFileHandler } - //================// - // helper methods // - //================// + //================// + // helper methods // + //================// - /** - * Return the name of the file that should contain the - * region at the given x and z.
- * Returns null if this object isn't ready to read and write.

- * - * example: "lod.0.0.txt"

- * - * Returns null if there is an IO Exception. - */ - private String getFileNameAndPathForRegion(int regionX, int regionZ, byte detailLevel) - { - try - { - // saveFolder is something like - // ".\Super Flat\DIM-1\data" - // or - // ".\Super Flat\data" - return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar + - DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar + - FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION; - } - catch (IOException | SecurityException e) - { - ClientProxy.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: "); - e.printStackTrace(); - return null; - } - } + /** + * Return the name of the file that should contain the + * region at the given x and z.
+ * Returns null if this object isn't ready to read and write.

+ *

+ * example: "lod.0.0.txt"

+ *

+ * Returns null if there is an IO Exception. + */ + private String getFileNameAndPathForRegion(int regionX, int regionZ, byte detailLevel) + { + try + { + // saveFolder is something like + // ".\Super Flat\DIM-1\data" + // or + // ".\Super Flat\data" + return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar + + DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar + + FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION; + } catch (IOException | SecurityException e) + { + ClientProxy.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: "); + e.printStackTrace(); + return null; + } + } } diff --git a/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java b/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java index 386535813..843fd8e8b 100644 --- a/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java +++ b/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java @@ -27,7 +27,7 @@ import net.minecraft.client.Minecraft; * This object is used to get variables from methods * where they are private. Specifically the fog setting * in Optifine. - * + * * @author James Seibel * @version 7-03-2021 */ @@ -35,15 +35,13 @@ public class ReflectionHandler { private Minecraft mc = Minecraft.getInstance(); public Field ofFogField = null; - + public ReflectionHandler() { setupFogField(); } - - - - + + /** * Similar to setupFovMethod. */ @@ -51,27 +49,24 @@ public class ReflectionHandler { // get every variable from the entity renderer Field[] optionFields = mc.options.getClass().getDeclaredFields(); - + // try and find the ofFogType variable in gameSettings - for(Field field : optionFields) + for (Field field : optionFields) { - if(field.getName().equals("ofFogType")) + if (field.getName().equals("ofFogType")) { ofFogField = field; return; } } - + // we didn't find the field, // either optifine isn't installed, or // optifine changed the name of the variable ofFogField = null; } - - - - - + + /** * Get what type of fog optifine is currently set to render. */ @@ -84,25 +79,24 @@ public class ReflectionHandler // the setup method wasn't called yet. return FogQuality.FANCY; } - + int returnNum = 0; - + try { - returnNum = (int)ofFogField.get(mc.options); - } - catch (IllegalArgumentException | IllegalAccessException e) + returnNum = (int) ofFogField.get(mc.options); + } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } - + switch (returnNum) { // optifine's "default" option, // it should never be called in this case case 0: return FogQuality.FAST; - + // normal options case 1: return FogQuality.FAST; @@ -110,10 +104,10 @@ public class ReflectionHandler return FogQuality.FANCY; case 3: return FogQuality.OFF; - + default: return FogQuality.FAST; } } - + } diff --git a/src/main/java/com/seibel/lod/objects/DataPoint.java b/src/main/java/com/seibel/lod/objects/DataPoint.java index 1df0e6ae3..3d8d2e6dc 100644 --- a/src/main/java/com/seibel/lod/objects/DataPoint.java +++ b/src/main/java/com/seibel/lod/objects/DataPoint.java @@ -3,38 +3,46 @@ 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[] 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 getHeight(short[] dataPoint) + { + return dataPoint[0]; + } - public static short getDepth(short[] dataPoint){ - return dataPoint[1]; - } + public static short getDepth(short[] dataPoint) + { + return dataPoint[1]; + } - public static short getRed(short[] dataPoint){ - return dataPoint[2]; - } + public static short getRed(short[] dataPoint) + { + return dataPoint[2]; + } - public static short getGreen(short[] dataPoint){ - return dataPoint[3]; - } + public static short getGreen(short[] dataPoint) + { + return dataPoint[3]; + } - public static short getBlue(short[] dataPoint){ - return dataPoint[4]; - } + public static short getBlue(short[] dataPoint) + { + return dataPoint[4]; + } - public static short[] getHeightDepth(short[] dataPoint){ - return new short[]{dataPoint[0], dataPoint[1]}; - } + 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; - } + 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/LevelContainer.java b/src/main/java/com/seibel/lod/objects/LevelContainer.java index 7ca9cdffe..75795b064 100644 --- a/src/main/java/com/seibel/lod/objects/LevelContainer.java +++ b/src/main/java/com/seibel/lod/objects/LevelContainer.java @@ -7,104 +7,104 @@ import com.seibel.lod.util.LodUtil; public class LevelContainer implements Serializable { - public static final char DATA_DELIMITER = ','; + public static final char DATA_DELIMITER = ','; - public final byte detailLevel; + public final byte detailLevel; - public final byte[][][] colors; + public final byte[][][] colors; - public final short[][] height; + public final short[][] height; - public final short[][] depth; + public final short[][] depth; - public final byte[][] generationType; + public final byte[][] generationType; - public final boolean[][] dataExistence; + public final boolean[][] dataExistence; - public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence) - { - this.detailLevel = detailLevel; - this.colors = colors; - this.height = height; - this.depth = depth; - this.generationType = generationType; - this.dataExistence = dataExistence; - } + public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence) + { + this.detailLevel = detailLevel; + this.colors = colors; + this.height = height; + this.depth = depth; + this.generationType = generationType; + this.dataExistence = dataExistence; + } - public LevelContainer(String data) - { + public LevelContainer(String data) + { - int index = 0; - int lastIndex = 0; + int index = 0; + int lastIndex = 0; - index = data.indexOf(DATA_DELIMITER, 0); - this.detailLevel = (byte) Integer.parseInt(data.substring(0, index)); - int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel); + index = data.indexOf(DATA_DELIMITER, 0); + this.detailLevel = (byte) Integer.parseInt(data.substring(0, index)); + int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel); - this.colors = new byte[size][size][3]; - this.height = new short[size][size]; - this.depth = new short[size][size]; - this.generationType = new byte[size][size]; - this.dataExistence = new boolean[size][size]; - int intCol; - for (int x = 0; x < size; x++) - { - for (int z = 0; z < size; z++) - { - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex + 1); - intCol = Integer.parseInt(data.substring(lastIndex + 1, index), 16); - colors[x][z][0] = (byte) ((intCol >> 16) - 128); - colors[x][z][1] = (byte) ((intCol >> 8) - 128); - colors[x][z][2] = (byte) (intCol - 128); + this.colors = new byte[size][size][3]; + this.height = new short[size][size]; + this.depth = new short[size][size]; + this.generationType = new byte[size][size]; + this.dataExistence = new boolean[size][size]; + int intCol; + for (int x = 0; x < size; x++) + { + for (int z = 0; z < size; z++) + { + lastIndex = index; + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + intCol = Integer.parseInt(data.substring(lastIndex + 1, index), 16); + colors[x][z][0] = (byte) ((intCol >> 16) - 128); + colors[x][z][1] = (byte) ((intCol >> 8) - 128); + colors[x][z][2] = (byte) (intCol - 128); - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex + 1); - height[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16); + lastIndex = index; + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + height[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16); - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex + 1); - depth[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16); + lastIndex = index; + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + depth[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16); - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex + 1); - generationType[x][z] = Byte.parseByte(data.substring(lastIndex + 1, index), 16); + lastIndex = index; + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + generationType[x][z] = Byte.parseByte(data.substring(lastIndex + 1, index), 16); - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex + 1); - dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex + 1, index)); - } - } + lastIndex = index; + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex + 1, index)); + } + } - } + } - @Override - public String toString() - { - StringBuilder stringBuilder = new StringBuilder(); - int combinedCol; - int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel); - stringBuilder.append(detailLevel); - stringBuilder.append(DATA_DELIMITER); - for (int x = 0; x < size; x++) - { - for (int z = 0; z < size; z++) - { - //Converting the colors to intColor and then to HEX - combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128)); - stringBuilder.append(Integer.toHexString(combinedCol)); - stringBuilder.append(DATA_DELIMITER); - stringBuilder.append(Integer.toHexString(height[x][z] & 0xffff)); - stringBuilder.append(DATA_DELIMITER); - stringBuilder.append(Integer.toHexString(depth[x][z] & 0xffff)); - stringBuilder.append(DATA_DELIMITER); - stringBuilder.append(Integer.toHexString(generationType[x][z] & 0xffff)); - stringBuilder.append(DATA_DELIMITER); - stringBuilder.append(dataExistence[x][z]); - stringBuilder.append(DATA_DELIMITER); - } - } - return stringBuilder.toString(); - } + @Override + public String toString() + { + StringBuilder stringBuilder = new StringBuilder(); + int combinedCol; + int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel); + stringBuilder.append(detailLevel); + stringBuilder.append(DATA_DELIMITER); + for (int x = 0; x < size; x++) + { + for (int z = 0; z < size; z++) + { + //Converting the colors to intColor and then to HEX + combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128)); + stringBuilder.append(Integer.toHexString(combinedCol)); + stringBuilder.append(DATA_DELIMITER); + stringBuilder.append(Integer.toHexString(height[x][z] & 0xffff)); + stringBuilder.append(DATA_DELIMITER); + stringBuilder.append(Integer.toHexString(depth[x][z] & 0xffff)); + stringBuilder.append(DATA_DELIMITER); + stringBuilder.append(Integer.toHexString(generationType[x][z] & 0xffff)); + stringBuilder.append(DATA_DELIMITER); + stringBuilder.append(dataExistence[x][z]); + stringBuilder.append(DATA_DELIMITER); + } + } + return stringBuilder.toString(); + } } diff --git a/src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java b/src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java index a57d3aef4..324fbb47b 100644 --- a/src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java +++ b/src/main/java/com/seibel/lod/objects/LevelPos/MutableLevelPos.java @@ -4,11 +4,11 @@ import com.seibel.lod.util.LodUtil; public interface MutableLevelPos { - public void convert(byte newDetailLevel); + public void convert(byte newDetailLevel); - public void performRegionModule(); + public void performRegionModule(); - public void applyOffset(int xOffset, int zOffset); + public void applyOffset(int xOffset, int zOffset); - public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ); + public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ); } diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java index 8ba339ae0..344cc4467 100644 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodDimension.java @@ -48,718 +48,718 @@ import net.minecraft.world.server.ServerWorld; public class LodDimension { - public final DimensionType dimension; - - /** - * measured in regions - */ - private volatile int width; - /** - * measured in regions - */ - private volatile int halfWidth; - - - public volatile LodRegion regions[][]; - public volatile boolean isRegionDirty[][]; - - private volatile RegionPos center; - private volatile ChunkPos lastGenChunk; - private volatile ChunkPos lastCutChunk; - private LodDimensionFileHandler fileHandler; - private ExecutorService cutAndGenThreads = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - cutAndGen")); - - /** - * Creates the dimension centered at (0,0) - * - * @param newWidth in regions - */ - public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newWidth) - { - lastCutChunk = null; - lastGenChunk = null; - dimension = newDimension; - width = newWidth; - halfWidth = (int) Math.floor(width / 2); - Minecraft mc = Minecraft.getInstance(); - if (newDimension != null && lodWorld != null) - { - try - { - - File saveDir; - if (mc.hasSingleplayerServer()) - { - // local world - - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension); - - // provider needs a separate variable to prevent - // the compiler from complaining - ServerChunkProvider provider = serverWorld.getChunkSource(); - saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod"); - } else - { - // connected to server - - saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() + - File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level)); - } - - fileHandler = new LodDimensionFileHandler(saveDir, this); - } catch (IOException e) - { - // the file handler wasn't able to be created - // we won't be able to read or write any files - } - } - - - regions = new LodRegion[width][width]; - isRegionDirty = new boolean[width][width]; - - //treeGenerator((int) mc.player.getX(),(int) mc.player.getZ()); - - // populate isRegionDirty - for (int i = 0; i < width; i++) - for (int j = 0; j < width; j++) - isRegionDirty[i][j] = false; - - center = new RegionPos(0, 0); - } - - - /** - * Move the center of this LodDimension and move all owned - * regions over by the given x and z offset.

- *

- * Synchronized to prevent multiple moves happening on top of each other. - */ - public synchronized void move(RegionPos regionOffset) - { - int xOffset = regionOffset.x; - int zOffset = regionOffset.z; - - // if the x or z offset is equal to or greater than - // the total size, just delete the current data - // and update the centerX and/or centerZ - if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width) - { - for (int x = 0; x < width; x++) - { - for (int z = 0; z < width; z++) - { - regions[x][z] = null; - } - } - - // update the new center - center.x += xOffset; - center.z += zOffset; - - return; - } - - - // X - if (xOffset > 0) - { - // move everything over to the left (as the center moves to the right) - for (int x = 0; x < width; x++) - { - for (int z = 0; z < width; z++) - { - if (x + xOffset < width) - regions[x][z] = regions[x + xOffset][z]; - else - regions[x][z] = null; - } - } - } else - { - // move everything over to the right (as the center moves to the left) - for (int x = width - 1; x >= 0; x--) - { - for (int z = 0; z < width; z++) - { - if (x + xOffset >= 0) - regions[x][z] = regions[x + xOffset][z]; - else - regions[x][z] = null; - } - } - } - - - // Z - if (zOffset > 0) - { - // move everything up (as the center moves down) - for (int x = 0; x < width; x++) - { - for (int z = 0; z < width; z++) - { - if (z + zOffset < width) - regions[x][z] = regions[x][z + zOffset]; - else - regions[x][z] = null; - } - } - } else - { - // move everything down (as the center moves up) - for (int x = 0; x < width; x++) - { - for (int z = width - 1; z >= 0; z--) - { - if (z + zOffset >= 0) - regions[x][z] = regions[x][z + zOffset]; - else - regions[x][z] = null; - } - } - } - - - // update the new center - center.x += xOffset; - center.z += zOffset; - } - - - /** - * return needed memory in byte - */ - public int getMinMemoryNeeded() - { - int regionX; - int regionZ; - int count = 0; - LodRegion region; - - for (int x = 0; x < regions.length; x++) - { - for (int z = 0; z < regions.length; z++) - { - region = regions[x][z]; - if (region != null) - { - count += region.getMinMemoryNeeded(); - } - } - } - return count; - } - - /** - * Gets the region at the given X and Z - *
- * Returns null if the region doesn't exist - * or is outside the loaded area. - */ - public LodRegion getRegion(LevelPos levelPos) - { - - RegionPos regionPos = levelPos.getRegionPos(); - int xIndex = (regionPos.x - center.x) + halfWidth; - int zIndex = (regionPos.z - center.z) + halfWidth; - - if (!regionIsInRange(regionPos.x, regionPos.z)) - throw new ArrayIndexOutOfBoundsException("Region for level pos " + levelPos + " out of range"); - else if (regions[xIndex][zIndex] == null) - throw new InvalidParameterException("Region for level pos " + levelPos + " not currently initialized"); - else if (regions[xIndex][zIndex].getMinDetailLevel() > levelPos.detailLevel) - throw new InvalidParameterException("Region for level pos " + levelPos + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel()); - return regions[xIndex][zIndex]; - } - - /** - * Gets the region at the given X and Z - *
- * Returns null if the region doesn't exist - * or is outside the loaded area. - */ - public LodRegion getRegion(RegionPos regionPos) - { - int xIndex = (regionPos.x - center.x) + halfWidth; - int zIndex = (regionPos.z - center.z) + halfWidth; - - if (!regionIsInRange(regionPos.x, regionPos.z)) - throw new ArrayIndexOutOfBoundsException("Region " + regionPos + " out of range"); - else if (regions[xIndex][zIndex] == null) - throw new InvalidParameterException("Region " + regionPos + " not currently initialized"); - return regions[xIndex][zIndex]; - } - - /** - * Overwrite the LodRegion at the location of newRegion with newRegion. - * - * @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension. - */ - public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException - { - int xIndex = (newRegion.regionPosX - center.x) + halfWidth; - int zIndex = (center.z - newRegion.regionPosZ) + halfWidth; - - if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ)) - // out of range - throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range"); - - regions[xIndex][zIndex] = newRegion; - } - - - /** - * - */ - public void treeCutter(int playerPosX, int playerPosZ) - { - ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos(); - if (lastCutChunk == null) - lastCutChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); - if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z) - { - lastCutChunk = newPlayerChunk; - Thread thread = new Thread(() -> - { - int regionX; - int regionZ; - LevelPos levelPos = new LevelPos(); - - for (int x = 0; x < regions.length; x++) - { - for (int z = 0; z < regions.length; z++) - { - regionX = (x + center.x) - halfWidth; - regionZ = (z + center.z) - halfWidth; - levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); - //we start checking from the first circle. If the whole region is in the circle - //we proceed to cut all the level lower than the level of circle 1 and we break - //if this is not the case w - for (byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.DETAIL_OPTIONS; index++) - { - if (DetailDistanceUtil.getDistanceTreeCut(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)) - { - - byte cutDetailLevel = DetailDistanceUtil.getCutLodDetail(index); - - if (regions[x][z] != null) - { - if (regions[x][z].getMinDetailLevel() > cutDetailLevel) - { - regions[x][z].cutTree(cutDetailLevel); - } - } - //once we - break; - } - } - }// region z - }// region z - - }); - cutAndGenThreads.execute(thread); - } - } - - /** - * - */ - public void treeGenerator(int playerPosX, int playerPosZ) - { - - ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos(); - - if (lastGenChunk == null) - lastGenChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); - if (newPlayerChunk.x != lastGenChunk.x || newPlayerChunk.z != lastGenChunk.z) - { - lastGenChunk = newPlayerChunk; - Thread thread = new Thread(() -> - { - int regionX; - int regionZ; - LodRegion region; - LevelPos levelPos = new LevelPos(); - List> genThreads = new ArrayList<>(); - for (int x = 0; x < regions.length; x++) - { - for (int z = 0; z < regions.length; z++) - { - regionX = (x + center.x) - halfWidth; - regionZ = (z + center.z) - halfWidth; - levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); - final RegionPos regionPos = new RegionPos(regionX, regionZ); - for (byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.REGION_DETAIL_LEVEL; index++) - { - - //As soon as we find in which circle the region should be we analyze it - if (DetailDistanceUtil.getDistanceTreeGen(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)) - { - - region = regions[x][z]; - //We require that the region we are checking is loaded with at least this level - byte targetDetailLevel = DetailDistanceUtil.getLodDetail(index).detailLevel; - - if (region == null) - { - //First case, region has to be initialized - - //We check if there is a file at the target level - regions[x][z] = getRegionFromFile(regionPos, targetDetailLevel); - - //if there is no file we initialize the region - if (regions[x][z] == null) - { - regions[x][z] = new LodRegion(targetDetailLevel, regionPos); - } - - } else if (region.getMinDetailLevel() > targetDetailLevel) - { - //Second case, region has been initialized but at a higher level - //We expand the region by introducing the missing layer - region.expand(targetDetailLevel); - } - break; - } - } - } - } - ; - }); - cutAndGenThreads.execute(thread); - } - } - - /** - * Add the given LOD to this dimension at the coordinate - * stored in the LOD. If an LOD already exists at the given - * coordinates it will be overwritten. - */ - public synchronized Boolean addData(LevelPos levelPos, short[] lodDataPoint, DistanceGenerationMode generationMode, boolean dontSave) - { - - // don't continue if the region can't be saved - RegionPos regionPos = levelPos.getRegionPos(); - if (!regionIsInRange(regionPos.x, regionPos.z)) - { - return false; - } - - LodRegion region = getRegion(levelPos); - - boolean nodeAdded = region.addData(levelPos, lodDataPoint, generationMode.complexity); - // only save valid LODs to disk - if (!dontSave && fileHandler != null) - { - try - { - // mark the region as dirty so it will be saved to disk - int xIndex = (regionPos.x - center.x) + halfWidth; - int zIndex = (regionPos.z - center.z) + halfWidth; - isRegionDirty[xIndex][zIndex] = true; - } catch (ArrayIndexOutOfBoundsException e) - { - // This method was probably called when the dimension was changing size. - // Hopefully this shouldn't be an issue. - } - } - return nodeAdded; - } - - - /** - * method to get all the quadtree level that have to be generated based on the position of the player - * - * @return list of quadTrees - */ - public List getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generation, byte detailLevel, int dataNumber) - { - - int n = regions.length; - int xIndex; - int zIndex; - LodRegion region; - RegionPos regionPos; - LevelPos regionLevelPos = new LevelPos(); - List listOfData = new ArrayList<>(); - for (int xRegion = 0; xRegion < n; xRegion++) - { - for (int zRegion = 0; zRegion < n; zRegion++) - { - try - { - xIndex = (xRegion + center.x) - halfWidth; - zIndex = (zRegion + center.z) - halfWidth; - regionPos = new RegionPos(xIndex, zIndex); - regionLevelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z); - if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) && - start <= regionLevelPos.maxDistance(playerPosX, playerPosZ)) - { - 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) - { - //e.printStackTrace(); - } - } - } - - List levelMinPosList = new ArrayList<>(); - dataNumber = Math.min(dataNumber, listOfData.size()); - - for (int i = 0; i < dataNumber; i++) - { - LevelPos min = Collections.min(listOfData, LevelPos.getPosComparator(playerPosX, playerPosZ)); - listOfData.remove(min); - levelMinPosList.add(min); - } - return levelMinPosList; - } - - /** - * method to get all the nodes that have to be rendered based on the position of the player - * - * @return list of nodes - */ - public void getDataToRender(ConcurrentNavigableMap> dataToRender, RegionPos regionPos, int playerPosX, int playerPosZ, int start, int end, byte detailLevel, boolean zFix) - { - LevelPos regionLevelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z); - try - { - if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) && - start <= regionLevelPos.maxDistance(playerPosX, playerPosZ)) - { - LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel)); - region.getDataToRender(dataToRender, playerPosX, playerPosZ, start, end, detailLevel, zFix); - } - } catch (Exception e) - { - e.printStackTrace(); - } - } - - /** - * 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. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - 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."); - - try - { - LodRegion region = getRegion(levelPos); - - if (region == null) - { - return null; - } - - return region.getData(levelPos); - - } catch (Exception e) - { - return null; - } - } - - - /** - * Get the data point 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 void updateData(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."); - - LodRegion region = getRegion(levelPos); - - - if (region == null) - { - return; - } - region.updateArea(levelPos); - } - - /** - * return true if and only if the node at that position exist - */ - - public boolean hasThisPositionBeenGenerated(LevelPos levelPos) - { - LodRegion region = getRegion(levelPos); - - if (region == null) - { - return false; - } - - return region.hasDataBeenGenerated(levelPos); - } - - /** - * return true if and only if the node at that position exist - */ - public boolean doesDataExist(LevelPos levelPos) - { - try - { - LodRegion region = getRegion(levelPos); - - if (region == null) - { - return false; - } - - return region.doesDataExist(levelPos.clone()); - } catch (Exception e) - { - return false; - } - } - - /** - * return true if and only if the node at that position exist - */ - public DistanceGenerationMode getGenerationMode(LevelPos levelPos) - { - LodRegion region = getRegion(levelPos); - - if (region == null) - { - return DistanceGenerationMode.NONE; - } - - return region.getGenerationMode(levelPos); - } - - /** - * Get the region at the given X and Z coordinates from the - * RegionFileHandler. - */ - public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel) - { - if (fileHandler != null) - return fileHandler.loadRegionFromFile(regionPos, detailLevel); - else - return null; - } - - /** - * Save all dirty regions in this LodDimension to file. - */ - public void saveDirtyRegionsToFileAsync() - { - fileHandler.saveDirtyRegionsToFileAsync(); - } - - - /** - * Returns whether the region at the given X and Z coordinates - * is within the loaded range. - */ - public boolean regionIsInRange(int regionX, int regionZ) - { - int xIndex = (regionX - center.x) + halfWidth; - int zIndex = (regionZ - center.z) + halfWidth; - - return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width; - } - - - public int getCenterX() - { - return center.x; - } - - public int getCenterZ() - { - return center.z; - } - - - public int getWidth() - { - if (regions != null) - { - // we want to get the length directly from the - // source to make sure it is in sync with region - // and isRegionDirty - return regions.length; - } else - { - return width; - } - } - - public void setRegionWidth(int newWidth) - { - width = newWidth; - halfWidth = (int) Math.floor(width / 2); - - regions = new LodRegion[width][width]; - isRegionDirty = new boolean[width][width]; - - // populate isRegionDirty - for (int i = 0; i < width; i++) - for (int j = 0; j < width; j++) - isRegionDirty[i][j] = false; - } - - - @Override - public String toString() - { - int regionX; - int regionZ; - LevelPos levelPos; - LodRegion region; - - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("Dimension : \n"); - for (int x = 0; x < regions.length; x++) - { - for (int z = 0; z < regions.length; z++) - { - region = regions[x][z]; - if (region == null) - { - stringBuilder.append("n"); - stringBuilder.append("\t"); - - } else - { - stringBuilder.append(region.getMinDetailLevel()); - stringBuilder.append("\t"); - } - } - stringBuilder.append("\n"); - } - System.out.println(stringBuilder); - return stringBuilder.toString(); - } + public final DimensionType dimension; + + /** + * measured in regions + */ + private volatile int width; + /** + * measured in regions + */ + private volatile int halfWidth; + + + public volatile LodRegion regions[][]; + public volatile boolean isRegionDirty[][]; + + private volatile RegionPos center; + private volatile ChunkPos lastGenChunk; + private volatile ChunkPos lastCutChunk; + private LodDimensionFileHandler fileHandler; + private ExecutorService cutAndGenThreads = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - cutAndGen")); + + /** + * Creates the dimension centered at (0,0) + * + * @param newWidth in regions + */ + public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newWidth) + { + lastCutChunk = null; + lastGenChunk = null; + dimension = newDimension; + width = newWidth; + halfWidth = (int) Math.floor(width / 2); + Minecraft mc = Minecraft.getInstance(); + if (newDimension != null && lodWorld != null) + { + try + { + + File saveDir; + if (mc.hasSingleplayerServer()) + { + // local world + + ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension); + + // provider needs a separate variable to prevent + // the compiler from complaining + ServerChunkProvider provider = serverWorld.getChunkSource(); + saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod"); + } else + { + // connected to server + + saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() + + File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level)); + } + + fileHandler = new LodDimensionFileHandler(saveDir, this); + } catch (IOException e) + { + // the file handler wasn't able to be created + // we won't be able to read or write any files + } + } + + + regions = new LodRegion[width][width]; + isRegionDirty = new boolean[width][width]; + + //treeGenerator((int) mc.player.getX(),(int) mc.player.getZ()); + + // populate isRegionDirty + for (int i = 0; i < width; i++) + for (int j = 0; j < width; j++) + isRegionDirty[i][j] = false; + + center = new RegionPos(0, 0); + } + + + /** + * Move the center of this LodDimension and move all owned + * regions over by the given x and z offset.

+ *

+ * Synchronized to prevent multiple moves happening on top of each other. + */ + public synchronized void move(RegionPos regionOffset) + { + int xOffset = regionOffset.x; + int zOffset = regionOffset.z; + + // if the x or z offset is equal to or greater than + // the total size, just delete the current data + // and update the centerX and/or centerZ + if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width) + { + for (int x = 0; x < width; x++) + { + for (int z = 0; z < width; z++) + { + regions[x][z] = null; + } + } + + // update the new center + center.x += xOffset; + center.z += zOffset; + + return; + } + + + // X + if (xOffset > 0) + { + // move everything over to the left (as the center moves to the right) + for (int x = 0; x < width; x++) + { + for (int z = 0; z < width; z++) + { + if (x + xOffset < width) + regions[x][z] = regions[x + xOffset][z]; + else + regions[x][z] = null; + } + } + } else + { + // move everything over to the right (as the center moves to the left) + for (int x = width - 1; x >= 0; x--) + { + for (int z = 0; z < width; z++) + { + if (x + xOffset >= 0) + regions[x][z] = regions[x + xOffset][z]; + else + regions[x][z] = null; + } + } + } + + + // Z + if (zOffset > 0) + { + // move everything up (as the center moves down) + for (int x = 0; x < width; x++) + { + for (int z = 0; z < width; z++) + { + if (z + zOffset < width) + regions[x][z] = regions[x][z + zOffset]; + else + regions[x][z] = null; + } + } + } else + { + // move everything down (as the center moves up) + for (int x = 0; x < width; x++) + { + for (int z = width - 1; z >= 0; z--) + { + if (z + zOffset >= 0) + regions[x][z] = regions[x][z + zOffset]; + else + regions[x][z] = null; + } + } + } + + + // update the new center + center.x += xOffset; + center.z += zOffset; + } + + + /** + * return needed memory in byte + */ + public int getMinMemoryNeeded() + { + int regionX; + int regionZ; + int count = 0; + LodRegion region; + + for (int x = 0; x < regions.length; x++) + { + for (int z = 0; z < regions.length; z++) + { + region = regions[x][z]; + if (region != null) + { + count += region.getMinMemoryNeeded(); + } + } + } + return count; + } + + /** + * Gets the region at the given X and Z + *
+ * Returns null if the region doesn't exist + * or is outside the loaded area. + */ + public LodRegion getRegion(LevelPos levelPos) + { + + RegionPos regionPos = levelPos.getRegionPos(); + int xIndex = (regionPos.x - center.x) + halfWidth; + int zIndex = (regionPos.z - center.z) + halfWidth; + + if (!regionIsInRange(regionPos.x, regionPos.z)) + throw new ArrayIndexOutOfBoundsException("Region for level pos " + levelPos + " out of range"); + else if (regions[xIndex][zIndex] == null) + throw new InvalidParameterException("Region for level pos " + levelPos + " not currently initialized"); + else if (regions[xIndex][zIndex].getMinDetailLevel() > levelPos.detailLevel) + throw new InvalidParameterException("Region for level pos " + levelPos + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel()); + return regions[xIndex][zIndex]; + } + + /** + * Gets the region at the given X and Z + *
+ * Returns null if the region doesn't exist + * or is outside the loaded area. + */ + public LodRegion getRegion(RegionPos regionPos) + { + int xIndex = (regionPos.x - center.x) + halfWidth; + int zIndex = (regionPos.z - center.z) + halfWidth; + + if (!regionIsInRange(regionPos.x, regionPos.z)) + throw new ArrayIndexOutOfBoundsException("Region " + regionPos + " out of range"); + else if (regions[xIndex][zIndex] == null) + throw new InvalidParameterException("Region " + regionPos + " not currently initialized"); + return regions[xIndex][zIndex]; + } + + /** + * Overwrite the LodRegion at the location of newRegion with newRegion. + * + * @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension. + */ + public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException + { + int xIndex = (newRegion.regionPosX - center.x) + halfWidth; + int zIndex = (center.z - newRegion.regionPosZ) + halfWidth; + + if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ)) + // out of range + throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range"); + + regions[xIndex][zIndex] = newRegion; + } + + + /** + * + */ + public void treeCutter(int playerPosX, int playerPosZ) + { + ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos(); + if (lastCutChunk == null) + lastCutChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); + if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z) + { + lastCutChunk = newPlayerChunk; + Thread thread = new Thread(() -> + { + int regionX; + int regionZ; + LevelPos levelPos = new LevelPos(); + + for (int x = 0; x < regions.length; x++) + { + for (int z = 0; z < regions.length; z++) + { + regionX = (x + center.x) - halfWidth; + regionZ = (z + center.z) - halfWidth; + levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); + //we start checking from the first circle. If the whole region is in the circle + //we proceed to cut all the level lower than the level of circle 1 and we break + //if this is not the case w + for (byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.DETAIL_OPTIONS; index++) + { + if (DetailDistanceUtil.getDistanceTreeCut(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)) + { + + byte cutDetailLevel = DetailDistanceUtil.getCutLodDetail(index); + + if (regions[x][z] != null) + { + if (regions[x][z].getMinDetailLevel() > cutDetailLevel) + { + regions[x][z].cutTree(cutDetailLevel); + } + } + //once we + break; + } + } + }// region z + }// region z + + }); + cutAndGenThreads.execute(thread); + } + } + + /** + * + */ + public void treeGenerator(int playerPosX, int playerPosZ) + { + + ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos(); + + if (lastGenChunk == null) + lastGenChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); + if (newPlayerChunk.x != lastGenChunk.x || newPlayerChunk.z != lastGenChunk.z) + { + lastGenChunk = newPlayerChunk; + Thread thread = new Thread(() -> + { + int regionX; + int regionZ; + LodRegion region; + LevelPos levelPos = new LevelPos(); + List> genThreads = new ArrayList<>(); + for (int x = 0; x < regions.length; x++) + { + for (int z = 0; z < regions.length; z++) + { + regionX = (x + center.x) - halfWidth; + regionZ = (z + center.z) - halfWidth; + levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); + final RegionPos regionPos = new RegionPos(regionX, regionZ); + for (byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.REGION_DETAIL_LEVEL; index++) + { + + //As soon as we find in which circle the region should be we analyze it + if (DetailDistanceUtil.getDistanceTreeGen(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)) + { + + region = regions[x][z]; + //We require that the region we are checking is loaded with at least this level + byte targetDetailLevel = DetailDistanceUtil.getLodDetail(index).detailLevel; + + if (region == null) + { + //First case, region has to be initialized + + //We check if there is a file at the target level + regions[x][z] = getRegionFromFile(regionPos, targetDetailLevel); + + //if there is no file we initialize the region + if (regions[x][z] == null) + { + regions[x][z] = new LodRegion(targetDetailLevel, regionPos); + } + + } else if (region.getMinDetailLevel() > targetDetailLevel) + { + //Second case, region has been initialized but at a higher level + //We expand the region by introducing the missing layer + region.expand(targetDetailLevel); + } + break; + } + } + } + } + ; + }); + cutAndGenThreads.execute(thread); + } + } + + /** + * Add the given LOD to this dimension at the coordinate + * stored in the LOD. If an LOD already exists at the given + * coordinates it will be overwritten. + */ + public synchronized Boolean addData(LevelPos levelPos, short[] lodDataPoint, DistanceGenerationMode generationMode, boolean dontSave) + { + + // don't continue if the region can't be saved + RegionPos regionPos = levelPos.getRegionPos(); + if (!regionIsInRange(regionPos.x, regionPos.z)) + { + return false; + } + + LodRegion region = getRegion(levelPos); + + boolean nodeAdded = region.addData(levelPos, lodDataPoint, generationMode.complexity); + // only save valid LODs to disk + if (!dontSave && fileHandler != null) + { + try + { + // mark the region as dirty so it will be saved to disk + int xIndex = (regionPos.x - center.x) + halfWidth; + int zIndex = (regionPos.z - center.z) + halfWidth; + isRegionDirty[xIndex][zIndex] = true; + } catch (ArrayIndexOutOfBoundsException e) + { + // This method was probably called when the dimension was changing size. + // Hopefully this shouldn't be an issue. + } + } + return nodeAdded; + } + + + /** + * method to get all the quadtree level that have to be generated based on the position of the player + * + * @return list of quadTrees + */ + public List getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generation, byte detailLevel, int dataNumber) + { + + int n = regions.length; + int xIndex; + int zIndex; + LodRegion region; + RegionPos regionPos; + LevelPos regionLevelPos = new LevelPos(); + List listOfData = new ArrayList<>(); + for (int xRegion = 0; xRegion < n; xRegion++) + { + for (int zRegion = 0; zRegion < n; zRegion++) + { + try + { + xIndex = (xRegion + center.x) - halfWidth; + zIndex = (zRegion + center.z) - halfWidth; + regionPos = new RegionPos(xIndex, zIndex); + regionLevelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z); + if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) && + start <= regionLevelPos.maxDistance(playerPosX, playerPosZ)) + { + 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) + { + //e.printStackTrace(); + } + } + } + + List levelMinPosList = new ArrayList<>(); + dataNumber = Math.min(dataNumber, listOfData.size()); + + for (int i = 0; i < dataNumber; i++) + { + LevelPos min = Collections.min(listOfData, LevelPos.getPosComparator(playerPosX, playerPosZ)); + listOfData.remove(min); + levelMinPosList.add(min); + } + return levelMinPosList; + } + + /** + * method to get all the nodes that have to be rendered based on the position of the player + * + * @return list of nodes + */ + public void getDataToRender(ConcurrentNavigableMap> dataToRender, RegionPos regionPos, int playerPosX, int playerPosZ, int start, int end, byte detailLevel, boolean zFix) + { + LevelPos regionLevelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z); + try + { + if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) && + start <= regionLevelPos.maxDistance(playerPosX, playerPosZ)) + { + LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel)); + region.getDataToRender(dataToRender, playerPosX, playerPosZ, start, end, detailLevel, zFix); + } + } catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 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. + *
+ * Returns null if the LodChunk doesn't exist or + * is outside the loaded area. + */ + 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."); + + try + { + LodRegion region = getRegion(levelPos); + + if (region == null) + { + return null; + } + + return region.getData(levelPos); + + } catch (Exception e) + { + return null; + } + } + + + /** + * Get the data point 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 void updateData(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."); + + LodRegion region = getRegion(levelPos); + + + if (region == null) + { + return; + } + region.updateArea(levelPos); + } + + /** + * return true if and only if the node at that position exist + */ + + public boolean hasThisPositionBeenGenerated(LevelPos levelPos) + { + LodRegion region = getRegion(levelPos); + + if (region == null) + { + return false; + } + + return region.hasDataBeenGenerated(levelPos); + } + + /** + * return true if and only if the node at that position exist + */ + public boolean doesDataExist(LevelPos levelPos) + { + try + { + LodRegion region = getRegion(levelPos); + + if (region == null) + { + return false; + } + + return region.doesDataExist(levelPos.clone()); + } catch (Exception e) + { + return false; + } + } + + /** + * return true if and only if the node at that position exist + */ + public DistanceGenerationMode getGenerationMode(LevelPos levelPos) + { + LodRegion region = getRegion(levelPos); + + if (region == null) + { + return DistanceGenerationMode.NONE; + } + + return region.getGenerationMode(levelPos); + } + + /** + * Get the region at the given X and Z coordinates from the + * RegionFileHandler. + */ + public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel) + { + if (fileHandler != null) + return fileHandler.loadRegionFromFile(regionPos, detailLevel); + else + return null; + } + + /** + * Save all dirty regions in this LodDimension to file. + */ + public void saveDirtyRegionsToFileAsync() + { + fileHandler.saveDirtyRegionsToFileAsync(); + } + + + /** + * Returns whether the region at the given X and Z coordinates + * is within the loaded range. + */ + public boolean regionIsInRange(int regionX, int regionZ) + { + int xIndex = (regionX - center.x) + halfWidth; + int zIndex = (regionZ - center.z) + halfWidth; + + return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width; + } + + + public int getCenterX() + { + return center.x; + } + + public int getCenterZ() + { + return center.z; + } + + + public int getWidth() + { + if (regions != null) + { + // we want to get the length directly from the + // source to make sure it is in sync with region + // and isRegionDirty + return regions.length; + } else + { + return width; + } + } + + public void setRegionWidth(int newWidth) + { + width = newWidth; + halfWidth = (int) Math.floor(width / 2); + + regions = new LodRegion[width][width]; + isRegionDirty = new boolean[width][width]; + + // populate isRegionDirty + for (int i = 0; i < width; i++) + for (int j = 0; j < width; j++) + isRegionDirty[i][j] = false; + } + + + @Override + public String toString() + { + int regionX; + int regionZ; + LevelPos levelPos; + LodRegion region; + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("Dimension : \n"); + for (int x = 0; x < regions.length; x++) + { + for (int z = 0; z < regions.length; z++) + { + region = regions[x][z]; + if (region == null) + { + stringBuilder.append("n"); + stringBuilder.append("\t"); + + } else + { + stringBuilder.append(region.getMinDetailLevel()); + stringBuilder.append("\t"); + } + } + stringBuilder.append("\n"); + } + System.out.println(stringBuilder); + return stringBuilder.toString(); + } } diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java index c06955431..6307106b7 100644 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ b/src/main/java/com/seibel/lod/objects/LodRegion.java @@ -22,675 +22,675 @@ import net.minecraft.util.math.ChunkPos; public class LodRegion implements Serializable { - //x coord, - private byte minDetailLevel; - private static final byte POSSIBLE_LOD = 10; - private int numberOfPoints; - - //For each of the following field the first slot is for the level of detail - //Important: byte have a [-128, 127] range. When converting from or to int a 128 should be added or removed - //If there is a bug with color then it's probably caused by this. - //in the future other fields like transparency and light level could be added - private byte[][][][] colors; - - private short[][][] height; - - private short[][][] depth; - - //a new node will have 0 as generationType - //a node with 1 is node - private byte[][][] generationType; - - private boolean[][][] dataExistence; - - public final int regionPosX; - public final int regionPosZ; - - public LodRegion(LevelContainer levelContainer, RegionPos regionPos) - { - this.regionPosX = regionPos.x; - this.regionPosZ = regionPos.z; - this.minDetailLevel = levelContainer.detailLevel; - - //Array of matrices of arrays - colors = new byte[POSSIBLE_LOD][][][]; - - //Arrays of matrices - height = new short[POSSIBLE_LOD][][]; - depth = new short[POSSIBLE_LOD][][]; - generationType = new byte[POSSIBLE_LOD][][]; - dataExistence = new boolean[POSSIBLE_LOD][][]; - - colors[minDetailLevel] = levelContainer.colors; - height[minDetailLevel] = levelContainer.height; - depth[minDetailLevel] = levelContainer.depth; - generationType[minDetailLevel] = levelContainer.generationType; - dataExistence[minDetailLevel] = levelContainer.dataExistence; - - //Initialize all the different matrices - for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) - { - int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod); - colors[lod] = new byte[size][size][3]; - height[lod] = new short[size][size]; - depth[lod] = new short[size][size]; - generationType[lod] = new byte[size][size]; - dataExistence[lod] = new boolean[size][size]; - } - int width; - LevelPos levelPos = new LevelPos(); - for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) - { - width = 1 << (LodUtil.REGION_DETAIL_LEVEL - tempLod); - for (int x = 0; x < width; x++) - { - for (int z = 0; z < width; z++) - { - levelPos.changeParameters(tempLod, x, z); - update(levelPos); - } - } - } - } - - public LodRegion(byte minDetailLevel, RegionPos regionPos) - { - this.minDetailLevel = minDetailLevel; - this.regionPosX = regionPos.x; - this.regionPosZ = regionPos.z; - - //Array of matrices of arrays - colors = new byte[POSSIBLE_LOD][][][]; - - //Arrays of matrices - height = new short[POSSIBLE_LOD][][]; - depth = new short[POSSIBLE_LOD][][]; - generationType = new byte[POSSIBLE_LOD][][]; - dataExistence = new boolean[POSSIBLE_LOD][][]; - - - //Initialize all the different matrices - for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) - { - int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod); - colors[lod] = new byte[size][size][3]; - height[lod] = new short[size][size]; - depth[lod] = new short[size][size]; - generationType[lod] = new byte[size][size]; - dataExistence[lod] = new boolean[size][size]; - - } - } - - /** - * This method can be used to insert data into the LodRegion - * - * @param levelPos - * @param dataPoint - * @param generationType - * @return - */ - public boolean addData(LevelPos levelPos, short[] dataPoint, byte generationType) - { - levelPos.performRegionModule(); - if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])) - { - - //update the number of node present - if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++; - - //add the node data - this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getHeight(dataPoint); - this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getDepth(dataPoint); - this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (DataPoint.getRed(dataPoint) - 128); - this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (DataPoint.getGreen(dataPoint) - 128); - this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (DataPoint.getBlue(dataPoint) - 128); - this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = generationType; - this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true; - return true; - } else - { - return false; - } - } - - - public short[] getData(ChunkPos chunkPos) - { - return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z)); - } - - /** - * This method will return the data in the position relative to the level of detail - * - * @param lod - * @return the data at the relative pos and level - */ - 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)); - return getData(new LevelPos(lod, posX, posZ)); - } - - /** - * This method will return the data in the position relative to the level of detail - * - * @param levelPos - * @return the data at the relative pos and level - */ - public short[] getData(LevelPos levelPos) - { - levelPos = levelPos.getRegionModuleLevelPos(); - return new short[]{height[levelPos.detailLevel][levelPos.posX][levelPos.posZ], - depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ], - (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) - }; - } - - /** - * This method will return all the levelPos that are renderable according to the requisite given in input - * - * @return - */ - public List getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generation, byte detailLevel, int dataNumber) - { - LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0); - List levelPosList = new ArrayList<>(); - getDataToGenerate(levelPosList, levelPos, playerPosX, playerPosZ, start, end, generation, detailLevel); - List levelMinPosList = new ArrayList<>(); - dataNumber = Math.min(dataNumber, levelPosList.size()); - - LevelPos min; - for (int i = 0; i < dataNumber; i++) - { - min = Collections.min(levelPosList, LevelPos.getPosComparator(playerPosX, playerPosZ)); - levelPosList.remove(min); - levelMinPosList.add(min); - } - - return levelMinPosList; - - } - - private void getDataToGenerate(List levelPosList, LevelPos levelPos, int playerPosX, int playerPosZ, int start, int end, byte generation, byte targetDetailLevel) - { - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel); - - //here i calculate the the LevelPos is in range - //This is important to avoid any kind of hole in the generation - int maxDistance = levelPos.maxDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); - int minDistance = levelPos.minDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); - - if (!(start <= maxDistance && minDistance < end) || levelPos.detailLevel < targetDetailLevel) - { - return; - } - - int posX = levelPos.posX; - int posZ = levelPos.posZ; - byte detailLevel = levelPos.detailLevel; - int childPosX = posX * 2; - int childPosZ = posZ * 2; - LevelPos childPos = new LevelPos(); - - int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel + 1); - //we have reached the target detail level - if (targetDetailLevel == levelPos.detailLevel) - { - if (generationType[detailLevel][posX][posZ] < generation) - { - levelPosList.add(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size)); - } - } else - { - //we want max a request per chunk. So for lod smaller than chunk we explore only the top rigth child - if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL) - { - int num = 0; - //We take all the children that are not generated to at least the generation level taken in input - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); - - if (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] < generation || !doesDataExist(levelPos)) - { - num++; - levelPosList.add(new LevelPos(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize)); - } - } - } - - //only if all the children are correctly generated we go deeper - if (num == 0) - { - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); - getDataToGenerate(levelPosList, levelPos - , playerPosX, playerPosZ, start, end, generation, targetDetailLevel); - } - } - } - } else - //now we keep exploring the top right child - { - levelPos.changeParameters(levelPos.detailLevel, levelPos.posX, levelPos.posZ); - levelPos.convert((byte) (levelPos.detailLevel - 1)); - if (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] < generation) - { - levelPosList.add(new LevelPos(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize)); - } else - { - if (levelPos.detailLevel != targetDetailLevel) - { - getDataToGenerate(levelPosList, levelPos, playerPosX, playerPosZ, start, end, generation, targetDetailLevel); - } - } - } - } - return; - } - - - /** - * @return - */ - public void getDataToRender(ConcurrentNavigableMap> dataToRender, int playerPosX, int playerPosZ, int start, int end, byte detailLevel, boolean zFix) - { - LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0); - getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ, start, end, detailLevel, zFix); - } - - /** - * @return - */ - private void getDataToRender(ConcurrentNavigableMap> dataToRender, LevelPos levelPos, int playerPosX, int playerPosZ, int start, int end, byte targetDetailLevel, boolean zFix) - { - - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel); - - - int posX = levelPos.posX; - int posZ = levelPos.posZ; - byte detailLevel = levelPos.detailLevel; - - //here i calculate the the LevelPos is in range - //This is important to avoid any kind of hole in the rendering - int maxDistance = levelPos.maxDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); - int minDistance = levelPos.minDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); - - //To avoid z fighting: if the pos is touching the end radius at detailLevel + 1 then we stop - //cause this area will be occupied by bigger block - if (detailLevel== targetDetailLevel + 1 && end <= maxDistance && minDistance <= end && zFix) - { - return; - } - - if (!(start <= maxDistance && minDistance < end) || detailLevel < targetDetailLevel) - { - return; - } - - //we have reached the target detail level - if (targetDetailLevel == detailLevel) - { - dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new ArrayList<>()); - } else - { - int childPosX = posX * 2; - int childPosZ = posZ * 2; - int childrenCount = 0; - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); - if (doesDataExist(levelPos)) childrenCount++; - } - } - - //If all the four children exist we go deeper - if (childrenCount == 4) - { - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); - getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ, start, end, targetDetailLevel, zFix); - } - } - } else - { - dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new ArrayList<>()); - } - } - return; - } - - /** - * @param levelPos - */ - public void updateArea(LevelPos levelPos) - { - int width; - int startX; - int startZ; - byte detailLevel = levelPos.detailLevel; - int posX = levelPos.posX; - int posZ = levelPos.posZ; - for (byte bottom = (byte) (minDetailLevel + 1); bottom <= detailLevel; bottom++) - { - levelPos.convert(bottom); - startX = levelPos.posX; - startZ = levelPos.posZ; - width = 1 << (detailLevel - bottom); - for (int x = 0; x < width; x++) - { - for (int z = 0; z < width; z++) - { - levelPos.changeParameters(bottom, startX + x, startZ + z); - update(levelPos); - } - } - levelPos.changeParameters(detailLevel, posX, posZ); - } - for (byte tempLod = (byte) (detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) - { - levelPos.convert(tempLod); - update(levelPos); - } - } - - /** - * @param levelPos - */ - private void update(LevelPos levelPos) - { - levelPos.performRegionModule(); - int numberOfChildren = 0; - int numberOfVoidChildren = 0; - - byte minGenerationType = 5; - int tempRed = 0; - int tempGreen = 0; - int tempBlue = 0; - int tempHeight = 0; - int tempDepth = 0; - int newPosX; - int newPosZ; - byte newDetailLevel; - int detailLevel = levelPos.detailLevel; - int posX = levelPos.posX; - int posZ = levelPos.posZ; - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - newPosX = 2 * posX + x; - newPosZ = 2 * posZ + z; - newDetailLevel = (byte) (detailLevel - 1); - levelPos.changeParameters(newDetailLevel, newPosX, newPosZ); - if (hasDataBeenGenerated(levelPos)) - { - if (height[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_HEIGHT - && depth[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_DEPTH) - { - numberOfChildren++; - - tempRed += colors[newDetailLevel][newPosX][newPosZ][0]; - tempGreen += colors[newDetailLevel][newPosX][newPosZ][1]; - tempBlue += colors[newDetailLevel][newPosX][newPosZ][2]; - tempHeight += height[newDetailLevel][newPosX][newPosZ]; - tempDepth += depth[newDetailLevel][newPosX][newPosZ]; - } else - { - // void children have the default height (most likely -1) - // and represent a LOD with no blocks in it - numberOfVoidChildren++; - } - - minGenerationType = (byte) Math.min(minGenerationType, generationType[newDetailLevel][newPosX][newPosZ]); - } - } - } - - if (numberOfChildren > 0) - { - colors[detailLevel][posX][posZ][0] = (byte) (tempRed / numberOfChildren); - colors[detailLevel][posX][posZ][1] = (byte) (tempGreen / numberOfChildren); - colors[detailLevel][posX][posZ][2] = (byte) (tempBlue / numberOfChildren); - height[detailLevel][posX][posZ] = (short) (tempHeight / numberOfChildren); - depth[detailLevel][posX][posZ] = (short) (tempDepth / numberOfChildren); - generationType[detailLevel][posX][posZ] = minGenerationType; - dataExistence[detailLevel][posX][posZ] = true; - } else if (numberOfVoidChildren > 0) - { - colors[detailLevel][posX][posZ][0] = (byte) 0; - colors[detailLevel][posX][posZ][1] = (byte) 0; - colors[detailLevel][posX][posZ][2] = (byte) 0; - - height[detailLevel][posX][posZ] = LodBuilder.DEFAULT_HEIGHT; - depth[detailLevel][posX][posZ] = LodBuilder.DEFAULT_DEPTH; - - generationType[detailLevel][posX][posZ] = minGenerationType; - dataExistence[detailLevel][posX][posZ] = true; - } - } - - /** - * @param levelPos - * @return - */ - private boolean[][] getChildren(LevelPos levelPos) - { - levelPos = levelPos.getRegionModuleLevelPos(); - boolean[][] children = new boolean[2][2]; - int numberOfChild = 0; - if (minDetailLevel == levelPos.detailLevel) - { - return children; - } - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - children[x][z] = (dataExistence[levelPos.detailLevel - 1][2 * levelPos.posX + x][2 * levelPos.posZ + z]); - } - } - return children; - } - - /** - * @param chunkPos - * @return - */ - public boolean doesDataExist(ChunkPos chunkPos) - { - return doesDataExist(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z)); - } - - /** - * @param levelPos - * @return - */ - public boolean doesDataExist(LevelPos levelPos) - { - levelPos = levelPos.getRegionModuleLevelPos(); - return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]; - } - - /** - * @param levelPos - * @return - */ - public DistanceGenerationMode getGenerationMode(LevelPos levelPos) - { - levelPos = levelPos.getRegionModuleLevelPos(); - DistanceGenerationMode generationMode = DistanceGenerationMode.NONE; - switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) - { - case 0: - generationMode = DistanceGenerationMode.NONE; - break; - case 1: - generationMode = DistanceGenerationMode.BIOME_ONLY; - break; - case 2: - generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; - break; - case 3: - generationMode = DistanceGenerationMode.SURFACE; - break; - case 4: - generationMode = DistanceGenerationMode.FEATURES; - break; - case 5: - generationMode = DistanceGenerationMode.SERVER; - break; - default: - generationMode = DistanceGenerationMode.NONE; - break; - - } - return generationMode; - } - - /** - * @param levelPos - * @return - */ - public boolean hasDataBeenGenerated(LevelPos levelPos) - { - levelPos.performRegionModule(); - try - { - return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0); - } catch (Exception e) - { - System.out.println(levelPos); - e.printStackTrace(); - throw e; - } - } - - public byte getMinDetailLevel() - { - return minDetailLevel; - } - - /** - * This will be used to save a level - * - * @param detailLevel - * @return - */ - public LevelContainer getLevel(byte detailLevel) - { - if (detailLevel < minDetailLevel) - { - throw new IllegalArgumentException("getLevel asked for a level that does not exist: minimum " + minDetailLevel + " level requested " + detailLevel); - } - return new LevelContainer(detailLevel, colors[detailLevel], height[detailLevel], depth[detailLevel], generationType[detailLevel], dataExistence[detailLevel]); - } - - /** - * @param levelContainer - */ - public void addLevel(LevelContainer levelContainer) - { - if (levelContainer.detailLevel < minDetailLevel - 1) - { - throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 "); - } - if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel; - colors[levelContainer.detailLevel] = levelContainer.colors; - height[levelContainer.detailLevel] = levelContainer.height; - depth[levelContainer.detailLevel] = levelContainer.depth; - generationType[levelContainer.detailLevel] = levelContainer.generationType; - dataExistence[levelContainer.detailLevel] = levelContainer.dataExistence; - - } - - /** - * @param detailLevel - */ - public void cutTree(byte detailLevel) - { - if (minDetailLevel < detailLevel) - { - for (byte tempLod = 0; tempLod < detailLevel; tempLod++) - { - colors[tempLod] = new byte[0][0][0]; - height[tempLod] = new short[0][0]; - depth[tempLod] = new short[0][0]; - generationType[tempLod] = new byte[0][0]; - dataExistence[tempLod] = new boolean[0][0]; - } - minDetailLevel = detailLevel; - } - } - - /** - * @param detailLevel - */ - public void expand(byte detailLevel) - { - if (detailLevel < minDetailLevel) - { - for (byte tempLod = minDetailLevel; tempLod < LodUtil.REGION_DETAIL_LEVEL; tempLod++) - { - int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod); - generationType[tempLod] = new byte[size][size]; - } - for (byte tempLod = detailLevel; tempLod < minDetailLevel; tempLod++) - { - int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod); - colors[tempLod] = new byte[size][size][3]; - height[tempLod] = new short[size][size]; - depth[tempLod] = new short[size][size]; - generationType[tempLod] = new byte[size][size]; - dataExistence[tempLod] = new boolean[size][size]; - } - minDetailLevel = detailLevel; - } - } - - /** - * return RegionPos of this lod region - */ - public RegionPos getRegionPos() - { - return new RegionPos(regionPosX, regionPosZ); - } - - /** - * return needed memory in byte - */ - public int getMinMemoryNeeded() - { - int count = 0; - for (byte tempLod = LodUtil.REGION_DETAIL_LEVEL; tempLod > minDetailLevel; tempLod--) - { - //i'm doing a upper limit of the minimum - //Color should be just 3 byte but i'm gonna calculate as 12 byte - //Height and depth should be just 4 byte but i'm gonna calculate as 8 byte - //count += Math.pow(2,LodUtil.REGION_DETAIL_LEVEL-tempLod) * (8 + 3 + 2 + 2 + 1 + 1) - count += Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod) * (24 + 8 + 8 + 8 + 8 + 8); - } - return count; - } - - @Override - public String toString() - { - return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString(); - } + //x coord, + private byte minDetailLevel; + private static final byte POSSIBLE_LOD = 10; + private int numberOfPoints; + + //For each of the following field the first slot is for the level of detail + //Important: byte have a [-128, 127] range. When converting from or to int a 128 should be added or removed + //If there is a bug with color then it's probably caused by this. + //in the future other fields like transparency and light level could be added + private byte[][][][] colors; + + private short[][][] height; + + private short[][][] depth; + + //a new node will have 0 as generationType + //a node with 1 is node + private byte[][][] generationType; + + private boolean[][][] dataExistence; + + public final int regionPosX; + public final int regionPosZ; + + public LodRegion(LevelContainer levelContainer, RegionPos regionPos) + { + this.regionPosX = regionPos.x; + this.regionPosZ = regionPos.z; + this.minDetailLevel = levelContainer.detailLevel; + + //Array of matrices of arrays + colors = new byte[POSSIBLE_LOD][][][]; + + //Arrays of matrices + height = new short[POSSIBLE_LOD][][]; + depth = new short[POSSIBLE_LOD][][]; + generationType = new byte[POSSIBLE_LOD][][]; + dataExistence = new boolean[POSSIBLE_LOD][][]; + + colors[minDetailLevel] = levelContainer.colors; + height[minDetailLevel] = levelContainer.height; + depth[minDetailLevel] = levelContainer.depth; + generationType[minDetailLevel] = levelContainer.generationType; + dataExistence[minDetailLevel] = levelContainer.dataExistence; + + //Initialize all the different matrices + for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) + { + int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod); + colors[lod] = new byte[size][size][3]; + height[lod] = new short[size][size]; + depth[lod] = new short[size][size]; + generationType[lod] = new byte[size][size]; + dataExistence[lod] = new boolean[size][size]; + } + int width; + LevelPos levelPos = new LevelPos(); + for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) + { + width = 1 << (LodUtil.REGION_DETAIL_LEVEL - tempLod); + for (int x = 0; x < width; x++) + { + for (int z = 0; z < width; z++) + { + levelPos.changeParameters(tempLod, x, z); + update(levelPos); + } + } + } + } + + public LodRegion(byte minDetailLevel, RegionPos regionPos) + { + this.minDetailLevel = minDetailLevel; + this.regionPosX = regionPos.x; + this.regionPosZ = regionPos.z; + + //Array of matrices of arrays + colors = new byte[POSSIBLE_LOD][][][]; + + //Arrays of matrices + height = new short[POSSIBLE_LOD][][]; + depth = new short[POSSIBLE_LOD][][]; + generationType = new byte[POSSIBLE_LOD][][]; + dataExistence = new boolean[POSSIBLE_LOD][][]; + + + //Initialize all the different matrices + for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) + { + int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod); + colors[lod] = new byte[size][size][3]; + height[lod] = new short[size][size]; + depth[lod] = new short[size][size]; + generationType[lod] = new byte[size][size]; + dataExistence[lod] = new boolean[size][size]; + + } + } + + /** + * This method can be used to insert data into the LodRegion + * + * @param levelPos + * @param dataPoint + * @param generationType + * @return + */ + public boolean addData(LevelPos levelPos, short[] dataPoint, byte generationType) + { + levelPos.performRegionModule(); + if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])) + { + + //update the number of node present + if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++; + + //add the node data + this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getHeight(dataPoint); + this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getDepth(dataPoint); + this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (DataPoint.getRed(dataPoint) - 128); + this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (DataPoint.getGreen(dataPoint) - 128); + this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (DataPoint.getBlue(dataPoint) - 128); + this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = generationType; + this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true; + return true; + } else + { + return false; + } + } + + + public short[] getData(ChunkPos chunkPos) + { + return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z)); + } + + /** + * This method will return the data in the position relative to the level of detail + * + * @param lod + * @return the data at the relative pos and level + */ + 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)); + return getData(new LevelPos(lod, posX, posZ)); + } + + /** + * This method will return the data in the position relative to the level of detail + * + * @param levelPos + * @return the data at the relative pos and level + */ + public short[] getData(LevelPos levelPos) + { + levelPos = levelPos.getRegionModuleLevelPos(); + return new short[]{height[levelPos.detailLevel][levelPos.posX][levelPos.posZ], + depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ], + (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) + }; + } + + /** + * This method will return all the levelPos that are renderable according to the requisite given in input + * + * @return + */ + public List getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generation, byte detailLevel, int dataNumber) + { + LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0); + List levelPosList = new ArrayList<>(); + getDataToGenerate(levelPosList, levelPos, playerPosX, playerPosZ, start, end, generation, detailLevel); + List levelMinPosList = new ArrayList<>(); + dataNumber = Math.min(dataNumber, levelPosList.size()); + + LevelPos min; + for (int i = 0; i < dataNumber; i++) + { + min = Collections.min(levelPosList, LevelPos.getPosComparator(playerPosX, playerPosZ)); + levelPosList.remove(min); + levelMinPosList.add(min); + } + + return levelMinPosList; + + } + + private void getDataToGenerate(List levelPosList, LevelPos levelPos, int playerPosX, int playerPosZ, int start, int end, byte generation, byte targetDetailLevel) + { + int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel); + + //here i calculate the the LevelPos is in range + //This is important to avoid any kind of hole in the generation + int maxDistance = levelPos.maxDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); + int minDistance = levelPos.minDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); + + if (!(start <= maxDistance && minDistance < end) || levelPos.detailLevel < targetDetailLevel) + { + return; + } + + int posX = levelPos.posX; + int posZ = levelPos.posZ; + byte detailLevel = levelPos.detailLevel; + int childPosX = posX * 2; + int childPosZ = posZ * 2; + LevelPos childPos = new LevelPos(); + + int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel + 1); + //we have reached the target detail level + if (targetDetailLevel == levelPos.detailLevel) + { + if (generationType[detailLevel][posX][posZ] < generation) + { + levelPosList.add(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size)); + } + } else + { + //we want max a request per chunk. So for lod smaller than chunk we explore only the top rigth child + if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL) + { + int num = 0; + //We take all the children that are not generated to at least the generation level taken in input + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { + levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); + + if (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] < generation || !doesDataExist(levelPos)) + { + num++; + levelPosList.add(new LevelPos(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize)); + } + } + } + + //only if all the children are correctly generated we go deeper + if (num == 0) + { + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { + levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); + getDataToGenerate(levelPosList, levelPos + , playerPosX, playerPosZ, start, end, generation, targetDetailLevel); + } + } + } + } else + //now we keep exploring the top right child + { + levelPos.changeParameters(levelPos.detailLevel, levelPos.posX, levelPos.posZ); + levelPos.convert((byte) (levelPos.detailLevel - 1)); + if (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] < generation) + { + levelPosList.add(new LevelPos(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize)); + } else + { + if (levelPos.detailLevel != targetDetailLevel) + { + getDataToGenerate(levelPosList, levelPos, playerPosX, playerPosZ, start, end, generation, targetDetailLevel); + } + } + } + } + return; + } + + + /** + * @return + */ + public void getDataToRender(ConcurrentNavigableMap> dataToRender, int playerPosX, int playerPosZ, int start, int end, byte detailLevel, boolean zFix) + { + LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0); + getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ, start, end, detailLevel, zFix); + } + + /** + * @return + */ + private void getDataToRender(ConcurrentNavigableMap> dataToRender, LevelPos levelPos, int playerPosX, int playerPosZ, int start, int end, byte targetDetailLevel, boolean zFix) + { + + int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel); + + + int posX = levelPos.posX; + int posZ = levelPos.posZ; + byte detailLevel = levelPos.detailLevel; + + //here i calculate the the LevelPos is in range + //This is important to avoid any kind of hole in the rendering + int maxDistance = levelPos.maxDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); + int minDistance = levelPos.minDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); + + //To avoid z fighting: if the pos is touching the end radius at detailLevel + 1 then we stop + //cause this area will be occupied by bigger block + if (detailLevel == targetDetailLevel + 1 && end <= maxDistance && minDistance <= end && zFix) + { + return; + } + + if (!(start <= maxDistance && minDistance < end) || detailLevel < targetDetailLevel) + { + return; + } + + //we have reached the target detail level + if (targetDetailLevel == detailLevel) + { + dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new ArrayList<>()); + } else + { + int childPosX = posX * 2; + int childPosZ = posZ * 2; + int childrenCount = 0; + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { + levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); + if (doesDataExist(levelPos)) childrenCount++; + } + } + + //If all the four children exist we go deeper + if (childrenCount == 4) + { + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { + levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); + getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ, start, end, targetDetailLevel, zFix); + } + } + } else + { + dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new ArrayList<>()); + } + } + return; + } + + /** + * @param levelPos + */ + public void updateArea(LevelPos levelPos) + { + int width; + int startX; + int startZ; + byte detailLevel = levelPos.detailLevel; + int posX = levelPos.posX; + int posZ = levelPos.posZ; + for (byte bottom = (byte) (minDetailLevel + 1); bottom <= detailLevel; bottom++) + { + levelPos.convert(bottom); + startX = levelPos.posX; + startZ = levelPos.posZ; + width = 1 << (detailLevel - bottom); + for (int x = 0; x < width; x++) + { + for (int z = 0; z < width; z++) + { + levelPos.changeParameters(bottom, startX + x, startZ + z); + update(levelPos); + } + } + levelPos.changeParameters(detailLevel, posX, posZ); + } + for (byte tempLod = (byte) (detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) + { + levelPos.convert(tempLod); + update(levelPos); + } + } + + /** + * @param levelPos + */ + private void update(LevelPos levelPos) + { + levelPos.performRegionModule(); + int numberOfChildren = 0; + int numberOfVoidChildren = 0; + + byte minGenerationType = 5; + int tempRed = 0; + int tempGreen = 0; + int tempBlue = 0; + int tempHeight = 0; + int tempDepth = 0; + int newPosX; + int newPosZ; + byte newDetailLevel; + int detailLevel = levelPos.detailLevel; + int posX = levelPos.posX; + int posZ = levelPos.posZ; + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { + newPosX = 2 * posX + x; + newPosZ = 2 * posZ + z; + newDetailLevel = (byte) (detailLevel - 1); + levelPos.changeParameters(newDetailLevel, newPosX, newPosZ); + if (hasDataBeenGenerated(levelPos)) + { + if (height[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_HEIGHT + && depth[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_DEPTH) + { + numberOfChildren++; + + tempRed += colors[newDetailLevel][newPosX][newPosZ][0]; + tempGreen += colors[newDetailLevel][newPosX][newPosZ][1]; + tempBlue += colors[newDetailLevel][newPosX][newPosZ][2]; + tempHeight += height[newDetailLevel][newPosX][newPosZ]; + tempDepth += depth[newDetailLevel][newPosX][newPosZ]; + } else + { + // void children have the default height (most likely -1) + // and represent a LOD with no blocks in it + numberOfVoidChildren++; + } + + minGenerationType = (byte) Math.min(minGenerationType, generationType[newDetailLevel][newPosX][newPosZ]); + } + } + } + + if (numberOfChildren > 0) + { + colors[detailLevel][posX][posZ][0] = (byte) (tempRed / numberOfChildren); + colors[detailLevel][posX][posZ][1] = (byte) (tempGreen / numberOfChildren); + colors[detailLevel][posX][posZ][2] = (byte) (tempBlue / numberOfChildren); + height[detailLevel][posX][posZ] = (short) (tempHeight / numberOfChildren); + depth[detailLevel][posX][posZ] = (short) (tempDepth / numberOfChildren); + generationType[detailLevel][posX][posZ] = minGenerationType; + dataExistence[detailLevel][posX][posZ] = true; + } else if (numberOfVoidChildren > 0) + { + colors[detailLevel][posX][posZ][0] = (byte) 0; + colors[detailLevel][posX][posZ][1] = (byte) 0; + colors[detailLevel][posX][posZ][2] = (byte) 0; + + height[detailLevel][posX][posZ] = LodBuilder.DEFAULT_HEIGHT; + depth[detailLevel][posX][posZ] = LodBuilder.DEFAULT_DEPTH; + + generationType[detailLevel][posX][posZ] = minGenerationType; + dataExistence[detailLevel][posX][posZ] = true; + } + } + + /** + * @param levelPos + * @return + */ + private boolean[][] getChildren(LevelPos levelPos) + { + levelPos = levelPos.getRegionModuleLevelPos(); + boolean[][] children = new boolean[2][2]; + int numberOfChild = 0; + if (minDetailLevel == levelPos.detailLevel) + { + return children; + } + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { + children[x][z] = (dataExistence[levelPos.detailLevel - 1][2 * levelPos.posX + x][2 * levelPos.posZ + z]); + } + } + return children; + } + + /** + * @param chunkPos + * @return + */ + public boolean doesDataExist(ChunkPos chunkPos) + { + return doesDataExist(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z)); + } + + /** + * @param levelPos + * @return + */ + public boolean doesDataExist(LevelPos levelPos) + { + levelPos = levelPos.getRegionModuleLevelPos(); + return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]; + } + + /** + * @param levelPos + * @return + */ + public DistanceGenerationMode getGenerationMode(LevelPos levelPos) + { + levelPos = levelPos.getRegionModuleLevelPos(); + DistanceGenerationMode generationMode = DistanceGenerationMode.NONE; + switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) + { + case 0: + generationMode = DistanceGenerationMode.NONE; + break; + case 1: + generationMode = DistanceGenerationMode.BIOME_ONLY; + break; + case 2: + generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; + break; + case 3: + generationMode = DistanceGenerationMode.SURFACE; + break; + case 4: + generationMode = DistanceGenerationMode.FEATURES; + break; + case 5: + generationMode = DistanceGenerationMode.SERVER; + break; + default: + generationMode = DistanceGenerationMode.NONE; + break; + + } + return generationMode; + } + + /** + * @param levelPos + * @return + */ + public boolean hasDataBeenGenerated(LevelPos levelPos) + { + levelPos.performRegionModule(); + try + { + return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0); + } catch (Exception e) + { + System.out.println(levelPos); + e.printStackTrace(); + throw e; + } + } + + public byte getMinDetailLevel() + { + return minDetailLevel; + } + + /** + * This will be used to save a level + * + * @param detailLevel + * @return + */ + public LevelContainer getLevel(byte detailLevel) + { + if (detailLevel < minDetailLevel) + { + throw new IllegalArgumentException("getLevel asked for a level that does not exist: minimum " + minDetailLevel + " level requested " + detailLevel); + } + return new LevelContainer(detailLevel, colors[detailLevel], height[detailLevel], depth[detailLevel], generationType[detailLevel], dataExistence[detailLevel]); + } + + /** + * @param levelContainer + */ + public void addLevel(LevelContainer levelContainer) + { + if (levelContainer.detailLevel < minDetailLevel - 1) + { + throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 "); + } + if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel; + colors[levelContainer.detailLevel] = levelContainer.colors; + height[levelContainer.detailLevel] = levelContainer.height; + depth[levelContainer.detailLevel] = levelContainer.depth; + generationType[levelContainer.detailLevel] = levelContainer.generationType; + dataExistence[levelContainer.detailLevel] = levelContainer.dataExistence; + + } + + /** + * @param detailLevel + */ + public void cutTree(byte detailLevel) + { + if (minDetailLevel < detailLevel) + { + for (byte tempLod = 0; tempLod < detailLevel; tempLod++) + { + colors[tempLod] = new byte[0][0][0]; + height[tempLod] = new short[0][0]; + depth[tempLod] = new short[0][0]; + generationType[tempLod] = new byte[0][0]; + dataExistence[tempLod] = new boolean[0][0]; + } + minDetailLevel = detailLevel; + } + } + + /** + * @param detailLevel + */ + public void expand(byte detailLevel) + { + if (detailLevel < minDetailLevel) + { + for (byte tempLod = minDetailLevel; tempLod < LodUtil.REGION_DETAIL_LEVEL; tempLod++) + { + int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod); + generationType[tempLod] = new byte[size][size]; + } + for (byte tempLod = detailLevel; tempLod < minDetailLevel; tempLod++) + { + int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod); + colors[tempLod] = new byte[size][size][3]; + height[tempLod] = new short[size][size]; + depth[tempLod] = new short[size][size]; + generationType[tempLod] = new byte[size][size]; + dataExistence[tempLod] = new boolean[size][size]; + } + minDetailLevel = detailLevel; + } + } + + /** + * return RegionPos of this lod region + */ + public RegionPos getRegionPos() + { + return new RegionPos(regionPosX, regionPosZ); + } + + /** + * return needed memory in byte + */ + public int getMinMemoryNeeded() + { + int count = 0; + for (byte tempLod = LodUtil.REGION_DETAIL_LEVEL; tempLod > minDetailLevel; tempLod--) + { + //i'm doing a upper limit of the minimum + //Color should be just 3 byte but i'm gonna calculate as 12 byte + //Height and depth should be just 4 byte but i'm gonna calculate as 8 byte + //count += Math.pow(2,LodUtil.REGION_DETAIL_LEVEL-tempLod) * (8 + 3 + 2 + 2 + 1 + 1) + count += Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod) * (24 + 8 + 8 + 8 + 8 + 8); + } + return count; + } + + @Override + public String toString() + { + return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString(); + } } diff --git a/src/main/java/com/seibel/lod/objects/LodWorld.java b/src/main/java/com/seibel/lod/objects/LodWorld.java index b8d77ebd2..330d335b8 100644 --- a/src/main/java/com/seibel/lod/objects/LodWorld.java +++ b/src/main/java/com/seibel/lod/objects/LodWorld.java @@ -33,127 +33,127 @@ import net.minecraft.world.DimensionType; */ public class LodWorld { - private String worldName; + private String worldName; - private Map lodDimensions; + private Map lodDimensions; - /** - * If true then the LOD world is setup and ready to use - */ - private boolean isWorldLoaded = false; + /** + * If true then the LOD world is setup and ready to use + */ + private boolean isWorldLoaded = false; - public static final String NO_WORLD_LOADED = "No world loaded"; + public static final String NO_WORLD_LOADED = "No world loaded"; - public LodWorld() - { - worldName = NO_WORLD_LOADED; - } + public LodWorld() + { + worldName = NO_WORLD_LOADED; + } - /** - * Set up the LodQuadTreeWorld with the given newWorldName.
- * This should be done whenever loading a new world. - * - * @param newWorldName name of the world - */ - public void selectWorld(String newWorldName) - { - if (newWorldName.isEmpty()) - { - deselectWorld(); - return; - } + /** + * Set up the LodQuadTreeWorld with the given newWorldName.
+ * This should be done whenever loading a new world. + * + * @param newWorldName name of the world + */ + public void selectWorld(String newWorldName) + { + if (newWorldName.isEmpty()) + { + deselectWorld(); + return; + } - if (worldName.equals(newWorldName)) - // don't recreate everything if we - // didn't actually change worlds - return; + if (worldName.equals(newWorldName)) + // don't recreate everything if we + // didn't actually change worlds + return; - worldName = newWorldName; - lodDimensions = new Hashtable(); - isWorldLoaded = true; - } + worldName = newWorldName; + lodDimensions = new Hashtable(); + isWorldLoaded = true; + } - /** - * Set the worldName to "No world loaded" - * and clear the lodDimensions Map.
- * This should be done whenever unloaded a world. - */ - public void deselectWorld() - { - worldName = NO_WORLD_LOADED; - lodDimensions = null; - isWorldLoaded = false; - } + /** + * Set the worldName to "No world loaded" + * and clear the lodDimensions Map.
+ * This should be done whenever unloaded a world. + */ + public void deselectWorld() + { + worldName = NO_WORLD_LOADED; + lodDimensions = null; + isWorldLoaded = false; + } - /** - * Adds newStorage to this world, if a LodQuadTreeDimension - * already exists for the given dimension it is replaced. - */ - public void addLodDimension(LodDimension newStorage) - { - if (lodDimensions == null) - return; + /** + * Adds newStorage to this world, if a LodQuadTreeDimension + * already exists for the given dimension it is replaced. + */ + public void addLodDimension(LodDimension newStorage) + { + if (lodDimensions == null) + return; - lodDimensions.put(newStorage.dimension, newStorage); - } + lodDimensions.put(newStorage.dimension, newStorage); + } - /** - * Returns null if no LodQuadTreeDimension exists for the given dimension - */ - public LodDimension getLodDimension(DimensionType dimension) - { - if (lodDimensions == null) - return null; + /** + * Returns null if no LodQuadTreeDimension exists for the given dimension + */ + public LodDimension getLodDimension(DimensionType dimension) + { + if (lodDimensions == null) + return null; - return lodDimensions.get(dimension); - } + return lodDimensions.get(dimension); + } - /** - * Resizes the max width in regions that each LodDimension - * should use. - */ - public void resizeDimensionRegionWidth(int newWidth) - { - if (lodDimensions == null) - return; + /** + * Resizes the max width in regions that each LodDimension + * should use. + */ + public void resizeDimensionRegionWidth(int newWidth) + { + if (lodDimensions == null) + return; - saveAllDimensions(); + saveAllDimensions(); - for (DimensionType key : lodDimensions.keySet()) - lodDimensions.get(key).setRegionWidth(newWidth); - } + for (DimensionType key : lodDimensions.keySet()) + lodDimensions.get(key).setRegionWidth(newWidth); + } - /** - * Requests all dimensions save any dirty regions they may have. - */ - public void saveAllDimensions() - { - if (lodDimensions == null) - return; + /** + * Requests all dimensions save any dirty regions they may have. + */ + public void saveAllDimensions() + { + if (lodDimensions == null) + return; - ClientProxy.LOGGER.info("Saving LODs"); + ClientProxy.LOGGER.info("Saving LODs"); - for (DimensionType key : lodDimensions.keySet()) - lodDimensions.get(key).saveDirtyRegionsToFileAsync(); - } + for (DimensionType key : lodDimensions.keySet()) + lodDimensions.get(key).saveDirtyRegionsToFileAsync(); + } - public boolean getIsWorldLoaded() - { - return isWorldLoaded; - } + public boolean getIsWorldLoaded() + { + return isWorldLoaded; + } - public String getWorldName() - { - return worldName; - } + public String getWorldName() + { + return worldName; + } - @Override - public String toString() - { - return "World name: " + worldName; - } + @Override + public String toString() + { + return "World name: " + worldName; + } } diff --git a/src/main/java/com/seibel/lod/objects/RegionPos.java b/src/main/java/com/seibel/lod/objects/RegionPos.java index 7fbf9ef50..27c7100ac 100644 --- a/src/main/java/com/seibel/lod/objects/RegionPos.java +++ b/src/main/java/com/seibel/lod/objects/RegionPos.java @@ -24,7 +24,7 @@ import net.minecraft.util.math.ChunkPos; /** * This object is similar to ChunkPos or BlockPos. - * + * * @author James Seibel * @version 8-21-2021 */ @@ -32,11 +32,11 @@ public class RegionPos { public int x; public int z; - - + + /** * Default Constructor
- * + *

* Sets x and z to 0 */ public RegionPos() @@ -44,38 +44,41 @@ public class RegionPos x = 0; z = 0; } - + public RegionPos(int newX, int newZ) { x = newX; z = newZ; } - + public RegionPos(BlockPos pos) { this(new ChunkPos(pos)); } - + public RegionPos(ChunkPos pos) { x = Math.floorDiv(pos.x, LodUtil.REGION_WIDTH_IN_CHUNKS); z = Math.floorDiv(pos.z, LodUtil.REGION_WIDTH_IN_CHUNKS); } - - /** Returns the ChunkPos at the center of this region */ + + /** + * Returns the ChunkPos at the center of this region + */ public ChunkPos chunkPos() { - return new ChunkPos((x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS/2, (z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS/2); + return new ChunkPos((x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2, (z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2); } - - /** Returns the BlockPos at the center of this region */ + + /** + * Returns the BlockPos at the center of this region + */ public BlockPos blockPos() { - return chunkPos().getWorldPosition().offset(LodUtil.CHUNK_WIDTH/2, 0, LodUtil.CHUNK_WIDTH/2); + return chunkPos().getWorldPosition().offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2); } - - - + + @Override public String toString() { diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 18c1321bf..8809cd937 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -57,266 +57,266 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; */ public class ClientProxy { - public static final Logger LOGGER = LogManager.getLogger("LOD"); + public static final Logger LOGGER = LogManager.getLogger("LOD"); - private static LodWorld lodWorld = new LodWorld(); - private static LodBuilder lodBuilder = new LodBuilder(); - private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); - private static LodRenderer renderer = new LodRenderer(lodBufferBuilder); - private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE; + private static LodWorld lodWorld = new LodWorld(); + private static LodBuilder lodBuilder = new LodBuilder(); + private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); + private static LodRenderer renderer = new LodRenderer(lodBufferBuilder); + private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE; - private boolean configOverrideReminderPrinted = false; + private boolean configOverrideReminderPrinted = false; - Minecraft mc = Minecraft.getInstance(); + Minecraft mc = Minecraft.getInstance(); - /** - * This is used to determine if the LODs should be regenerated - */ - public static int previousChunkRenderDistance = 0; - /** - * This is used to determine if the LODs should be regenerated - */ - public static int previousLodRenderDistance = 0; + /** + * This is used to determine if the LODs should be regenerated + */ + public static int previousChunkRenderDistance = 0; + /** + * This is used to determine if the LODs should be regenerated + */ + public static int previousLodRenderDistance = 0; - /** - * can be set if we want to recalculate variables related - * to the LOD view distance - */ - private boolean recalculateWidths = false; + /** + * can be set if we want to recalculate variables related + * to the LOD view distance + */ + private boolean recalculateWidths = false; - public ClientProxy() - { + public ClientProxy() + { - } + } - //==============// - // render event // - //==============// + //==============// + // render event // + //==============// - /** - * Do any setup that is required to draw LODs - * and then tell the LodRenderer to draw. - */ - public void renderLods(float partialTicks) - { - if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) - return; + /** + * Do any setup that is required to draw LODs + * and then tell the LodRenderer to draw. + */ + public void renderLods(float partialTicks) + { + if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) + return; - viewDistanceChangedEvent(); + viewDistanceChangedEvent(); - LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); - if (lodDim == null) - return; + LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); + if (lodDim == null) + return; - playerMoveEvent(lodDim); - //System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte"); - //System.out.println(lodDim); + playerMoveEvent(lodDim); + //System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte"); + //System.out.println(lodDim); - lodDim.treeCutter((int) mc.player.getX(), (int) mc.player.getZ()); - lodDim.treeGenerator((int) mc.player.getX(), (int) mc.player.getZ()); + lodDim.treeCutter((int) mc.player.getX(), (int) mc.player.getZ()); + lodDim.treeGenerator((int) mc.player.getX(), (int) mc.player.getZ()); - // comment out when creating a release - applyConfigOverrides(); + // comment out when creating a release + applyConfigOverrides(); - // Note to self: - // if "unspecified" shows up in the pie chart, it is - // possibly because the amount of time between sections - // is too small for the profiler to measure - IProfiler profiler = mc.getProfiler(); - profiler.pop(); // get out of "terrain" - profiler.push("LOD"); - renderer.drawLODs(lodDim, partialTicks, mc.getProfiler()); + // Note to self: + // if "unspecified" shows up in the pie chart, it is + // possibly because the amount of time between sections + // is too small for the profiler to measure + IProfiler profiler = mc.getProfiler(); + profiler.pop(); // get out of "terrain" + profiler.push("LOD"); + renderer.drawLODs(lodDim, partialTicks, mc.getProfiler()); - profiler.pop(); // end LOD - profiler.push("terrain"); // restart "terrain" + profiler.pop(); // end LOD + profiler.push("terrain"); // restart "terrain" - // 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 - previousChunkRenderDistance = mc.options.renderDistance; - previousLodRenderDistance = LodConfig.CLIENT.lodChunkRenderDistance.get(); - } + // 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 + previousChunkRenderDistance = mc.options.renderDistance; + previousLodRenderDistance = LodConfig.CLIENT.lodChunkRenderDistance.get(); + } - private void applyConfigOverrides() - { - // remind the developer(s). that config override is active - if (!configOverrideReminderPrinted) - { - mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID()); - configOverrideReminderPrinted = true; - } + private void applyConfigOverrides() + { + // remind the developer(s). that config override is active + if (!configOverrideReminderPrinted) + { + mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID()); + configOverrideReminderPrinted = true; + } - // LodConfig.CLIENT.drawLODs.set(true); - LodConfig.CLIENT.debugMode.set(false); + // LodConfig.CLIENT.drawLODs.set(true); + LodConfig.CLIENT.debugMode.set(false); - LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL); - LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL); + LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL); + LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL); - LodConfig.CLIENT.fogDistance.set(FogDistance.FAR); - LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY); - LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES); - LodConfig.CLIENT.brightnessMultiplier.set(1.0); - LodConfig.CLIENT.saturationMultiplier.set(1.0); + LodConfig.CLIENT.fogDistance.set(FogDistance.FAR); + LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY); + LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES); + LodConfig.CLIENT.brightnessMultiplier.set(1.0); + LodConfig.CLIENT.saturationMultiplier.set(1.0); - LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); - LodConfig.CLIENT.lodChunkRenderDistance.set(128); - LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); - LodConfig.CLIENT.lodQuality.set(2); - LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); - LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors()); + LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); + LodConfig.CLIENT.lodChunkRenderDistance.set(128); + LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); + LodConfig.CLIENT.lodQuality.set(2); + LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); + LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors()); - // has to be set in the config file - // LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16); - } + // has to be set in the config file + // LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16); + } - //==============// - // forge events // - //==============// + //==============// + // forge events // + //==============// - @SubscribeEvent - public void serverTickEvent(TickEvent.ServerTickEvent event) - { - if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) - return; + @SubscribeEvent + public void serverTickEvent(TickEvent.ServerTickEvent event) + { + if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) + return; - LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); - if (lodDim == null) - return; + LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); + if (lodDim == null) + return; - lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder); - } + lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder); + } - @SubscribeEvent - public void chunkLoadEvent(ChunkEvent.Load event) - { - lodBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER); - } + @SubscribeEvent + public void chunkLoadEvent(ChunkEvent.Load event) + { + lodBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER); + } - @SubscribeEvent - public void worldSaveEvent(WorldEvent.Save event) - { - if (lodWorld != null) - lodWorld.saveAllDimensions(); - } + @SubscribeEvent + public void worldSaveEvent(WorldEvent.Save event) + { + if (lodWorld != null) + lodWorld.saveAllDimensions(); + } - @SubscribeEvent - public void worldLoadEvent(WorldEvent.Load event) - { - // the player just loaded a new world/dimension - lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld())); - // make sure the correct LODs are being rendered - // (if this isn't done the previous world's LODs may be drawn) - renderer.regenerateLODsNextFrame(); - } + @SubscribeEvent + public void worldLoadEvent(WorldEvent.Load event) + { + // the player just loaded a new world/dimension + lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld())); + // make sure the correct LODs are being rendered + // (if this isn't done the previous world's LODs may be drawn) + renderer.regenerateLODsNextFrame(); + } - @SubscribeEvent - public void worldUnloadEvent(WorldEvent.Unload event) - { - // the player just unloaded a world/dimension + @SubscribeEvent + public void worldUnloadEvent(WorldEvent.Unload event) + { + // the player just unloaded a world/dimension - if (mc.getConnection().getLevel() == null) - { - // if this isn't done unfinished tasks may be left in the queue - // preventing new LodChunks form being generated - LodNodeGenWorker.restartExecuterService(); + if (mc.getConnection().getLevel() == null) + { + // if this isn't done unfinished tasks may be left in the queue + // preventing new LodChunks form being generated + LodNodeGenWorker.restartExecuterService(); - LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); - // the player has disconnected from a server - lodWorld.deselectWorld(); + LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); + // the player has disconnected from a server + lodWorld.deselectWorld(); - // hopefully this should reduce issues related to the buffer builder - // breaking when changing worlds. - renderer.destroyBuffers(); - recalculateWidths = true; - } - } + // hopefully this should reduce issues related to the buffer builder + // breaking when changing worlds. + renderer.destroyBuffers(); + recalculateWidths = true; + } + } - @SubscribeEvent - public void blockChangeEvent(BlockEvent event) - { - if (event.getClass() == BlockEvent.BreakEvent.class || - event.getClass() == BlockEvent.EntityPlaceEvent.class || - event.getClass() == BlockEvent.EntityMultiPlaceEvent.class || - event.getClass() == BlockEvent.FluidPlaceBlockEvent.class || - event.getClass() == BlockEvent.PortalSpawnEvent.class) - { - // recreate the LOD where the blocks were changed - lodBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld()); - } - } + @SubscribeEvent + public void blockChangeEvent(BlockEvent event) + { + if (event.getClass() == BlockEvent.BreakEvent.class || + event.getClass() == BlockEvent.EntityPlaceEvent.class || + event.getClass() == BlockEvent.EntityMultiPlaceEvent.class || + event.getClass() == BlockEvent.FluidPlaceBlockEvent.class || + event.getClass() == BlockEvent.PortalSpawnEvent.class) + { + // recreate the LOD where the blocks were changed + lodBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld()); + } + } - //==================// - // frame LOD events // - //==================// + //==================// + // frame LOD events // + //==================// - /** - * Re-centers the given LodDimension if it needs to be. - */ - private void playerMoveEvent(LodDimension lodDim) - { - // make sure the dimension is centered - RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition()); - RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ()); - if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0) - { - lodWorld.saveAllDimensions(); - lodDim.move(worldRegionOffset); - LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ()); - } - } + /** + * Re-centers the given LodDimension if it needs to be. + */ + private void playerMoveEvent(LodDimension lodDim) + { + // make sure the dimension is centered + RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition()); + RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ()); + if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0) + { + lodWorld.saveAllDimensions(); + lodDim.move(worldRegionOffset); + LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ()); + } + } - /** - * Re-sizes all LodDimensions if they needs to be. - */ - private void viewDistanceChangedEvent() - { - // calculate how wide the dimension(s) should be in regions - int chunksWide = LodConfig.CLIENT.lodChunkRenderDistance.get() * 2 + 1; - int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS); - newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions + /** + * Re-sizes all LodDimensions if they needs to be. + */ + private void viewDistanceChangedEvent() + { + // calculate how wide the dimension(s) should be in regions + int chunksWide = LodConfig.CLIENT.lodChunkRenderDistance.get() * 2 + 1; + int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS); + newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions - // do the dimensions need to change in size? - if (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths) - { - // update the dimensions to fit the new width - lodWorld.resizeDimensionRegionWidth(newWidth); - lodBuilder.defaultDimensionWidthInRegions = newWidth; - renderer.setupBuffers(newWidth); + // do the dimensions need to change in size? + if (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths) + { + // update the dimensions to fit the new width + lodWorld.resizeDimensionRegionWidth(newWidth); + lodBuilder.defaultDimensionWidthInRegions = newWidth; + renderer.setupBuffers(newWidth); - recalculateWidths = false; - //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); - } - } + recalculateWidths = false; + //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); + } + } - //================// - // public getters // - //================// + //================// + // public getters // + //================// - public static LodWorld getLodWorld() - { - return lodWorld; - } + public static LodWorld getLodWorld() + { + return lodWorld; + } - public static LodBuilder getLodBuilder() - { - return lodBuilder; - } + public static LodBuilder getLodBuilder() + { + return lodBuilder; + } - public static LodRenderer getRenderer() - { - return renderer; - } + public static LodRenderer getRenderer() + { + return renderer; + } } diff --git a/src/main/java/com/seibel/lod/util/ColorUtil.java b/src/main/java/com/seibel/lod/util/ColorUtil.java index d5d019fd2..00ed96c41 100644 --- a/src/main/java/com/seibel/lod/util/ColorUtil.java +++ b/src/main/java/com/seibel/lod/util/ColorUtil.java @@ -4,57 +4,67 @@ import java.awt.*; public class ColorUtil { - public static int rgbToInt(int red, int green, int blue){ - return (0xFF << 24) | (red << 16) | (green << 8) | blue; - } + public static int rgbToInt(int red, int green, int blue) + { + return (0xFF << 24) | (red << 16) | (green << 8) | blue; + } - public static int rgbToInt( int alpha, int red, int green, int blue){ - return (alpha << 24) | (red << 16) | (green << 8) | blue; - } + public static int rgbToInt(int alpha, int red, int green, int blue) + { + return (alpha << 24) | (red << 16) | (green << 8) | blue; + } - public static int getAlpha(int color){ - return (color>>24)&0xFF; - } + public static int getAlpha(int color) + { + return (color >> 24) & 0xFF; + } - public static int getRed(int color){ - return (color>>16)&0xFF; - } + public static int getRed(int color) + { + return (color >> 16) & 0xFF; + } - public static int getGreen(int color){ - return (color>>8)&0xFF; - } + public static int getGreen(int color) + { + return (color >> 8) & 0xFF; + } - public static int getBlue(int color){ - return color &0xFF; - } + public static int getBlue(int color) + { + return color & 0xFF; + } - public static int applyShade(int color, int shade){ - if(shade<0) - { - return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0); - }else{ - return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255); - } - } + public static int applyShade(int color, int shade) + { + if (shade < 0) + { + return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0); + } else + { + return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255); + } + } - public static int applyShade(int color, float shade){ - if(shade<1) - { - return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0); - }else{ - return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) |(int) Math.min(getBlue(color) * shade, 255); - } - } + public static int applyShade(int color, float shade) + { + if (shade < 1) + { + return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0); + } else + { + return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) | (int) Math.min(getBlue(color) * shade, 255); + } + } - /** - * Edit the given color as a HSV (Hue Saturation Value) color. - */ - public static int applySaturationAndBrightnessMultipliers(int color, float saturationMultiplier, float brightnessMultiplier) - { - float[] hsv = Color.RGBtoHSB( getRed(color), getGreen(color), getBlue(color), null); - return Color.getHSBColor( - hsv[0], // hue - LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f), - LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB(); - } + /** + * Edit the given color as a HSV (Hue Saturation Value) color. + */ + public static int applySaturationAndBrightnessMultipliers(int color, float saturationMultiplier, float brightnessMultiplier) + { + float[] hsv = Color.RGBtoHSB(getRed(color), getGreen(color), getBlue(color), null); + return Color.getHSBColor( + hsv[0], // hue + LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f), + LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB(); + } } diff --git a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java index 5e08f43a0..a94405331 100644 --- a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java +++ b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java @@ -7,26 +7,26 @@ import net.minecraft.client.Minecraft; public class DetailDistanceUtil { - private static double genMultiplier = 1.25; - private static double treeGenMultiplier = 1.5; - private static double treeCutMultiplier = 1.25; - private static int minDetail = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; - private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1; - private static int minDistance = 0; - private static int maxDistance = LodConfig.CLIENT.lodChunkRenderDistance.get() * 16; + private static double genMultiplier = 1.25; + private static double treeGenMultiplier = 1.5; + private static double treeCutMultiplier = 1.25; + private static int minDetail = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; + private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1; + private static int minDistance = 0; + private static int maxDistance = LodConfig.CLIENT.lodChunkRenderDistance.get() * 16; - private static DistanceGenerationMode[] distancesGenerators = { - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE, - DistanceGenerationMode.SURFACE}; + private static DistanceGenerationMode[] distancesGenerators = { + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE, + DistanceGenerationMode.SURFACE}; /*private static DistanceGenerationMode[] distancesGenerators = { DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT, @@ -40,113 +40,114 @@ public class DetailDistanceUtil DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT, DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT};*/ - private static LodDetail[] lodDetails = { - LodDetail.FULL, - LodDetail.HALF, - LodDetail.QUAD, - LodDetail.DOUBLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE}; + private static LodDetail[] lodDetails = { + LodDetail.FULL, + LodDetail.HALF, + LodDetail.QUAD, + LodDetail.DOUBLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE}; - private static LodDetail[] lodDetailsCut = { - LodDetail.FULL, - LodDetail.HALF, - LodDetail.QUAD, - LodDetail.DOUBLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE, - LodDetail.SINGLE}; + private static LodDetail[] lodDetailsCut = { + LodDetail.FULL, + LodDetail.HALF, + LodDetail.QUAD, + LodDetail.DOUBLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE, + LodDetail.SINGLE}; - public static int getDistanceRendering(int detail) - { - int initial; - int distance = 0; - if(detail <= minDetail) - return minDistance; - if(detail == maxDetail) - return maxDistance*2; - if(detail == maxDetail+1) - return maxDistance*3; - switch (LodConfig.CLIENT.lodDistanceCalculatorType.get()) - { - case LINEAR: - initial = LodConfig.CLIENT.lodQuality.get() * 128; - return (detail * initial); - case QUADRATIC: - initial = LodConfig.CLIENT.lodQuality.get() * 128; - return (int) (Math.pow(2, detail) * initial); - case RENDER_DEPENDANT: - int realRenderDistance = Minecraft.getInstance().options.renderDistance * 16; - int border = 64; - byte detailAtBorder = (byte) 4; - if(detail > detailAtBorder){ - return (detail * (border-realRenderDistance)/detailAtBorder + realRenderDistance); - }else{ - return ((maxDetail - detail) * (maxDistance-border)/(maxDetail - detailAtBorder) + border); - } - } - return distance; - } + public static int getDistanceRendering(int detail) + { + int initial; + int distance = 0; + if (detail <= minDetail) + return minDistance; + if (detail == maxDetail) + return maxDistance * 2; + if (detail == maxDetail + 1) + return maxDistance * 3; + switch (LodConfig.CLIENT.lodDistanceCalculatorType.get()) + { + case LINEAR: + initial = LodConfig.CLIENT.lodQuality.get() * 128; + return (detail * initial); + case QUADRATIC: + initial = LodConfig.CLIENT.lodQuality.get() * 128; + return (int) (Math.pow(2, detail) * initial); + case RENDER_DEPENDANT: + int realRenderDistance = Minecraft.getInstance().options.renderDistance * 16; + int border = 64; + byte detailAtBorder = (byte) 4; + if (detail > detailAtBorder) + { + return (detail * (border - realRenderDistance) / detailAtBorder + realRenderDistance); + } else + { + return ((maxDetail - detail) * (maxDistance - border) / (maxDetail - detailAtBorder) + border); + } + } + return distance; + } - public static int getDistanceGeneration(int detail) - { - if(detail == maxDetail) - return maxDistance; - return (int) (getDistanceRendering(detail) * genMultiplier); - } - public static int getDistanceTreeCut(int detail) - { - if(detail == maxDetail) - return maxDistance; - return (int) (getDistanceRendering(detail) * treeCutMultiplier); - } - public static int getDistanceTreeGen(int detail) - { - if(detail == maxDetail) - return maxDistance; - return (int) (getDistanceRendering(detail) * treeGenMultiplier); - } + public static int getDistanceGeneration(int detail) + { + if (detail == maxDetail) + return maxDistance; + return (int) (getDistanceRendering(detail) * genMultiplier); + } - public static DistanceGenerationMode getDistanceGenerationMode(int detail) - { - return distancesGenerators[detail]; - } + public static int getDistanceTreeCut(int detail) + { + if (detail == maxDetail) + return maxDistance; + return (int) (getDistanceRendering(detail) * treeCutMultiplier); + } - public static LodDetail getLodDetail(int detail) - { - if(detail < minDetail) - { - return lodDetails[minDetail]; - } - else - { - return lodDetails[detail]; - } - } + public static int getDistanceTreeGen(int detail) + { + if (detail == maxDetail) + return maxDistance; + return (int) (getDistanceRendering(detail) * treeGenMultiplier); + } + + public static DistanceGenerationMode getDistanceGenerationMode(int detail) + { + return distancesGenerators[detail]; + } + + public static LodDetail getLodDetail(int detail) + { + if (detail < minDetail) + { + return lodDetails[minDetail]; + } else + { + return lodDetails[detail]; + } + } - public static byte getCutLodDetail(int detail) - { - if(detail < minDetail) - { - return lodDetailsCut[minDetail].detailLevel; - } - else if(detail == maxDetail) - { - return LodUtil.REGION_DETAIL_LEVEL; - } - else - { - return lodDetailsCut[detail].detailLevel; - } - } + public static byte getCutLodDetail(int detail) + { + if (detail < minDetail) + { + return lodDetailsCut[minDetail].detailLevel; + } else if (detail == maxDetail) + { + return LodUtil.REGION_DETAIL_LEVEL; + } else + { + return lodDetailsCut[detail].detailLevel; + } + } }