diff --git a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java index 91418f7e7..ad5edaf29 100644 --- a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java @@ -25,7 +25,6 @@ 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; @@ -49,447 +48,494 @@ import net.minecraft.world.chunk.IChunk; import net.minecraft.world.gen.Heightmap; /** - * This object is in charge of creating Lod - * related objects. - * (specifically: Lod World, Dimension, Region, and Chunk objects) + * This object is in charge of creating Lod related objects. (specifically: Lod + * World, Dimension, Region, and Chunk objects) * * @author James Seibel - * @version 8-9-2021 + * @version 8-10-2021 */ -public class LodNodeBuilder { - private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(); - - public static final int CHUNK_DATA_WIDTH = LodQuadTreeNode.CHUNK_WIDTH; - public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH; - public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; - - /** - * Default size of any LOD regions we use - */ - public int regionWidth = 5; - - - public LodNodeBuilder() { - - } - - public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world) { - if (lodWorld == null || !lodWorld.getIsWorldLoaded()) - return; - - // don't try to create an LOD object - // if for some reason we aren't - // given a valid chunk object - if (chunk == null) - return; - - Thread thread = new Thread(() -> - { - try { - DimensionType dim = world.dimensionType(); - - List nodeList = generateLodNodeFromChunk(chunk); - - LodQuadTreeDimension lodDim; - - if (lodWorld.getLodDimension(dim) == null) { - lodDim = new LodQuadTreeDimension(dim, lodWorld, regionWidth); - lodWorld.addLodDimension(lodDim); - } else { - lodDim = lodWorld.getLodDimension(dim); - } - 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 - // 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 List generateLodNodeFromChunk(IChunk chunk) throws IllegalArgumentException { - return generateLodNodeFromChunk(chunk, new LodBuilderConfig()); - } - - /** - * Creates a LodChunk for a chunk in the given world. - * - * @return - * @throws IllegalArgumentException thrown if either the chunk or world is null. - */ - 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"); - 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]; - - // 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; - } - - - //=====================// - // 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; - } - - +public class LodNodeBuilder +{ + private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(); + + public static final int CHUNK_DATA_WIDTH = LodQuadTreeNode.CHUNK_WIDTH; + public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH; + public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; + + /** + * Default size of any LOD regions we use + */ + public int regionWidth = 5; + + public LodNodeBuilder() + { + + } + + public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world) + { + if (lodWorld == null || !lodWorld.getIsWorldLoaded()) + return; + + // don't try to create an LOD object + // if for some reason we aren't + // given a valid chunk object + if (chunk == null) + return; + + Thread thread = new Thread(() -> + { + try + { + DimensionType dim = world.dimensionType(); + + List nodeList = generateLodNodeFromChunk(chunk); + + LodQuadTreeDimension lodDim; + + if (lodWorld.getLodDimension(dim) == null) + { + lodDim = new LodQuadTreeDimension(dim, lodWorld, regionWidth); + lodWorld.addLodDimension(lodDim); + } + else + { + lodDim = lodWorld.getLodDimension(dim); + } + 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 + // 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 List generateLodNodeFromChunk(IChunk chunk) throws IllegalArgumentException + { + return generateLodNodeFromChunk(chunk, new LodBuilderConfig()); + } + + /** + * Creates a LodChunk for a chunk in the given world. + * + * @return + * @throws IllegalArgumentException thrown if either the chunk or world is null. + */ + public List generateLodNodeFromChunk(IChunk chunk, LodBuilderConfig config) + throws IllegalArgumentException + { + LodDetail detail = LodDetail.FULL; + List lodNodeList = new ArrayList<>(); + 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]; + + // 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; + } + + // =====================// + // 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; + } + }