diff --git a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java index ed45d736a..840c9f82c 100644 --- a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java @@ -58,486 +58,481 @@ import net.minecraft.world.gen.Heightmap; */ public class LodNodeBuilder { - private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); - - public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH; - public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH; - public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; - - /** How wide LodDimensions should be in regions */ - public int defaultDimensionWidthInRegions = 5; - - public LodNodeBuilder() - { - - } - - public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world) - { - generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER); - } - - - public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode) - { - if (lodWorld == null || !lodWorld.getIsWorldLoaded()) - return; - - // don't try to create an LOD object - // if for some reason we aren't - // given a valid chunk object - if (chunk == null) - return; - - Thread thread = new Thread(() -> - { - try - { - DimensionType dim = world.dimensionType(); - - - LodDimension lodDim; - - if (lodWorld.getLodDimension(dim) == null) - { - lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions); - lodWorld.addLodDimension(lodDim); - } - else - { - lodDim = lodWorld.getLodDimension(dim); - } - - generateLodNodeFromChunk(lodDim ,chunk, new LodBuilderConfig(generationMode)); - } - catch (IllegalArgumentException | NullPointerException e) - { - e.printStackTrace(); - // if the world changes while LODs are being generated - // they will throw errors as they try to access things that no longer - // exist. - } - }); - lodGenThreadPool.execute(thread); - - return; - } - - /** - * Creates a LodChunk for a chunk in the given world. - * - * @throws IllegalArgumentException thrown if either the chunk or world is null. - */ - public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk) throws IllegalArgumentException - { - generateLodNodeFromChunk(lodDim, 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 void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config) - throws IllegalArgumentException - { - LodDetail detail = LodConfig.CLIENT.maxGenerationDetail.get(); - - if (chunk == null) - throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); - - - 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 = 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; - } - LevelPos levelPos = new LevelPos((byte)0 , - chunk.getPos().x*16 + startX, - chunk.getPos().z*16 + startZ); - LodDataPoint data = new LodDataPoint(height, depth, color); - lodDim.addData(levelPos.convert((byte) detail.detailLevel), - data, - config.distanceGenerationMode, - true, - false); - } - } - - // =====================// - // 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) - { - // I have no idea why I need to bit shift to the right, but - // if I don't the biomes don't show up correctly. - Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2, - z >> 2); - colorInt = getColorForBiome(x, z, biome); - } - else - { - - // the bit shift is equivalent to dividing by 4 - Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2, - z >> 2); - colorInt = getColorForBlock(x, z, blockState, biome); - } - - 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; - } - - /** - * Returns a color int for the given biome. - */ - private int getColorForBiome(int x, int z, Biome biome) - { - int colorInt = 0; - - switch (biome.getBiomeCategory()) - { - - case NETHER: - colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col; - break; - - case THEEND: - colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col; - break; - - case BEACH: - case DESERT: - colorInt = Blocks.SAND.defaultBlockState().materialColor.col; - break; - - case EXTREME_HILLS: - colorInt = Blocks.STONE.defaultMaterialColor().col; - break; - - case MUSHROOM: - colorInt = MaterialColor.COLOR_LIGHT_GRAY.col; - break; - - case ICY: - colorInt = Blocks.SNOW.defaultMaterialColor().col; - break; - - case MESA: - colorInt = Blocks.RED_SAND.defaultMaterialColor().col; - break; - - case OCEAN: - case RIVER: - colorInt = biome.getWaterColor(); - break; - - case NONE: - case FOREST: - case TAIGA: - case JUNGLE: - case PLAINS: - case SAVANNA: - case SWAMP: - default: - Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); - tmp = tmp.darker(); - colorInt = LodUtil.colorToInt(tmp); - break; - - } - - return colorInt; - } - - /** - * Is the layer between the given X, Z, and dataIndex values a valid LOD point? - */ - private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z) - { - if (chunkSections[sectionIndex] == null) - { - // this section doesn't have any blocks, - // it is not a valid section - return false; - } - else - { - if (chunkSections[sectionIndex].getBlockState(x, y, z) != null - && chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR) - { - return true; - } - } - - return false; - } + private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); + + public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH; + public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH; + public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; + + /** + * How wide LodDimensions should be in regions + */ + public int defaultDimensionWidthInRegions = 5; + + public LodNodeBuilder() + { + + } + + public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world) + { + generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER); + } + + + public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode) + { + if (lodWorld == null || !lodWorld.getIsWorldLoaded()) + return; + + // don't try to create an LOD object + // if for some reason we aren't + // given a valid chunk object + if (chunk == null) + return; + + Thread thread = new Thread(() -> + { + try + { + DimensionType dim = world.dimensionType(); + + + LodDimension lodDim; + + if (lodWorld.getLodDimension(dim) == null) + { + lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions); + lodWorld.addLodDimension(lodDim); + } else + { + lodDim = lodWorld.getLodDimension(dim); + } + + generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode)); + } catch (IllegalArgumentException | NullPointerException e) + { + e.printStackTrace(); + // if the world changes while LODs are being generated + // they will throw errors as they try to access things that no longer + // exist. + } + }); + lodGenThreadPool.execute(thread); + + return; + } + + /** + * Creates a LodChunk for a chunk in the given world. + * + * @throws IllegalArgumentException thrown if either the chunk or world is null. + */ + public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk) throws IllegalArgumentException + { + generateLodNodeFromChunk(lodDim, 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 void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config) + throws IllegalArgumentException + { + LodDetail detail = LodConfig.CLIENT.maxGenerationDetail.get(); + + if (chunk == null) + throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); + + + 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 = 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; + } + LevelPos levelPos = new LevelPos((byte) 0, + chunk.getPos().x * 16 + startX, + chunk.getPos().z * 16 + startZ); + LodDataPoint data = new LodDataPoint(height, depth, color); + lodDim.addData(levelPos.convert((byte) detail.detailLevel), + data, + config.distanceGenerationMode, + true, + false); + } + } + + // =====================// + // 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) + { + // I have no idea why I need to bit shift to the right, but + // if I don't the biomes don't show up correctly. + Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2, + z >> 2); + colorInt = getColorForBiome(x, z, biome); + } else + { + + // the bit shift is equivalent to dividing by 4 + Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2, + z >> 2); + colorInt = getColorForBlock(x, z, blockState, biome); + } + + 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; + } + + /** + * Returns a color int for the given biome. + */ + private int getColorForBiome(int x, int z, Biome biome) + { + int colorInt = 0; + + switch (biome.getBiomeCategory()) + { + + case NETHER: + colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col; + break; + + case THEEND: + colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col; + break; + + case BEACH: + case DESERT: + colorInt = Blocks.SAND.defaultBlockState().materialColor.col; + break; + + case EXTREME_HILLS: + colorInt = Blocks.STONE.defaultMaterialColor().col; + break; + + case MUSHROOM: + colorInt = MaterialColor.COLOR_LIGHT_GRAY.col; + break; + + case ICY: + colorInt = Blocks.SNOW.defaultMaterialColor().col; + break; + + case MESA: + colorInt = Blocks.RED_SAND.defaultMaterialColor().col; + break; + + case OCEAN: + case RIVER: + colorInt = biome.getWaterColor(); + break; + + case NONE: + case FOREST: + case TAIGA: + case JUNGLE: + case PLAINS: + case SAVANNA: + case SWAMP: + default: + Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); + tmp = tmp.darker(); + colorInt = LodUtil.colorToInt(tmp); + break; + + } + + return colorInt; + } + + /** + * Is the layer between the given X, Z, and dataIndex values a valid LOD point? + */ + private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z) + { + if (chunkSections[sectionIndex] == null) + { + // this section doesn't have any blocks, + // it is not a valid section + return false; + } else + { + if (chunkSections[sectionIndex].getBlockState(x, y, z) != null + && chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR) + { + return true; + } + } + + return false; + } } diff --git a/src/main/java/com/seibel/lod/enums/LodQuality.java b/src/main/java/com/seibel/lod/enums/LodQuality.java index 9bd33519a..cf64e314f 100644 --- a/src/main/java/com/seibel/lod/enums/LodQuality.java +++ b/src/main/java/com/seibel/lod/enums/LodQuality.java @@ -24,27 +24,29 @@ package com.seibel.lod.enums; * SURFACE
* FEATURES
* SERVER

- * + *

* In order of fastest to slowest. - * + * * @author James Seibel * @author Leonardo Amato * @version 8-7-2021 */ public enum LodQuality { - LOW(1), + LOW(1), - MEDIUM(2), + MEDIUM(2), - HIGH(3); + HIGH(3); - /** The higher the number the more complete the generation is. */ - public final int quality; + /** + * The higher the number the more complete the generation is. + */ + public final int quality; - LodQuality(int quality) - { - this.quality = quality; - } + LodQuality(int quality) + { + this.quality = quality; + } } diff --git a/src/main/java/com/seibel/lod/handlers/LodConfig.java b/src/main/java/com/seibel/lod/handlers/LodConfig.java index 2cfb13186..aa0d91cbe 100644 --- a/src/main/java/com/seibel/lod/handlers/LodConfig.java +++ b/src/main/java/com/seibel/lod/handlers/LodConfig.java @@ -40,238 +40,236 @@ import net.minecraftforge.fml.config.ModConfig; /** * This handles any configuration the user has access to. - * + * * @author James Seibel * @version 8-10-2021 */ @Mod.EventBusSubscriber public class LodConfig { - public static class Client - { - public ForgeConfigSpec.BooleanValue drawLODs; - - public ForgeConfigSpec.EnumValue fogDistance; - - public ForgeConfigSpec.EnumValue fogDrawOverride; - - public ForgeConfigSpec.BooleanValue debugMode; - - public ForgeConfigSpec.EnumValue lodTemplate; - - public ForgeConfigSpec.EnumValue maxDrawDetail; - - public ForgeConfigSpec.EnumValue maxGenerationDetail; - - public ForgeConfigSpec.EnumValue distanceGenerationMode; - - public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; - - public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads; - - public ForgeConfigSpec.EnumValue shadingMode; - - /** this is multiplied by the default view distance - * to determine how far out to generate/render LODs */ - public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier; + public static class Client + { + public ForgeConfigSpec.BooleanValue drawLODs; - public ForgeConfigSpec.IntValue lodQuality; + public ForgeConfigSpec.EnumValue fogDistance; - public ForgeConfigSpec.IntValue lodChunkRenderDistane; - - public ForgeConfigSpec.DoubleValue brightnessMultiplier; - - public ForgeConfigSpec.DoubleValue saturationMultiplier; - - - - - Client(ForgeConfigSpec.Builder builder) - { - builder.comment(ModInfo.MODNAME + " configuration settings").push("client"); - - drawLODs = builder - .comment("\n\n" - + " If false LODs will not be drawn, \n" - + " however they will still be generated \n" - + " and saved to file for later use. \n") - .define("drawLODs", true); - - fogDistance = builder - .comment("\n\n" - + " At what distance should Fog be drawn on the LODs? \n" - + " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n" - + " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n") - .defineEnum("fogDistance", FogDistance.NEAR_AND_FAR); - - fogDrawOverride = builder - .comment("\n\n" - + " When should fog be drawn? \n" - + " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n" - + " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n" - + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n" - + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n") - .defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING); - - debugMode = builder - .comment("\n\n" - + " If false the LODs will draw with their normal colors. \n" - + " If true LODs colors will be based on their detail \n" - + " level. \n") - .define("debugMode", false); - - lodTemplate = builder - .comment("\n\n" - + " How should the LODs be drawn? \n" - + " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n" - + " \n" - + " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n" - + " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n" - + " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n" - + " " + " unless a neighboring chunk is at a significantly different height. \n") - .defineEnum("lodTemplate", LodTemplate.CUBIC); - - maxDrawDetail = builder - .comment("\n\n" - + " What is the maximum detail level that LODs should be drawn at? \n" - + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" - + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" - + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" - + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" - + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") - .defineEnum("lodDrawQuality", LodDetail.DOUBLE); - - maxGenerationDetail = builder - .comment("\n\n" - + " What is the maximum detail level that LODs should be generated at? \n" - + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" - + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" - + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" - + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" - + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") - .defineEnum("lodGenerationQuality", LodDetail.DOUBLE); + public ForgeConfigSpec.EnumValue fogDrawOverride; + + public ForgeConfigSpec.BooleanValue debugMode; + + public ForgeConfigSpec.EnumValue lodTemplate; + + public ForgeConfigSpec.EnumValue maxDrawDetail; + + public ForgeConfigSpec.EnumValue maxGenerationDetail; + + public ForgeConfigSpec.EnumValue distanceGenerationMode; + + public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; + + public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads; + + public ForgeConfigSpec.EnumValue shadingMode; + + /** + * this is multiplied by the default view distance + * to determine how far out to generate/render LODs + */ + public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier; + + public ForgeConfigSpec.IntValue lodQuality; + + public ForgeConfigSpec.IntValue lodChunkRenderDistane; + + public ForgeConfigSpec.DoubleValue brightnessMultiplier; + + public ForgeConfigSpec.DoubleValue saturationMultiplier; - lodChunkRadiusMultiplier = builder - .comment("\n\n" - + " This is multiplied by the default view distance \n" - + " to determine how far out to generate/render LODs. \n" - + " A value of 2 means that there is 1 render distance worth \n" - + " of LODs in each cardinal direction. \n") - .defineInRange("lodChunkRadiusMultiplier", 8, 2, 16); + Client(ForgeConfigSpec.Builder builder) + { + builder.comment(ModInfo.MODNAME + " configuration settings").push("client"); + + drawLODs = builder + .comment("\n\n" + + " If false LODs will not be drawn, \n" + + " however they will still be generated \n" + + " and saved to file for later use. \n") + .define("drawLODs", true); + + fogDistance = builder + .comment("\n\n" + + " At what distance should Fog be drawn on the LODs? \n" + + " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n" + + " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n") + .defineEnum("fogDistance", FogDistance.NEAR_AND_FAR); + + fogDrawOverride = builder + .comment("\n\n" + + " When should fog be drawn? \n" + + " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n" + + " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n" + + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n" + + " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n") + .defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING); + + debugMode = builder + .comment("\n\n" + + " If false the LODs will draw with their normal colors. \n" + + " If true LODs colors will be based on their detail \n" + + " level. \n") + .define("debugMode", false); + + lodTemplate = builder + .comment("\n\n" + + " How should the LODs be drawn? \n" + + " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n" + + " \n" + + " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n" + + " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n" + + " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n" + + " " + " unless a neighboring chunk is at a significantly different height. \n") + .defineEnum("lodTemplate", LodTemplate.CUBIC); + + maxDrawDetail = builder + .comment("\n\n" + + " What is the maximum detail level that LODs should be drawn at? \n" + + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" + + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" + + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" + + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" + + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") + .defineEnum("lodDrawQuality", LodDetail.DOUBLE); + + maxGenerationDetail = builder + .comment("\n\n" + + " What is the maximum detail level that LODs should be generated at? \n" + + " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n" + + " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n" + + " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n" + + " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n" + + " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n") + .defineEnum("lodGenerationQuality", LodDetail.DOUBLE); + + + lodChunkRadiusMultiplier = builder + .comment("\n\n" + + " This is multiplied by the default view distance \n" + + " to determine how far out to generate/render LODs. \n" + + " A value of 2 means that there is 1 render distance worth \n" + + " of LODs in each cardinal direction. \n") + .defineInRange("lodChunkRadiusMultiplier", 8, 2, 16); + + distanceGenerationMode = builder + .comment("\n\n" + + " Note: The times listed here are the amount of time it took \n" + + " the developer's PC to generate 1 chunk, \n" + + " and are included so you can compare the \n" + + " different generation options. Your mileage may vary. \n" + + "\n" + + + " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n" + + " Only generate the biomes and use biome \n" + + " grass/foliage color, water color, or snow color \n" + + " to generate the color. \n" + + " Doesn't generate height, everything is shown at sea level. \n" + + " Multithreaded - Fastest (2-5 ms) \n" + + + "\n" + + " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n" + + " Same as BIOME_ONLY, except instead \n" + + " of always using sea level as the LOD height \n" + + " different biome types (mountain, ocean, forest, etc.) \n" + + " use predetermined heights to simulate having height data. \n" + + " Multithreaded - Fastest (2-5 ms) \n" + + + "\n" + + " " + DistanceGenerationMode.SURFACE.toString() + " \n" + + " Generate the world surface, \n" + + " this does NOT include caves, trees, \n" + + " or structures. \n" + + " Multithreaded - Faster (10-20 ms) \n" + + + "\n" + + " " + DistanceGenerationMode.FEATURES.toString() + " \n" + + " Generate everything except structures. \n" + + " WARNING: This may cause world generation bugs or instability! \n" + + " Multithreaded - Fast (15-20 ms) \n" + + + "\n" + + " " + DistanceGenerationMode.SERVER.toString() + " \n" + + " Ask the server to generate/load each chunk. \n" + + " This is the most compatible, but causes server/simulation lag. \n" + + " This will also show player made structures if you \n" + + " are adding the mod to a pre-existing world. \n" + + " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n") + .defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE); + + allowUnstableFeatureGeneration = builder + .comment("\n\n" + + " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n" + + " some features may not be thread safe, which could \n" + + " cause instability and crashes. \n" + + " By default (false) those features are skipped, \n" + + " improving stability, but decreasing how many features are \n" + + " actually generated. \n" + + " (for example: some tree generation is unstable, \n" + + " so some trees may not be generated.) \n" + + " By setting this to true, all features will be generated, \n" + + " but your game will be more unstable and crashes may occur. \n" + + " \n" + + " I would love to remove this option and always generate everything, \n" + + " but I'm not sure how to do that. \n" + + " If you are a Java wizard, check out the git issue here: \n" + + " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n") + .define("allowUnstableFeatureGeneration", false); + + numberOfWorldGenerationThreads = builder + .comment("\n\n" + + " This is how many threads are used when generating terrain. \n" + + " If you experience stuttering when generating terrain, decrease \n" + + " this number. If you want to increase LOD generation speed, \n" + + " increase the number. \n" + + " The max is the number of processors on your CPU. \n" + + "\n" + + " Requires a restart to take effect. \n" + ) + .defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors()); + + shadingMode = builder + .comment("\n\n" + + " What kind of shading should LODs have? \n" + + " " + ShadingMode.NONE.toString() + " \n" + + " " + "LODs will have the same lighting on every side. \n" + + " " + "Can make large similarly colored areas hard to differentiate. \n" + + " " + "Fastest" + + "/n" + + " " + ShadingMode.DARKEN_SIDES.toString() + " \n" + + " " + "LODs will have darker sides and bottoms to simulate top down lighting." + + " " + "Fastest /n") + .defineEnum("lightingMode", ShadingMode.DARKEN_SIDES); + + + brightnessMultiplier = builder + .comment("\n\n" + + " Change how bright LOD colors are. \n" + + " 0 = black \n" + + " 1 = normal color value \n" + + " 2 = washed out colors \n") + .defineInRange("brightnessMultiplier", 1.0, 0, 2); + + saturationMultiplier = builder + .comment("\n\n" + + " Change how saturated LOD colors are. \n" + + " 0 = black and white \n" + + " 1 = normal saturation \n" + + " 2 = very saturated \n") + .defineInRange("saturationMultiplier", 1.0, 0, 2); + + builder.pop(); + } + } - distanceGenerationMode = builder - .comment("\n\n" - + " Note: The times listed here are the amount of time it took \n" - + " the developer's PC to generate 1 chunk, \n" - + " and are included so you can compare the \n" - + " different generation options. Your mileage may vary. \n" - + "\n" - - + " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n" - + " Only generate the biomes and use biome \n" - + " grass/foliage color, water color, or snow color \n" - + " to generate the color. \n" - + " Doesn't generate height, everything is shown at sea level. \n" - + " Multithreaded - Fastest (2-5 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n" - + " Same as BIOME_ONLY, except instead \n" - + " of always using sea level as the LOD height \n" - + " different biome types (mountain, ocean, forest, etc.) \n" - + " use predetermined heights to simulate having height data. \n" - + " Multithreaded - Fastest (2-5 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.SURFACE.toString() + " \n" - + " Generate the world surface, \n" - + " this does NOT include caves, trees, \n" - + " or structures. \n" - + " Multithreaded - Faster (10-20 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.FEATURES.toString() + " \n" - + " Generate everything except structures. \n" - + " WARNING: This may cause world generation bugs or instability! \n" - + " Multithreaded - Fast (15-20 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.SERVER.toString() + " \n" - + " Ask the server to generate/load each chunk. \n" - + " This is the most compatible, but causes server/simulation lag. \n" - + " This will also show player made structures if you \n" - + " are adding the mod to a pre-existing world. \n" - + " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n") - .defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE); - - allowUnstableFeatureGeneration = builder - .comment("\n\n" - + " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n" - + " some features may not be thread safe, which could \n" - + " cause instability and crashes. \n" - + " By default (false) those features are skipped, \n" - + " improving stability, but decreasing how many features are \n" - + " actually generated. \n" - + " (for example: some tree generation is unstable, \n" - + " so some trees may not be generated.) \n" - + " By setting this to true, all features will be generated, \n" - + " but your game will be more unstable and crashes may occur. \n" - + " \n" - + " I would love to remove this option and always generate everything, \n" - + " but I'm not sure how to do that. \n" - + " If you are a Java wizard, check out the git issue here: \n" - + " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n") - .define("allowUnstableFeatureGeneration", false); - - numberOfWorldGenerationThreads = builder - .comment("\n\n" - + " This is how many threads are used when generating terrain. \n" - + " If you experience stuttering when generating terrain, decrease \n" - + " this number. If you want to increase LOD generation speed, \n" - + " increase the number. \n" - + " The max is the number of processors on your CPU. \n" - + "\n" - + " Requires a restart to take effect. \n" - ) - .defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors()); - - shadingMode = builder - .comment("\n\n" - + " What kind of shading should LODs have? \n" - + " " + ShadingMode.NONE.toString() + " \n" - + " " + "LODs will have the same lighting on every side. \n" - + " " + "Can make large similarly colored areas hard to differentiate. \n" - + " " + "Fastest" - + "/n" - + " " + ShadingMode.DARKEN_SIDES.toString() + " \n" - + " " + "LODs will have darker sides and bottoms to simulate top down lighting." - + " " + "Fastest /n") - .defineEnum("lightingMode", ShadingMode.DARKEN_SIDES); - - - - brightnessMultiplier = builder - .comment("\n\n" - + " Change how bright LOD colors are. \n" - + " 0 = black \n" - + " 1 = normal color value \n" - + " 2 = washed out colors \n") - .defineInRange("brightnessMultiplier", 1.0, 0, 2); - - saturationMultiplier = builder - .comment("\n\n" - + " Change how saturated LOD colors are. \n" - + " 0 = black and white \n" - + " 1 = normal saturation \n" - + " 2 = very saturated \n") - .defineInRange("saturationMultiplier", 1.0, 0, 2); - - builder.pop(); - } - } - - /** * {@link Path} to the configuration file of this mod @@ -281,11 +279,13 @@ public class LodConfig public static final ForgeConfigSpec clientSpec; public static final Client CLIENT; - static { + + static + { final Pair specPair = new ForgeConfigSpec.Builder().configure(Client::new); clientSpec = specPair.getRight(); CLIENT = specPair.getLeft(); - + // setup the config file CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH) .writingMode(WritingMode.REPLACE) @@ -294,21 +294,19 @@ public class LodConfig config.save(); clientSpec.setConfig(config); } - - + + @SubscribeEvent public static void onLoad(final ModConfig.Loading configEvent) { LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName()); } - + @SubscribeEvent public static void onFileChange(final ModConfig.Reloading configEvent) { LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!"); } - - - - + + } diff --git a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java index 088f307f3..b4ed0bb3c 100644 --- a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java @@ -41,7 +41,8 @@ import java.util.concurrent.Executors; * @author James Seibel * @version 8-14-2021 */ -public class LodDimensionFileHandler { +public class LodDimensionFileHandler +{ /** * This is what separates each piece of data */ @@ -89,7 +90,8 @@ public class LodDimensionFileHandler { private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); - public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension) { + public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension) + { if (newSaveFolder == null) throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to."); @@ -113,36 +115,43 @@ public class LodDimensionFileHandler { * Return the LodRegion region at the given coordinates. * (null if the file doesn't exist) */ - public LodRegion loadRegionFromFile(RegionPos regionPos) { + public LodRegion loadRegionFromFile(RegionPos regionPos) + { int regionX = regionPos.x; int regionZ = regionPos.z; String fileName = getFileNameAndPathForRegion(regionX, regionZ); File f = new File(fileName); - if (!f.exists()) { + if (!f.exists()) + { // there wasn't a file, don't // return anything return null; } String data = ""; - try { + try + { BufferedReader bufferedReader = new BufferedReader(new FileReader(f)); data = bufferedReader.readLine(); int fileVersion = -1; - if (data != null && !data.isEmpty()) { + if (data != null && !data.isEmpty()) + { // try to get the file version - try { + try + { fileVersion = Integer.parseInt(data.substring(data.indexOf(' ')).trim()); - } catch (NumberFormatException | StringIndexOutOfBoundsException e) { + } 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) { + if (fileVersion < LOD_SAVE_FILE_VERSION) + { // the file we are reading is an older version, // close the reader and delete the file. bufferedReader.close(); @@ -152,7 +161,8 @@ public class LodDimensionFileHandler { " File was been deleted."); return null; - } else if (fileVersion > LOD_SAVE_FILE_VERSION) { + } 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. @@ -163,7 +173,8 @@ public class LodDimensionFileHandler { return null; } - } else { + } else + { // there is no data in this file bufferedReader.close(); return null; @@ -174,7 +185,8 @@ public class LodDimensionFileHandler { data = bufferedReader.readLine(); bufferedReader.close(); - } catch (IOException e) { + } catch (IOException e) + { // the buffered reader encountered a // problem reading the file return null; @@ -190,15 +202,19 @@ public class LodDimensionFileHandler { /** * Save all dirty regions in this LodDimension to file. */ - public void saveDirtyRegionsToFileAsync() { + 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) { + 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) + { saveRegionToFile(loadedDimension.regions[i][j]); loadedDimension.isRegionDirty[i][j] = false; } @@ -214,7 +230,8 @@ public class LodDimensionFileHandler { * 2. This will save to the LodDimension that this * handler is associated with. */ - private void saveRegionToFile(LodRegion region) { + private void saveRegionToFile(LodRegion region) + { // convert to region coordinates int x = region.regionPosX; int z = region.regionPosZ; @@ -224,15 +241,18 @@ public class LodDimensionFileHandler { byte minDetailLevel = region.getMinDetailLevel(); File oldFile = new File(getFileNameAndPathForRegion(x, z)); - try { + try + { // make sure the file and folder exists - if (!oldFile.exists()) { + if (!oldFile.exists()) + { // the file doesn't exist, // create it and the folder if need be if (!oldFile.getParentFile().exists()) oldFile.getParentFile().mkdirs(); oldFile.createNewFile(); - } else { + } else + { // the file exists, make sure it // is the correct version. // (to make sure we don't overwrite a newer @@ -242,11 +262,14 @@ public class LodDimensionFileHandler { String s = br.readLine(); int fileVersion = LOD_SAVE_FILE_VERSION; - if (s != null && !s.isEmpty()) { + if (s != null && !s.isEmpty()) + { // try to get the file version - try { + try + { fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim()); - } catch (NumberFormatException | StringIndexOutOfBoundsException e) { + } catch (NumberFormatException | StringIndexOutOfBoundsException e) + { // this file doesn't have a correctly formated version // just overwrite the file } @@ -254,7 +277,8 @@ public class LodDimensionFileHandler { br.close(); // check if this file can be written to by the file handler - if (fileVersion <= LOD_SAVE_FILE_VERSION) { + if (fileVersion <= LOD_SAVE_FILE_VERSION) + { // we are good to continue and overwrite the old file } else //if(fileVersion > LOD_SAVE_FILE_VERSION) { @@ -279,7 +303,8 @@ public class LodDimensionFileHandler { // overwrite the old file with the new one Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); - } catch (Exception e) { + } catch (Exception e) + { ClientProxy.LOGGER.error("LOD file write error: "); e.printStackTrace(); } @@ -298,15 +323,18 @@ public class LodDimensionFileHandler { *

* example: "lod.0.0.txt" */ - private String getFileNameAndPathForRegion(int regionX, int regionZ) { - try { + private String getFileNameAndPathForRegion(int regionX, int regionZ) + { + try + { // saveFolder is something like // ".\Super Flat\DIM-1\data" // or // ".\Super Flat\data" return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar + FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION; - } catch (IOException e) { + } catch (IOException e) + { return null; } } diff --git a/src/main/java/com/seibel/lod/objects/LevelContainer.java b/src/main/java/com/seibel/lod/objects/LevelContainer.java index cfe5b7f44..b7f0612db 100644 --- a/src/main/java/com/seibel/lod/objects/LevelContainer.java +++ b/src/main/java/com/seibel/lod/objects/LevelContainer.java @@ -4,7 +4,8 @@ import java.io.Serializable; import com.seibel.lod.util.LodUtil; -public class LevelContainer implements Serializable { +public class LevelContainer implements Serializable +{ public static final char DATA_DELIMITER = ','; @@ -20,7 +21,8 @@ public class LevelContainer implements Serializable { public final boolean[][] dataExistence; - public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence){ + public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence) + { this.detailLevel = detailLevel; this.colors = colors; this.height = height; @@ -29,14 +31,15 @@ public class LevelContainer implements Serializable { this.dataExistence = dataExistence; } - public LevelContainer(String data){ + public LevelContainer(String data) + { int index = 0; int lastIndex = 0; index = data.indexOf(DATA_DELIMITER, 0); - this.detailLevel = (byte) Integer.parseInt(data.substring(0,index)); + this.detailLevel = (byte) Integer.parseInt(data.substring(0, index)); int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel); this.colors = new byte[size][size][3]; @@ -45,46 +48,51 @@ public class LevelContainer implements Serializable { this.generationType = new byte[size][size]; this.dataExistence = new boolean[size][size]; int intCol; - for (int x = 0; x < size; x++) { - for (int z = 0; z < size; z++) { + for (int x = 0; x < size; x++) + { + for (int z = 0; z < size; z++) + { lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - intCol = Integer.parseInt(data.substring(lastIndex+1,index), 16); + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + intCol = Integer.parseInt(data.substring(lastIndex + 1, index), 16); colors[x][z][0] = (byte) ((intCol >> 16) - 128); colors[x][z][1] = (byte) ((intCol >> 8) - 128); colors[x][z][2] = (byte) (intCol - 128); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - height[x][z] = Short.parseShort(data.substring(lastIndex+1,index), 16); + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + height[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - depth[x][z] = Short.parseShort(data.substring(lastIndex+1,index), 16); + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + depth[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - generationType[x][z] = Byte.parseByte(data.substring(lastIndex+1,index), 16); + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + generationType[x][z] = Byte.parseByte(data.substring(lastIndex + 1, index), 16); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex+1,index)); + index = data.indexOf(DATA_DELIMITER, lastIndex + 1); + dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex + 1, index)); } } } @Override - public String toString() { + public String toString() + { StringBuilder stringBuilder = new StringBuilder(); int combinedCol; int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel); stringBuilder.append(detailLevel); stringBuilder.append(DATA_DELIMITER); - for (int x = 0; x < size; x++) { - for (int z = 0; z < size; z++) { + for (int x = 0; x < size; x++) + { + for (int z = 0; z < size; z++) + { //Converting the colors to intColor and then to HEX - combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128) & 0xFF); + combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128) & 0xFF); stringBuilder.append(Integer.toHexString(combinedCol)); stringBuilder.append(DATA_DELIMITER); stringBuilder.append(Integer.toHexString(height[x][z])); diff --git a/src/main/java/com/seibel/lod/objects/LodDataPoint.java b/src/main/java/com/seibel/lod/objects/LodDataPoint.java index dcdff72fe..c554441fe 100644 --- a/src/main/java/com/seibel/lod/objects/LodDataPoint.java +++ b/src/main/java/com/seibel/lod/objects/LodDataPoint.java @@ -27,108 +27,120 @@ import com.seibel.lod.util.LodUtil; /** * This stores the height and color * for a specific area in a LodChunk. - * + * * @author James Seibel * @version 8-8-2021 */ public class LodDataPoint implements Serializable { - /** This is what separates each piece of data in the toData method */ - private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER; - - /** this is how many pieces of data are exported when toData is called */ - public static final int NUMBER_OF_DELIMITERS = 5; - - /** a empty data point that can be used for comparisons */ - public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint(); - - - /** highest point */ - public short height; - - /** lowest point */ - public short depth; - - /** The average color for the 6 cardinal directions */ - public Color color; - - - /** - * Creates an empty LodDataPoint - */ - public LodDataPoint() - { - height = -1; - depth = -1; - color = LodUtil.COLOR_INVISIBLE; - } - - - public LodDataPoint(short newHeight, short newDepth, Color newColor) - { - height = newHeight; - depth = newDepth; - color = newColor; - } - - public LodDataPoint(int newHeight, int newDepth, Color newColor) - { - height = (short) newHeight; - depth = (short) newDepth; - color = newColor; - } + /** + * This is what separates each piece of data in the toData method + */ + private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER; - @Override - public int hashCode() - { - return Objects.hash(this.height, this.depth, this.color); - } + /** + * this is how many pieces of data are exported when toData is called + */ + public static final int NUMBER_OF_DELIMITERS = 5; - public boolean equals(LodDataPoint other) - { - return (this.height == other.height - && this.depth == other.depth - && this.color == other.color); - } - - public boolean isEmpty() - { - return this.equals(EMPTY_DATA_POINT); - } + /** + * a empty data point that can be used for comparisons + */ + public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint(); - /** - * Outputs all data in a csv format - * with the given delimiter. - *
- * Exports data in the form: - *
- * height, depth, rgb color data - * - *
- * example output: - *
- * 4, 0, 255,255,255, - */ - public String toData() - { - String s = Short.toString(height) + DATA_DELIMITER; - - s += Short.toString(depth) + DATA_DELIMITER; - - s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER; - - return s; - } - - @Override - public String toString() - { - String s = Short.toString(height) + DATA_DELIMITER; - - s += Short.toString(depth) + DATA_DELIMITER; - - s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER; - - return s; - } + + /** + * highest point + */ + public short height; + + /** + * lowest point + */ + public short depth; + + /** + * The average color for the 6 cardinal directions + */ + public Color color; + + + /** + * Creates an empty LodDataPoint + */ + public LodDataPoint() + { + height = -1; + depth = -1; + color = LodUtil.COLOR_INVISIBLE; + } + + + public LodDataPoint(short newHeight, short newDepth, Color newColor) + { + height = newHeight; + depth = newDepth; + color = newColor; + } + + public LodDataPoint(int newHeight, int newDepth, Color newColor) + { + height = (short) newHeight; + depth = (short) newDepth; + color = newColor; + } + + @Override + public int hashCode() + { + return Objects.hash(this.height, this.depth, this.color); + } + + public boolean equals(LodDataPoint other) + { + return (this.height == other.height + && this.depth == other.depth + && this.color == other.color); + } + + public boolean isEmpty() + { + return this.equals(EMPTY_DATA_POINT); + } + + /** + * Outputs all data in a csv format + * with the given delimiter. + *
+ * Exports data in the form: + *
+ * height, depth, rgb color data + * + *
+ * example output: + *
+ * 4, 0, 255,255,255, + */ + public String toData() + { + String s = Short.toString(height) + DATA_DELIMITER; + + s += Short.toString(depth) + DATA_DELIMITER; + + s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER; + + return s; + } + + @Override + public String toString() + { + String s = Short.toString(height) + DATA_DELIMITER; + + s += Short.toString(depth) + DATA_DELIMITER; + + s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER; + + return s; + } } diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java index fab22071a..63e88a046 100644 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodDimension.java @@ -42,9 +42,13 @@ public class LodDimension public final DimensionType dimension; - /** measured in regions */ + /** + * measured in regions + */ private volatile int width; - /** measured in regions */ + /** + * measured in regions + */ private volatile int halfWidth; @@ -65,9 +69,9 @@ public class LodDimension { dimension = newDimension; width = newWidth; - halfWidth = (int)Math.floor(width / 2); + halfWidth = (int) Math.floor(width / 2); - if(newDimension != null && lodWorld != null) + if (newDimension != null && lodWorld != null) { try { @@ -84,8 +88,7 @@ public class LodDimension // the compiler from complaining ServerChunkProvider provider = serverWorld.getChunkSource(); saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod"); - } - else + } else { // connected to server @@ -94,8 +97,7 @@ public class LodDimension } fileHandler = new LodDimensionFileHandler(saveDir, this); - } - catch (IOException e) + } catch (IOException e) { // the file handler wasn't able to be created // we won't be able to read or write any files @@ -103,23 +105,22 @@ public class LodDimension } - 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++) + for (int i = 0; i < width; i++) + for (int j = 0; j < width; j++) isRegionDirty[i][j] = false; - center = new RegionPos(0,0); + center = new RegionPos(0, 0); } /** * Move the center of this LodDimension and move all owned * regions over by the given x and z offset.

- * + *

* Synchronized to prevent multiple moves happening on top of each other. */ public synchronized void move(RegionPos regionOffset) @@ -132,9 +133,9 @@ public class LodDimension // 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 x = 0; x < width; x++) { - for(int z = 0; z < width; z++) + for (int z = 0; z < width; z++) { regions[x][z] = null; } @@ -149,28 +150,27 @@ public class LodDimension // X - if(xOffset > 0) + 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 x = 0; x < width; x++) { - for(int z = 0; z < width; z++) + for (int z = 0; z < width; z++) { - if(x + xOffset < width) + if (x + xOffset < width) regions[x][z] = regions[x + xOffset][z]; else regions[x][z] = null; } } - } - else + } else { // move everything over to the right (as the center moves to the left) - for(int x = width - 1; x >= 0; x--) + for (int x = width - 1; x >= 0; x--) { - for(int z = 0; z < width; z++) + for (int z = 0; z < width; z++) { - if(x + xOffset >= 0) + if (x + xOffset >= 0) regions[x][z] = regions[x + xOffset][z]; else regions[x][z] = null; @@ -179,30 +179,28 @@ public class LodDimension } - // Z - if(zOffset > 0) + if (zOffset > 0) { // move everything up (as the center moves down) - for(int x = 0; x < width; x++) + for (int x = 0; x < width; x++) { - for(int z = 0; z < width; z++) + for (int z = 0; z < width; z++) { - if(z + zOffset < width) + if (z + zOffset < width) regions[x][z] = regions[x][z + zOffset]; else regions[x][z] = null; } } - } - else + } else { // move everything down (as the center moves up) - for(int x = 0; x < width; x++) + for (int x = 0; x < width; x++) { - for(int z = width - 1; z >= 0; z--) + for (int z = width - 1; z >= 0; z--) { - if(z + zOffset >= 0) + if (z + zOffset >= 0) regions[x][z] = regions[x][z + zOffset]; else regions[x][z] = null; @@ -211,17 +209,12 @@ public class LodDimension } - // update the new center center.x += xOffset; center.z += zOffset; } - - - - /** * Gets the region at the given X and Z *
@@ -244,7 +237,7 @@ public class LodDimension if (regions[xIndex][zIndex] == null) { /**TODO the value is currently 0 but should be determinated by the distance of the player)*/ - regions[xIndex][zIndex] = new LodRegion((byte) 0,regionPos); + regions[xIndex][zIndex] = new LodRegion((byte) 0, regionPos); } } @@ -270,7 +263,7 @@ public class LodDimension /** - *this method creates all null regions + * this method creates all null regions */ public void initializeNullRegions() { @@ -279,19 +272,19 @@ public class LodDimension RegionPos regionPos; LodRegion region; - for(int x = 0; x < regions.length; x++) + for (int x = 0; x < regions.length; x++) { - for(int z = 0; z < regions.length; z++) + for (int z = 0; z < regions.length; z++) { regionX = (x + center.x) - halfWidth; regionZ = (z + center.z) - halfWidth; - regionPos = new RegionPos(regionX,regionZ); + regionPos = new RegionPos(regionX, regionZ); region = getRegion(regionPos); if (region == null) { // if no region exists, create it - region = new LodRegion((byte) 0,regionPos); + region = new LodRegion((byte) 0, regionPos); addOrOverwriteRegion(region); } } @@ -318,10 +311,10 @@ public class LodDimension if (region == null) { // if no region exists, create it - region = new LodRegion((byte) 0,regionPos); + region = new LodRegion((byte) 0, regionPos); addOrOverwriteRegion(region); } - boolean nodeAdded = region.setData(levelPos,lodDataPoint,(byte) generationMode.complexity,true); + boolean nodeAdded = region.setData(levelPos, lodDataPoint, (byte) generationMode.complexity, true); // only save valid LODs to disk if (!dontSave && fileHandler != null) { @@ -331,8 +324,7 @@ public class LodDimension int xIndex = (regionPos.x - center.x) + halfWidth; int zIndex = (regionPos.z - center.z) + halfWidth; isRegionDirty[xIndex][zIndex] = true; - } - catch(ArrayIndexOutOfBoundsException e) + } catch (ArrayIndexOutOfBoundsException e) { // This method was probably called when the dimension was changing size. // Hopefully this shouldn't be an issue. @@ -370,7 +362,7 @@ public class LodDimension LodRegion region = getRegion(levelPos.getRegionPos()); - if(region == null) + if (region == null) { return null; } @@ -385,7 +377,7 @@ public class LodDimension { LodRegion region = getRegion(LodUtil.convertGenericPosToRegionPos(chunkPos.x, chunkPos.z, LodUtil.CHUNK_DETAIL_LEVEL)); - if(region == null) + if (region == null) { return false; } @@ -401,7 +393,7 @@ public class LodDimension { LodRegion region = getRegion(levelPos.getRegionPos()); - if(region == null) + if (region == null) { return false; } @@ -416,7 +408,7 @@ public class LodDimension { LodRegion region = getRegion(levelPos.getRegionPos()); - if(region == null) + if (region == null) { return false; } @@ -431,7 +423,7 @@ public class LodDimension { LodRegion region = getRegion(levelPos.getRegionPos()); - if(region == null) + if (region == null) { return DistanceGenerationMode.NONE; } @@ -473,11 +465,6 @@ public class LodDimension } - - - - - public int getCenterX() { return center.x; @@ -491,7 +478,7 @@ public class LodDimension /** * TODO Double check that this method works as expected - * + *

* Returns how many non-null LodChunks * are stored in this LodDimension. */ @@ -511,8 +498,7 @@ public class LodDimension // source to make sure it is in sync with region // and isRegionDirty return regions.length; - } - else + } else { return width; } @@ -521,14 +507,14 @@ public class LodDimension public void setRegionWidth(int newWidth) { width = newWidth; - halfWidth = (int)Math.floor(width / 2); + halfWidth = (int) Math.floor(width / 2); regions = new LodRegion[width][width]; isRegionDirty = new boolean[width][width]; // populate isRegionDirty - for(int i = 0; i < width; i++) - for(int j = 0; j < width; j++) + for (int i = 0; i < width; i++) + for (int j = 0; j < width; j++) isRegionDirty[i][j] = false; } diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java index 0f8c01a36..8b827685c 100644 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ b/src/main/java/com/seibel/lod/objects/LodRegion.java @@ -16,7 +16,8 @@ import java.io.Serializable; * 0 for x, 1 for y, 2 for z in 3D */ -public class LodRegion implements Serializable { +public class LodRegion implements Serializable +{ //x coord, private byte minDetailLevel; private static final byte POSSIBLE_LOD = 10; @@ -41,8 +42,8 @@ public class LodRegion implements Serializable { public final int regionPosX; public final int regionPosZ; - public LodRegion(LevelContainer levelContainer, RegionPos regionPos) { - /**TODO there is some error here in the update*/ + public LodRegion(LevelContainer levelContainer, RegionPos regionPos) + { this.regionPosX = regionPos.x; this.regionPosZ = regionPos.z; this.minDetailLevel = levelContainer.detailLevel; @@ -63,7 +64,8 @@ public class LodRegion implements Serializable { dataExistence[minDetailLevel] = levelContainer.dataExistence; //Initialize all the different matrices - for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) { + for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) + { int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod); colors[lod] = new byte[size][size][3]; height[lod] = new short[size][size]; @@ -73,10 +75,13 @@ public class LodRegion implements Serializable { } int sizeDiff; LevelPos levelPos; - for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) { - sizeDiff = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL - tempLod); - for (int x = 0; x < sizeDiff; x++) { - for (int z = 0; z < sizeDiff; z++) { + for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) + { + sizeDiff = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod); + for (int x = 0; x < sizeDiff; x++) + { + for (int z = 0; z < sizeDiff; z++) + { levelPos = new LevelPos(tempLod, x, z); update(levelPos); } @@ -84,7 +89,8 @@ public class LodRegion implements Serializable { } } - public LodRegion(byte minDetailLevel, RegionPos regionPos) { + public LodRegion(byte minDetailLevel, RegionPos regionPos) + { this.minDetailLevel = minDetailLevel; this.regionPosX = regionPos.x; this.regionPosZ = regionPos.z; @@ -100,7 +106,8 @@ public class LodRegion implements Serializable { //Initialize all the different matrices - for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) { + for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) + { int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod); colors[lod] = new byte[size][size][3]; height[lod] = new short[size][size]; @@ -120,12 +127,14 @@ public class LodRegion implements Serializable { * @param update * @return */ - public boolean setData(LevelPos levelPos, LodDataPoint dataPoint, byte generationType, boolean update) { + public boolean setData(LevelPos levelPos, LodDataPoint dataPoint, byte generationType, boolean update) + { levelPos = levelPos.regionModule(); - if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])) { + if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])) + { //update the number of node present - //if (this.generationType[lod][posX][posZ] == 0) numberOfPoints++; + if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++; //add the node data this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint.color.getRed() - 128); @@ -138,20 +147,24 @@ public class LodRegion implements Serializable { //update could be stopped and a single big update could be done at the end LevelPos tempLevelPos = levelPos; - if (update) { - for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) { + if (update) + { + for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) + { tempLevelPos = tempLevelPos.convert(tempLod); update(tempLevelPos); } } return true; - } else { + } else + { return false; } } - public LodDataPoint getData(ChunkPos chunkPos) { + public LodDataPoint getData(ChunkPos chunkPos) + { return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z)); } @@ -161,7 +174,8 @@ public class LodRegion implements Serializable { * @param lod * @return the data at the relative pos and level */ - public LodDataPoint getData(byte lod, BlockPos blockPos) { + public LodDataPoint getData(byte lod, BlockPos blockPos) + { int posX = Math.floorMod(blockPos.getX(), (int) Math.pow(2, lod)); int posZ = Math.floorMod(blockPos.getZ(), (int) Math.pow(2, lod)); return getData(new LevelPos(lod, posX, posZ)); @@ -173,7 +187,8 @@ public class LodRegion implements Serializable { * @param levelPos * @return the data at the relative pos and level */ - public LodDataPoint getData(LevelPos levelPos) { + public LodDataPoint getData(LevelPos levelPos) + { levelPos = levelPos.regionModule(); return new LodDataPoint( height[levelPos.detailLevel][levelPos.posX][levelPos.posZ], @@ -188,37 +203,29 @@ public class LodRegion implements Serializable { /** * @return */ - /* - public List getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generetion, byte detailLevel) { - if(detailLevel < minDetailLevel) detailLevel = minDetailLevel; - LevelPos levelPos + /* + public List getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generetion, byte detailLevel) + { + if (detailLevel < minDetailLevel) detailLevel = minDetailLevel; + LevelPos levelPos; int size; int width; int posX; int posZ; int distance; - for(int tempLod = detailLevel; tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++){ - size = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL-tempLod); - width = (int) Math.pow(2,tempLod); - for(int x = 0; x < size; x++){ - for(int z = 0; z < size; z++){ - posX = regionPosX * 512 + x * width + width/2; - posZ = regionPosZ * 512 + z * width + width/2; - distance = (int) Math.sqrt(Math.pow(playerPosX - posX, 2) + Math.pow(playerPosZ - posZ, 2)) - if(distance >= start && distance <= end){ - - } - } - } - } + size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod); + width = (int) Math.pow(2, tempLod); + posX = regionPosX * 512 + width / 2; + posZ = regionPosZ * 512 + width / 2; + distance = (int) Math.sqrt(Math.pow(playerPosX - posX, 2) + Math.pow(playerPosZ - posZ, 2)) } - */ + */ /** * @return - */ - /* - public List getDataToRender(int playerPosX, int playerPosZ, int start, int end, byte detailLevel) { + *//* + public List getDataToRender(int playerPosX, int playerPosZ, int start, int end, byte detailLevel) + { if(detailLevel < minDetailLevel) detailLevel = minDetailLevel; int size; int width; @@ -234,13 +241,14 @@ public class LodRegion implements Serializable { } } } - */ + */ /**TODO a method to update a whole area, to be used as a single big update*/ /** * @param levelPos */ - private void updateArea(LevelPos levelPos) { + private void updateArea(LevelPos levelPos) + { /* LevelPos tempLevelPos = levelPos; int sizeDiff; @@ -259,13 +267,25 @@ public class LodRegion implements Serializable { } + int sizeDiff; + LevelPos levelPos; + for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) { + sizeDiff = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL - tempLod); + for (int x = 0; x < sizeDiff; x++) { + for (int z = 0; z < sizeDiff; z++) { + levelPos = new LevelPos(tempLod, x, z); + update(levelPos); + } + } + } */ } /** * @param levelPos */ - private void update(LevelPos levelPos) { + private void update(LevelPos levelPos) + { levelPos = levelPos.regionModule(); boolean[][] children = getChildren(levelPos); @@ -282,13 +302,16 @@ public class LodRegion implements Serializable { int newPosZ; byte newLod; LevelPos childPos; - for (int x = 0; x <= 1; x++) { - for (int z = 0; z <= 1; z++) { + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { newPosX = 2 * levelPos.posX + x; newPosZ = 2 * levelPos.posZ + z; newLod = (byte) (levelPos.detailLevel - 1); childPos = new LevelPos(newLod, newPosX, newPosZ); - if (hasDataBeenGenerated(childPos)) { + if (hasDataBeenGenerated(childPos)) + { numberOfChildren++; tempRed += colors[newLod][newPosX][newPosZ][0]; @@ -301,7 +324,8 @@ public class LodRegion implements Serializable { } } - if (numberOfChildren > 0) { + if (numberOfChildren > 0) + { colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (tempRed / numberOfChildren); colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (tempGreen / numberOfChildren); colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (tempBlue / numberOfChildren); @@ -316,15 +340,19 @@ public class LodRegion implements Serializable { * @param levelPos * @return */ - private boolean[][] getChildren(LevelPos levelPos) { + private boolean[][] getChildren(LevelPos levelPos) + { levelPos = levelPos.regionModule(); boolean[][] children = new boolean[2][2]; int numberOfChild = 0; - if (minDetailLevel == levelPos.detailLevel) { + if (minDetailLevel == levelPos.detailLevel) + { return children; } - for (int x = 0; x <= 1; x++) { - for (int z = 0; z <= 1; z++) { + for (int x = 0; x <= 1; x++) + { + for (int z = 0; z <= 1; z++) + { children[x][z] = (dataExistence[levelPos.detailLevel - 1][2 * levelPos.posX + x][2 * levelPos.posZ + z]); } } @@ -335,7 +363,8 @@ public class LodRegion implements Serializable { * @param chunkPos * @return */ - public boolean doesDataExist(ChunkPos chunkPos) { + public boolean doesDataExist(ChunkPos chunkPos) + { return doesDataExist(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z)); } @@ -343,7 +372,8 @@ public class LodRegion implements Serializable { * @param levelPos * @return */ - public boolean doesDataExist(LevelPos levelPos) { + public boolean doesDataExist(LevelPos levelPos) + { levelPos = levelPos.regionModule(); return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]; } @@ -352,10 +382,12 @@ public class LodRegion implements Serializable { * @param levelPos * @return */ - public DistanceGenerationMode getGenerationMode(LevelPos levelPos) { + public DistanceGenerationMode getGenerationMode(LevelPos levelPos) + { levelPos = levelPos.regionModule(); DistanceGenerationMode generationMode = DistanceGenerationMode.NONE; - switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) { + switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) + { case 0: generationMode = DistanceGenerationMode.NONE; break; @@ -386,12 +418,14 @@ public class LodRegion implements Serializable { * @param levelPos * @return */ - public boolean hasDataBeenGenerated(LevelPos levelPos) { + public boolean hasDataBeenGenerated(LevelPos levelPos) + { levelPos = levelPos.regionModule(); return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0); } - public byte getMinDetailLevel() { + public byte getMinDetailLevel() + { return minDetailLevel; } @@ -401,15 +435,18 @@ public class LodRegion implements Serializable { * @param lod * @return */ - public LevelContainer getLevel(byte lod) { + public LevelContainer getLevel(byte lod) + { return new LevelContainer(lod, colors[lod], height[lod], depth[lod], generationType[lod], dataExistence[lod]); } /** * @param levelContainer */ - public void addLevel(LevelContainer levelContainer) { - if (levelContainer.detailLevel < minDetailLevel - 1) { + public void addLevel(LevelContainer levelContainer) + { + if (levelContainer.detailLevel < minDetailLevel - 1) + { throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 "); } if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel; @@ -424,8 +461,10 @@ public class LodRegion implements Serializable { /** * @param lod */ - public void removeDetailLevel(byte lod) { - for (byte tempLod = 0; tempLod <= lod; tempLod++) { + public void removeDetailLevel(byte lod) + { + for (byte tempLod = 0; tempLod <= lod; tempLod++) + { colors[tempLod] = new byte[0][0][0]; height[tempLod] = new short[0][0]; depth[tempLod] = new short[0][0]; @@ -434,7 +473,8 @@ public class LodRegion implements Serializable { } } - public String toString(){ + public String toString() + { return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString(); } } diff --git a/src/main/java/com/seibel/lod/objects/LodWorld.java b/src/main/java/com/seibel/lod/objects/LodWorld.java index 422718c2e..b8d77ebd2 100644 --- a/src/main/java/com/seibel/lod/objects/LodWorld.java +++ b/src/main/java/com/seibel/lod/objects/LodWorld.java @@ -26,133 +26,134 @@ import net.minecraft.world.DimensionType; /** * This stores all LODs for a given world. - * + * * @author James Seibel * @author Leonardo Amato * @version 8-17-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 LodQuadTreeWorld with the given newWorldName.
- * This should be done whenever loading a new world. - * - * @param newWorldName name of the world - */ - public void selectWorld(String newWorldName) - { - if (newWorldName.isEmpty()) - { - deselectWorld(); - return; - } - - if (worldName.equals(newWorldName)) - // don't recreate everything if we - // didn't actually change worlds - return; - - 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; - } - - - /** - * Adds newStorage to this world, if a LodQuadTreeDimension - * already exists for the given dimension it is replaced. - */ - public void addLodDimension(LodDimension newStorage) - { - if (lodDimensions == null) - return; - - lodDimensions.put(newStorage.dimension, newStorage); - } - - /** - * Returns null if no LodQuadTreeDimension exists for the given dimension - */ - public LodDimension getLodDimension(DimensionType dimension) - { - if (lodDimensions == null) - return null; - - return lodDimensions.get(dimension); - } - - /** - * Resizes the max width in regions that each LodDimension - * should use. - */ - public void resizeDimensionRegionWidth(int newWidth) - { - if (lodDimensions == null) - return; - - saveAllDimensions(); - - for (DimensionType key : lodDimensions.keySet()) - lodDimensions.get(key).setRegionWidth(newWidth); - } - - /** - * Requests all dimensions save any dirty regions they may have. - */ - public void saveAllDimensions() - { - if (lodDimensions == null) - return; - - ClientProxy.LOGGER.info("Saving LODs"); - - for (DimensionType key : lodDimensions.keySet()) - lodDimensions.get(key).saveDirtyRegionsToFileAsync(); - } - - - public boolean getIsWorldLoaded() - { - return isWorldLoaded; - } - - public String getWorldName() - { - return worldName; - } - - @Override - public String toString() - { - return "World name: " + worldName; - } + 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 LodQuadTreeWorld with the given newWorldName.
+ * This should be done whenever loading a new world. + * + * @param newWorldName name of the world + */ + public void selectWorld(String newWorldName) + { + if (newWorldName.isEmpty()) + { + deselectWorld(); + return; + } + + if (worldName.equals(newWorldName)) + // don't recreate everything if we + // didn't actually change worlds + return; + + 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; + } + + + /** + * Adds newStorage to this world, if a LodQuadTreeDimension + * already exists for the given dimension it is replaced. + */ + public void addLodDimension(LodDimension newStorage) + { + if (lodDimensions == null) + return; + + lodDimensions.put(newStorage.dimension, newStorage); + } + + /** + * Returns null if no LodQuadTreeDimension exists for the given dimension + */ + public LodDimension getLodDimension(DimensionType dimension) + { + if (lodDimensions == null) + return null; + + return lodDimensions.get(dimension); + } + + /** + * Resizes the max width in regions that each LodDimension + * should use. + */ + public void resizeDimensionRegionWidth(int newWidth) + { + if (lodDimensions == null) + return; + + saveAllDimensions(); + + for (DimensionType key : lodDimensions.keySet()) + lodDimensions.get(key).setRegionWidth(newWidth); + } + + /** + * Requests all dimensions save any dirty regions they may have. + */ + public void saveAllDimensions() + { + if (lodDimensions == null) + return; + + ClientProxy.LOGGER.info("Saving LODs"); + + for (DimensionType key : lodDimensions.keySet()) + lodDimensions.get(key).saveDirtyRegionsToFileAsync(); + } + + + 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 3284d1c6e..8f592024a 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -46,241 +46,240 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; /** * This handles all events sent to the client, * and is the starting point for most of this program. - * + * * @author James_Seibel * @version 8-17-2021 */ public class ClientProxy { - public static final Logger LOGGER = LogManager.getLogger("LOD"); - - private static LodWorld lodWorld = new LodWorld(); - private static LodNodeBuilder lodNodeBuilder = new LodNodeBuilder(); - private static LodNodeBufferBuilder lodBufferBuilder = new LodNodeBufferBuilder(lodNodeBuilder); - private static LodNodeRenderer renderer = new LodNodeRenderer(lodBufferBuilder); - - private boolean configOverrideReminderPrinted = false; - - Minecraft mc = Minecraft.getInstance(); - - /** This is used to determine if the LODs should be regenerated */ - public static int previousChunkRenderDistance = 0; - /** This is used to determine if the LODs should be regenerated */ - public static int previousLodMultiplierDistance = 0; - - - public ClientProxy() - { - - } - - - - - //==============// - // render event // - //==============// - - /** - * Do any setup that is required to draw LODs - * and then tell the LodRenderer to draw. - */ - public void renderLods(float partialTicks) - { - if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) - return; + public static final Logger LOGGER = LogManager.getLogger("LOD"); + + private static LodWorld lodWorld = new LodWorld(); + private static LodNodeBuilder lodNodeBuilder = new LodNodeBuilder(); + private static LodNodeBufferBuilder lodBufferBuilder = new LodNodeBufferBuilder(lodNodeBuilder); + private static LodNodeRenderer renderer = new LodNodeRenderer(lodBufferBuilder); + + private boolean configOverrideReminderPrinted = false; + + Minecraft mc = Minecraft.getInstance(); + + /** + * This is used to determine if the LODs should be regenerated + */ + public static int previousChunkRenderDistance = 0; + /** + * This is used to determine if the LODs should be regenerated + */ + public static int previousLodMultiplierDistance = 0; - viewDistanceChangedEvent(); + public ClientProxy() + { - LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); - if (lodDim == null) - return; - - playerMoveEvent(lodDim); - - - // comment out when creating a release - applyConfigOverrides(); - - - - // Note to self: - // if "unspecified" shows up in the pie chart, it is - // possibly because the amount of time between sections - // is too small for the profiler to measure - IProfiler profiler = mc.getProfiler(); - profiler.pop(); // get out of "terrain" - profiler.push("LOD"); - - renderer.drawLODs(lodDim, partialTicks, mc.getProfiler()); - - profiler.pop(); // end LOD - profiler.push("terrain"); // restart "terrain" - - - // these can't be set until after the buffers are built (in renderer.drawLODs) - // otherwise the buffers may be set to the wrong size, or not changed at all - previousChunkRenderDistance = mc.options.renderDistance; - previousLodMultiplierDistance = LodConfig.CLIENT.lodChunkRadiusMultiplier.get(); - } + } - - private void applyConfigOverrides() - { - // remind the developer(s). that config override is active - if (!configOverrideReminderPrinted) - { - mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID()); - configOverrideReminderPrinted = true; - } - + //==============// + // render event // + //==============// + + /** + * Do any setup that is required to draw LODs + * and then tell the LodRenderer to draw. + */ + public void renderLods(float partialTicks) + { + if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) + return; + + + viewDistanceChangedEvent(); + + LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); + if (lodDim == null) + return; + + playerMoveEvent(lodDim); + + + // comment out when creating a release + applyConfigOverrides(); + + + // Note to self: + // if "unspecified" shows up in the pie chart, it is + // possibly because the amount of time between sections + // is too small for the profiler to measure + IProfiler profiler = mc.getProfiler(); + profiler.pop(); // get out of "terrain" + profiler.push("LOD"); + + renderer.drawLODs(lodDim, partialTicks, mc.getProfiler()); + + profiler.pop(); // end LOD + profiler.push("terrain"); // restart "terrain" + + + // these can't be set until after the buffers are built (in renderer.drawLODs) + // otherwise the buffers may be set to the wrong size, or not changed at all + previousChunkRenderDistance = mc.options.renderDistance; + previousLodMultiplierDistance = LodConfig.CLIENT.lodChunkRadiusMultiplier.get(); + } + + + private void applyConfigOverrides() + { + // remind the developer(s). that config override is active + if (!configOverrideReminderPrinted) + { + mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID()); + configOverrideReminderPrinted = true; + } + // LodConfig.CLIENT.drawLODs.set(true); // LodConfig.CLIENT.debugMode.set(false); - - LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL); - LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL); - - LodConfig.CLIENT.lodChunkRadiusMultiplier.set(16); - LodConfig.CLIENT.fogDistance.set(FogDistance.FAR); - LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY); - LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES); + + LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL); + LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL); + + LodConfig.CLIENT.lodChunkRadiusMultiplier.set(16); + LodConfig.CLIENT.fogDistance.set(FogDistance.FAR); + LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY); + LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES); // LodConfig.CLIENT.brightnessMultiplier.set(1.0); // LodConfig.CLIENT.saturationMultiplier.set(1.0); - - LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); - LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); - - LodConfig.CLIENT.numberOfWorldGenerationThreads.set(2); - } - - - //==============// - // forge events // - //==============// - - @SubscribeEvent - public void chunkLoadEvent(ChunkEvent.Load event) - { - lodNodeBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER); - } - - @SubscribeEvent - public void worldSaveEvent(WorldEvent.Save event) - { - if (lodWorld != null) - lodWorld.saveAllDimensions(); - } - - @SubscribeEvent - public void worldLoadEvent(WorldEvent.Load event) - { - // the player just loaded a new world/dimension - lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld())); - // make sure the correct LODs are being rendered - // (if this isn't done the previous world's LODs may be drawn) - renderer.regenerateLODsNextFrame(); - } - - @SubscribeEvent - public void worldUnloadEvent(WorldEvent.Unload event) - { - // the player just unloaded a world/dimension - - if(mc.getConnection().getLevel() == null) - { - // if this isn't done unfinished tasks may be left in the queue - // preventing new LodChunks form being generated - LodNodeGenWorker.restartExecuterService(); - - lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0); - // the player has disconnected from a server - lodWorld.deselectWorld(); - } - } - - - @SubscribeEvent - public void blockChangeEvent(BlockEvent event) - { - if (event.getClass() == BlockEvent.BreakEvent.class || - event.getClass() == BlockEvent.EntityPlaceEvent.class || - event.getClass() == BlockEvent.EntityMultiPlaceEvent.class || - event.getClass() == BlockEvent.FluidPlaceBlockEvent.class || - event.getClass() == BlockEvent.PortalSpawnEvent.class) - { - // recreate the LOD where the blocks were changed - lodNodeBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld()); - } - } - - - - //==================// - // frame LOD events // - //==================// - - /** - * Re-centers the given LodDimension if it needs to be. - */ - private void playerMoveEvent(LodDimension lodDim) - { - // make sure the dimension is centered - RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition()); - RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ()); - if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0) - { - lodWorld.saveAllDimensions(); - lodDim.move(worldRegionOffset); - - //LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ()); - } - } + + LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES); + LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); + + LodConfig.CLIENT.numberOfWorldGenerationThreads.set(10); + } - /** - * Re-sizes all LodDimensions if they needs to be. - */ - private void viewDistanceChangedEvent() - { - // calculate how wide the dimension(s) should be in regions - //int chunksWide = (mc.options.renderDistance * 2) * LodConfig.CLIENT.lodChunkRadiusMultiplier.get(); - int chunksWide = 8 * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + 1; - int newWidth = (int)Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS); - newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions - - // do the dimensions need to change in size? - if (lodNodeBuilder.defaultDimensionWidthInRegions != newWidth) - { - // TODO make this async - - // update the dimensions to fit the new width - lodWorld.resizeDimensionRegionWidth(newWidth); - lodNodeBuilder.defaultDimensionWidthInRegions = newWidth; - renderer.setupBuffers(newWidth); - - //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); - } - } + //==============// + // forge events // + //==============// - - //================// - // public getters // - //================// - - public static LodWorld getLodWorld() - { - return lodWorld; - } - - public static LodNodeBuilder getLodBuilder() - { - return lodNodeBuilder; - } - - public static LodNodeRenderer getRenderer() - { - return renderer; - } + @SubscribeEvent + public void chunkLoadEvent(ChunkEvent.Load event) + { + lodNodeBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER); + } + + @SubscribeEvent + public void worldSaveEvent(WorldEvent.Save event) + { + if (lodWorld != null) + lodWorld.saveAllDimensions(); + } + + @SubscribeEvent + public void worldLoadEvent(WorldEvent.Load event) + { + // the player just loaded a new world/dimension + lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld())); + // make sure the correct LODs are being rendered + // (if this isn't done the previous world's LODs may be drawn) + renderer.regenerateLODsNextFrame(); + } + + @SubscribeEvent + public void worldUnloadEvent(WorldEvent.Unload event) + { + // the player just unloaded a world/dimension + + if (mc.getConnection().getLevel() == null) + { + // if this isn't done unfinished tasks may be left in the queue + // preventing new LodChunks form being generated + LodNodeGenWorker.restartExecuterService(); + + lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0); + // the player has disconnected from a server + lodWorld.deselectWorld(); + } + } + + + @SubscribeEvent + public void blockChangeEvent(BlockEvent event) + { + if (event.getClass() == BlockEvent.BreakEvent.class || + event.getClass() == BlockEvent.EntityPlaceEvent.class || + event.getClass() == BlockEvent.EntityMultiPlaceEvent.class || + event.getClass() == BlockEvent.FluidPlaceBlockEvent.class || + event.getClass() == BlockEvent.PortalSpawnEvent.class) + { + // recreate the LOD where the blocks were changed + lodNodeBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld()); + } + } + + + //==================// + // frame LOD events // + //==================// + + /** + * Re-centers the given LodDimension if it needs to be. + */ + private void playerMoveEvent(LodDimension lodDim) + { + // make sure the dimension is centered + RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition()); + RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ()); + if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0) + { + lodWorld.saveAllDimensions(); + lodDim.move(worldRegionOffset); + + //LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ()); + } + } + + + /** + * Re-sizes all LodDimensions if they needs to be. + */ + private void viewDistanceChangedEvent() + { + // calculate how wide the dimension(s) should be in regions + //int chunksWide = (mc.options.renderDistance * 2) * LodConfig.CLIENT.lodChunkRadiusMultiplier.get(); + int chunksWide = 8 * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + 1; + int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS); + newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions + + // do the dimensions need to change in size? + if (lodNodeBuilder.defaultDimensionWidthInRegions != newWidth) + { + // TODO make this async + + // update the dimensions to fit the new width + lodWorld.resizeDimensionRegionWidth(newWidth); + lodNodeBuilder.defaultDimensionWidthInRegions = newWidth; + renderer.setupBuffers(newWidth); + + //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); + } + } + + + //================// + // public getters // + //================// + + public static LodWorld getLodWorld() + { + return lodWorld; + } + + public static LodNodeBuilder getLodBuilder() + { + return lodNodeBuilder; + } + + public static LodNodeRenderer getRenderer() + { + return renderer; + } } diff --git a/src/main/java/com/seibel/lod/render/LodNodeRenderer.java b/src/main/java/com/seibel/lod/render/LodNodeRenderer.java index 955e05269..b3d786824 100644 --- a/src/main/java/com/seibel/lod/render/LodNodeRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodNodeRenderer.java @@ -62,786 +62,771 @@ import net.minecraft.util.math.vector.Vector3f; /** * This is where all the magic happens.
- * This is where LODs are draw to the world. - * + * This is where LODs are draw to the world. + * * @author James Seibel * @version 8-17-2021 */ public class LodNodeRenderer { - /** 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; + /** + * 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; + /** + * 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; + /** + * 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; - /** 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 int farPlaneBlockDistance; - private ReflectionHandler reflectionHandler; + private Minecraft mc; + private GameRenderer gameRender; + private IProfiler profiler; + private int farPlaneBlockDistance; + private ReflectionHandler reflectionHandler; - /** This is used to generate the buildable buffers */ - private LodNodeBufferBuilder lodNodeBufferBuilder; - - /** Each VertexBuffer represents 1 region */ - private VertexBuffer[][] vbos; - public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR; + /** + * This is used to generate the buildable buffers + */ + private LodNodeBufferBuilder lodNodeBufferBuilder; + + /** + * Each VertexBuffer represents 1 region + */ + private VertexBuffer[][] vbos; + public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR; - - /** 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; + /** + * 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; + /** + * 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<>(); + /** + * This HashSet contains every chunk that Vanilla Minecraft + * is going to render + */ + public HashSet vanillaRenderedChunks = new HashSet<>(); + public LodNodeRenderer(LodNodeBufferBuilder newLodNodeBufferBuilder) + { + mc = Minecraft.getInstance(); + gameRender = mc.gameRenderer; - public LodNodeRenderer(LodNodeBufferBuilder newLodNodeBufferBuilder) - { - mc = Minecraft.getInstance(); - gameRender = mc.gameRenderer; - - reflectionHandler = new ReflectionHandler(); - lodNodeBufferBuilder = newLodNodeBufferBuilder; - } - - - /** - * Besides drawing the LODs this method also starts - * the async process of generating the Buffers that hold those LODs. - * - * @param newDim 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 (!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 || - ClientProxy.previousChunkRenderDistance != mc.options.renderDistance || - ClientProxy.previousLodMultiplierDistance != LodConfig.CLIENT.lodChunkRadiusMultiplier.get() || - 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 - //farPlaneBlockDistance = mc.options.renderDistance * LodUtil.CHUNK_WIDTH; - farPlaneBlockDistance = 8 * LodUtil.CHUNK_WIDTH; - - // set how how far the LODs will go - //int numbChunksWide = mc.options.renderDistance * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get(); - int numbChunksWide = 8 * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + 1; - - // 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 && !lodNodeBufferBuilder.generatingBuffers && !lodNodeBufferBuilder.newBuffersAvaliable()) - { - // generate the LODs on a separate thread to prevent stuttering or freezing - lodNodeBufferBuilder.generateLodBuffersAsync(this, lodDim, player.blockPosition(), 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 (lodNodeBufferBuilder.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]; + reflectionHandler = new ReflectionHandler(); + lodNodeBufferBuilder = newLodNodeBufferBuilder; + } + + + /** + * Besides drawing the LODs this method also starts + * the async process of generating the Buffers that hold those LODs. + * + * @param newDim 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 (!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 || + ClientProxy.previousChunkRenderDistance != mc.options.renderDistance || + ClientProxy.previousLodMultiplierDistance != LodConfig.CLIENT.lodChunkRadiusMultiplier.get() || + 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 + //farPlaneBlockDistance = mc.options.renderDistance * LodUtil.CHUNK_WIDTH; + farPlaneBlockDistance = 8 * LodUtil.CHUNK_WIDTH; + + // set how how far the LODs will go + //int numbChunksWide = mc.options.renderDistance * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get(); + int numbChunksWide = 8 * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + 1; + + // 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 && !lodNodeBufferBuilder.generatingBuffers && !lodNodeBufferBuilder.newBuffersAvaliable()) + { + // generate the LODs on a separate thread to prevent stuttering or freezing + lodNodeBufferBuilder.generateLodBuffersAsync(this, lodDim, player.blockPosition(), 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 (lodNodeBufferBuilder.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"); - - if (vbos != null) - { - for (int i = 0; i < vbos.length; i++) - { - for (int j = 0; j < vbos.length; j++) - { - setupFog(fogSettings.near.distance, fogSettings.near.quality); - sendLodsToGpuAndDraw(vbos[i][j], 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(); - - // 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; - + 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"); + + if (vbos != null) + { + for (int i = 0; i < vbos.length; i++) + { + for (int j = 0; j < vbos.length; j++) + { + setupFog(fogSettings.near.distance, fogSettings.near.quality); + sendLodsToGpuAndDraw(vbos[i][j], 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(); + + // 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(farPlaneBlockDistance * 1.75f); - RenderSystem.fogStart(farPlaneBlockDistance * 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(farPlaneBlockDistance * 1.5f); - RenderSystem.fogStart(farPlaneBlockDistance * 2.0f); - } - } - else if(fogDistance == FogDistance.FAR) - { - if (fogQuality == FogQuality.FANCY) - { - RenderSystem.fogStart(farPlaneBlockDistance * 0.85f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - } - else if(fogQuality == FogQuality.FAST) - { - RenderSystem.fogStart(farPlaneBlockDistance * 0.5f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - RenderSystem.fogEnd(farPlaneBlockDistance * 0.75f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); - } - } - - - GL11.glEnable(GL11.GL_FOG); - RenderSystem.enableFog(); + } + + + //=================// + // 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(farPlaneBlockDistance * 1.75f); + RenderSystem.fogStart(farPlaneBlockDistance * 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(farPlaneBlockDistance * 1.5f); + RenderSystem.fogStart(farPlaneBlockDistance * 2.0f); + } + } else if (fogDistance == FogDistance.FAR) + { + if (fogQuality == FogQuality.FANCY) + { + RenderSystem.fogStart(farPlaneBlockDistance * 0.85f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); + RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); + } else if (fogQuality == FogQuality.FAST) + { + RenderSystem.fogStart(farPlaneBlockDistance * 0.5f * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()); + RenderSystem.fogEnd(farPlaneBlockDistance * 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(); + /** + * 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(); + + + // 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(); + } + + + /** + * 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.farPlaneBlockDistance * 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 + + 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.farPlaneBlockDistance * 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. - */ - public void setupBuffers(int numbRegionsWide) - { - // calculate the max amount of memory needed (in bytes) - int bufferMemory = RenderUtil.getBufferMemoryForRegion(); - - // if the required memory is greater than the - // MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier - // to fit. - if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY) - { - ClientProxy.LOGGER.warn("setupBuffers tried to allocate too much memory for the BufferBuilders." - + " It tried to allocate \"" + bufferMemory + "\" bytes, when \"" + MAX_ALOCATEABLE_DIRECT_MEMORY + "\" is the max."); - } - - lodNodeBufferBuilder.setupBuffers(numbRegionsWide, 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 Vertex Buffers with the newly - * created buffers from the lodBufferBuilder. - */ - private void swapBuffers() - { - // replace the drawable buffers with - // the newly created buffers from the lodBufferBuilder - vbos = lodNodeBufferBuilder.getVertexBuffers(); - } - - - 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++) - { + 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. + */ + public void setupBuffers(int numbRegionsWide) + { + // calculate the max amount of memory needed (in bytes) + int bufferMemory = RenderUtil.getBufferMemoryForRegion(); + + // if the required memory is greater than the + // MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier + // to fit. + if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY) + { + ClientProxy.LOGGER.warn("setupBuffers tried to allocate too much memory for the BufferBuilders." + + " It tried to allocate \"" + bufferMemory + "\" bytes, when \"" + MAX_ALOCATEABLE_DIRECT_MEMORY + "\" is the max."); + } + + lodNodeBufferBuilder.setupBuffers(numbRegionsWide, 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 Vertex Buffers with the newly + * created buffers from the lodBufferBuilder. + */ + private void swapBuffers() + { + // replace the drawable buffers with + // the newly created buffers from the lodBufferBuilder + vbos = lodNodeBufferBuilder.getVertexBuffers(); + } + + + 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++) + { + + LevelPos levelPos = new LevelPos((byte) 4, x, z); + if (lodDim.doesDataExist(levelPos)) + { + short lodHighestPoint = lodDim.getData(levelPos).height; + + 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; + } - LevelPos levelPos = new LevelPos((byte) 4, x, z); - if (lodDim.doesDataExist(levelPos)) - { - short lodHighestPoint = lodDim.getData(levelPos).height; - 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