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