From d1417069d9d47a8abef8f4c548df22f8a0bb0e6a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 11 Aug 2021 07:33:51 -0500 Subject: [PATCH] Remove the old LOD objects --- .../seibel/lod/builders/LodBufferBuilder.java | 432 --------- .../seibel/lod/builders/LodChunkBuilder.java | 551 ----------- .../lodTemplates/AbstractLodTemplate.java | 54 -- .../lodTemplates/CubicLodTemplate.java | 187 ---- .../lodTemplates/DynamicLodTemplate.java | 51 - .../lodTemplates/TriangularLodTemplate.java | 49 - .../worldGeneration/LodChunkGenWorker.java | 646 ------------- .../lod/handlers/LodDimensionFileHandler.java | 357 ------- .../java/com/seibel/lod/objects/LodChunk.java | 495 ---------- .../com/seibel/lod/objects/LodDimension.java | 433 --------- .../com/seibel/lod/objects/LodRegion.java | 103 -- .../java/com/seibel/lod/objects/LodWorld.java | 127 --- .../com/seibel/lod/proxy/ClientProxy.java | 2 - .../com/seibel/lod/render/LodRenderer.java | 885 ------------------ .../java/com/seibel/lod/util/LodUtil.java | 4 +- 15 files changed, 2 insertions(+), 4374 deletions(-) delete mode 100644 src/main/java/com/seibel/lod/builders/LodBufferBuilder.java delete mode 100644 src/main/java/com/seibel/lod/builders/LodChunkBuilder.java delete mode 100644 src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java delete mode 100644 src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodChunk.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodDimension.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodRegion.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodWorld.java delete mode 100644 src/main/java/com/seibel/lod/render/LodRenderer.java diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java deleted file mode 100644 index bd2718986..000000000 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.builders; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; - -import org.lwjgl.opengl.GL11; - -import com.seibel.lod.builders.worldGeneration.LodChunkGenWorker; -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LodChunk; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.NearFarBuffer; -import com.seibel.lod.render.LodRenderer; -import com.seibel.lod.util.LodUtil; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.server.ServerWorld; -import net.minecraftforge.common.WorldWorkerManager; - -/** - * This object is used to create NearFarBuffer objects. - * - * @author James Seibel - * @version 07-25-2021 - */ -public class LodBufferBuilder -{ - private Minecraft mc; - - /** This holds the thread used to generate new LODs off the main thread. */ - private ExecutorService genThread = Executors.newSingleThreadExecutor(); - - private LodChunkBuilder lodChunkBuilder; - - /** The buffers that are used to create LODs using near fog */ - public volatile BufferBuilder buildableNearBuffer; - /** The buffers that are used to create LODs using far fog */ - public volatile BufferBuilder buildableFarBuffer; - - /** if this is true the LOD buffers are currently being - * regenerated. */ - public volatile boolean generatingBuffers = false; - - /** if this is true new LOD buffers have been generated - * and are waiting to be swapped with the drawable buffers*/ - private volatile boolean switchBuffers = false; - - /** This keeps track of how many chunk generation requests are on going. - * This is to prevent chunks from being generated for a long time in an area - * the player is no longer in. */ - public volatile AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); - - - - /** how many chunks to generate outside of the player's - * view distance at one time. (or more specifically how - * many requests to make at one time). - * I multiply by 8 to make sure there is always a buffer of chunk requests, - * to make sure the CPU is always busy and we can generate LODs as quickly as - * possible. */ - public int maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8; - - - public LodBufferBuilder(LodChunkBuilder newLodBuilder) - { - mc = Minecraft.getInstance(); - lodChunkBuilder = newLodBuilder; - } - - - /** - * 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, - double playerX, double playerZ, int numbChunksWide) - { - // only allow one generation process to happen at a time - if (generatingBuffers) - return; - - if (buildableNearBuffer == null || buildableFarBuffer == null) - throw new IllegalStateException("generateLodBuffersAsync was called before the buildableNearBuffer and buildableFarBuffer were created."); - - generatingBuffers = true; - - - // this seemingly useless math is required, - // just using (int) playerX/Z doesn't work - int playerXChunkOffset = ((int) playerX / LodUtil.CHUNK_WIDTH) * LodUtil.CHUNK_WIDTH; - int playerZChunkOffset = ((int) playerZ / LodUtil.CHUNK_WIDTH) * LodUtil.CHUNK_WIDTH; - // this is where we will start drawing squares - // (exactly half the total width) - int startX = (-LodUtil.CHUNK_WIDTH * (numbChunksWide / 2)) + playerXChunkOffset; - int startZ = (-LodUtil.CHUNK_WIDTH * (numbChunksWide / 2)) + playerZChunkOffset; - - - Thread thread = new Thread(()-> - { - // index of the chunk currently being added to the - // generation list - int chunkGenIndex = 0; - - ChunkPos[] chunksToGen = new ChunkPos[maxChunkGenRequests]; - // if we don't have a full number of chunks to generate in chunksToGen - // we can top it off from the reserve - ChunkPos[] chunksToGenReserve = new ChunkPos[maxChunkGenRequests]; - int minChunkDist = Integer.MAX_VALUE; - ChunkPos playerChunkPos = new ChunkPos((int)playerX / LodUtil.CHUNK_WIDTH, (int)playerZ / LodUtil.CHUNK_WIDTH); - - - // generate our new buildable buffers - buildableNearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); - buildableFarBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); - - - - // x axis - for (int i = 0; i < numbChunksWide; i++) - { - // z axis - for (int j = 0; j < numbChunksWide; j++) - { - int chunkX = i + (startX / LodUtil.CHUNK_WIDTH); - int chunkZ = j + (startZ / LodUtil.CHUNK_WIDTH); - - // skip any chunks that Minecraft is going to render - if(isCoordInCenterArea(i, j, (numbChunksWide / 2)) - && renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ))) - { - continue; - } - - LodDetail detail = LodConfig.CLIENT.maxDrawDetail.get(); - - // set where this square will be drawn in the world - double xOffset = (LodUtil.CHUNK_WIDTH * i) + // offset by the number of LOD blocks - startX + // offset so the center LOD block is centered underneath the player - 8; //detail.offset; // truncation(?) correction - double yOffset = 0; - double zOffset = (LodUtil.CHUNK_WIDTH * j) + startZ + 8;//detail.offset; - - LodChunk lod = lodDim.getLodFromCoordinates(chunkX, chunkZ); - - if (lod == null || lod.isLodEmpty()) - { - // generate a new chunk if no chunk currently exists - // and we aren't waiting on any other chunks to generate - if (lod == null && numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests) - { - ChunkPos pos = new ChunkPos(chunkX, chunkZ); - - - // can be used for debugging -// if (chunksToGen == null) -// { -// chunkGenIndex = 0; -// chunksToGen = new ChunkPos[maxChunkGenRequests]; -// } -// -// if (chunkGenIndex < maxChunkGenRequests) -// { -// chunksToGen[chunkGenIndex] = pos; -// chunkGenIndex++; -// } - - - - - // determine if this position is closer to the player - // than the previous - int newDistance = playerChunkPos.getChessboardDistance(pos); - - // issue #40 - // TODO optimize this code, - // using the purely optimized code above we can achieve close to - // 100% CPU utilization, this code generally achieves 40 - 50% - // after a certain point; and I'm sure there is a better data - // structure for this. - 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 - ChunkPos[] newReserve = new ChunkPos[maxChunkGenRequests]; - int oldToGenIndex = 0; - int oldReserveIndex = 0; - for(int tmpIndex = 0; tmpIndex < newReserve.length; tmpIndex++) - { - // we don't check if the boundaries are good since - // the tmp array will always be the same length - // as chunksToGen and chunksToGenReserve - - if (chunksToGen[oldToGenIndex] != null) - { - // add all the closest chunks... - newReserve[tmpIndex] = chunksToGen[oldToGenIndex]; - oldToGenIndex++; - } - else if (chunksToGenReserve[oldReserveIndex] != null) - { - // ...then add all the previous reserve chunks - // (which are farther away) - newReserve[tmpIndex] = chunksToGenReserve[oldToGenIndex]; - oldReserveIndex++; - } - else - { - // we have moved all the items from - // the old chunksToGen and reserve - break; - } - } - chunksToGenReserve = newReserve; - - - - chunkGenIndex = 0; - chunksToGen = new ChunkPos[maxChunkGenRequests]; - chunksToGen[chunkGenIndex] = pos; - chunkGenIndex++; - } - else if (newDistance <= minChunkDist) - { - // this chunk position is as close or closers than the - // minimum distance - if(chunkGenIndex < maxChunkGenRequests) - { - // we are still under the number of chunks to generate - // add this position to the list - chunksToGen[chunkGenIndex] = pos; - chunkGenIndex++; - } - } - } // lod null and can generate more chunks - - // don't render this null/empty chunk - continue; - - } // lod null or empty - - - BufferBuilder currentBuffer = null; - if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2)) - currentBuffer = buildableNearBuffer; - else - currentBuffer = buildableFarBuffer; - - throw new UnsupportedOperationException("Not Implemented"); - // get the desired LodTemplate and - // add this LOD to the buffer -// LodConfig.CLIENT.lodTemplate.get(). -// template.addLodToBuffer(currentBuffer, lodDim, lod, -// xOffset, yOffset, zOffset, renderer.debugging); - } - } - - // issue #19 - // TODO add a way for a server side mod to generate chunks requested here - if(mc.hasSingleplayerServer()) - { - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension); - - // make sure we have as many chunks to generate as we are allowed - if (chunkGenIndex < maxChunkGenRequests) - { - for(int i = chunkGenIndex, j = 0; i < maxChunkGenRequests; i++, j++) - { - chunksToGen[i] = chunksToGenReserve[j]; - } - } - - // start chunk generation - for(ChunkPos chunkPos : chunksToGen) - { - if(chunkPos == null) - break; - - // make sure we don't generate this chunk again - lodDim.addLod(new LodChunk(chunkPos)); - - numberOfChunksWaitingToGenerate.addAndGet(1); - - LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, renderer, lodChunkBuilder, this, lodDim, serverWorld); - WorldWorkerManager.addWorker(genWorker); - } - } - - // finish the buffer building - buildableNearBuffer.end(); - buildableFarBuffer.end(); - - // mark that the buildable buffers as ready to swap - generatingBuffers = false; - switchBuffers = true; - }); - - genThread.execute(thread); - - return; - } - - - - - - - - - - - //====================// - // generation helpers // - //====================// - - /** - * Returns if the given coordinate is in the loaded area of the world. - * @param centerCoordinate the center of the loaded world - */ - private boolean isCoordInCenterArea(int i, int j, int centerCoordinate) - { - return (i >= centerCoordinate - mc.options.renderDistance - && i <= centerCoordinate + mc.options.renderDistance) - && - (j >= centerCoordinate - mc.options.renderDistance - && j <= centerCoordinate + mc.options.renderDistance); - } - - - /** - * Find the coordinates that are in the center half of the given - * 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius). - */ - private static boolean isCoordinateInNearFogArea(int chunkX, int chunkZ, int lodRadius) - { - int halfRadius = lodRadius / 2; - - return (chunkX >= lodRadius - halfRadius - && chunkX <= lodRadius + halfRadius) - && - (chunkZ >= lodRadius - halfRadius - && chunkZ <= lodRadius + halfRadius); - } - - - - - - //===============================// - // BufferBuilder related methods // - //===============================// - - - /** - * Called from the LodRenderer to create the - * BufferBuilders at the right size. - * - * @param bufferMaxCapacity - */ - public void setupBuffers(int bufferMaxCapacity) - { - buildableNearBuffer = new BufferBuilder(bufferMaxCapacity); - buildableFarBuffer = new BufferBuilder(bufferMaxCapacity); - } - - /** - * Swap the drawable and buildable buffers and return - * the old drawable buffers. - * @param drawableNearBuffer - * @param drawableFarBuffer - */ - public NearFarBuffer swapBuffers(BufferBuilder drawableNearBuffer, BufferBuilder drawableFarBuffer) - { - // swap the BufferBuilders - BufferBuilder tmp = buildableNearBuffer; - buildableNearBuffer = drawableNearBuffer; - drawableNearBuffer = tmp; - - tmp = buildableFarBuffer; - buildableFarBuffer = drawableFarBuffer; - drawableFarBuffer = tmp; - - - // the buffers have been swapped - switchBuffers = false; - - return new NearFarBuffer(drawableNearBuffer, drawableFarBuffer); - } - - /** - * If this is true the buildable near and far - * buffers have been generated and are ready to be - * sent to the LodRenderer. - */ - public boolean newBuffersAvaliable() - { - return switchBuffers; - } - - - - -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/builders/LodChunkBuilder.java b/src/main/java/com/seibel/lod/builders/LodChunkBuilder.java deleted file mode 100644 index 36f3420e8..000000000 --- a/src/main/java/com/seibel/lod/builders/LodChunkBuilder.java +++ /dev/null @@ -1,551 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.builders; - -import java.awt.Color; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LodChunk; -import com.seibel.lod.objects.LodDataPoint; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.LodWorld; -import com.seibel.lod.util.LodUtil; - -import net.minecraft.block.AbstractPlantBlock; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.BushBlock; -import net.minecraft.block.GrassBlock; -import net.minecraft.block.IGrowable; -import net.minecraft.block.LeavesBlock; -import net.minecraft.block.material.MaterialColor; -import net.minecraft.world.DimensionType; -import net.minecraft.world.IWorld; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.chunk.ChunkSection; -import net.minecraft.world.chunk.IChunk; -import net.minecraft.world.gen.Heightmap; - -/** - * This object is in charge of creating Lod - * related objects. - * (specifically: Lod World, Dimension, Region, and Chunk objects) - * - * @author James Seibel - * @version 8-9-2021 - */ -public class LodChunkBuilder -{ - private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(); - - /** Default size of any LOD regions we use */ - public int regionWidth = 5; - - - public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH; - public static final int CHUNK_SECTION_HEIGHT = LodUtil.CHUNK_WIDTH; - - - - // reminder now how to access biome info -// String name = biome.getRegistryName().getPath(); -// String deepColdName = Biomes.DEEP_COLD_OCEAN.location().getPath(); -// Biome deepCold = WorldGenRegistries.BIOME.get(Biomes.DEEP_COLD_OCEAN); - - - - public LodChunkBuilder() - { - - } - - - public void generateLodChunkAsync(IChunk chunk, LodWorld lodWorld, IWorld world) - { - generateLodChunkAsync(chunk, new LodBuilderConfig(), lodWorld, world); - } - - public void generateLodChunkAsync(IChunk chunk, LodBuilderConfig config, LodWorld lodWorld, IWorld world) - { - 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(); - - LodChunk lod = generateLodFromChunk(chunk, config); - - LodDimension lodDim; - - if (lodWorld.getLodDimension(dim) == null) - { - lodDim = new LodDimension(dim, lodWorld, regionWidth); - lodWorld.addLodDimension(lodDim); - } - else - { - lodDim = lodWorld.getLodDimension(dim); - } - - lodDim.addLod(lod); - } - catch(IllegalArgumentException | NullPointerException e) - { - // 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 LodChunk generateLodFromChunk(IChunk chunk) throws IllegalArgumentException - { - return generateLodFromChunk(chunk, new LodBuilderConfig()); - } - - /** - * Creates a LodChunk for a chunk in the given world. - * - * @throws IllegalArgumentException - * thrown if either the chunk or world is null. - */ - public LodChunk generateLodFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException - { - if(chunk == null) - throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); - - - LodDetail detail = LodConfig.CLIENT.maxGenerationDetail.get(); - LodDataPoint[][] dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount]; - - for(int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++) - { - int startX = detail.startX[i]; - int startZ = detail.startZ[i]; - int endX = detail.endX[i]; - int endZ = detail.endZ[i]; - - Color color; - - color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ); - - - short height; - short depth; - - 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; - } - - int x = i / detail.dataPointLengthCount; - int z = i % detail.dataPointLengthCount; - - dataPoints[x][z] = new LodDataPoint(height, depth, color); - } - - return new LodChunk(chunk.getPos(), dataPoints, detail); - } - - - - - - - //=====================// - // constructor helpers // - //=====================// - - - /** - * Find the lowest valid point from the bottom. - * @param chunkSections - * @param startX - * @param startZ - * @param endX - * @param endZ - */ - 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 -1; - } - - /** - * 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 - * @param chunkSections - * @param startX - * @param startZ - * @param endX - * @param endZ - */ - 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 -1; - } - - /** - * 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 Color 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) - { - // the bit shift is equivalent to dividing by 4 - Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2, z >> 2); - - if (biome.getBiomeCategory() == Biome.Category.OCEAN || - biome.getBiomeCategory() == Biome.Category.RIVER) - { - if (config.useSolidBlocksInColorGen && - blockState != Blocks.WATER.defaultBlockState()) - { - // non-water block - colorInt = getColorForBlock(x, z, blockState, biome); - } - else - { - // water block - colorInt = biome.getWaterColor(); - } - } - else if (biome.getBiomeCategory() == Biome.Category.EXTREME_HILLS) - { - colorInt = Blocks.STONE.defaultMaterialColor().col; - } - else if (biome.getBiomeCategory() == Biome.Category.ICY) - { - colorInt = LodUtil.colorToInt(Color.WHITE); - } - else if (biome.getBiomeCategory() == Biome.Category.THEEND) - { - colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col; - } - else if (config.useSolidBlocksInColorGen) - { - colorInt = getColorForBlock(x, z, blockState, biome); - } - else - { - Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); - tmp = tmp.darker(); - colorInt = LodUtil.colorToInt(tmp); - } - } - else - { - // 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 + i * chunkSections.length >> 2, z >> 2); - colorInt = getColorForBlock(x,z, blockState, biome); - } - - - Color c = LodUtil.intToColor(colorInt); - - red += c.getRed(); - green += c.getGreen(); - blue += c.getBlue(); - - 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 Color(red, green, 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()) - { - Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); - tmp = tmp.darker(); - colorInt = LodUtil.colorToInt(tmp); - } - else if (blockState == Blocks.MYCELIUM.defaultBlockState()) - { - colorInt = MaterialColor.COLOR_LIGHT_GRAY.col; - } - - // plant life - else if (blockState.getBlock() instanceof LeavesBlock) - { - Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker(); - colorInt = LodUtil.colorToInt(leafColor); - } - else if (blockState.getBlock() instanceof GrassBlock || - blockState.getBlock() instanceof AbstractPlantBlock || - blockState.getBlock() instanceof BushBlock || - blockState.getBlock() instanceof IGrowable) - { - Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); - tmp = tmp.darker(); - colorInt = LodUtil.colorToInt(tmp); - } - - // water - else if (blockState.getBlock() == Blocks.WATER) - { - colorInt = biome.getWaterColor(); - } - - // everything else - else - { - colorInt = blockState.materialColor.col; - } - - 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) - { - return true; - } - } - - return false; - } - - - - - - - - - -} diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java deleted file mode 100644 index 81cdf17d3..000000000 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.builders.lodTemplates; - -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.objects.LodQuadTreeDimension; -import com.seibel.lod.objects.LodQuadTreeNode; - -import net.minecraft.client.renderer.BufferBuilder; - -/** - * This is the abstract class used to create different - * BufferBuilders. - * - * @author James Seibel - * @version 06-16-2021 - */ -public abstract class AbstractLodTemplate -{ - public abstract void addLodToBuffer(BufferBuilder buffer, - LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod, - double xOffset, double yOffset, double zOffset, - boolean debugging); - - /** 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) - { - buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex(); - } - - /** Returns in bytes how much buffer memory is required - * for one LOD object */ - public abstract int getBufferMemoryForSingleLod(LodDetail detail); - - - -} diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java deleted file mode 100644 index c960473fb..000000000 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.builders.lodTemplates; - -import java.awt.Color; - -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.enums.ShadingMode; -import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LodQuadTreeDimension; -import com.seibel.lod.objects.LodQuadTreeNode; -import com.seibel.lod.util.LodUtil; - -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.util.math.AxisAlignedBB; - -/** - * Builds LODs as rectangular prisms. - * - * @author James Seibel - * @version 8-8-2021 - */ -public class CubicLodTemplate extends AbstractLodTemplate -{ - - public CubicLodTemplate() - { - - } - - - @Override - public void addLodToBuffer(BufferBuilder buffer, - LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod, - double xOffset, double yOffset, double zOffset, - boolean debugging) - { - AxisAlignedBB bbox; - - // Add this LOD to the BufferBuilder - // using the quality setting set by the config - LodDetail detail = LodConfig.CLIENT.maxDrawDetail.get(); - - // returns null if the lod is empty at the given location - bbox = generateBoundingBox( - centerLod.getLodDataPoint().height, - centerLod.getLodDataPoint().depth, - detail.dataPointWidth, - xOffset - (centerLod.width / 2), - yOffset, - zOffset - (centerLod.width / 2)); - - if (bbox != null) - { - addBoundingBoxToBuffer(buffer, bbox, centerLod.getLodDataPoint().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; - - 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); - } - - - - private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c) - { - Color topColor = c; - Color northSouthColor = c; - Color eastWestColor = c; - Color bottomColor = 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 - int northSouthDarkenAmount = 25; - int eastWestDarkenAmount = 50; - int bottomDarkenAmount = 75; - - northSouthColor = new Color(Math.max(0, c.getRed() - northSouthDarkenAmount), Math.max(0, c.getGreen() - northSouthDarkenAmount), Math.max(0, c.getBlue() - northSouthDarkenAmount), c.getAlpha()); - eastWestColor = new Color(Math.max(0, c.getRed() - eastWestDarkenAmount), Math.max(0, c.getGreen() - eastWestDarkenAmount), Math.max(0, c.getBlue() - eastWestDarkenAmount), c.getAlpha()); - bottomColor = new Color(Math.max(0, c.getRed() - bottomDarkenAmount), Math.max(0, c.getGreen() - bottomDarkenAmount), Math.max(0, c.getBlue() - bottomDarkenAmount), c.getAlpha()); - } - - - // apply the user specified saturation and brightness - float saturationMultiplier = LodConfig.CLIENT.saturationMultiplier.get().floatValue(); - float brightnessMultiplier = LodConfig.CLIENT.brightnessMultiplier.get().floatValue(); - - topColor = applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier); - northSouthColor = applySaturationAndBrightnessMultipliers(northSouthColor, saturationMultiplier, brightnessMultiplier); - bottomColor = applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier); - - - - // top (facing up) - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, topColor.getRed(), topColor.getGreen(), topColor.getBlue(), topColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, topColor.getRed(), topColor.getGreen(), topColor.getBlue(), topColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, topColor.getRed(), topColor.getGreen(), topColor.getBlue(), topColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, topColor.getRed(), topColor.getGreen(), topColor.getBlue(), topColor.getAlpha()); - // bottom (facing down) - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, bottomColor.getRed(), bottomColor.getGreen(), bottomColor.getBlue(), bottomColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, bottomColor.getRed(), bottomColor.getGreen(), bottomColor.getBlue(), bottomColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, bottomColor.getRed(), bottomColor.getGreen(), bottomColor.getBlue(), bottomColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, bottomColor.getRed(), bottomColor.getGreen(), bottomColor.getBlue(), bottomColor.getAlpha()); - - // south (facing -Z) - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - // north (facing +Z) - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha()); - - // west (facing -X) - addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - // east (facing +X) - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha()); - } - - - /** - * Edit the given color as a HSV (Hue Saturation Value) color. - */ - private Color applySaturationAndBrightnessMultipliers(Color color, float saturationMultiplier, float brightnessMultiplier) - { - float[] hsv = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), 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)); - } - - - @Override - public int getBufferMemoryForSingleLod(LodDetail detail) - { - // (sidesOnACube * pointsInASquare * (positionPoints + colorPoints))) * howManyPointsPerLodChunk - return (6 * 4 * (3 + 4)) * detail.dataPointLengthCount * detail.dataPointLengthCount; - } - - - - -} diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java deleted file mode 100644 index 81a6b15ce..000000000 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.builders.lodTemplates; - -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.objects.LodQuadTreeDimension; -import com.seibel.lod.objects.LodQuadTreeNode; - -import net.minecraft.client.renderer.BufferBuilder; - -/** - * TODO DynamicLodTemplate - * Chunks smoothly transition between - * each other, unless a neighboring chunk - * is at a significantly different height. - * - * @author James Seibel - * @version 06-16-2021 - */ -public class DynamicLodTemplate extends AbstractLodTemplate -{ - @Override - public void addLodToBuffer(BufferBuilder buffer, - LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod, - double xOffset, double yOffset, double zOffset, - boolean debugging) - { - System.err.println("DynamicLodTemplate not implemented!"); - } - - @Override - public int getBufferMemoryForSingleLod(LodDetail detail) { - // TODO Auto-generated method stub - return 0; - } -} diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java deleted file mode 100644 index d95822666..000000000 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.builders.lodTemplates; - -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.objects.LodQuadTreeDimension; -import com.seibel.lod.objects.LodQuadTreeNode; - -import net.minecraft.client.renderer.BufferBuilder; - -/** - * TODO #21 TriangularLodTemplate - * Builds each LOD chunk as a singular rectangular prism. - * - * @author James Seibel - * @version 06-16-2021 - */ -public class TriangularLodTemplate extends AbstractLodTemplate -{ - @Override - public void addLodToBuffer(BufferBuilder buffer, - LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod, - double xOffset, double yOffset, double zOffset, - boolean debugging) - { - System.err.println("DynamicLodTemplate not implemented!"); - } - - @Override - public int getBufferMemoryForSingleLod(LodDetail detail) { - // TODO Auto-generated method stub - return 0; - } -} diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java deleted file mode 100644 index f115a0001..000000000 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java +++ /dev/null @@ -1,646 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.builders.worldGeneration; - -import java.util.ConcurrentModificationException; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Supplier; - -import com.seibel.lod.builders.LodBufferBuilder; -import com.seibel.lod.builders.LodBuilderConfig; -import com.seibel.lod.builders.LodChunkBuilder; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.objects.LodChunk; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.render.LodRenderer; -import com.seibel.lod.util.LodUtil; - -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.util.WeightedList.Entry; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.palette.UpgradeData; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.chunk.ChunkPrimer; -import net.minecraft.world.chunk.ChunkStatus; -import net.minecraft.world.chunk.IChunk; -import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.Heightmap; -import net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider; -import net.minecraft.world.gen.feature.BlockClusterFeatureConfig; -import net.minecraft.world.gen.feature.ConfiguredFeature; -import net.minecraft.world.gen.feature.DecoratedFeatureConfig; -import net.minecraft.world.gen.feature.FeatureSpread; -import net.minecraft.world.gen.feature.FeatureSpreadConfig; -import net.minecraft.world.gen.feature.IceAndSnowFeature; -import net.minecraft.world.gen.feature.NoFeatureConfig; -import net.minecraft.world.gen.placement.ConfiguredPlacement; -import net.minecraft.world.gen.placement.DecoratedPlacementConfig; -import net.minecraft.world.gen.placement.IPlacementConfig; -import net.minecraft.world.gen.placement.NoiseDependant; -import net.minecraft.world.server.ServerChunkProvider; -import net.minecraft.world.server.ServerWorld; -import net.minecraft.world.server.ServerWorldLightManager; -import net.minecraftforge.common.WorldWorkerManager.IWorker; - -/** - * This is used to generate a LodChunk at a given ChunkPos. - * - * @author James Seibel - * @version 7-26-2021 - */ -public class LodChunkGenWorker implements IWorker -{ - public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get()); - - private boolean threadStarted = false; - private LodChunkGenThread thread; - - /** If a configured feature fails for whatever reason, - * add it to this list, this is to hopefully remove any - * features that could cause issues down the line. */ - private static ConcurrentHashMap> configuredFeaturesToAvoid = new ConcurrentHashMap<>(); - - - - public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer, - LodChunkBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder, - LodDimension newLodDimension, ServerWorld newServerWorld) - { - // just a few sanity checks - if (newPos == null) - throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos"); - - if (newLodRenderer == null) - throw new IllegalArgumentException("LodChunkGenWorker must have a non-null LodRenderer"); - - if (newLodBuilder == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder"); - - if (newLodBufferBuilder == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodBufferBuilder"); - - if (newLodDimension == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension"); - - if (newServerWorld == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld"); - - - - thread = new LodChunkGenThread(newPos, newLodRenderer, - newLodBuilder, newLodBufferBuilder, - newLodDimension, newServerWorld); - } - - @Override - public boolean doWork() - { - if (!threadStarted) - { - thread.lodBufferBuilder.numberOfChunksWaitingToGenerate.addAndGet(-1); - - if (LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.SERVER) - { - // if we are using SERVER generation that has to be done - // synchronously to prevent crashing and harmful - // interactions with the normal world generator - thread.run(); - } - else - { - // Every other method can - // be done asynchronously - genThreads.execute(thread); - } - - threadStarted = true; - - // useful for debugging -// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods()); -// ClientProxy.LOGGER.info(genThreads.toString()); - } - - return false; - } - - @Override - public boolean hasWork() - { - return !threadStarted; - } - - - - - private class LodChunkGenThread implements Runnable - { - public final ServerWorld serverWorld; - public final LodDimension lodDim; - public final LodChunkBuilder lodChunkBuilder; - public final LodRenderer lodRenderer; - private final LodBufferBuilder lodBufferBuilder; - - private ChunkPos pos; - - public LodChunkGenThread(ChunkPos newPos, LodRenderer newLodRenderer, - LodChunkBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder, - LodDimension newLodDimension, ServerWorld newServerWorld) - { - pos = newPos; - lodRenderer = newLodRenderer; - lodChunkBuilder = newLodBuilder; - lodBufferBuilder = newLodBufferBuilder; - lodDim = newLodDimension; - serverWorld = newServerWorld; - } - - @Override - public void run() - { - // only generate LodChunks if they can - // be added to the current LodDimension - if (lodDim.regionIsInRange(pos.x / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.z / LodUtil.REGION_WIDTH_IN_CHUNKS)) - { -// long startTime = System.currentTimeMillis(); - - switch(LodConfig.CLIENT.distanceGenerationMode.get()) - { - case NONE: - // don't generate - break; - case BIOME_ONLY: - case BIOME_ONLY_SIMULATE_HEIGHT: - // fastest - generateUsingBiomesOnly(); - break; - case SURFACE: - // faster - generateUsingSurface(); - break; - case FEATURES: - // fast - generateUsingFeatures(); - break; - case SERVER: - // very slow - generateWithServer(); - break; - default: - break; - } - - lodRenderer.regenerateLODsNextFrame(); - - -// if (lodDim.getLodFromCoordinates(pos.x, pos.z) != null) -// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!"); -// else -// ClientProxy.LOGGER.info(pos.x + " " + pos.z); - -// long endTime = System.currentTimeMillis(); -// System.out.println(endTime - startTime); - - }// if in range - - }// run - - - - /** - * takes about 2-5 ms - */ - private void generateUsingBiomesOnly() - { - List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY); - chunkList.add(chunk); - - ServerChunkProvider chunkSource = serverWorld.getChunkSource(); - ChunkGenerator chunkGen = chunkSource.generator; - - - // generate the terrain (this is thread safe) - ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - // override the chunk status so we can run the next generator stage - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - - - // generate fake height data for this LOD - int seaLevel = serverWorld.getSeaLevel(); - - boolean simulateHeight = LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; - boolean inTheEnd = false; - - // add fake heightmap data so our LODs aren't at height 0 - Heightmap heightmap = new Heightmap(chunk, LodUtil.DEFAULT_HEIGHTMAP); - for(int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++) - { - for(int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++) - { - if (simulateHeight) - { - // TODO use the biomes around each block to smooth out the transition - - // these heights are of course aren't super accurate, - // they are just to simulate height data where there isn't any - switch(chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory()) - { - case NETHER: - heightmap.setHeight(x, z, serverWorld.getHeight() / 2); - break; - - case EXTREME_HILLS: - heightmap.setHeight(x, z, seaLevel + 30); - break; - case MESA: - heightmap.setHeight(x, z, seaLevel + 20); - break; - case JUNGLE: - heightmap.setHeight(x, z, seaLevel + 20); - break; - case BEACH: - heightmap.setHeight(x, z, seaLevel + 5); - break; - case NONE: - heightmap.setHeight(x, z, 0); - break; - - case OCEAN: - case RIVER: - heightmap.setHeight(x, z, seaLevel); - break; - - case THEEND: - inTheEnd = true; - break; - - // DESERT - // FOREST - // ICY - // MUSHROOM - // SAVANNA - // SWAMP - // TAIGA - // PLAINS - default: - heightmap.setHeight(x, z, seaLevel + 10); - break; - }// heightmap switch - } - else - { - // we aren't simulating height - // always use sea level - heightmap.setHeight(x, z, seaLevel); - } - }// z - }// x - - chunk.setHeightmap(LodUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData()); - - - LodChunk lod; - if (!inTheEnd) - { - lod = lodChunkBuilder.generateLodFromChunk(chunk, new LodBuilderConfig(true, true, false)); - } - else - { - // if we are in the end, don't generate any chunks. - // Since we don't know where the islands are, everything - // generates the same and it looks really bad. - lod = new LodChunk(chunk.getPos().x, chunk.getPos().z); - } - lodDim.addLod(lod); - } - - - /** - * takes about 10 - 20 ms - */ - private void generateUsingSurface() - { - List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY); - chunkList.add(chunk); - LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); - - ServerChunkProvider chunkSource = serverWorld.getChunkSource(); - ChunkGenerator chunkGen = chunkSource.generator; - - - // generate the terrain (this is thread safe) - ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - // override the chunk status so we can run the next generator stage - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - - // this feature has proved to be thread safe - // so we will add it - IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC); - snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null); - - - LodChunk lod = lodChunkBuilder.generateLodFromChunk(chunk); - lodDim.addLod(lod); - } - - - /** - * takes about 15 - 20 ms - * - * Causes concurrentModification Exceptions, - * which could cause instability or world generation bugs - */ - private void generateUsingFeatures() - { - List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY); - chunkList.add(chunk); - LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); - - ServerChunkProvider chunkSource = serverWorld.getChunkSource(); - ChunkGenerator chunkGen = chunkSource.generator; - - - // generate the terrain (this is thread safe) - ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - // override the chunk status so we can run the next generator stage - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - - - // get all the biomes in the chunk - HashSet biomes = new HashSet<>(); - for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) - { - for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) - { - Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2); - - // Issue #35 - // For some reason Jungle biomes cause incredible lag - // the features here must be interacting with each other - // in unpredictable ways (specifically tree feature generation). - // When generating Features my CPU usage generally hovers around 30 - 40% - // when generating Jungles it spikes to 100%. - if (biome.getBiomeCategory() != Biome.Category.JUNGLE) - { - // should probably use the heightmap here instead of seaLevel, - // but this seems to get the job done well enough - biomes.add(biome); - } - } - } - - boolean allowUnstableFeatures = LodConfig.CLIENT.allowUnstableFeatureGeneration.get(); - - // generate all the features related to this chunk. - // this may or may not be thread safe - for (Biome biome : biomes) - { - List>>> featuresForState = biome.generationSettings.features(); - - for(int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++) - { - for(Supplier> featureSupplier : featuresForState.get(featureStateToGenerate)) - { - ConfiguredFeature configuredFeature = featureSupplier.get(); - - if (!allowUnstableFeatures && - configuredFeaturesToAvoid.containsKey(configuredFeature.hashCode())) - continue; - - - try - { - configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition()); - } - catch(ConcurrentModificationException e) - { - // This will happen. I'm not sure what to do about it - // except pray that it doesn't effect the normal world generation - // in any harmful way. - // Update: this can cause crashes and high CPU usage. - - // Issue #35 - // I tried cloning the config for each feature, but that - // path was blocked since I can't clone lambda methods. - // I tried using a deep cloning library and discovered - // the problem there. - // ( https://github.com/kostaskougios/cloning - // and - // https://github.com/EsotericSoftware/kryo ) - - if (!allowUnstableFeatures) - configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature); -// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); - } - catch(UnsupportedOperationException e) - { - // This will happen when the LodServerWorld - // isn't able to return something that a feature - // generator needs - - if (!allowUnstableFeatures) - configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature); -// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); - } - catch(Exception e) - { - // I'm not sure what happened, print to the log - - System.out.println(); - System.out.println(); - e.printStackTrace(); - System.out.println(); - System.out.println(); - - if (!allowUnstableFeatures) - configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature); -// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); - } - } - } - } - - // generate a Lod like normal - LodChunk lod = lodChunkBuilder.generateLodFromChunk(chunk); - lodDim.addLod(lod); - } - - - /** - * on pre generated chunks 0 - 1 ms - * on un generated chunks 0 - 50 ms - * with the median seeming to hover around 15 - 30 ms - * and outliers in the 100 - 200 ms range - * - * Note this should not be multithreaded and does cause server/simulation lag - * (Higher lag for generating than loading) - */ - private void generateWithServer() - { - throw new UnsupportedOperationException("Not Implemented"); - //lodChunkBuilder.generateLodChunkAsync(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), ClientProxy.getLodWorld(), serverWorld); - } - - - - - - - //================// - // Unused methods // - //================// - - // Sadly I wasn't able to get these to work, - // they are here for documentation purposes - - @SuppressWarnings({ "rawtypes", "unchecked", "unused" }) - private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config) - { - IPlacementConfig placementConfig = null; - - Class oldConfigClass = config.decorator.config().getClass(); - - if (oldConfigClass == FeatureSpreadConfig.class) - { - FeatureSpreadConfig oldPlacementConfig = (FeatureSpreadConfig) config.decorator.config(); - FeatureSpread oldSpread = oldPlacementConfig.count(); - - placementConfig = new FeatureSpreadConfig(oldSpread); - } - else if(oldConfigClass == DecoratedPlacementConfig.class) - { - DecoratedPlacementConfig oldPlacementConfig = (DecoratedPlacementConfig) config.decorator.config(); - placementConfig = new DecoratedPlacementConfig(oldPlacementConfig.inner(), oldPlacementConfig.outer()); - } - else if(oldConfigClass == NoiseDependant.class) - { - NoiseDependant oldPlacementConfig = (NoiseDependant) config.decorator.config(); - placementConfig = new NoiseDependant(oldPlacementConfig.noiseLevel, oldPlacementConfig.belowNoise, oldPlacementConfig.aboveNoise); - } - else - { -// ClientProxy.LOGGER.debug("unkown decorated placement config: \"" + config.decorator.config().getClass() + "\""); - return config; - } - - - ConfiguredPlacement newPlacement = new ConfiguredPlacement(config.decorator.decorator, placementConfig); - return new DecoratedFeatureConfig(config.feature, newPlacement); - } - - - @SuppressWarnings("unused") - private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config) - { - WeightedBlockStateProvider provider = new WeightedBlockStateProvider(); - for(Entry state : ((WeightedBlockStateProvider) config.stateProvider).weightedList.entries) - provider.weightedList.entries.add(state); - - HashSet whitelist = new HashSet<>(); - for(Block block : config.whitelist) - whitelist.add(block); - - HashSet blacklist = new HashSet<>(); - for(BlockState state : config.blacklist) - blacklist.add(state); - - - BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer); - builder.whitelist(whitelist); - builder.blacklist(blacklist); - builder.xspread(config.xspread); - builder.yspread(config.yspread); - builder.zspread(config.zspread); - if(config.canReplace) { builder.canReplace(); } - if(config.needWater) { builder.needWater(); } - if(config.project) { builder.noProjection(); } - builder.tries(config.tries); - - - return builder.build(); - } - - } - - - /** - * Stops the current genThreads if they are running - * and then recreates the Executer service.

- * - * This is done to clear any outstanding tasks - * that may exist after the player leaves their current world. - * If this isn't done unfinished tasks may be left in the queue - * preventing new LodChunks form being generated. - */ - public static void restartExecuterService() - { - if (genThreads != null && !genThreads.isShutdown()) - { - genThreads.shutdownNow(); - } - genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get()); - } - - - - - - /* - * performance/generation tests related to - * serverWorld.getChunk(x, z, ChunkStatus. *** ) - - true/false is whether they generated blocks or not - the time is how long it took to generate - - ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P) - ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks) - ChunkStatus.BIOMES 1 - 10 ms false (no height) - ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone) - ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass) - ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass) - ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass) - ChunkStatus.FEATURES 7 - 25 ms true - ChunkStatus.HEIGHTMAPS 20 - 40 ms true - ChunkStatus.LIGHT 20 - 40 ms true - ChunkStatus.FULL 30 - 50 ms true - ChunkStatus.SPAWN 50 - 80 ms true - - - At this point I would suggest using FEATURES, as it generates snow and trees - (and any other object that is needed to make biomes distinct) - - Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much) - */ -} diff --git a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java deleted file mode 100644 index 1c6986294..000000000 --- a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.handlers; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.objects.LodChunk; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.LodRegion; -import com.seibel.lod.proxy.ClientProxy; - -/** - * This object handles creating LodRegions - * from files and saving LodRegion objects - * to file. - * - * @author James Seibel - * @version 6-27-2021 - */ -public class LodDimensionFileHandler -{ - private LodDimension loadedDimension = null; - public long regionLastWriteTime[][]; - - private File dimensionDataSaveFolder; - - /** lod */ - private final String FILE_NAME_PREFIX = "lod"; - /** .txt */ - private final String FILE_EXTENSION = ".txt"; - - /** 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 = 2; - - /** 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(); - - - 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; - - 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 // - //================// - - - - /** - * Return the LodRegion at the given coordinates. - * (null if the file doesn't exist) - */ - public LodRegion loadRegionFromFile(int regionX, int regionZ) - { - - String fileName = getFileNameAndPathForRegion(regionX, regionZ, LodConfig.CLIENT.maxDrawDetail.get()); - - File f = new File(fileName); - - if (!f.exists()) - { - // there wasn't a file, don't - // return anything - return null; - } - - LodRegion region = new LodRegion(regionX, regionZ); - - try - { - BufferedReader br = new BufferedReader(new FileReader(f)); - String s = br.readLine(); - int fileVersion = -1; - - if(s != null && !s.isEmpty()) - { - // try to get the file version - try - { - fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim()); - } - catch(NumberFormatException | StringIndexOutOfBoundsException e) - { - // this file doesn't have a version - // keep the version as -1 - fileVersion = -1; - } - - // check if this file can be read by this file handler - if(fileVersion < LOD_SAVE_FILE_VERSION) - { - // the file we are reading is an older version, - // close the reader and delete the file. - br.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."); - - return null; - } - else if(fileVersion > LOD_SAVE_FILE_VERSION) - { - // the file we are reading is a newer version, - // close the reader and ignore the file, we don't - // want to accidently delete anything the user may want. - br.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."); - - return null; - } - } - else - { - // there is no data in this file - br.close(); - return null; - } - - - // this file is a readable version, begin reading the file - s = br.readLine(); - - while(s != null && !s.isEmpty()) - { - try - { - // convert each line into an LOD object and add it to the region - LodChunk lod = new LodChunk(s, LodConfig.CLIENT.maxDrawDetail.get()); - - region.addLod(lod); - } - catch(IllegalArgumentException e) - { - // we were unable to create this chunk - // for whatever reason. - // skip to the next chunk - ClientProxy.LOGGER.warn(e.getMessage()); - } - - s = br.readLine(); - } - - br.close(); - } - catch (IOException e) - { - // the buffered reader encountered a - // problem reading the file - return null; - } - - return region; - } - - - - - - - - - //==============// - // Save to File // - //==============// - - /** - * Save all dirty regions in this LodDimension to file. - */ - public void saveDirtyRegionsToFileAsync() - { - fileWritingThreadPool.execute(saveDirtyRegionsThread); - } - - private Thread saveDirtyRegionsThread = new Thread(() -> - { - for(int i = 0; i < loadedDimension.getWidth(); i++) - { - for(int j = 0; j < loadedDimension.getWidth(); j++) - { - if(loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null) - { - saveRegionToDisk(loadedDimension.regions[i][j]); - loadedDimension.isRegionDirty[i][j] = false; - } - } - } - }); - - /** - * Save a specific region to disk.
- * Note:
- * 1. If a file already exists for a newer version - * the file won't be written.
- * 2. This will save to the LodDimension that this - * handler is associated with. - */ - private void saveRegionToDisk(LodRegion region) - { - // convert chunk coordinates to region - // coordinates - int x = region.x; - int z = region.z; - - File f = new File(getFileNameAndPathForRegion(x, z, LodConfig.CLIENT.maxDrawDetail.get())); - - try - { - // make sure the file and folder exists - if (!f.exists()) - { - // the file doesn't exist, - // create it and the folder if need be - if(!f.getParentFile().exists()) - f.getParentFile().mkdirs(); - f.createNewFile(); - } - else - { - // the file exists, make sure it - // is the correct version. - // (to make sure we don't overwrite a newer - // version file if it exists) - - BufferedReader br = new BufferedReader(new FileReader(f)); - String s = br.readLine(); - int fileVersion = LOD_SAVE_FILE_VERSION; - - if(s != null && !s.isEmpty()) - { - // try to get the file version - try - { - fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim()); - } - catch(NumberFormatException | StringIndexOutOfBoundsException e) - { - // this file doesn't have a correctly formated version - // just overwrite the file - } - } - br.close(); - - // check if this file can be written to by the file handler - if(fileVersion <= LOD_SAVE_FILE_VERSION) - { - // we are good to continue and overwrite the old file - } - else //if(fileVersion > LOD_SAVE_FILE_VERSION) - { - // the file we are reading is a newer version, - // don't write anything, we don't want to accidently - // delete anything the user may want. - return; - } - } - - FileWriter fw = new FileWriter(f); - - // add the version of this file - fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n"); - - // add each LodChunk to the file - for(LodChunk[] chunkArray : region.getAllLods()) - for(LodChunk chunk : chunkArray) - if(chunk != null && !chunk.isPlaceholder()) - fw.write(chunk.toData() + "\n"); - - fw.close(); - } - catch(Exception e) - { - ClientProxy.LOGGER.error("LOD file write error: " + e.getMessage()); - } - } - - - - - - - - //================// - // 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.FULL.0.0.txt" - */ - private String getFileNameAndPathForRegion(int regionX, int regionZ, LodDetail detail) - { - try - { - // saveFolder is something like - // ".\Super Flat\DIM-1\data" - // or - // ".\Super Flat\data" - return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar + - FILE_NAME_PREFIX + "." + detail.toString() + "." + regionX + "." + regionZ + FILE_EXTENSION; - } - catch(IOException e) - { - return null; - } - } - -} diff --git a/src/main/java/com/seibel/lod/objects/LodChunk.java b/src/main/java/com/seibel/lod/objects/LodChunk.java deleted file mode 100644 index 14954fabc..000000000 --- a/src/main/java/com/seibel/lod/objects/LodChunk.java +++ /dev/null @@ -1,495 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.objects; - -import java.awt.Color; - -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler; - -import net.minecraft.util.math.ChunkPos; - -/** - * This object contains position - * and color data for an LOD object. - * - * @author James Seibel - * @version 6-27-2021 - */ -public class LodChunk -{ - /** This is what separates each piece of data in the toData method */ - private static final char DATA_DELIMITER = LodQuadTreeDimensionFileHandler.DATA_DELIMITER; - - /** alpha used when drawing chunks in debug mode */ - private static final int DEBUG_ALPHA = 255; // 0 - 255 - private static final Color DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA); - private static final Color DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA); - private static final Color INVISIBLE = new Color(0,0,0,0); - - public LodDetail detail = LodDetail.SINGLE; - - /** If this is set to true then toData will return - * the empty string */ - public boolean dontSave = false; - - // TODO store the DistanceGenerationMethod used for this chunk (so we can upgrade old chunks if we want to) - - - /** The x coordinate of the chunk. */ - public int x = 0; - /** The z coordinate of the chunk. */ - public int z = 0; - - - /** This stores the height and color for each data point in the LodChunk */ - public LodDataPoint dataPoints[][]; - - /** If true then this LodChunk contains no data */ - private boolean empty = false; - - - /** - * Create an empty, invisible, non-saving LodChunk at (0,0) - */ - public LodChunk() - { - dontSave = true; - empty = true; - - x = 0; - z = 0; - - detail = LodDetail.SINGLE; - - dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount]; - - // fill with dummy data, to prevent null pointers - for(int i = 0; i < detail.dataPointLengthCount; i++) - for(int j = 0; j < detail.dataPointLengthCount; j++) - dataPoints[i][j] = new LodDataPoint(); - } - - /** - * Create an empty, invisible, non-saving LodChunk at the given ChunkPos - */ - public LodChunk(ChunkPos pos) - { - this(); - - x = pos.x; - z = pos.z; - } - - /** - * Create an empty, invisible, non-saving LodChunk at the given ChunkPos - */ - public LodChunk(int newX, int newZ) - { - this(); - - x = newX; - z = newZ; - } - - - /** - * Creates an LodChunk from the string - * generated by the toData method. - * - * @throws IllegalArgumentException if the data isn't valid to create a LodChunk - * @throws NumberFormatException if any piece of data can't be converted at any point - */ - public LodChunk(String data, LodDetail newDetail) throws IllegalArgumentException, NumberFormatException - { - /* - * data format: - * x, z, dataPoint[0][0], dataPoint[0][1], ... - * x, z, height, depth, rgb color data, height, depth, rgb.... - * - * example: - * 5,8, 4, 0, 255,255,255, 4, 0, 255, 255, 255, ... - */ - - dontSave = false; - - // make sure there are the correct number of entries - // in the data string - int count = 0; - detail = newDetail; - - for(int i = 0; i < data.length(); i++) - if(data.charAt(i) == DATA_DELIMITER) - count++; - - if(count != detail.lodChunkStringDelimiterCount) - throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + detail.lodChunkStringDelimiterCount + "."); - - - dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount]; - - // index we will use when going through the String - int index = 0; - int lastIndex = 0; - - - - // x and z position - index = data.indexOf(DATA_DELIMITER, 0); - this.x = Integer.parseInt(data.substring(0,index)); - - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - this.z = Integer.parseInt(data.substring(lastIndex+1,index)); - - - // LodDataPoints - for(int blockX = 0; blockX < detail.dataPointLengthCount; blockX++) - { - for(int blockZ = 0; blockZ < detail.dataPointLengthCount; blockZ++) - { - // height - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - int height = Short.parseShort(data.substring(lastIndex+1,index)); - - // depth - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - int depth = Short.parseShort(data.substring(lastIndex+1,index)); - - - // color - int red = 0; - int green = 0; - int blue = 0; - - // get r,g,b - for(int i = 0; i < 3; i++) - { - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex + 1); - - String raw = ""; - switch(i) - { - case 0: - raw = data.substring(lastIndex+1,index); - red = Short.parseShort(raw); - break; - case 1: - raw = data.substring(lastIndex+1,index); - green = Short.parseShort(raw); - break; - case 2: - raw = data.substring(lastIndex+1,index); - blue = Short.parseShort(raw); - break; - } - } - - dataPoints[blockX][blockZ] = new LodDataPoint((short)height, (short)depth, new Color(red, green, blue)); - } - } - - - empty = determineIfEmtpy(); - } - - /** - * Create a LodChunk from the given values. - */ - public LodChunk(ChunkPos pos, LodDataPoint[][] newDataPoints, LodDetail newDetail) - { - x = pos.x; - z = pos.z; - - dataPoints = newDataPoints; - - dontSave = false; - detail = newDetail; - - empty = determineIfEmtpy(); - } - - - - - - - //================// - // misc functions // - //================// - - /** - * Returns true if this LodChunk is an emptyPlaceholder - */ - public boolean isPlaceholder() - { - return empty; - } - - public boolean isLodEmpty() - { - return empty; - } - - /** - * Returns true if this LOD is either invisible - * from every direction or doesn't have a valid height. - */ - private boolean determineIfEmtpy() - { - for(LodDataPoint[] dataPointArray : dataPoints) - { - for(LodDataPoint dataPoint : dataPointArray) - { - if (dataPoint == null) - continue; - - // we don't check the depth since the - // height should always be greater than or equal - // to the depth - if(dataPoint.height >= 0) - { - // the height is valid, - - if (dataPoint.color == INVISIBLE) - continue; - - // the color and height are valid - // this LodChunk isn't empty - return false; - } - } - } - - // we checked everywhere, this LodChunk is empty - return true; - } - - - - - //========// - // output // - //========// - - /** - * Returns the data point for the given relative block position. - */ - public LodDataPoint getDataPointForBlockPos(int blockX, int blockZ) - { - return dataPoints[blockX / detail.dataPointWidth][blockZ / detail.dataPointWidth]; - } - - public Color getColorForBlockPos(int blockX, int blockZ) - { - return getDataPointForBlockPos(blockX, blockZ).color; - } - - public short getHeightForBlockPos(int blockX, int blockZ) - { - return getDataPointForBlockPos(blockX, blockZ).height; - } - - public short getDepthForBlockPos(int blockX, int blockZ) - { - return getDataPointForBlockPos(blockX, blockZ).depth; - } - - - public Color getColor(int xIndex, int zIndex) - { - return dataPoints[xIndex][zIndex].color; - } - - public short getHeight(int xIndex, int zIndex) - { - return dataPoints[xIndex][zIndex].height; - } - - public short getDepth(int xIndex, int zIndex) - { - return dataPoints[xIndex][zIndex].depth; - } - - public short calculateHighestPoint() - { - short highest = 0; - - for(int x = 0; x < detail.dataPointLengthCount; x++) - { - for(int z = 0; z < detail.dataPointLengthCount; z++) - { - if (getHeight(x,z) > highest) - highest = getHeight(x,z); - } - } - - return highest; - } - - - - /** - * @param startX - * @param startZ - * @param endX - * @param endZ - * @return - */ - public short getAverageHeightOverArea(int startX, int startZ, int endX, int endZ) - { - if (startX == endX || startZ == endZ) - // we were given an area with 0 blocks in it - return getHeightForBlockPos(startX,startZ); - - int average = 0; - - for(int x = startX; x < endX; x++) - for(int z = startZ; z < endZ; z++) - average += getHeightForBlockPos(x,z); - - return (short) (average / ((endX - startX) * (endZ - startZ))); - } - - /** - * @param startX - * @param startZ - * @param endX - * @param endZ - * @return - */ - public short getAverageDepthOverArea(int startX, int startZ, int endX, int endZ) - { - if (startX == endX || startZ == endZ) - // we were given an area with 0 blocks in it - return getDepthForBlockPos(startX,startZ); - - int average = 0; - - for(int x = startX; x < endX; x++) - for(int z = startZ; z < endZ; z++) - average += getDepthForBlockPos(x,z); - - return (short) (average / ((endX - startX) * (endZ - startZ))); - } - - - /** - * Determine the color of this LOD. - */ - public Color getAverageColorOverArea(int startX, int startZ, int endX, int endZ, boolean debugging) - { - if (startX == endX || startZ == endZ) - // we were given an area with 0 blocks in it - return getColorForBlockPos(startX,startZ); - - - - int[] colorComponents = new int[3]; - - - if (debugging) - { - // draw the squares as a black and white, - // like a checker board - - // only return 1 color to prevent - // getting back gray - if ((startX + startZ) % 2 == 0) - return DEBUG_WHITE; - else - return DEBUG_BLACK; - } - - - for(int x = startX; x < endX; x++) - { - for(int z = startZ; z < endZ; z++) - { - colorComponents = addColorToColorAverages(colorComponents, getColorForBlockPos(x,z)); - } - } - - int numbPoints = ((endX - startX) * (endZ - startZ)); - - return new Color(colorComponents[0]/numbPoints, colorComponents[1]/numbPoints, colorComponents[2]/numbPoints); - } - - - private int[] addColorToColorAverages(int[] colorAverages, Color colorToAdd) - { - // convert the colorToAdd to an int array - float[] colorCompoments = new float[4]; - colorCompoments = colorToAdd.getColorComponents(colorCompoments); - - // add each color component to the array - for(int rgbIndex = 0; rgbIndex < 3; rgbIndex++) - { - // * 255 + 0.5 taken from the Color java class - colorAverages[rgbIndex] += (int) (colorCompoments[rgbIndex] * 255 + 0.5); - } - - return colorAverages; - } - - - - - - /** - * Outputs all data in a csv format - * with the given delimiter, if - * dontSave is false.

- * - * data format:
- * x, z, dataPoint[0][0], dataPoint[0][1], ...
- * x, z, height, depth, rgb color data, height, depth, rgb....

- * - * example:
- * 5,8, 4, 0, 255,255,255, 4, 0, 255, 255, 255, ... - */ - public String toData() - { - if (dontSave) - return ""; - - String s = ""; - s += Integer.toString(x) + DATA_DELIMITER + Integer.toString(z) + DATA_DELIMITER; - - for (int i = 0; i < dataPoints.length; i++) - for (int j = 0; j < dataPoints[i].length; j++) - s += dataPoints[i][j].toData(); - - return s; - } - - - @Override - public String toString() - { - String s = ""; - - s += "x: " + x + " z: " + z + "\t"; - - return s; - } - -} diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java deleted file mode 100644 index 703d5aabb..000000000 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.objects; - -import java.io.File; -import java.io.IOException; - -import com.seibel.lod.handlers.LodDimensionFileHandler; -import com.seibel.lod.util.LodUtil; - -import net.minecraft.client.Minecraft; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.DimensionType; -import net.minecraft.world.server.ServerChunkProvider; -import net.minecraft.world.server.ServerWorld; - -/** - * This object holds all loaded LOD regions - * for a given dimension. - * - * @author James Seibel - * @version 06-27-2021 - */ -public class LodDimension -{ - public final DimensionType dimension; - - private volatile int width; - private volatile int halfWidth; - - public volatile LodRegion regions[][]; - public volatile boolean isRegionDirty[][]; - - private volatile int centerX; - private volatile int centerZ; - - private LodDimensionFileHandler fileHandler; - - - public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newMaxWidth) - { - dimension = newDimension; - width = newMaxWidth; - - try - { - Minecraft mc = Minecraft.getInstance(); - - 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]; - - // populate isRegionDirty - for(int i = 0; i < width; i++) - for(int j = 0; j < width; j++) - isRegionDirty[i][j] = false; - - centerX = 0; - centerZ = 0; - - halfWidth = (int)Math.floor(width / 2); - } - - - /** - * Move the center of this LodDimension and move all owned - * regions over by the given x and z offset. - */ - public synchronized void move(int xOffset, int zOffset) - { - // 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 - centerX += xOffset; - centerZ += 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 - centerX += xOffset; - centerZ += zOffset; - } - - - - - - - /** - * 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(int regionX, int regionZ) - { - int xIndex = (regionX - centerX) + halfWidth; - int zIndex = (regionZ - centerZ) + halfWidth; - - if (!regionIsInRange(regionX, regionZ)) - // out of range - return null; - - if (regions[xIndex][zIndex] == null) - { - regions[xIndex][zIndex] = getRegionFromFile(regionX, regionZ); - if (regions[xIndex][zIndex] == null) - { - regions[xIndex][zIndex] = new LodRegion(regionX, regionZ); - } - } - - 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 void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException - { - int xIndex = (newRegion.x - centerX) + halfWidth; - int zIndex = (centerZ - newRegion.z) + halfWidth; - - if (!regionIsInRange(newRegion.x, newRegion.z)) - // out of range - throw new ArrayIndexOutOfBoundsException(); - - regions[xIndex][zIndex] = newRegion; - } - - - - - - /** - * 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 void addLod(LodChunk lod) - { - RegionPos pos = new RegionPos(new ChunkPos(lod.x, lod.z)); - - // don't continue if the region can't be saved - if (!regionIsInRange(pos.x, pos.z)) - { - return; - } - - LodRegion region = getRegion(pos.x, pos.z); - - if (region == null) - { - // if no region exists, create it - region = new LodRegion(pos.x, pos.z); - addOrOverwriteRegion(region); - } - - region.addLod(lod); - - // only save valid LODs to disk - if (!(lod.isPlaceholder() || lod.dontSave) && fileHandler != null) - { - // mark the region as dirty so it will be saved to disk - int xIndex = (pos.x - centerX) + halfWidth; - int zIndex = (pos.z - centerZ) + halfWidth; - isRegionDirty[xIndex][zIndex] = true; - fileHandler.saveDirtyRegionsToFileAsync(); - } - } - - /** - * Get the LodChunk 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 LodChunk getLodFromCoordinates(int chunkX, int chunkZ) - { - RegionPos pos = new RegionPos(new ChunkPos(chunkX, chunkZ)); - - LodRegion region = getRegion(pos.x, pos.z); - - if(region == null) - return null; - - return region.getLod(chunkX, chunkZ); - } - - - /** - * Get the region at the given X and Z coordinates from the - * RegionFileHandler. - */ - public LodRegion getRegionFromFile(int regionX, int regionZ) - { - if (fileHandler != null) - return fileHandler.loadRegionFromFile(regionX, regionZ); - else - return null; - } - - - /** - * 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 - centerX) + halfWidth; - int zIndex = (regionZ - centerZ) + halfWidth; - - return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width; - } - - - - - - - - public int getCenterX() - { - return centerX; - } - - public int getCenterZ() - { - return centerZ; - } - - - /** - * Returns how many non-null LodChunks - * are stored in this LodDimension. - */ - public int getNumberOfLods() - { - int numbLods = 0; - for (LodRegion[] regions : regions) - { - if(regions == null) - continue; - - for (LodRegion region : regions) - { - if(region == null) - continue; - - for(LodChunk[] lods : region.getAllLods()) - { - if(lods == null) - continue; - - for(LodChunk lod : lods) - { - if (lod != null) - numbLods++; - } - } - } - } - - return numbLods; - } - - - 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; - - 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() - { - String s = ""; - - s += "dim: " + dimension.toString() + "\t"; - s += "(" + centerX + "," + centerZ + ")"; - - return s; - } -} - - - - - diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java deleted file mode 100644 index 2fcbd6b17..000000000 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.objects; - -import com.seibel.lod.util.LodUtil; - -/** - * A LodRegion is a 32x32 - * 2D array of LodChunk objects. - * Each LodRegion corresponds to - * one file in the file system. - * - * @author James Seibel - * @version 6-12-2021 - */ -public class LodRegion -{ - /** X coordinate of this region */ - public final int x; - /** Z coordinate of this region */ - public final int z; - - private LodChunk chunks[][]; - - - public LodRegion(int regionX, int regionZ) - { - x = regionX; - z = regionZ; - - chunks = new LodChunk[LodUtil.REGION_WIDTH_IN_CHUNKS][LodUtil.REGION_WIDTH_IN_CHUNKS]; - } - - - /** - * Add the given LOD to this region at the coordinate - * stored in the LOD. If an LOD already exists at the given - * coordinates it will be overwritten. - */ - public void addLod(LodChunk lod) - { - // we use ABS since LODs can be negative, but if they are - // the region will negative first, therefore we don't have to - // store the LOD chunks at negative indexes since we search - // LOD the region first - int xIndex = Math.abs(lod.x % LodUtil.REGION_WIDTH_IN_CHUNKS); - int zIndex = Math.abs(lod.z % LodUtil.REGION_WIDTH_IN_CHUNKS); - - chunks[xIndex][zIndex] = lod; - } - - /** - * Get the LodChunk at the given X and Z coordinates - * in this region. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public LodChunk getLod(int chunkX, int chunkZ) - { - // since we add LOD's with ABS, we get them the same way - int arrayX = Math.abs(chunkX % LodUtil.REGION_WIDTH_IN_CHUNKS); - int arrayZ = Math.abs(chunkZ % LodUtil.REGION_WIDTH_IN_CHUNKS); - - return chunks[arrayX][arrayZ]; - } - - - /** - * Returns all LodChunks in this region - */ - public LodChunk[][] getAllLods() - { - return chunks; - } - - - - @Override - public String toString() - { - String s = ""; - - s += "x: " + x + " z: " + z + "\t"; - - return s; - } -} diff --git a/src/main/java/com/seibel/lod/objects/LodWorld.java b/src/main/java/com/seibel/lod/objects/LodWorld.java deleted file mode 100644 index 784e71c76..000000000 --- a/src/main/java/com/seibel/lod/objects/LodWorld.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.objects; - -import java.util.Hashtable; -import java.util.Map; - -import net.minecraft.world.DimensionType; - -/** - * This stores all LODs for a given world. - * - * @author James Seibel - * @version 04-01-2021 - */ -public class LodWorld -{ - private String worldName; - - private Map lodDimensions; - /** 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 LodWorld() - { - worldName = NO_WORLD_LOADED; - } - - /** - * Set up the LodWorld with the given newWorldName.
- * This should be done whenever loading a new world. - * @param newWorldName - */ - 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; - - 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; - } - - - public void addLodDimension(LodDimension newStorage) - { - if (lodDimensions == null) - throw new IllegalStateException("LodWorld hasn't been given a world yet."); - - lodDimensions.put(newStorage.dimension, newStorage); - } - - public LodDimension getLodDimension(DimensionType dimension) - { - if (lodDimensions == null) - throw new IllegalStateException("LodWorld hasn't been given a world yet."); - - return lodDimensions.get(dimension); - } - - /** - * Resizes the max width in regions that each LodDimension - * should use. - */ - public void resizeDimensionRegionWidth(int newWidth) - { - if (lodDimensions == null) - throw new IllegalStateException("LodWorld hasn't been given a world yet."); - - for(DimensionType key : lodDimensions.keySet()) - lodDimensions.get(key).setRegionWidth(newWidth); - } - - - public boolean getIsWorldLoaded() - { - return isWorldLoaded; - } - - public String getWorldName() - { - return worldName; - } - - @Override - public String toString() - { - return "World name: " + worldName; - } -} diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index a6fe4cb8c..2362fbb2b 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -22,7 +22,6 @@ import org.apache.logging.log4j.Logger; import com.seibel.lod.builders.LodNodeBufferBuilder; import com.seibel.lod.builders.LodNodeBuilder; -import com.seibel.lod.builders.worldGeneration.LodChunkGenWorker; import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.FogDistance; @@ -206,7 +205,6 @@ public class ClientProxy { // if this isn't done unfinished tasks may be left in the queue // preventing new LodChunks form being generated - LodChunkGenWorker.restartExecuterService(); LodNodeGenWorker.restartExecuterService(); lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0); diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java deleted file mode 100644 index 65704a5a4..000000000 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ /dev/null @@ -1,885 +0,0 @@ -/* - * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.seibel.lod.render; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.HashSet; - -import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.NVFogDistance; - -import com.mojang.blaze3d.matrix.MatrixStack; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import com.seibel.lod.builders.LodBufferBuilder; -import com.seibel.lod.enums.FogDistance; -import com.seibel.lod.enums.FogDrawOverride; -import com.seibel.lod.enums.FogQuality; -import com.seibel.lod.handlers.LodConfig; -import com.seibel.lod.handlers.ReflectionHandler; -import com.seibel.lod.objects.LodChunk; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.NearFarBuffer; -import com.seibel.lod.objects.NearFarFogSettings; -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.util.LodUtil; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.entity.player.ClientPlayerEntity; -import net.minecraft.client.renderer.ActiveRenderInfo; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.FogRenderer; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.renderer.vertex.VertexBuffer; -import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.potion.Effects; -import net.minecraft.profiler.IProfiler; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.vector.Matrix4f; -import net.minecraft.util.math.vector.Vector3d; -import net.minecraft.util.math.vector.Vector3f; - - -/** - * This is where all the magic happens.
- * This is where LODs are draw to the world. - * - * @author James Seibel - * @version 07-4-2021 - */ -public class LodRenderer -{ - /** this is the light used when rendering the LODs, - * it should be something different than what is used by Minecraft */ - private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2; - - /** - * 64 MB by default is the maximum amount of memory that - * can be directly allocated.

- * - * I know there are commands to change that amount - * (specifically "-XX:MaxDirectMemorySize"), but - * I have no idea how to access that amount.
- * So I guess this will be the hard limit for now.

- * - * https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx - */ - public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024; - - /** Does this computer's GPU support fancy fog? */ - private static Boolean fancyFogAvailable = null; - - - - /** If true the LODs colors will be replaced with - * a checkerboard, this can be used for debugging. */ - public boolean debugging = false; - - private Minecraft mc; - private GameRenderer gameRender; - private IProfiler profiler; - private float farPlaneDistance; - private ReflectionHandler reflectionHandler; - - - /** This is used to generate the buildable buffers */ - private LodBufferBuilder lodBufferBuilder; - - /** The buffers that are used to draw LODs using near fog */ - private volatile BufferBuilder drawableNearBuffer; - /** The buffers that are used to draw LODs using far fog */ - private volatile BufferBuilder drawableFarBuffer; - - /** This is the VertexBuffer used to draw any LODs that use near fog */ - private volatile VertexBuffer nearVbo; - /** This is the VertexBuffer used to draw any LODs that use far fog */ - private volatile VertexBuffer farVbo; - public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR; - - - /** This is used to determine if the LODs should be regenerated */ - private int previousChunkRenderDistance = 0; - /** This is used to determine if the LODs should be regenerated */ - private int prevChunkX = 0; - /** This is used to determine if the LODs should be regenerated */ - private int prevChunkZ = 0; - /** This is used to determine if the LODs should be regenerated */ - private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR; - - /** if this is true the LOD buffers should be regenerated, - * provided they aren't already being regenerated. */ - private volatile boolean regen = false; - - /** This HashSet contains every chunk that Vanilla Minecraft - * is going to render */ - public HashSet vanillaRenderedChunks = new HashSet<>(); - - - - public LodRenderer(LodBufferBuilder newLodBufferBuilder) - { - mc = Minecraft.getInstance(); - gameRender = mc.gameRenderer; - - reflectionHandler = new ReflectionHandler(); - lodBufferBuilder = newLodBufferBuilder; - } - - - /** - * Besides drawing the LODs this method also starts - * the async process of generating the Buffers that hold those LODs. - * - * @param newDimension The dimension to draw, if null doesn't replace the current dimension. - * @param partialTicks how far into the current tick this method was called. - */ - @SuppressWarnings("deprecation") - public void drawLODs(LodDimension lodDim, float partialTicks, IProfiler newProfiler) - { - if (lodDim == null) - { - // if there aren't any loaded LodChunks - // don't try drawing anything - return; - } - - - - - - - //===============// - // initial setup // - //===============// - - profiler = newProfiler; - profiler.push("LOD setup"); - - - // only check the GPU capability's once - if (fancyFogAvailable == null) - { - // see if this GPU can run fancy fog - fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance; - - if (!LodRenderer.fancyFogAvailable) - { - ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that fancy fog options will not be available."); - } - } - - - ClientPlayerEntity player = mc.player; - - // should LODs be regenerated? - if ((int)player.getX() / LodUtil.CHUNK_WIDTH != prevChunkX || - (int)player.getZ() / LodUtil.CHUNK_WIDTH != prevChunkZ || - previousChunkRenderDistance != mc.options.renderDistance || - prevFogDistance != LodConfig.CLIENT.fogDistance.get()) - { - // yes - regen = true; - - prevChunkX = (int)player.getX() / LodUtil.CHUNK_WIDTH; - prevChunkZ = (int)player.getZ() / LodUtil.CHUNK_WIDTH; - prevFogDistance = LodConfig.CLIENT.fogDistance.get(); - } - else - { - // nope, the player hasn't moved, the - // render distance hasn't changed, and - // the dimension is the same - } - - // did the user change the debug setting? - if (LodConfig.CLIENT.debugMode.get() != debugging) - { - debugging = LodConfig.CLIENT.debugMode.get(); - regen = true; - } - - - // determine how far the game's render distance is currently set - int renderDistWidth = mc.options.renderDistance; - farPlaneDistance = renderDistWidth * LodUtil.CHUNK_WIDTH; - - // set how big the LODs will be and how far they will go - int totalLength = (int) farPlaneDistance * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() * 2; - int numbChunksWide = (totalLength / LodUtil.CHUNK_WIDTH); - - // determine which LODs should not be rendered close to the player - HashSet chunkPosToSkip = getNearbyLodChunkPosToSkip(lodDim, player.blockPosition()); - - // see if the chunks Minecraft is going to render are the - // same as last time - if (!vanillaRenderedChunks.containsAll(chunkPosToSkip)) - { - regen = true; - vanillaRenderedChunks = chunkPosToSkip; - } - - - - - - //=================// - // create the LODs // - //=================// - - // only regenerate the LODs if: - // 1. we want to regenerate LODs - // 2. we aren't already regenerating the LODs - // 3. we aren't waiting for the build and draw buffers to swap - // (this is to prevent thread conflicts) - if (regen && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvaliable()) - { - // this will mainly happen when the view distance is changed - if (drawableNearBuffer == null || drawableFarBuffer == null || - previousChunkRenderDistance != mc.options.renderDistance) - setupBuffers(numbChunksWide); - - // generate the LODs on a separate thread to prevent stuttering or freezing - lodBufferBuilder.generateLodBuffersAsync(this, lodDim, player.getX(), player.getZ(), numbChunksWide); - - // the regen process has been started, - // it will be done when lodBufferBuilder.newBuffersAvaliable - // is true - regen = false; - } - - // replace the buffers used to draw and build, - // this is only done when the createLodBufferGenerationThread - // has finished executing on a parallel thread. - if (lodBufferBuilder.newBuffersAvaliable()) - { - swapBuffers(); - } - - - - - - //===========================// - // GL settings for rendering // - //===========================// - - // set the required open GL settings - GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GL11.glDisable(GL11.GL_TEXTURE_2D); - GL11.glEnable(GL11.GL_CULL_FACE); - GL11.glEnable(GL11.GL_COLOR_MATERIAL); - GL11.glEnable(GL11.GL_DEPTH_TEST); - - // disable the lights Minecraft uses - GL11.glDisable(GL11.GL_LIGHT0); - GL11.glDisable(GL11.GL_LIGHT1); - - // get the default projection matrix so we can - // reset it after drawing the LODs - float[] defaultProjMatrix = new float[16]; - GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, defaultProjMatrix); - - Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks); - - setupProjectionMatrix(partialTicks); - setupLighting(lodDim, partialTicks); - - NearFarFogSettings fogSettings = determineFogSettings(); - - // determine the current fog settings so they can be - // reset after drawing the LODs - float defaultFogStartDist = GL11.glGetFloat(GL11.GL_FOG_START); - float defaultFogEndDist = GL11.glGetFloat(GL11.GL_FOG_END); - int defaultFogMode = GL11.glGetInteger(GL11.GL_FOG_MODE); - int defaultFogDistance = GL11.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV); - - - - - - - //===========// - // rendering // - //===========// - profiler.popPush("LOD draw"); - - setupFog(fogSettings.near.distance, fogSettings.near.quality); - sendLodsToGpuAndDraw(nearVbo, modelViewMatrix); - - setupFog(fogSettings.far.distance, fogSettings.far.quality); - sendLodsToGpuAndDraw(farVbo, modelViewMatrix); - - - - - - //=========// - // cleanup // - //=========// - - profiler.popPush("LOD cleanup"); - - GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); - GL11.glEnable(GL11.GL_TEXTURE_2D); - GL11.glDisable(LOD_GL_LIGHT_NUMBER); - // re-enable the lights Minecraft uses - GL11.glEnable(GL11.GL_LIGHT0); - GL11.glEnable(GL11.GL_LIGHT1); - RenderSystem.disableLighting(); - - // this can't be called until after the buffers are built - // because otherwise the buffers may be set to the wrong size - previousChunkRenderDistance = mc.options.renderDistance; - - // reset the fog settings so the normal chunks - // will be drawn correctly - cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance); - - // reset the projection matrix so anything drawn after - // the LODs will use the correct projection matrix - Matrix4f mvm = new Matrix4f(defaultProjMatrix); - mvm.transpose(); - gameRender.resetProjectionMatrix(mvm); - - // clear the depth buffer so anything drawn is drawn - // over the LODs - GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); - - - // end of internal LOD profiling - profiler.pop(); - } - - - - /** - * This is where the actual drawing happens. - * - * @param buffers the buffers sent to the GPU to draw - */ - private void sendLodsToGpuAndDraw(VertexBuffer vbo, Matrix4f modelViewMatrix) - { - if (vbo == null) - return; - - vbo.bind(); - // 0L is the starting pointer - LOD_VERTEX_FORMAT.setupBufferState(0L); - - vbo.draw(modelViewMatrix, GL11.GL_QUADS); - - VertexBuffer.unbind(); - LOD_VERTEX_FORMAT.clearBufferState(); - } - - - - - - - - //=================// - // Setup Functions // - //=================// - - @SuppressWarnings("deprecation") - private void setupFog(FogDistance fogDistance, FogQuality fogQuality) - { - if(fogQuality == FogQuality.OFF) - { - FogRenderer.setupNoFog(); - RenderSystem.disableFog(); - return; - } - - if(fogDistance == FogDistance.NEAR_AND_FAR) - { - throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance."); - } - - - // determine the fog distance mode to use - int glFogDistanceMode; - if (fogQuality == FogQuality.FANCY) - { - // fancy fog (fragment distance based fog) - glFogDistanceMode = NVFogDistance.GL_EYE_RADIAL_NV; - } - else - { - // fast fog (frustum distance based fog) - glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV; - } - - - // the multipliers are percentages - // of the regular view distance. - if(fogDistance == FogDistance.NEAR) - { - // the reason that I wrote fogEnd then fogStart backwards - // is because we are using fog backwards to how - // it is normally used, with it hiding near objects - // instead of far objects. - - if (fogQuality == FogQuality.FANCY) - { - RenderSystem.fogEnd(farPlaneDistance * 1.75f); - RenderSystem.fogStart(farPlaneDistance * 1.95f); - } - else if(fogQuality == FogQuality.FAST) - { - // for the far fog of the normal chunks - // to start right where the LODs' end use: - // end = 0.8f, start = 1.5f - - RenderSystem.fogEnd(farPlaneDistance * 1.5f); - RenderSystem.fogStart(farPlaneDistance * 2.0f); - } - } - else if(fogDistance == FogDistance.FAR) - { - if (fogQuality == FogQuality.FANCY) - { - RenderSystem.fogStart(farPlaneDistance * 0.85f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - RenderSystem.fogEnd(farPlaneDistance * 1.0f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - } - else if(fogQuality == FogQuality.FAST) - { - RenderSystem.fogStart(farPlaneDistance * 0.5f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - RenderSystem.fogEnd(farPlaneDistance * 0.75f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - } - } - - - GL11.glEnable(GL11.GL_FOG); - RenderSystem.enableFog(); - RenderSystem.setupNvFogDistance(); - RenderSystem.fogMode(GlStateManager.FogMode.LINEAR); - GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode); - } - - /** - * Revert any changes that were made to the fog. - */ - @SuppressWarnings("deprecation") - private void cleanupFog(NearFarFogSettings fogSettings, - float defaultFogStartDist, float defaultFogEndDist, - int defaultFogMode, int defaultFogDistance) - { - RenderSystem.fogStart(defaultFogStartDist); - RenderSystem.fogEnd(defaultFogEndDist); - RenderSystem.fogMode(defaultFogMode); - GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, defaultFogDistance); - - // disable fog if Minecraft wasn't rendering fog - // but we were - if(!fogSettings.vanillaIsRenderingFog && - (fogSettings.near.quality != FogQuality.OFF || - fogSettings.far.quality != FogQuality.OFF)) - { - GL11.glDisable(GL11.GL_FOG); - } - } - - - /** - * Create the model view matrix to move the LODs - * from object space into world space. - */ - private Matrix4f generateModelViewMatrix(float partialTicks) - { - // get all relevant camera info - ActiveRenderInfo renderInfo = mc.gameRenderer.getMainCamera(); - Vector3d projectedView = renderInfo.getPosition(); - - - // generate the model view matrix - MatrixStack matrixStack = new MatrixStack(); - matrixStack.pushPose(); - // translate and rotate to the current camera location - matrixStack.mulPose(Vector3f.XP.rotationDegrees(renderInfo.getXRot())); - matrixStack.mulPose(Vector3f.YP.rotationDegrees(renderInfo.getYRot() + 180)); - matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z); - - return matrixStack.last().pose(); - } - - - /** - * create a new projection matrix and send it over to the GPU - *

- * A lot of this code is copied from renderLevel (line 567) - * in the GameRender class. The code copied is anything with - * a matrixStack and is responsible for making sure the LOD - * objects distort correctly relative to the rest of the world. - * Distortions are caused by: standing in a nether portal, - * nausea potion effect, walking bobbing. - * - * @param partialTicks how many ticks into the frame we are - */ - private void setupProjectionMatrix(float partialTicks) - { - // Note: if the LOD objects don't distort correctly - // compared to regular minecraft terrain, make sure - // all the transformations in renderWorld are here too - - MatrixStack matrixStack = new MatrixStack(); - matrixStack.pushPose(); - - gameRender.bobHurt(matrixStack, partialTicks); - if (this.mc.options.bobView) { - gameRender.bobView(matrixStack, partialTicks); - } - - // potion and nausea effects - float f = MathHelper.lerp(partialTicks, this.mc.player.oPortalTime, this.mc.player.portalTime) * this.mc.options.screenEffectScale * this.mc.options.screenEffectScale; - if (f > 0.0F) { - int i = this.mc.player.hasEffect(Effects.CONFUSION) ? 7 : 20; - float f1 = 5.0F / (f * f + 5.0F) - f * 0.04F; - f1 = f1 * f1; - Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_OF_TWO / 2.0F, MathHelper.SQRT_OF_TWO / 2.0F); - matrixStack.mulPose(vector3f.rotationDegrees((gameRender.tick + partialTicks) * i)); - matrixStack.scale(1.0F / f1, 1.0F, 1.0F); - float f2 = -(gameRender.tick + partialTicks) * i; - matrixStack.mulPose(vector3f.rotationDegrees(f2)); - } - - // this projection matrix allows us to see past the normal - // world render distance - Matrix4f projectionMatrix = - Matrix4f.perspective( - getFov(partialTicks, true), - (float)this.mc.getWindow().getScreenWidth() / (float)this.mc.getWindow().getScreenHeight(), - // it is possible to see the near clip plane, but - // you have to be flying quickly in spectator mode through ungenerated - // terrain, so I don't think it is much of an issue. - LodConfig.CLIENT.lodChunkRadiusMultiplier.get(), - this.farPlaneDistance * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() * 2); - - // add the screen space distortions - projectionMatrix.multiply(matrixStack.last().pose()); - gameRender.resetProjectionMatrix(projectionMatrix); - return; - } - - - /** - * setup the lighting to be used for the LODs - */ - @SuppressWarnings("deprecation") - private void setupLighting(LodDimension lodDimension, float partialTicks) - { - float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.level.getSkyDarken(partialTicks) : 0.2f; - float gammaMultiplyer = (float)mc.options.gamma - 0.5f; - float lightStrength = ((sunBrightness / 2f) - 0.2f) + (gammaMultiplyer * 0.3f); - - float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f}; - - // can be used for debugging -// if (partialTicks < 0.005) -// ClientProxy.LOGGER.debug(lightStrength); - - ByteBuffer temp = ByteBuffer.allocateDirect(16); - temp.order(ByteOrder.nativeOrder()); - GL11.glLightfv(LOD_GL_LIGHT_NUMBER, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip()); - GL11.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting - - RenderSystem.enableLighting(); - } - - /** - * Create all buffers that will be used. - */ - private void setupBuffers(int numbChunksWide) - { - // calculate the max amount of memory needed (in bytes) - int bufferMemory = RenderUtil.getBufferMemoryForRadiusMultiplier(LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - - // if the required memory is greater than the - // MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier - // to fit. - if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY) - { - int maxRadiusMultiplier = RenderUtil.getMaxRadiusMultiplierWithAvaliableMemory(LodConfig.CLIENT.lodTemplate.get(), LodUtil.CHUNK_DETAIL_LEVEL); - - ClientProxy.LOGGER.warn("The lodChunkRadiusMultiplier was set too high " - + "and had to be lowered to fit memory constraints " - + "from " + LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + " " - + "to " + maxRadiusMultiplier); - - LodConfig.CLIENT.lodChunkRadiusMultiplier.set( - maxRadiusMultiplier); - - bufferMemory = RenderUtil.getBufferMemoryForRadiusMultiplier(maxRadiusMultiplier); - } - - drawableNearBuffer = new BufferBuilder(bufferMemory); - drawableFarBuffer = new BufferBuilder(bufferMemory); - - lodBufferBuilder.setupBuffers(bufferMemory); - } - - - - - - //======================// - // Other Misc Functions // - //======================// - - /** - * If this is called then the next time "drawLODs" is called - * the LODs will be regenerated; the same as if the player moved. - */ - public void regenerateLODsNextFrame() - { - regen = true; - } - - - /** - * Replace the current drawable buffers with the newly - * created buffers from the lodBufferBuilder. - */ - private void swapBuffers() - { - // replace the drawable buffers with - // the newly created buffers from the lodBufferBuilder - NearFarBuffer newBuffers = lodBufferBuilder.swapBuffers(drawableNearBuffer, drawableFarBuffer); - drawableNearBuffer = newBuffers.nearBuffer; - drawableFarBuffer = newBuffers.farBuffer; - - - // bind the buffers with their respective VBOs - if (nearVbo != null) - nearVbo.close(); - - nearVbo = new VertexBuffer(LOD_VERTEX_FORMAT); - nearVbo.upload(drawableNearBuffer); - - - if (farVbo != null) - farVbo.close(); - - farVbo = new VertexBuffer(LOD_VERTEX_FORMAT); - farVbo.upload(drawableFarBuffer); - } - - - private double getFov(float partialTicks, boolean useFovSetting) - { - return mc.gameRenderer.getFov(mc.gameRenderer.getMainCamera(), partialTicks, useFovSetting); - } - - - /** - * Return what fog settings should be used when rendering. - */ - private NearFarFogSettings determineFogSettings() - { - NearFarFogSettings fogSettings = new NearFarFogSettings(); - - - FogQuality quality = reflectionHandler.getFogQuality(); - FogDrawOverride override = LodConfig.CLIENT.fogDrawOverride.get(); - - - if (quality == FogQuality.OFF) - fogSettings.vanillaIsRenderingFog = false; - else - fogSettings.vanillaIsRenderingFog = true; - - - // use any fog overrides the user may have set - switch(override) - { - case ALWAYS_DRAW_FOG_FANCY: - quality = FogQuality.FANCY; - break; - - case NEVER_DRAW_FOG: - quality = FogQuality.OFF; - break; - - case ALWAYS_DRAW_FOG_FAST: - quality = FogQuality.FAST; - break; - - case USE_OPTIFINE_FOG_SETTING: - // don't override anything - break; - } - - - // only use fancy fog if the user's GPU can deliver - if (!fancyFogAvailable && quality == FogQuality.FANCY) - { - quality = FogQuality.FAST; - } - - - // how different distances are drawn depends on the quality set - switch(quality) - { - case FANCY: - fogSettings.near.quality = FogQuality.FANCY; - fogSettings.far.quality = FogQuality.FANCY; - - switch(LodConfig.CLIENT.fogDistance.get()) - { - case NEAR_AND_FAR: - fogSettings.near.distance = FogDistance.NEAR; - fogSettings.far.distance = FogDistance.FAR; - break; - - case NEAR: - fogSettings.near.distance = FogDistance.NEAR; - fogSettings.far.distance = FogDistance.NEAR; - break; - - case FAR: - fogSettings.near.distance = FogDistance.FAR; - fogSettings.far.distance = FogDistance.FAR; - break; - } - break; - - case FAST: - fogSettings.near.quality = FogQuality.FAST; - fogSettings.far.quality = FogQuality.FAST; - - // fast fog setting should only have one type of - // fog, since the LODs are separated into a near - // and far portion; and fast fog is rendered from the - // frustrum's perspective instead of the camera - switch(LodConfig.CLIENT.fogDistance.get()) - { - case NEAR_AND_FAR: - fogSettings.near.distance = FogDistance.NEAR; - fogSettings.far.distance = FogDistance.NEAR; - break; - - case NEAR: - fogSettings.near.distance = FogDistance.NEAR; - fogSettings.far.distance = FogDistance.NEAR; - break; - - case FAR: - fogSettings.near.distance = FogDistance.FAR; - fogSettings.far.distance = FogDistance.FAR; - break; - } - break; - - case OFF: - - fogSettings.near.quality = FogQuality.OFF; - fogSettings.far.quality = FogQuality.OFF; - break; - - } - - - return fogSettings; - } - - - - /** - * Get a HashSet of all ChunkPos within the normal render distance - * that should not be rendered. - */ - private HashSet getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos) - { - int chunkRenderDist = mc.options.renderDistance; - int blockRenderDist = chunkRenderDist * 16; - ChunkPos centerChunk = new ChunkPos(playerPos); - - // skip chunks that are already going to be rendered by Minecraft - HashSet posToSkip = getRenderedChunks(); - - - // go through each chunk within the normal view distance - for(int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++) - { - for(int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++) - { - LodChunk lod = lodDim.getLodFromCoordinates(x, z); - if (lod != null) - { - short lodHighestPoint = lod.calculateHighestPoint(); - - if (playerPos.getY() < lodHighestPoint) - { - // don't draw Lod's that are taller than the player - // to prevent LODs being drawn on top of the player - posToSkip.add(new ChunkPos(x, z)); - } - else if (blockRenderDist < Math.abs(playerPos.getY() - lodHighestPoint)) - { - // draw Lod's that are lower than the player's view range - posToSkip.remove(new ChunkPos(x, z)); - } - } - } - } - - return posToSkip; - } - - /** - * This method returns the ChunkPos of all chunks that Minecraft - * is going to render this frame.

- * - * Note: This isn't perfect. It will return some chunks that are outside - * the clipping plane. (For example, if you are high above the ground some chunks - * will be incorrectly added, even though they are outside render range). - */ - public static HashSet getRenderedChunks() - { - HashSet loadedPos = new HashSet<>(); - - Minecraft mc = Minecraft.getInstance(); - - // Wow those are some long names! - - // go through every RenderInfo to get the compiled chunks - for(WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : mc.levelRenderer.renderChunks) - { - if (!worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk().hasNoRenderableLayers()) - { - // add the ChunkPos for every empty compiled chunk - BlockPos bpos = worldrenderer$localrenderinformationcontainer.chunk.getOrigin(); - - loadedPos.add(new ChunkPos(bpos.getX() / 16, bpos.getZ() / 16)); - } - } - - return loadedPos; - } - - -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java index 0263eea72..18384b500 100644 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/util/LodUtil.java @@ -37,7 +37,7 @@ import net.minecraft.world.server.ServerWorld; * This class holds methods and constants that may be used in multiple places. * * @author James Seibel - * @version 8-10-2021 + * @version 8-11-2021 */ public class LodUtil { @@ -77,7 +77,7 @@ public class LodUtil public static final int REGION_WIDTH_IN_CHUNKS = 32; - /** If we ever have to use a heightmap for any reason, use this one. */ + /** If we ever need to use a heightmap for any reason, use this one. */ public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;