diff --git a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java index 3373df446..1c1421c4a 100644 --- a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java @@ -18,10 +18,14 @@ package com.seibel.lod.builders; import java.awt.Color; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.seibel.lod.enums.DistanceGenerationMode; +import com.seibel.lod.enums.LodDetail; +import com.seibel.lod.handlers.LodConfig; import com.seibel.lod.objects.LodChunk; import com.seibel.lod.objects.LodDataPoint; import com.seibel.lod.objects.LodQuadTreeDimension; @@ -46,9 +50,9 @@ import net.minecraft.world.gen.Heightmap; /** * This object is in charge of creating Lod - * related objects. + * related objects. * (specifically: Lod World, Dimension, Region, and Chunk objects) - * + * * @author James Seibel * @version 8-9-2021 */ @@ -63,15 +67,13 @@ public class LodNodeBuilder { * Default size of any LOD regions we use */ public int regionWidth = 5; - - public LodNodeBuilder() - { - + + public LodNodeBuilder() { + } - public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world) - { + public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world) { if (lodWorld == null || !lodWorld.getIsWorldLoaded()) return; @@ -86,24 +88,20 @@ public class LodNodeBuilder { try { DimensionType dim = world.dimensionType(); - LodQuadTreeNode node = generateLodNodeFromChunk(chunk); + List nodeList = generateLodNodeFromChunk(chunk); LodQuadTreeDimension lodDim; - if (lodWorld.getLodDimension(dim) == null) - { + if (lodWorld.getLodDimension(dim) == null) { lodDim = new LodQuadTreeDimension(dim, lodWorld, regionWidth); lodWorld.addLodDimension(lodDim); - } - else - { + } else { lodDim = lodWorld.getLodDimension(dim); } - - lodDim.addNode(node); - } - catch (IllegalArgumentException | NullPointerException e) - { + for(LodQuadTreeNode node : nodeList) { + lodDim.addNode(node); + } + } 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 @@ -121,46 +119,51 @@ public class LodNodeBuilder { * * @throws IllegalArgumentException thrown if either the chunk or world is null. */ - public LodQuadTreeNode generateLodNodeFromChunk(IChunk chunk) throws IllegalArgumentException - { + public List generateLodNodeFromChunk(IChunk chunk) throws IllegalArgumentException { return generateLodNodeFromChunk(chunk, new LodBuilderConfig()); } /** * Creates a LodChunk for a chunk in the given world. * - * @throws IllegalArgumentException thrown if either the chunk or world is null. * @return + * @throws IllegalArgumentException thrown if either the chunk or world is null. */ - public LodQuadTreeNode generateLodNodeFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException - { + public List generateLodNodeFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException { + LodDetail detail = LodDetail.QUAD; + List lodNodeList = new ArrayList<>(); if (chunk == null) - throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); - - // TODO startX/Z and endX/Z are relative coordinates - // getMin/Max appear to return world block coordinates - int startX = 0; //chunk.getPos().getMinBlockX(); - int startZ = 0; //chunk.getPos().getMinBlockZ(); - int endX = 15; //chunk.getPos().getMaxBlockX(); - int endZ = 15; //chunk.getPos().getMaxBlockZ(); - - 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(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ); - depth = 0; - } - - return new LodQuadTreeNode(LodQuadTreeNode.CHUNK_LEVEL, chunk.getPos().x, chunk.getPos().z, new LodDataPoint(height, depth, color), DistanceGenerationMode.SERVER); + throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); + for (int x = 0; x < detail.dataPointLengthCount; x++) { + for (int z = 0; z < detail.dataPointLengthCount; z++) { + int startX = detail.startX[x]; + int startZ = detail.startZ[z]; + int endX = detail.endX[x]; + int endZ = detail.endZ[z]; + + // TODO startX/Z and endX/Z are relative coordinates + // getMin/Max appear to return world block coordinates + + 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(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ); + depth = 0; + } + lodNodeList.add(new LodQuadTreeNode((byte) detail.detailLevel, + LodUtil.convertLevelPos(chunk.getPos().getMinBlockX() + startX,0,detail.detailLevel), + LodUtil.convertLevelPos(chunk.getPos().getMinBlockZ() + startZ,0,detail.detailLevel) , + new LodDataPoint(height, depth, color), DistanceGenerationMode.SERVER)); + } + } + + return lodNodeList; } @@ -262,9 +265,8 @@ public class LodNodeBuilder { /** * Find the highest point from the Top */ - private short determineHeightPoint(Heightmap heightmap, - int startX, int startZ, int endX, int endZ) - { + 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++) { @@ -278,221 +280,195 @@ public class LodNodeBuilder { } /** - * 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); - } + * 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; - } - + 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; - } - - - + 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? @@ -500,32 +476,20 @@ public class LodNodeBuilder { private boolean isLayerValidLodPoint( ChunkSection[] chunkSections, int sectionIndex, int y, - int x, int z) - { - if (chunkSections[sectionIndex] == null) - { + int x, int z) { + if (chunkSections[sectionIndex] == null) { // this section doesn't have any blocks, // it is not a valid section return false; - } - else - { + } else { if (chunkSections[sectionIndex].getBlockState(x, y, z) != null && - chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR) - { + chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR) { return true; } } return false; } - - - - - - - - - + + } diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java index 7626308a0..1b5984f7d 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java @@ -335,19 +335,21 @@ public class LodNodeGenWorker implements IWorker chunk.setHeightmap(LodChunk.DEFAULT_HEIGHTMAP, heightmap.getRawData()); - LodQuadTreeNode lod; + List nodeList; if (!inTheEnd) { - lod = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig(true, true, false)); + nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig(true, true, false)); } else { // if we are in the end, don't generate any chunks. // Since we don't know where the islands are, everything // generates the same and it looks really bad. - lod = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig(true, true, false)); + nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig(true, true, false)); + } + for(LodQuadTreeNode node : nodeList) { + lodDim.addNode(node); } - lodDim.addNode(lod); } @@ -378,8 +380,10 @@ public class LodNodeGenWorker implements IWorker IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC); snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null); - LodQuadTreeNode lod = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig()); - lodDim.addNode(lod); + List nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig()); + for(LodQuadTreeNode node : nodeList) { + lodDim.addNode(node); + } } @@ -505,8 +509,11 @@ public class LodNodeGenWorker implements IWorker // generate a Lod like normal - LodQuadTreeNode lod = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig()); - lodDim.addNode(lod); + List nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig()); + + for(LodQuadTreeNode node : nodeList) { + lodDim.addNode(node); + } } diff --git a/src/main/java/com/seibel/lod/enums/LodDetail.java b/src/main/java/com/seibel/lod/enums/LodDetail.java index e94ad72a0..de0c77852 100644 --- a/src/main/java/com/seibel/lod/enums/LodDetail.java +++ b/src/main/java/com/seibel/lod/enums/LodDetail.java @@ -28,19 +28,19 @@ import com.seibel.lod.objects.LodDataPoint; public enum LodDetail { /** render 1 LOD for each chunk */ - SINGLE(1, 4), + SINGLE(1, 4, 4), /** render 4 LODs for each chunk */ - DOUBLE(2, 2), + DOUBLE(2, 2, 3), /** render 16 LODs for each chunk */ - QUAD(4, 1), + QUAD(4, 1, 2), /** render 64 LODs for each chunk */ - HALF(8, 0), + HALF(8, 0, 1), /** render 256 LODs for each chunk */ - FULL(16, 0); + FULL(16, 0 , 0); /** How many DataPoints should @@ -48,6 +48,7 @@ public enum LodDetail public final int dataPointLengthCount; /** How wide each LOD DataPoint is */ public final int dataPointWidth; + public final int detailLevel; /* Start/End X/Z give the block positions * for each individual dataPoint in a LodChunk */ @@ -68,8 +69,9 @@ public enum LodDetail public final int offset; - private LodDetail(int newLengthCount, int newOffset) + private LodDetail(int newLengthCount, int newOffset, int newDetail) { + detailLevel = newDetail; dataPointLengthCount = newLengthCount; dataPointWidth = 16 / dataPointLengthCount;