new line brackets
This commit is contained in:
@@ -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 <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 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 <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 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,27 +24,29 @@ package com.seibel.lod.enums;
|
||||
* SURFACE <br>
|
||||
* FEATURES <br>
|
||||
* SERVER <br><br>
|
||||
*
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,238 +40,236 @@ import net.minecraftforge.fml.config.ModConfig;
|
||||
|
||||
/**
|
||||
* This handles any configuration the user has access to.
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-10-2021
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class LodConfig
|
||||
{
|
||||
public static class Client
|
||||
{
|
||||
public ForgeConfigSpec.BooleanValue drawLODs;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
|
||||
|
||||
public ForgeConfigSpec.BooleanValue debugMode;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
|
||||
|
||||
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
|
||||
|
||||
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
|
||||
|
||||
/** this is multiplied by the default view distance
|
||||
* to determine how far out to generate/render LODs */
|
||||
public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier;
|
||||
public static class Client
|
||||
{
|
||||
public ForgeConfigSpec.BooleanValue drawLODs;
|
||||
|
||||
public ForgeConfigSpec.IntValue lodQuality;
|
||||
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
|
||||
|
||||
public ForgeConfigSpec.IntValue lodChunkRenderDistane;
|
||||
|
||||
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
|
||||
|
||||
public ForgeConfigSpec.DoubleValue saturationMultiplier;
|
||||
|
||||
|
||||
|
||||
|
||||
Client(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
|
||||
|
||||
drawLODs = builder
|
||||
.comment("\n\n"
|
||||
+ " If false LODs will not be drawn, \n"
|
||||
+ " however they will still be generated \n"
|
||||
+ " and saved to file for later use. \n")
|
||||
.define("drawLODs", true);
|
||||
|
||||
fogDistance = builder
|
||||
.comment("\n\n"
|
||||
+ " At what distance should Fog be drawn on the LODs? \n"
|
||||
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
|
||||
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
|
||||
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
|
||||
|
||||
fogDrawOverride = builder
|
||||
.comment("\n\n"
|
||||
+ " When should fog be drawn? \n"
|
||||
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
|
||||
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
|
||||
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
|
||||
|
||||
debugMode = builder
|
||||
.comment("\n\n"
|
||||
+ " If false the LODs will draw with their normal colors. \n"
|
||||
+ " If true LODs colors will be based on their detail \n"
|
||||
+ " level. \n")
|
||||
.define("debugMode", false);
|
||||
|
||||
lodTemplate = builder
|
||||
.comment("\n\n"
|
||||
+ " How should the LODs be drawn? \n"
|
||||
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
|
||||
+ " \n"
|
||||
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
|
||||
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
|
||||
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
|
||||
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
|
||||
.defineEnum("lodTemplate", LodTemplate.CUBIC);
|
||||
|
||||
maxDrawDetail = builder
|
||||
.comment("\n\n"
|
||||
+ " What is the maximum detail level that LODs should be drawn at? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("lodDrawQuality", LodDetail.DOUBLE);
|
||||
|
||||
maxGenerationDetail = builder
|
||||
.comment("\n\n"
|
||||
+ " What is the maximum detail level that LODs should be generated at? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("lodGenerationQuality", LodDetail.DOUBLE);
|
||||
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
|
||||
|
||||
public ForgeConfigSpec.BooleanValue debugMode;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
|
||||
|
||||
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
|
||||
|
||||
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
|
||||
|
||||
/**
|
||||
* this is multiplied by the default view distance
|
||||
* to determine how far out to generate/render LODs
|
||||
*/
|
||||
public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier;
|
||||
|
||||
public ForgeConfigSpec.IntValue lodQuality;
|
||||
|
||||
public ForgeConfigSpec.IntValue lodChunkRenderDistane;
|
||||
|
||||
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
|
||||
|
||||
public ForgeConfigSpec.DoubleValue saturationMultiplier;
|
||||
|
||||
|
||||
lodChunkRadiusMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " This is multiplied by the default view distance \n"
|
||||
+ " to determine how far out to generate/render LODs. \n"
|
||||
+ " A value of 2 means that there is 1 render distance worth \n"
|
||||
+ " of LODs in each cardinal direction. \n")
|
||||
.defineInRange("lodChunkRadiusMultiplier", 8, 2, 16);
|
||||
Client(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
|
||||
|
||||
drawLODs = builder
|
||||
.comment("\n\n"
|
||||
+ " If false LODs will not be drawn, \n"
|
||||
+ " however they will still be generated \n"
|
||||
+ " and saved to file for later use. \n")
|
||||
.define("drawLODs", true);
|
||||
|
||||
fogDistance = builder
|
||||
.comment("\n\n"
|
||||
+ " At what distance should Fog be drawn on the LODs? \n"
|
||||
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
|
||||
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
|
||||
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
|
||||
|
||||
fogDrawOverride = builder
|
||||
.comment("\n\n"
|
||||
+ " When should fog be drawn? \n"
|
||||
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
|
||||
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
|
||||
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
|
||||
|
||||
debugMode = builder
|
||||
.comment("\n\n"
|
||||
+ " If false the LODs will draw with their normal colors. \n"
|
||||
+ " If true LODs colors will be based on their detail \n"
|
||||
+ " level. \n")
|
||||
.define("debugMode", false);
|
||||
|
||||
lodTemplate = builder
|
||||
.comment("\n\n"
|
||||
+ " How should the LODs be drawn? \n"
|
||||
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
|
||||
+ " \n"
|
||||
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
|
||||
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
|
||||
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
|
||||
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
|
||||
.defineEnum("lodTemplate", LodTemplate.CUBIC);
|
||||
|
||||
maxDrawDetail = builder
|
||||
.comment("\n\n"
|
||||
+ " What is the maximum detail level that LODs should be drawn at? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("lodDrawQuality", LodDetail.DOUBLE);
|
||||
|
||||
maxGenerationDetail = builder
|
||||
.comment("\n\n"
|
||||
+ " What is the maximum detail level that LODs should be generated at? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("lodGenerationQuality", LodDetail.DOUBLE);
|
||||
|
||||
|
||||
lodChunkRadiusMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " This is multiplied by the default view distance \n"
|
||||
+ " to determine how far out to generate/render LODs. \n"
|
||||
+ " A value of 2 means that there is 1 render distance worth \n"
|
||||
+ " of LODs in each cardinal direction. \n")
|
||||
.defineInRange("lodChunkRadiusMultiplier", 8, 2, 16);
|
||||
|
||||
distanceGenerationMode = builder
|
||||
.comment("\n\n"
|
||||
+ " Note: The times listed here are the amount of time it took \n"
|
||||
+ " the developer's PC to generate 1 chunk, \n"
|
||||
+ " and are included so you can compare the \n"
|
||||
+ " different generation options. Your mileage may vary. \n"
|
||||
+ "\n"
|
||||
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
|
||||
+ " Only generate the biomes and use biome \n"
|
||||
+ " grass/foliage color, water color, or snow color \n"
|
||||
+ " to generate the color. \n"
|
||||
+ " Doesn't generate height, everything is shown at sea level. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
|
||||
+ " Same as BIOME_ONLY, except instead \n"
|
||||
+ " of always using sea level as the LOD height \n"
|
||||
+ " different biome types (mountain, ocean, forest, etc.) \n"
|
||||
+ " use predetermined heights to simulate having height data. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
|
||||
+ " Generate the world surface, \n"
|
||||
+ " this does NOT include caves, trees, \n"
|
||||
+ " or structures. \n"
|
||||
+ " Multithreaded - Faster (10-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
|
||||
+ " Generate everything except structures. \n"
|
||||
+ " WARNING: This may cause world generation bugs or instability! \n"
|
||||
+ " Multithreaded - Fast (15-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
|
||||
+ " Ask the server to generate/load each chunk. \n"
|
||||
+ " This is the most compatible, but causes server/simulation lag. \n"
|
||||
+ " This will also show player made structures if you \n"
|
||||
+ " are adding the mod to a pre-existing world. \n"
|
||||
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
|
||||
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
|
||||
|
||||
allowUnstableFeatureGeneration = builder
|
||||
.comment("\n\n"
|
||||
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
|
||||
+ " some features may not be thread safe, which could \n"
|
||||
+ " cause instability and crashes. \n"
|
||||
+ " By default (false) those features are skipped, \n"
|
||||
+ " improving stability, but decreasing how many features are \n"
|
||||
+ " actually generated. \n"
|
||||
+ " (for example: some tree generation is unstable, \n"
|
||||
+ " so some trees may not be generated.) \n"
|
||||
+ " By setting this to true, all features will be generated, \n"
|
||||
+ " but your game will be more unstable and crashes may occur. \n"
|
||||
+ " \n"
|
||||
+ " I would love to remove this option and always generate everything, \n"
|
||||
+ " but I'm not sure how to do that. \n"
|
||||
+ " If you are a Java wizard, check out the git issue here: \n"
|
||||
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
|
||||
.define("allowUnstableFeatureGeneration", false);
|
||||
|
||||
numberOfWorldGenerationThreads = builder
|
||||
.comment("\n\n"
|
||||
+ " This is how many threads are used when generating terrain. \n"
|
||||
+ " If you experience stuttering when generating terrain, decrease \n"
|
||||
+ " this number. If you want to increase LOD generation speed, \n"
|
||||
+ " increase the number. \n"
|
||||
+ " The max is the number of processors on your CPU. \n"
|
||||
+ "\n"
|
||||
+ " Requires a restart to take effect. \n"
|
||||
)
|
||||
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
shadingMode = builder
|
||||
.comment("\n\n"
|
||||
+ " What kind of shading should LODs have? \n"
|
||||
+ " " + ShadingMode.NONE.toString() + " \n"
|
||||
+ " " + "LODs will have the same lighting on every side. \n"
|
||||
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
|
||||
+ " " + "Fastest"
|
||||
+ "/n"
|
||||
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
|
||||
+ " " + "LODs will have darker sides and bottoms to simulate top down lighting."
|
||||
+ " " + "Fastest /n")
|
||||
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
|
||||
|
||||
|
||||
brightnessMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " Change how bright LOD colors are. \n"
|
||||
+ " 0 = black \n"
|
||||
+ " 1 = normal color value \n"
|
||||
+ " 2 = washed out colors \n")
|
||||
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
|
||||
|
||||
saturationMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " Change how saturated LOD colors are. \n"
|
||||
+ " 0 = black and white \n"
|
||||
+ " 1 = normal saturation \n"
|
||||
+ " 2 = very saturated \n")
|
||||
.defineInRange("saturationMultiplier", 1.0, 0, 2);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
distanceGenerationMode = builder
|
||||
.comment("\n\n"
|
||||
+ " Note: The times listed here are the amount of time it took \n"
|
||||
+ " the developer's PC to generate 1 chunk, \n"
|
||||
+ " and are included so you can compare the \n"
|
||||
+ " different generation options. Your mileage may vary. \n"
|
||||
+ "\n"
|
||||
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
|
||||
+ " Only generate the biomes and use biome \n"
|
||||
+ " grass/foliage color, water color, or snow color \n"
|
||||
+ " to generate the color. \n"
|
||||
+ " Doesn't generate height, everything is shown at sea level. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
|
||||
+ " Same as BIOME_ONLY, except instead \n"
|
||||
+ " of always using sea level as the LOD height \n"
|
||||
+ " different biome types (mountain, ocean, forest, etc.) \n"
|
||||
+ " use predetermined heights to simulate having height data. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
|
||||
+ " Generate the world surface, \n"
|
||||
+ " this does NOT include caves, trees, \n"
|
||||
+ " or structures. \n"
|
||||
+ " Multithreaded - Faster (10-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
|
||||
+ " Generate everything except structures. \n"
|
||||
+ " WARNING: This may cause world generation bugs or instability! \n"
|
||||
+ " Multithreaded - Fast (15-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
|
||||
+ " Ask the server to generate/load each chunk. \n"
|
||||
+ " This is the most compatible, but causes server/simulation lag. \n"
|
||||
+ " This will also show player made structures if you \n"
|
||||
+ " are adding the mod to a pre-existing world. \n"
|
||||
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
|
||||
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
|
||||
|
||||
allowUnstableFeatureGeneration = builder
|
||||
.comment("\n\n"
|
||||
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
|
||||
+ " some features may not be thread safe, which could \n"
|
||||
+ " cause instability and crashes. \n"
|
||||
+ " By default (false) those features are skipped, \n"
|
||||
+ " improving stability, but decreasing how many features are \n"
|
||||
+ " actually generated. \n"
|
||||
+ " (for example: some tree generation is unstable, \n"
|
||||
+ " so some trees may not be generated.) \n"
|
||||
+ " By setting this to true, all features will be generated, \n"
|
||||
+ " but your game will be more unstable and crashes may occur. \n"
|
||||
+ " \n"
|
||||
+ " I would love to remove this option and always generate everything, \n"
|
||||
+ " but I'm not sure how to do that. \n"
|
||||
+ " If you are a Java wizard, check out the git issue here: \n"
|
||||
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
|
||||
.define("allowUnstableFeatureGeneration", false);
|
||||
|
||||
numberOfWorldGenerationThreads = builder
|
||||
.comment("\n\n"
|
||||
+ " This is how many threads are used when generating terrain. \n"
|
||||
+ " If you experience stuttering when generating terrain, decrease \n"
|
||||
+ " this number. If you want to increase LOD generation speed, \n"
|
||||
+ " increase the number. \n"
|
||||
+ " The max is the number of processors on your CPU. \n"
|
||||
+ "\n"
|
||||
+ " Requires a restart to take effect. \n"
|
||||
)
|
||||
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
shadingMode = builder
|
||||
.comment("\n\n"
|
||||
+ " What kind of shading should LODs have? \n"
|
||||
+ " " + ShadingMode.NONE.toString() + " \n"
|
||||
+ " " + "LODs will have the same lighting on every side. \n"
|
||||
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
|
||||
+ " " + "Fastest"
|
||||
+ "/n"
|
||||
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
|
||||
+ " " + "LODs will have darker sides and bottoms to simulate top down lighting."
|
||||
+ " " + "Fastest /n")
|
||||
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
|
||||
|
||||
|
||||
|
||||
brightnessMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " Change how bright LOD colors are. \n"
|
||||
+ " 0 = black \n"
|
||||
+ " 1 = normal color value \n"
|
||||
+ " 2 = washed out colors \n")
|
||||
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
|
||||
|
||||
saturationMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " Change how saturated LOD colors are. \n"
|
||||
+ " 0 = black and white \n"
|
||||
+ " 1 = normal saturation \n"
|
||||
+ " 2 = very saturated \n")
|
||||
.defineInRange("saturationMultiplier", 1.0, 0, 2);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@link Path} to the configuration file of this mod
|
||||
@@ -281,11 +279,13 @@ public class LodConfig
|
||||
|
||||
public static final ForgeConfigSpec clientSpec;
|
||||
public static final Client CLIENT;
|
||||
static {
|
||||
|
||||
static
|
||||
{
|
||||
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
|
||||
clientSpec = specPair.getRight();
|
||||
CLIENT = specPair.getLeft();
|
||||
|
||||
|
||||
// setup the config file
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH)
|
||||
.writingMode(WritingMode.REPLACE)
|
||||
@@ -294,21 +294,19 @@ public class LodConfig
|
||||
config.save();
|
||||
clientSpec.setConfig(config);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLoad(final ModConfig.Loading configEvent)
|
||||
{
|
||||
LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onFileChange(final ModConfig.Reloading configEvent)
|
||||
{
|
||||
LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ import java.util.concurrent.Executors;
|
||||
* @author James Seibel
|
||||
* @version 8-14-2021
|
||||
*/
|
||||
public class LodDimensionFileHandler {
|
||||
public class LodDimensionFileHandler
|
||||
{
|
||||
/**
|
||||
* This is what separates each piece of data
|
||||
*/
|
||||
@@ -89,7 +90,8 @@ public class LodDimensionFileHandler {
|
||||
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||
|
||||
|
||||
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension) {
|
||||
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
|
||||
{
|
||||
if (newSaveFolder == null)
|
||||
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
|
||||
|
||||
@@ -113,36 +115,43 @@ public class LodDimensionFileHandler {
|
||||
* Return the LodRegion region at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(RegionPos regionPos) {
|
||||
public LodRegion loadRegionFromFile(RegionPos regionPos)
|
||||
{
|
||||
int regionX = regionPos.x;
|
||||
int regionZ = regionPos.z;
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ);
|
||||
|
||||
File f = new File(fileName);
|
||||
|
||||
if (!f.exists()) {
|
||||
if (!f.exists())
|
||||
{
|
||||
// there wasn't a file, don't
|
||||
// return anything
|
||||
return null;
|
||||
}
|
||||
String data = "";
|
||||
try {
|
||||
try
|
||||
{
|
||||
BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
|
||||
data = bufferedReader.readLine();
|
||||
int fileVersion = -1;
|
||||
|
||||
if (data != null && !data.isEmpty()) {
|
||||
if (data != null && !data.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try {
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(data.substring(data.indexOf(' ')).trim());
|
||||
} catch (NumberFormatException | StringIndexOutOfBoundsException e) {
|
||||
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a version
|
||||
// keep the version as -1
|
||||
fileVersion = -1;
|
||||
}
|
||||
|
||||
// check if this file can be read by this file handler
|
||||
if (fileVersion < LOD_SAVE_FILE_VERSION) {
|
||||
if (fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
bufferedReader.close();
|
||||
@@ -152,7 +161,8 @@ public class LodDimensionFileHandler {
|
||||
" File was been deleted.");
|
||||
|
||||
return null;
|
||||
} else if (fileVersion > LOD_SAVE_FILE_VERSION) {
|
||||
} else if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// close the reader and ignore the file, we don't
|
||||
// want to accidently delete anything the user may want.
|
||||
@@ -163,7 +173,8 @@ public class LodDimensionFileHandler {
|
||||
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
// there is no data in this file
|
||||
bufferedReader.close();
|
||||
return null;
|
||||
@@ -174,7 +185,8 @@ public class LodDimensionFileHandler {
|
||||
data = bufferedReader.readLine();
|
||||
|
||||
bufferedReader.close();
|
||||
} catch (IOException e) {
|
||||
} catch (IOException e)
|
||||
{
|
||||
// the buffered reader encountered a
|
||||
// problem reading the file
|
||||
return null;
|
||||
@@ -190,15 +202,19 @@ public class LodDimensionFileHandler {
|
||||
/**
|
||||
* Save all dirty regions in this LodDimension to file.
|
||||
*/
|
||||
public void saveDirtyRegionsToFileAsync() {
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||
}
|
||||
|
||||
private Thread saveDirtyRegionsThread = new Thread(() ->
|
||||
{
|
||||
for (int i = 0; i < loadedDimension.getWidth(); i++) {
|
||||
for (int j = 0; j < loadedDimension.getWidth(); j++) {
|
||||
if (loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null) {
|
||||
for (int i = 0; i < loadedDimension.getWidth(); i++)
|
||||
{
|
||||
for (int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
{
|
||||
if (loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null)
|
||||
{
|
||||
saveRegionToFile(loadedDimension.regions[i][j]);
|
||||
loadedDimension.isRegionDirty[i][j] = false;
|
||||
}
|
||||
@@ -214,7 +230,8 @@ public class LodDimensionFileHandler {
|
||||
* 2. This will save to the LodDimension that this
|
||||
* handler is associated with.
|
||||
*/
|
||||
private void saveRegionToFile(LodRegion region) {
|
||||
private void saveRegionToFile(LodRegion region)
|
||||
{
|
||||
// convert to region coordinates
|
||||
int x = region.regionPosX;
|
||||
int z = region.regionPosZ;
|
||||
@@ -224,15 +241,18 @@ public class LodDimensionFileHandler {
|
||||
byte minDetailLevel = region.getMinDetailLevel();
|
||||
|
||||
File oldFile = new File(getFileNameAndPathForRegion(x, z));
|
||||
try {
|
||||
try
|
||||
{
|
||||
// make sure the file and folder exists
|
||||
if (!oldFile.exists()) {
|
||||
if (!oldFile.exists())
|
||||
{
|
||||
// the file doesn't exist,
|
||||
// create it and the folder if need be
|
||||
if (!oldFile.getParentFile().exists())
|
||||
oldFile.getParentFile().mkdirs();
|
||||
oldFile.createNewFile();
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
// the file exists, make sure it
|
||||
// is the correct version.
|
||||
// (to make sure we don't overwrite a newer
|
||||
@@ -242,11 +262,14 @@ public class LodDimensionFileHandler {
|
||||
String s = br.readLine();
|
||||
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||
|
||||
if (s != null && !s.isEmpty()) {
|
||||
if (s != null && !s.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try {
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
|
||||
} catch (NumberFormatException | StringIndexOutOfBoundsException e) {
|
||||
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a correctly formated version
|
||||
// just overwrite the file
|
||||
}
|
||||
@@ -254,7 +277,8 @@ public class LodDimensionFileHandler {
|
||||
br.close();
|
||||
|
||||
// check if this file can be written to by the file handler
|
||||
if (fileVersion <= LOD_SAVE_FILE_VERSION) {
|
||||
if (fileVersion <= LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// we are good to continue and overwrite the old file
|
||||
} else //if(fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
@@ -279,7 +303,8 @@ public class LodDimensionFileHandler {
|
||||
|
||||
// overwrite the old file with the new one
|
||||
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e)
|
||||
{
|
||||
ClientProxy.LOGGER.error("LOD file write error: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -298,15 +323,18 @@ public class LodDimensionFileHandler {
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]));
|
||||
|
||||
@@ -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.
|
||||
* <br>
|
||||
* Exports data in the form:
|
||||
* <br>
|
||||
* height, depth, rgb color data
|
||||
*
|
||||
* <br>
|
||||
* example output:
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* Exports data in the form:
|
||||
* <br>
|
||||
* height, depth, rgb color data
|
||||
*
|
||||
* <br>
|
||||
* example output:
|
||||
* <br>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. <br><br>
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* <br>
|
||||
@@ -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
|
||||
*
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<LevelPos> getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generetion, byte detailLevel) {
|
||||
if(detailLevel < minDetailLevel) detailLevel = minDetailLevel;
|
||||
LevelPos levelPos
|
||||
/*
|
||||
public List<LevelPos> getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generetion, byte detailLevel)
|
||||
{
|
||||
if (detailLevel < minDetailLevel) detailLevel = minDetailLevel;
|
||||
LevelPos levelPos;
|
||||
int size;
|
||||
int width;
|
||||
int posX;
|
||||
int posZ;
|
||||
int distance;
|
||||
for(int tempLod = detailLevel; tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++){
|
||||
size = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL-tempLod);
|
||||
width = (int) Math.pow(2,tempLod);
|
||||
for(int x = 0; x < size; x++){
|
||||
for(int z = 0; z < size; z++){
|
||||
posX = regionPosX * 512 + x * width + width/2;
|
||||
posZ = regionPosZ * 512 + z * width + width/2;
|
||||
distance = (int) Math.sqrt(Math.pow(playerPosX - posX, 2) + Math.pow(playerPosZ - posZ, 2))
|
||||
if(distance >= start && distance <= end){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod);
|
||||
width = (int) Math.pow(2, tempLod);
|
||||
posX = regionPosX * 512 + width / 2;
|
||||
posZ = regionPosZ * 512 + width / 2;
|
||||
distance = (int) Math.sqrt(Math.pow(playerPosX - posX, 2) + Math.pow(playerPosZ - posZ, 2))
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
/*
|
||||
public List<LevelPos> getDataToRender(int playerPosX, int playerPosZ, int start, int end, byte detailLevel) {
|
||||
*//*
|
||||
public List<LevelPos> getDataToRender(int playerPosX, int playerPosZ, int start, int end, byte detailLevel)
|
||||
{
|
||||
if(detailLevel < minDetailLevel) detailLevel = minDetailLevel;
|
||||
int size;
|
||||
int width;
|
||||
@@ -234,13 +241,14 @@ public class LodRegion implements Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
/**TODO a method to update a whole area, to be used as a single big update*/
|
||||
/**
|
||||
* @param levelPos
|
||||
*/
|
||||
private void updateArea(LevelPos levelPos) {
|
||||
private void updateArea(LevelPos levelPos)
|
||||
{
|
||||
/*
|
||||
LevelPos tempLevelPos = levelPos;
|
||||
int sizeDiff;
|
||||
@@ -259,13 +267,25 @@ public class LodRegion implements Serializable {
|
||||
|
||||
}
|
||||
|
||||
int sizeDiff;
|
||||
LevelPos levelPos;
|
||||
for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
|
||||
sizeDiff = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL - tempLod);
|
||||
for (int x = 0; x < sizeDiff; x++) {
|
||||
for (int z = 0; z < sizeDiff; z++) {
|
||||
levelPos = new LevelPos(tempLod, x, z);
|
||||
update(levelPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @param levelPos
|
||||
*/
|
||||
private void update(LevelPos levelPos) {
|
||||
private void update(LevelPos levelPos)
|
||||
{
|
||||
|
||||
levelPos = levelPos.regionModule();
|
||||
boolean[][] children = getChildren(levelPos);
|
||||
@@ -282,13 +302,16 @@ public class LodRegion implements Serializable {
|
||||
int newPosZ;
|
||||
byte newLod;
|
||||
LevelPos childPos;
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
newPosX = 2 * levelPos.posX + x;
|
||||
newPosZ = 2 * levelPos.posZ + z;
|
||||
newLod = (byte) (levelPos.detailLevel - 1);
|
||||
childPos = new LevelPos(newLod, newPosX, newPosZ);
|
||||
if (hasDataBeenGenerated(childPos)) {
|
||||
if (hasDataBeenGenerated(childPos))
|
||||
{
|
||||
numberOfChildren++;
|
||||
|
||||
tempRed += colors[newLod][newPosX][newPosZ][0];
|
||||
@@ -301,7 +324,8 @@ public class LodRegion implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
if (numberOfChildren > 0) {
|
||||
if (numberOfChildren > 0)
|
||||
{
|
||||
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (tempRed / numberOfChildren);
|
||||
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (tempGreen / numberOfChildren);
|
||||
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (tempBlue / numberOfChildren);
|
||||
@@ -316,15 +340,19 @@ public class LodRegion implements Serializable {
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
private boolean[][] getChildren(LevelPos levelPos) {
|
||||
private boolean[][] getChildren(LevelPos levelPos)
|
||||
{
|
||||
levelPos = levelPos.regionModule();
|
||||
boolean[][] children = new boolean[2][2];
|
||||
int numberOfChild = 0;
|
||||
if (minDetailLevel == levelPos.detailLevel) {
|
||||
if (minDetailLevel == levelPos.detailLevel)
|
||||
{
|
||||
return children;
|
||||
}
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
children[x][z] = (dataExistence[levelPos.detailLevel - 1][2 * levelPos.posX + x][2 * levelPos.posZ + z]);
|
||||
}
|
||||
}
|
||||
@@ -335,7 +363,8 @@ public class LodRegion implements Serializable {
|
||||
* @param chunkPos
|
||||
* @return
|
||||
*/
|
||||
public boolean doesDataExist(ChunkPos chunkPos) {
|
||||
public boolean doesDataExist(ChunkPos chunkPos)
|
||||
{
|
||||
return doesDataExist(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
|
||||
}
|
||||
|
||||
@@ -343,7 +372,8 @@ public class LodRegion implements Serializable {
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
public boolean doesDataExist(LevelPos levelPos) {
|
||||
public boolean doesDataExist(LevelPos levelPos)
|
||||
{
|
||||
levelPos = levelPos.regionModule();
|
||||
return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ];
|
||||
}
|
||||
@@ -352,10 +382,12 @@ public class LodRegion implements Serializable {
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
public DistanceGenerationMode getGenerationMode(LevelPos levelPos) {
|
||||
public DistanceGenerationMode getGenerationMode(LevelPos levelPos)
|
||||
{
|
||||
levelPos = levelPos.regionModule();
|
||||
DistanceGenerationMode generationMode = DistanceGenerationMode.NONE;
|
||||
switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) {
|
||||
switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])
|
||||
{
|
||||
case 0:
|
||||
generationMode = DistanceGenerationMode.NONE;
|
||||
break;
|
||||
@@ -386,12 +418,14 @@ public class LodRegion implements Serializable {
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
public boolean hasDataBeenGenerated(LevelPos levelPos) {
|
||||
public boolean hasDataBeenGenerated(LevelPos levelPos)
|
||||
{
|
||||
levelPos = levelPos.regionModule();
|
||||
return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0);
|
||||
}
|
||||
|
||||
public byte getMinDetailLevel() {
|
||||
public byte getMinDetailLevel()
|
||||
{
|
||||
return minDetailLevel;
|
||||
}
|
||||
|
||||
@@ -401,15 +435,18 @@ public class LodRegion implements Serializable {
|
||||
* @param lod
|
||||
* @return
|
||||
*/
|
||||
public LevelContainer getLevel(byte lod) {
|
||||
public LevelContainer getLevel(byte lod)
|
||||
{
|
||||
return new LevelContainer(lod, colors[lod], height[lod], depth[lod], generationType[lod], dataExistence[lod]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param levelContainer
|
||||
*/
|
||||
public void addLevel(LevelContainer levelContainer) {
|
||||
if (levelContainer.detailLevel < minDetailLevel - 1) {
|
||||
public void addLevel(LevelContainer levelContainer)
|
||||
{
|
||||
if (levelContainer.detailLevel < minDetailLevel - 1)
|
||||
{
|
||||
throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 ");
|
||||
}
|
||||
if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel;
|
||||
@@ -424,8 +461,10 @@ public class LodRegion implements Serializable {
|
||||
/**
|
||||
* @param lod
|
||||
*/
|
||||
public void removeDetailLevel(byte lod) {
|
||||
for (byte tempLod = 0; tempLod <= lod; tempLod++) {
|
||||
public void removeDetailLevel(byte lod)
|
||||
{
|
||||
for (byte tempLod = 0; tempLod <= lod; tempLod++)
|
||||
{
|
||||
colors[tempLod] = new byte[0][0][0];
|
||||
height[tempLod] = new short[0][0];
|
||||
depth[tempLod] = new short[0][0];
|
||||
@@ -434,7 +473,8 @@ public class LodRegion implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
public String toString()
|
||||
{
|
||||
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,133 +26,134 @@ import net.minecraft.world.DimensionType;
|
||||
|
||||
/**
|
||||
* This stores all LODs for a given world.
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leonardo Amato
|
||||
* @version 8-17-2021
|
||||
*/
|
||||
public class LodWorld
|
||||
{
|
||||
private String worldName;
|
||||
|
||||
private Map<DimensionType, LodDimension> lodDimensions;
|
||||
|
||||
/** If true then the LOD world is setup and ready to use */
|
||||
private boolean isWorldLoaded = false;
|
||||
|
||||
public static final String NO_WORLD_LOADED = "No world loaded";
|
||||
|
||||
|
||||
|
||||
public LodWorld()
|
||||
{
|
||||
worldName = NO_WORLD_LOADED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the LodQuadTreeWorld with the given newWorldName. <br>
|
||||
* 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<DimensionType, LodDimension>();
|
||||
isWorldLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the worldName to "No world loaded"
|
||||
* and clear the lodDimensions Map. <br>
|
||||
* 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<DimensionType, LodDimension> lodDimensions;
|
||||
|
||||
/**
|
||||
* If true then the LOD world is setup and ready to use
|
||||
*/
|
||||
private boolean isWorldLoaded = false;
|
||||
|
||||
public static final String NO_WORLD_LOADED = "No world loaded";
|
||||
|
||||
|
||||
public LodWorld()
|
||||
{
|
||||
worldName = NO_WORLD_LOADED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the LodQuadTreeWorld with the given newWorldName. <br>
|
||||
* 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<DimensionType, LodDimension>();
|
||||
isWorldLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the worldName to "No world loaded"
|
||||
* and clear the lodDimensions Map. <br>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user