Close #28 (Optimize Distance Lod Generation)
This commit is contained in:
@@ -4,7 +4,6 @@ import java.awt.Color;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.enums.ColorDirection;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
@@ -15,10 +14,12 @@ import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.color.BlockColors;
|
||||
import net.minecraft.block.FlowingFluidBlock;
|
||||
import net.minecraft.block.GrassBlock;
|
||||
import net.minecraft.block.LeavesBlock;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.ChunkSection;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
@@ -29,12 +30,10 @@ import net.minecraft.world.gen.Heightmap;
|
||||
* (specifically: Lod World, Dimension, Region, and Chunk objects)
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-19-2021
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodBuilder
|
||||
{
|
||||
private static final Color INVISIBLE = new Color(0,0,0,0);
|
||||
|
||||
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor();
|
||||
|
||||
/** Default size of any LOD regions we use */
|
||||
@@ -52,18 +51,15 @@ public class LodBuilder
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void generateLodChunkAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
generateLodChunkAsync(chunk, new LodBuilderConfig(), lodWorld, world);
|
||||
}
|
||||
|
||||
public void generateLodChunkAsync(IChunk chunk, LodBuilderConfig config, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
// is this chunk from the same world as the lodWorld?
|
||||
if (!lodWorld.getWorldName().equals(LodUtil.getWorldID(world)))
|
||||
// we are not in the same world anymore
|
||||
// don't add this LOD
|
||||
return;
|
||||
|
||||
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
@@ -77,7 +73,7 @@ public class LodBuilder
|
||||
{
|
||||
DimensionType dim = world.getDimensionType();
|
||||
|
||||
LodChunk lod = generateLodFromChunk(chunk);
|
||||
LodChunk lod = generateLodFromChunk(chunk, config);
|
||||
|
||||
LodDimension lodDim;
|
||||
|
||||
@@ -117,7 +113,7 @@ public class LodBuilder
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk) throws IllegalArgumentException
|
||||
{
|
||||
return generateLodFromChunk(chunk, false);
|
||||
return generateLodFromChunk(chunk, new LodBuilderConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +122,7 @@ public class LodBuilder
|
||||
* @throws IllegalArgumentException
|
||||
* thrown if either the chunk or world is null.
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk, boolean useHeightmap) throws IllegalArgumentException
|
||||
public LodChunk generateLodFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException
|
||||
{
|
||||
if(chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
@@ -142,20 +138,22 @@ public class LodBuilder
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
Color color;
|
||||
|
||||
Color color = generateLodColorForAreaInDirection(chunk, ColorDirection.TOP, startX, startZ, endX, endZ);
|
||||
color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
|
||||
|
||||
|
||||
short height;
|
||||
short depth;
|
||||
|
||||
if (!useHeightmap)
|
||||
if (!config.useHeightmap)
|
||||
{
|
||||
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = determineHeightPoint(chunk.getHeightmap(Heightmap.Type.WORLD_SURFACE), startX, startZ, endX, endZ);
|
||||
height = determineHeightPoint(chunk.getHeightmap(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ);
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
@@ -304,38 +302,156 @@ public class LodBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the color for the given chunk in the given ColorDirection.
|
||||
* NOTE: only vertical is currently implemented for area,
|
||||
* the horizontal colors will always be the same regardless of the area.
|
||||
* Generate the color for the given chunk using biome
|
||||
* water color, foliage color, and grass color.
|
||||
*
|
||||
* @param config_useSolidBlocksInColorGen <br>
|
||||
* If true we look down from the top of the <br>
|
||||
* chunk until we find a non-invisible block, and then use <br>
|
||||
* its color. If false we generate the color immediately for <br>
|
||||
* each x and z.
|
||||
* @param config_useBiomeColors <br>
|
||||
* If true use biome foliage, water, and grass colors, <br>
|
||||
* otherwise use the
|
||||
*/
|
||||
private Color generateLodColorForAreaInDirection(IChunk chunk, ColorDirection colorDir, int startX, int startZ, int endX, int endZ)
|
||||
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
BlockColors bc = mc.getBlockColors();
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
switch (colorDir)
|
||||
int numbOfBlocks = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
case TOP:
|
||||
return generateLodColorVerticalOverArea(chunk, colorDir, bc, startX, startZ, endX, endZ);
|
||||
case BOTTOM:
|
||||
return generateLodColorVerticalOverArea(chunk, colorDir, bc, startX, startZ, endX, endZ);
|
||||
|
||||
case NORTH:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
case SOUTH:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
|
||||
case EAST:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
case WEST:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
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.colorValue;
|
||||
}
|
||||
|
||||
if(colorInt == 0 && config.useSolidBlocksInColorGen)
|
||||
{
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.useBiomeColors)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z);
|
||||
|
||||
if (biome.getCategory() == Biome.Category.OCEAN ||
|
||||
biome.getCategory() == Biome.Category.RIVER)
|
||||
{
|
||||
colorInt = biome.getWaterColor();
|
||||
}
|
||||
else if (biome.getCategory() == Biome.Category.EXTREME_HILLS)
|
||||
{
|
||||
colorInt = Blocks.STONE.getMaterialColor().colorValue;
|
||||
}
|
||||
else if (biome.getCategory() == Biome.Category.ICY)
|
||||
{
|
||||
colorInt = LodUtil.colorToInt(Color.WHITE);
|
||||
}
|
||||
else if (biome.getCategory() == Biome.Category.THEEND)
|
||||
{
|
||||
colorInt = Blocks.END_STONE.getDefaultState().materialColor.colorValue;
|
||||
}
|
||||
else if (config.useSolidBlocksInColorGen)
|
||||
{
|
||||
colorInt = getColorForBlock(x, z, blockState, biome);
|
||||
}
|
||||
else
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return INVISIBLE;
|
||||
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;
|
||||
|
||||
if (blockState == Blocks.AIR.getDefaultState())
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
|
||||
else if (blockState.getBlock() instanceof LeavesBlock)
|
||||
{
|
||||
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
|
||||
colorInt = LodUtil.colorToInt(leafColor);
|
||||
}
|
||||
else if (blockState.getBlock() instanceof GrassBlock)
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
else if (blockState.getBlock() instanceof FlowingFluidBlock)
|
||||
{
|
||||
colorInt = biome.getWaterColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
colorInt = blockState.materialColor.colorValue;
|
||||
}
|
||||
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -366,311 +482,7 @@ public class LodBuilder
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the color of the top or bottom of the given chunk.
|
||||
*
|
||||
* @throws IllegalArgumentException if given a ColorDirection other than TOP or BOTTOM
|
||||
*/
|
||||
private Color generateLodColorVerticalOverArea(
|
||||
IChunk chunk, ColorDirection colorDir, BlockColors bc,
|
||||
int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
if(colorDir != ColorDirection.TOP && colorDir != ColorDirection.BOTTOM)
|
||||
{
|
||||
throw new IllegalArgumentException("generateLodColorVertical only accepts the ColorDirection TOP or BOTTOM");
|
||||
}
|
||||
|
||||
|
||||
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
boolean goTopDown = (colorDir == ColorDirection.TOP);
|
||||
|
||||
|
||||
// either go top down or bottom up
|
||||
int dataStart = goTopDown? chunkSections.length - 1 : 0;
|
||||
int dataMax = chunkSections.length;
|
||||
int dataMin = 0;
|
||||
int dataIncrement = goTopDown? -1 : 1;
|
||||
|
||||
int topStart = goTopDown? CHUNK_SECTION_HEIGHT - 1 : 0;
|
||||
int topMax = CHUNK_SECTION_HEIGHT;
|
||||
int topMin = 0;
|
||||
int topIncrement = goTopDown? -1 : 1;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
boolean foundBlock = false;
|
||||
|
||||
for(int i = dataStart; !foundBlock && i >= dataMin && i < dataMax; i += dataIncrement)
|
||||
{
|
||||
if(!foundBlock && chunkSections[i] != null)
|
||||
{
|
||||
for(int y = topStart; !foundBlock && y >= topMin && y < topMax; y += topIncrement)
|
||||
{
|
||||
int ci;
|
||||
ci = chunkSections[i].getBlockState(x, y, z).materialColor.colorValue;
|
||||
|
||||
if(ci == 0)
|
||||
{
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
Color c = LodUtil.intToColor(ci);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* unused variation that can be used with only the heightmap,
|
||||
* although it just returns the foliage color, so it shouldn't
|
||||
* be used normally.
|
||||
|
||||
Heightmap heightmap = chunk.getHeightmap(Heightmap.Type.WORLD_SURFACE_WG);
|
||||
|
||||
int numbOfBlocks = CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
for(int x = 0; x < CHUNK_DATA_WIDTH; x++)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x,z, heightmap.getHeight(x, z));
|
||||
Color c = intToColor(biome.getFoliageColor());
|
||||
|
||||
red += c.getRed();
|
||||
green += c.getGreen();
|
||||
blue += c.getBlue();
|
||||
}
|
||||
}
|
||||
|
||||
red /= numbOfBlocks;
|
||||
green /= numbOfBlocks;
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Generates the color for the sides of the given chunk.
|
||||
*/
|
||||
private Color generateLodColorHorizontal(
|
||||
IChunk chunk, ColorDirection colorDir, BlockColors bc)
|
||||
{
|
||||
if(colorDir != ColorDirection.NORTH && colorDir != ColorDirection.SOUTH && colorDir != ColorDirection.EAST && colorDir != ColorDirection.WEST)
|
||||
{
|
||||
throw new IllegalArgumentException("generateLodColorHorizontal only accepts the ColorDirection N (North), S (South), E (East), or W (West)");
|
||||
}
|
||||
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
|
||||
// these don't change since the over direction doesn't matter
|
||||
int overStart = 0;
|
||||
int overIncrement = 1;
|
||||
|
||||
// determine which direction is "in"
|
||||
int inStart = 0;
|
||||
int inIncrement = 1;
|
||||
switch (colorDir)
|
||||
{
|
||||
case NORTH:
|
||||
inStart = 0;
|
||||
inIncrement = 1;
|
||||
break;
|
||||
case SOUTH:
|
||||
inStart = CHUNK_DATA_WIDTH - 1;
|
||||
inIncrement = -1;
|
||||
break;
|
||||
case EAST:
|
||||
inStart = 0;
|
||||
inIncrement = 1;
|
||||
break;
|
||||
case WEST:
|
||||
inStart = CHUNK_DATA_WIDTH - 1;
|
||||
inIncrement = -1;
|
||||
break;
|
||||
default:
|
||||
// we were given an invalid position, return invisible.
|
||||
// this shouldn't happen and is mostly here to make the
|
||||
// compiler happy
|
||||
return INVISIBLE;
|
||||
}
|
||||
|
||||
|
||||
for (int section = 0; section < chunkSections.length; section++)
|
||||
{
|
||||
if (chunkSections[section] == null)
|
||||
continue;
|
||||
|
||||
for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
|
||||
{
|
||||
boolean foundBlock = false;
|
||||
|
||||
// over moves "over" the side of the chunk
|
||||
// in moves "into" the chunk until it finds a block
|
||||
for (int over = overStart; !foundBlock && over >= 0 && over < CHUNK_DATA_WIDTH; over += overIncrement)
|
||||
{
|
||||
for (int in = inStart; !foundBlock && in >= 0 && in < CHUNK_DATA_WIDTH; in += inIncrement)
|
||||
{
|
||||
int x = -1;
|
||||
int z = -1;
|
||||
|
||||
// determine which should be X and Z
|
||||
switch(colorDir)
|
||||
{
|
||||
case NORTH:
|
||||
x = over;
|
||||
z = in;
|
||||
break;
|
||||
case SOUTH:
|
||||
x = over;
|
||||
z = in;
|
||||
break;
|
||||
case EAST:
|
||||
x = in;
|
||||
z = over;
|
||||
break;
|
||||
case WEST:
|
||||
x = in;
|
||||
z = over;
|
||||
break;
|
||||
default:
|
||||
// here to make the compiler happy
|
||||
break;
|
||||
}
|
||||
|
||||
// if this block is buried, under other blocks
|
||||
// don't add it
|
||||
if(!isBlockPosVisible(chunkSections[section], x,y,z))
|
||||
{
|
||||
// go to the next "over" block location,
|
||||
// don't look at the next "in" location,
|
||||
// since the next "in" location will more than
|
||||
// likely still be covered
|
||||
in = CHUNK_DATA_WIDTH + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
int ci;
|
||||
ci = chunkSections[section].getBlockState(x, y, z).getMaterial().getColor().colorValue;
|
||||
|
||||
if (ci == 0) {
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
Color c = LodUtil.intToColor(ci);
|
||||
|
||||
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 we didn't find any blocks return invisible
|
||||
if(numbOfBlocks == 0)
|
||||
return INVISIBLE;
|
||||
|
||||
red /= numbOfBlocks;
|
||||
green /= numbOfBlocks;
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static BlockState airState = Blocks.AIR.getDefaultState();
|
||||
|
||||
/**
|
||||
* returns true if the block at the given coordinates is open to
|
||||
* air on at least one side.
|
||||
*/
|
||||
private boolean isBlockPosVisible(ChunkSection chunkSection, int x, int y, int z)
|
||||
{
|
||||
/*
|
||||
// above
|
||||
if (y+1 < CHUNK_SECTION_HEIGHT) // don't go over the top
|
||||
if (chunkSection.getBlockState(x, y+1, z).getBlock() == (Blocks.AIR))
|
||||
return true;
|
||||
// below
|
||||
if (y-1 >= 0) // don't go below the bottom
|
||||
if (chunkSection.getBlockState(x, y-1, z).getBlock() == (Blocks.AIR))
|
||||
return true;
|
||||
*/
|
||||
|
||||
// north
|
||||
if (z-1 > 0)
|
||||
if (chunkSection.getBlockState(x, y, z-1) == airState)
|
||||
return true;
|
||||
// south
|
||||
if (z+1 < LodChunk.WIDTH)
|
||||
if (chunkSection.getBlockState(x, y, z+1) == airState)
|
||||
return true;
|
||||
|
||||
// east
|
||||
if (x+1 <= LodChunk.WIDTH)
|
||||
if (chunkSection.getBlockState(x+1, y, z) == airState)
|
||||
return true;
|
||||
// west
|
||||
if (x-1 >= 0)
|
||||
if (chunkSection.getBlockState(x-1, y, z) == airState)
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user