Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
package com.seibel.lod;
|
||||
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args){
|
||||
LodRegion lodRegion = new LodRegion((byte) 0,new RegionPos(0,0));
|
||||
lodRegion.setData((byte) 2,0,0, new LodDataPoint((short) 2,(short) 30, new Color(100,100,100)), (byte) 2,true);
|
||||
try {
|
||||
System.out.print("test ");
|
||||
System.out.println(lodRegion.getData((byte) 6, 0, 0));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,15 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.seibel.lod.objects.LevelPos;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodQuadTree;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodNodeRenderer;
|
||||
@@ -101,7 +101,7 @@ public class LodNodeBufferBuilder
|
||||
}
|
||||
|
||||
|
||||
private LodQuadTreeDimension previousDimension = null;
|
||||
private LodDimension previousDimension = null;
|
||||
|
||||
|
||||
/**
|
||||
@@ -113,7 +113,7 @@ public class LodNodeBufferBuilder
|
||||
* After the buildable buffers have been generated they must be
|
||||
* swapped with the drawable buffers in the LodRenderer to be drawn.
|
||||
*/
|
||||
public void generateLodBuffersAsync(LodNodeRenderer renderer, LodQuadTreeDimension lodDim,
|
||||
public void generateLodBuffersAsync(LodNodeRenderer renderer, LodDimension lodDim,
|
||||
BlockPos playerBlockPos, int numbChunksWide)
|
||||
{
|
||||
// only allow one generation process to happen at a time
|
||||
@@ -191,14 +191,12 @@ public class LodNodeBufferBuilder
|
||||
startBlockPos.getX(); // offset so the center LOD block is centered underneath the player
|
||||
double yOffset = 0;
|
||||
double zOffset = (LodUtil.CHUNK_WIDTH * j) + startBlockPos.getZ();
|
||||
|
||||
LodQuadTreeNode lod = lodDim.getLodFromCoordinates(new ChunkPos(chunkX, chunkZ), LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
|
||||
if (lod == null || lod.complexity == DistanceGenerationMode.NONE)
|
||||
|
||||
if (!lodDim.doesDataExist(new LevelPos((byte) LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ)))
|
||||
{
|
||||
// generate a new chunk if no chunk currently exists
|
||||
// and we aren't waiting on any other chunks to generate
|
||||
if (lod == null && numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
|
||||
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
|
||||
{
|
||||
ChunkPos pos = new ChunkPos(chunkX, chunkZ);
|
||||
|
||||
@@ -304,38 +302,30 @@ public class LodNodeBufferBuilder
|
||||
|
||||
|
||||
// determine detail level should this LOD be drawn at
|
||||
int distance = (int) Math.sqrt(Math.pow((playerBlockPosRounded.getX() - lod.getCenter().getX()), 2) + Math.pow((playerBlockPosRounded.getZ() - lod.getCenter().getZ()), 2));
|
||||
LodDetail detail = LodDetail.getDetailForDistance(LodConfig.CLIENT.maxDrawDetail.get(), distance, maxBlockDistance);
|
||||
|
||||
|
||||
int distance = (int) Math.sqrt(Math.pow((playerBlockPosRounded.getX() - chunkX*16 + 8), 2) + Math.pow((playerBlockPosRounded.getZ() - chunkZ*16 + 8), 2));
|
||||
int posX;
|
||||
int posZ;
|
||||
LevelPos levelPos;
|
||||
LodDataPoint lodData;
|
||||
LodDetail detail = LodDetail.getDetailForDistance(LodConfig.CLIENT.maxDrawDetail.get(), distance, 16*128);
|
||||
|
||||
for (int k = 0; k < detail.dataPointLengthCount * detail.dataPointLengthCount; k++)
|
||||
{
|
||||
// how much to offset this LOD by
|
||||
int startX = detail.startX[k];
|
||||
int startZ = detail.startZ[k];
|
||||
|
||||
// get the QuadTree location of this
|
||||
LodQuadTree lodTree = lodDim.getLevelFromPos(
|
||||
LodUtil.convertLevelPos((int) xOffset + startX, 0, LodUtil.CHUNK_DETAIL_LEVEL),
|
||||
LodUtil.convertLevelPos((int) zOffset + startZ, 0, LodUtil.CHUNK_DETAIL_LEVEL),
|
||||
LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
|
||||
if (lodTree == null)
|
||||
continue;
|
||||
|
||||
LodQuadTreeNode newLod = lodTree.getNodeAtPos(
|
||||
LodUtil.convertLevelPos((int) xOffset + startX, 0, detail.detailLevel),
|
||||
LodUtil.convertLevelPos((int) zOffset + startZ, 0, detail.detailLevel),
|
||||
detail.detailLevel);
|
||||
|
||||
if (newLod != null)
|
||||
{
|
||||
// get the desired LodTemplate and
|
||||
// add this LOD to the buffer
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
template.addLodToBuffer(currentBuffer, lodDim, newLod,
|
||||
xOffset + startX, yOffset, zOffset + startZ, renderer.debugging, detail);
|
||||
posX = (int) (xOffset + detail.startX[k]);
|
||||
posZ = (int) (zOffset + detail.startZ[k]);
|
||||
levelPos = new LevelPos((byte) 0, posX, posZ).convert((byte) detail.detailLevel);
|
||||
if (lodDim.hasThisPositionBeenGenerated(levelPos)) {
|
||||
lodData = lodDim.getData(levelPos);
|
||||
}else {
|
||||
lodData = lodDim.getData(levelPos);
|
||||
}
|
||||
// get the desired LodTemplate and
|
||||
// add this LOD to the buffer
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
template.addLodToBuffer(currentBuffer, lodDim, lodData,
|
||||
posX, yOffset, posZ, renderer.debugging, detail);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@ import java.util.concurrent.Executors;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LevelPos;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodQuadTreeWorld;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
@@ -72,47 +72,43 @@ public class LodNodeBuilder
|
||||
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world)
|
||||
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
|
||||
|
||||
|
||||
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();
|
||||
|
||||
List<LodQuadTreeNode> nodeList = generateLodNodeFromChunk(chunk, new LodBuilderConfig(generationMode));
|
||||
|
||||
LodQuadTreeDimension lodDim;
|
||||
|
||||
|
||||
|
||||
LodDimension lodDim;
|
||||
|
||||
if (lodWorld.getLodDimension(dim) == null)
|
||||
{
|
||||
lodDim = new LodQuadTreeDimension(dim, lodWorld, defaultDimensionWidthInRegions);
|
||||
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
|
||||
lodWorld.addLodDimension(lodDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
lodDim = lodWorld.getLodDimension(dim);
|
||||
}
|
||||
|
||||
for (LodQuadTreeNode node : nodeList)
|
||||
{
|
||||
lodDim.addNode(node);
|
||||
|
||||
}
|
||||
|
||||
generateLodNodeFromChunk(lodDim ,chunk, new LodBuilderConfig(generationMode));
|
||||
}
|
||||
catch (IllegalArgumentException | NullPointerException e)
|
||||
{
|
||||
@@ -123,47 +119,46 @@ public class LodNodeBuilder
|
||||
}
|
||||
});
|
||||
lodGenThreadPool.execute(thread);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a LodChunk for a chunk in the given world.
|
||||
*
|
||||
* @throws IllegalArgumentException thrown if either the chunk or world is null.
|
||||
*/
|
||||
public List<LodQuadTreeNode> generateLodNodeFromChunk(IChunk chunk) throws IllegalArgumentException
|
||||
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk) throws IllegalArgumentException
|
||||
{
|
||||
return generateLodNodeFromChunk(chunk, new LodBuilderConfig());
|
||||
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 List<LodQuadTreeNode> generateLodNodeFromChunk(IChunk chunk, LodBuilderConfig config)
|
||||
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
LodDetail detail = LodConfig.CLIENT.maxGenerationDetail.get();
|
||||
List<LodQuadTreeNode> lodNodeList = new ArrayList<>();
|
||||
|
||||
|
||||
if (chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||
{
|
||||
int startX = detail.startX[i];
|
||||
int startZ = detail.startZ[i];
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
|
||||
Color color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
|
||||
|
||||
|
||||
short height;
|
||||
short depth;
|
||||
|
||||
|
||||
if (!config.useHeightmap)
|
||||
{
|
||||
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
@@ -175,21 +170,22 @@ public class LodNodeBuilder
|
||||
startZ, endX, endZ);
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
lodNodeList.add(new LodQuadTreeNode((byte) detail.detailLevel,
|
||||
LodUtil.convertLevelPos(chunk.getPos().getMinBlockX() + startX, 0, detail.detailLevel),
|
||||
LodUtil.convertLevelPos(chunk.getPos().getMinBlockZ() + startZ, 0, detail.detailLevel),
|
||||
new LodDataPoint(height, depth, color), config.distanceGenerationMode));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return lodNodeList;
|
||||
}
|
||||
|
||||
|
||||
// =====================//
|
||||
// constructor helpers //
|
||||
// =====================//
|
||||
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
*
|
||||
@@ -202,14 +198,14 @@ public class LodNodeBuilder
|
||||
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++)
|
||||
@@ -217,7 +213,7 @@ public class LodNodeBuilder
|
||||
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
|
||||
{
|
||||
numberOfBlocksFound++;
|
||||
|
||||
|
||||
if (numberOfBlocksFound >= numberOfBlocksRequired)
|
||||
{
|
||||
// we found
|
||||
@@ -231,11 +227,11 @@ public class LodNodeBuilder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we never found a valid LOD point
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
*/
|
||||
@@ -246,7 +242,7 @@ public class LodNodeBuilder
|
||||
// doesn't have any info about how low they go
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the highest valid point from the Top
|
||||
*
|
||||
@@ -265,7 +261,7 @@ public class LodNodeBuilder
|
||||
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++)
|
||||
@@ -273,7 +269,7 @@ public class LodNodeBuilder
|
||||
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
|
||||
{
|
||||
numberOfBlocksFound++;
|
||||
|
||||
|
||||
if (numberOfBlocksFound >= numberOfBlocksRequired)
|
||||
{
|
||||
// we found
|
||||
@@ -287,11 +283,11 @@ public class LodNodeBuilder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we never found a valid LOD point
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the highest point from the Top
|
||||
*/
|
||||
@@ -307,10 +303,10 @@ public class LodNodeBuilder
|
||||
highest = newHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the color for the given chunk using biome water color, foliage
|
||||
* color, and grass color.
|
||||
@@ -330,21 +326,21 @@ public class LodNodeBuilder
|
||||
* material color
|
||||
*/
|
||||
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
|
||||
int endZ)
|
||||
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--)
|
||||
{
|
||||
@@ -354,19 +350,19 @@ public class LodNodeBuilder
|
||||
{
|
||||
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
|
||||
@@ -377,21 +373,21 @@ public class LodNodeBuilder
|
||||
}
|
||||
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;
|
||||
@@ -400,24 +396,24 @@ public class LodNodeBuilder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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())
|
||||
{
|
||||
@@ -429,7 +425,7 @@ public class LodNodeBuilder
|
||||
{
|
||||
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
|
||||
}
|
||||
|
||||
|
||||
// plant life
|
||||
else if (blockState.getBlock() instanceof LeavesBlock)
|
||||
{
|
||||
@@ -443,84 +439,84 @@ public class LodNodeBuilder
|
||||
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;
|
||||
|
||||
|
||||
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?
|
||||
*/
|
||||
@@ -540,8 +536,8 @@ public class LodNodeBuilder
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+16
-16
@@ -20,8 +20,8 @@ package com.seibel.lod.builders.lodNodeTemplates;
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
@@ -29,30 +29,30 @@ import net.minecraft.client.renderer.BufferBuilder;
|
||||
/**
|
||||
* This is the abstract class used to create different
|
||||
* BufferBuilders.
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-8-2021
|
||||
*/
|
||||
public abstract class AbstractLodNodeTemplate
|
||||
{
|
||||
public abstract void addLodToBuffer(BufferBuilder buffer,
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail) ;
|
||||
|
||||
LodDimension lodDim, LodDataPoint lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail);
|
||||
|
||||
/** add the given position and color to the buffer */
|
||||
protected void addPosAndColor(BufferBuilder buffer,
|
||||
double x, double y, double z,
|
||||
int red, int green, int blue, int alpha)
|
||||
protected void addPosAndColor(BufferBuilder buffer,
|
||||
double x, double y, double z,
|
||||
int red, int green, int blue, int alpha)
|
||||
{
|
||||
buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex();
|
||||
}
|
||||
|
||||
|
||||
/** Returns in bytes how much buffer memory is required
|
||||
* for one LOD object */
|
||||
public abstract int getBufferMemoryForSingleNode(int level);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Edit the given color as a HSV (Hue Saturation Value) color.
|
||||
*/
|
||||
@@ -61,9 +61,9 @@ public abstract class AbstractLodNodeTemplate
|
||||
float[] hsv = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
|
||||
return Color.getHSBColor(
|
||||
hsv[0], // hue
|
||||
LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f),
|
||||
LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f),
|
||||
LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ import java.awt.Color;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.ShadingMode;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
@@ -39,77 +39,77 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
{
|
||||
public CubicLodNodeTemplate()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail)
|
||||
LodDimension lodDim, LodDataPoint lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail)
|
||||
{
|
||||
AxisAlignedBB bbox;
|
||||
|
||||
|
||||
// add each LOD for the detail level
|
||||
bbox = generateBoundingBox(
|
||||
lod.getLodDataPoint().height,
|
||||
lod.getLodDataPoint().depth,
|
||||
lod.width,
|
||||
lod.height,
|
||||
lod.depth,
|
||||
detail.dataPointWidth,
|
||||
xOffset,
|
||||
yOffset,
|
||||
zOffset);
|
||||
|
||||
Color color = lod.getLodDataPoint().color;
|
||||
|
||||
Color color = lod.color;
|
||||
if (LodConfig.CLIENT.debugMode.get())
|
||||
{
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detail.detailLevel];
|
||||
}
|
||||
|
||||
|
||||
if (bbox != null)
|
||||
{
|
||||
addBoundingBoxToBuffer(buffer, bbox, color);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @Override public void addLodToBuffer(BufferBuilder buffer,
|
||||
* LodQuadTreeDimension lodDim, LodQuadTreeNode lod, double xOffset, double
|
||||
* yOffset, double zOffset, boolean debugging) { AxisAlignedBB bbox;
|
||||
*
|
||||
*
|
||||
* bbox = generateBoundingBox( lod.getLodDataPoint().height,
|
||||
* lod.getLodDataPoint().depth, lod.width, xOffset, yOffset, zOffset);
|
||||
*
|
||||
*
|
||||
* Color color = lod.getLodDataPoint().color;
|
||||
*
|
||||
*
|
||||
* if (bbox != null) { addBoundingBoxToBuffer(buffer, bbox, color); }
|
||||
*
|
||||
*
|
||||
* }
|
||||
*/
|
||||
|
||||
|
||||
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset)
|
||||
{
|
||||
// don't add an LOD if it is empty
|
||||
if (height == -1 && depth == -1)
|
||||
return null;
|
||||
|
||||
|
||||
if (depth == height)
|
||||
{
|
||||
// if the top and bottom points are at the same height
|
||||
// render this LOD as 1 block thick
|
||||
height++;
|
||||
}
|
||||
|
||||
|
||||
return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset);
|
||||
}
|
||||
|
||||
|
||||
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c)
|
||||
{
|
||||
Color topColor = c;
|
||||
Color northSouthColor = c;
|
||||
Color eastWestColor = c;
|
||||
Color bottomColor = c;
|
||||
|
||||
|
||||
// darken the bottom and side colors if requested
|
||||
if (LodConfig.CLIENT.shadingMode.get() == ShadingMode.DARKEN_SIDES)
|
||||
{
|
||||
@@ -119,20 +119,20 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
int northSouthDarkenAmount = 25;
|
||||
int eastWestDarkenAmount = 50;
|
||||
int bottomDarkenAmount = 75;
|
||||
|
||||
|
||||
northSouthColor = new Color(Math.max(0, c.getRed() - northSouthDarkenAmount), Math.max(0, c.getGreen() - northSouthDarkenAmount), Math.max(0, c.getBlue() - northSouthDarkenAmount), c.getAlpha());
|
||||
eastWestColor = new Color(Math.max(0, c.getRed() - eastWestDarkenAmount), Math.max(0, c.getGreen() - eastWestDarkenAmount), Math.max(0, c.getBlue() - eastWestDarkenAmount), c.getAlpha());
|
||||
bottomColor = new Color(Math.max(0, c.getRed() - bottomDarkenAmount), Math.max(0, c.getGreen() - bottomDarkenAmount), Math.max(0, c.getBlue() - bottomDarkenAmount), c.getAlpha());
|
||||
}
|
||||
|
||||
|
||||
// apply the user specified saturation and brightness
|
||||
float saturationMultiplier = LodConfig.CLIENT.saturationMultiplier.get().floatValue();
|
||||
float brightnessMultiplier = LodConfig.CLIENT.brightnessMultiplier.get().floatValue();
|
||||
|
||||
|
||||
topColor = applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier);
|
||||
northSouthColor = applySaturationAndBrightnessMultipliers(northSouthColor, saturationMultiplier, brightnessMultiplier);
|
||||
bottomColor = applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier);
|
||||
|
||||
|
||||
// top (facing up)
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, topColor.getRed(), topColor.getGreen(), topColor.getBlue(), topColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, topColor.getRed(), topColor.getGreen(), topColor.getBlue(), topColor.getAlpha());
|
||||
@@ -143,7 +143,7 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, bottomColor.getRed(), bottomColor.getGreen(), bottomColor.getBlue(), bottomColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, bottomColor.getRed(), bottomColor.getGreen(), bottomColor.getBlue(), bottomColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, bottomColor.getRed(), bottomColor.getGreen(), bottomColor.getBlue(), bottomColor.getAlpha());
|
||||
|
||||
|
||||
// south (facing -Z)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
|
||||
@@ -154,7 +154,7 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
|
||||
|
||||
|
||||
// west (facing -X)
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
|
||||
@@ -166,7 +166,7 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleNode(int detailLevel)
|
||||
{
|
||||
@@ -174,5 +174,5 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
// howManyPointsPerLodChunk
|
||||
return (6 * 4 * (3 + 4));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package com.seibel.lod.builders.lodNodeTemplates;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
@@ -36,9 +36,9 @@ public class DynamicLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail)
|
||||
LodDimension lodDim, LodDataPoint lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
+5
-5
@@ -18,8 +18,8 @@
|
||||
package com.seibel.lod.builders.lodNodeTemplates;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
@@ -34,9 +34,9 @@ public class TriangularLodNodeTemplate extends AbstractLodNodeTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail)
|
||||
LodDimension lodDim, LodDataPoint lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging, LodDetail detail)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@ import com.seibel.lod.builders.LodNodeBufferBuilder;
|
||||
import com.seibel.lod.builders.LodNodeBuilder;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodNodeRenderer;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
@@ -69,165 +68,165 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-4-2021
|
||||
*/
|
||||
public class LodNodeGenWorker implements IWorker
|
||||
{
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
|
||||
|
||||
private boolean threadStarted = false;
|
||||
private LodChunkGenThread thread;
|
||||
private boolean threadStarted = false;
|
||||
private LodChunkGenThread thread;
|
||||
|
||||
/** If a configured feature fails for whatever reason,
|
||||
* add it to this list, this is to hopefully remove any
|
||||
* features that could cause issues down the line. */
|
||||
private static ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> configuredFeaturesToAvoid = new ConcurrentHashMap<>();
|
||||
/** If a configured feature fails for whatever reason,
|
||||
* add it to this list, this is to hopefully remove any
|
||||
* features that could cause issues down the line. */
|
||||
private static ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> configuredFeaturesToAvoid = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
public LodNodeGenWorker(ChunkPos newPos, LodNodeRenderer newLodRenderer,
|
||||
public LodNodeGenWorker(ChunkPos newPos, LodNodeRenderer newLodRenderer,
|
||||
LodNodeBuilder newLodBuilder, LodNodeBufferBuilder newLodBufferBuilder,
|
||||
LodQuadTreeDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
// just a few sanity checks
|
||||
if (newPos == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
|
||||
|
||||
if (newLodRenderer == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null LodRenderer");
|
||||
|
||||
if (newLodBuilder == null)
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
// just a few sanity checks
|
||||
if (newPos == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
|
||||
|
||||
if (newLodRenderer == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null LodRenderer");
|
||||
|
||||
if (newLodBuilder == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
|
||||
|
||||
if (newLodBufferBuilder == null)
|
||||
|
||||
if (newLodBufferBuilder == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodBufferBuilder");
|
||||
|
||||
if (newLodDimension == null)
|
||||
|
||||
if (newLodDimension == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension");
|
||||
|
||||
if (newServerWorld == null)
|
||||
if (newServerWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
|
||||
|
||||
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newLodRenderer,
|
||||
newLodBuilder, newLodBufferBuilder,
|
||||
newLodDimension, newServerWorld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
if (!threadStarted)
|
||||
{
|
||||
if (LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.SERVER)
|
||||
|
||||
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newLodRenderer,
|
||||
newLodBuilder, newLodBufferBuilder,
|
||||
newLodDimension, newServerWorld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
if (!threadStarted)
|
||||
{
|
||||
if (LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.SERVER)
|
||||
{
|
||||
// if we are using SERVER generation that has to be done
|
||||
// synchronously to prevent crashing and harmful
|
||||
// interactions with the normal world generator
|
||||
thread.run();
|
||||
// if we are using SERVER generation that has to be done
|
||||
// synchronously to prevent crashing and harmful
|
||||
// interactions with the normal world generator
|
||||
thread.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
threadStarted = true;
|
||||
|
||||
// useful for debugging
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
threadStarted = true;
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWork()
|
||||
{
|
||||
return !threadStarted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private class LodChunkGenThread implements Runnable
|
||||
{
|
||||
public final ServerWorld serverWorld;
|
||||
public final LodQuadTreeDimension lodDim;
|
||||
public final LodNodeBuilder lodNodeBuilder;
|
||||
public final LodNodeRenderer lodRenderer;
|
||||
private LodNodeBufferBuilder lodBufferBuilder;
|
||||
|
||||
private ChunkPos pos;
|
||||
|
||||
public LodChunkGenThread(ChunkPos newPos, LodNodeRenderer newLodRenderer,
|
||||
LodNodeBuilder newLodBuilder, LodNodeBufferBuilder newLodBufferBuilder,
|
||||
LodQuadTreeDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
pos = newPos;
|
||||
lodRenderer = newLodRenderer;
|
||||
lodNodeBuilder = newLodBuilder;
|
||||
lodBufferBuilder = newLodBufferBuilder;
|
||||
lodDim = newLodDimension;
|
||||
serverWorld = newServerWorld;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWork()
|
||||
{
|
||||
return !threadStarted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private class LodChunkGenThread implements Runnable
|
||||
{
|
||||
public final ServerWorld serverWorld;
|
||||
public final LodDimension lodDim;
|
||||
public final LodNodeBuilder lodNodeBuilder;
|
||||
public final LodNodeRenderer lodRenderer;
|
||||
private LodNodeBufferBuilder lodBufferBuilder;
|
||||
|
||||
private ChunkPos pos;
|
||||
|
||||
public LodChunkGenThread(ChunkPos newPos, LodNodeRenderer newLodRenderer,
|
||||
LodNodeBuilder newLodBuilder, LodNodeBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
pos = newPos;
|
||||
lodRenderer = newLodRenderer;
|
||||
lodNodeBuilder = newLodBuilder;
|
||||
lodBufferBuilder = newLodBufferBuilder;
|
||||
lodDim = newLodDimension;
|
||||
serverWorld = newServerWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
// be added to the current LodDimension
|
||||
if (lodDim.regionIsInRange(pos.x / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.z / LodUtil.REGION_WIDTH_IN_CHUNKS))
|
||||
{
|
||||
// long startTime = System.currentTimeMillis();
|
||||
|
||||
|
||||
switch(LodConfig.CLIENT.distanceGenerationMode.get())
|
||||
{
|
||||
case NONE:
|
||||
// don't generate
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
generateUsingBiomesOnly();
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
generateUsingSurface();
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
generateUsingFeatures();
|
||||
break;
|
||||
case SERVER:
|
||||
// very slow
|
||||
generateWithServer();
|
||||
break;
|
||||
case NONE:
|
||||
// don't generate
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
generateUsingBiomesOnly();
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
generateUsingSurface();
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
generateUsingFeatures();
|
||||
break;
|
||||
case SERVER:
|
||||
// very slow
|
||||
generateWithServer();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
lodRenderer.regenerateLODsNextFrame();
|
||||
|
||||
|
||||
|
||||
|
||||
// if (lodDim.getLodFromCoordinates(pos) != null)
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||
|
||||
|
||||
// shows the pool size, active threads, queued tasks and completed tasks
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
|
||||
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(endTime - startTime);
|
||||
|
||||
|
||||
}// if in range
|
||||
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -238,11 +237,11 @@ public class LodNodeGenWorker implements IWorker
|
||||
// decrement how many threads are running
|
||||
thread.lodBufferBuilder.numberOfChunksWaitingToGenerate.addAndGet(-1);
|
||||
}
|
||||
|
||||
|
||||
}// run
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* takes about 2-5 ms
|
||||
*/
|
||||
@@ -251,26 +250,26 @@ public class LodNodeGenWorker implements IWorker
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
|
||||
|
||||
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
|
||||
ChunkGenerator chunkGen = chunkSource.generator;
|
||||
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// generate fake height data for this LOD
|
||||
int seaLevel = serverWorld.getSeaLevel();
|
||||
|
||||
|
||||
boolean simulateHeight = LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
boolean inTheEnd = false;
|
||||
|
||||
|
||||
// add fake heightmap data so our LODs aren't at height 0
|
||||
Heightmap heightmap = new Heightmap(chunk, LodUtil.DEFAULT_HEIGHTMAP);
|
||||
for(int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++)
|
||||
@@ -280,51 +279,51 @@ public class LodNodeGenWorker implements IWorker
|
||||
if (simulateHeight)
|
||||
{
|
||||
// TODO use the biomes around each block to smooth out the transition
|
||||
|
||||
|
||||
// these heights are of course aren't super accurate,
|
||||
// they are just to simulate height data where there isn't any
|
||||
switch(chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory())
|
||||
{
|
||||
case NETHER:
|
||||
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
|
||||
break;
|
||||
|
||||
case EXTREME_HILLS:
|
||||
heightmap.setHeight(x, z, seaLevel + 30);
|
||||
break;
|
||||
case MESA:
|
||||
heightmap.setHeight(x, z, seaLevel + 20);
|
||||
break;
|
||||
case JUNGLE:
|
||||
heightmap.setHeight(x, z, seaLevel + 20);
|
||||
break;
|
||||
case BEACH:
|
||||
heightmap.setHeight(x, z, seaLevel + 5);
|
||||
break;
|
||||
case NONE:
|
||||
heightmap.setHeight(x, z, 0);
|
||||
break;
|
||||
|
||||
case OCEAN:
|
||||
case RIVER:
|
||||
heightmap.setHeight(x, z, seaLevel);
|
||||
break;
|
||||
|
||||
case THEEND:
|
||||
inTheEnd = true;
|
||||
break;
|
||||
|
||||
// DESERT
|
||||
// FOREST
|
||||
// ICY
|
||||
// MUSHROOM
|
||||
// SAVANNA
|
||||
// SWAMP
|
||||
// TAIGA
|
||||
// PLAINS
|
||||
default:
|
||||
heightmap.setHeight(x, z, seaLevel + 10);
|
||||
break;
|
||||
case NETHER:
|
||||
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
|
||||
break;
|
||||
|
||||
case EXTREME_HILLS:
|
||||
heightmap.setHeight(x, z, seaLevel + 30);
|
||||
break;
|
||||
case MESA:
|
||||
heightmap.setHeight(x, z, seaLevel + 20);
|
||||
break;
|
||||
case JUNGLE:
|
||||
heightmap.setHeight(x, z, seaLevel + 20);
|
||||
break;
|
||||
case BEACH:
|
||||
heightmap.setHeight(x, z, seaLevel + 5);
|
||||
break;
|
||||
case NONE:
|
||||
heightmap.setHeight(x, z, 0);
|
||||
break;
|
||||
|
||||
case OCEAN:
|
||||
case RIVER:
|
||||
heightmap.setHeight(x, z, seaLevel);
|
||||
break;
|
||||
|
||||
case THEEND:
|
||||
inTheEnd = true;
|
||||
break;
|
||||
|
||||
// DESERT
|
||||
// FOREST
|
||||
// ICY
|
||||
// MUSHROOM
|
||||
// SAVANNA
|
||||
// SWAMP
|
||||
// TAIGA
|
||||
// PLAINS
|
||||
default:
|
||||
heightmap.setHeight(x, z, seaLevel + 10);
|
||||
break;
|
||||
}// heightmap switch
|
||||
}
|
||||
else
|
||||
@@ -335,36 +334,29 @@ public class LodNodeGenWorker implements IWorker
|
||||
}
|
||||
}// z
|
||||
}// x
|
||||
|
||||
|
||||
chunk.setHeightmap(LodUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData());
|
||||
|
||||
|
||||
List<LodQuadTreeNode> nodeList;
|
||||
|
||||
|
||||
if (!inTheEnd)
|
||||
{
|
||||
nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig(true, true, false));
|
||||
lodNodeBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(true, true, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are in the end, don't generate any chunks.
|
||||
// Since we don't know where the islands are, everything
|
||||
// generates the same and it looks really bad.
|
||||
nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig(true, true, false));
|
||||
lodNodeBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(true, true, false));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// long startTime = System.currentTimeMillis();
|
||||
|
||||
for(LodQuadTreeNode node : nodeList)
|
||||
{
|
||||
lodDim.addNode(node);
|
||||
}
|
||||
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(endTime - startTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* takes about 10 - 20 ms
|
||||
*/
|
||||
@@ -374,11 +366,11 @@ public class LodNodeGenWorker implements IWorker
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
|
||||
|
||||
|
||||
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
|
||||
ChunkGenerator chunkGen = chunkSource.generator;
|
||||
|
||||
|
||||
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
@@ -386,22 +378,19 @@ public class LodNodeGenWorker implements IWorker
|
||||
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
|
||||
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
|
||||
|
||||
// this feature has proved to be thread safe
|
||||
// so we will add it
|
||||
IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC);
|
||||
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
|
||||
|
||||
List<LodQuadTreeNode> nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig());
|
||||
for(LodQuadTreeNode node : nodeList) {
|
||||
lodDim.addNode(node);
|
||||
}
|
||||
lodNodeBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(DistanceGenerationMode.SURFACE));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* takes about 15 - 20 ms
|
||||
*
|
||||
*
|
||||
* Causes concurrentModification Exceptions,
|
||||
* which could cause instability or world generation bugs
|
||||
*/
|
||||
@@ -411,11 +400,11 @@ public class LodNodeGenWorker implements IWorker
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
|
||||
|
||||
|
||||
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
|
||||
ChunkGenerator chunkGen = chunkSource.generator;
|
||||
|
||||
|
||||
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
@@ -423,8 +412,8 @@ public class LodNodeGenWorker implements IWorker
|
||||
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
|
||||
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
|
||||
|
||||
|
||||
|
||||
// get all the biomes in the chunk
|
||||
HashSet<Biome> biomes = new HashSet<>();
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
@@ -432,7 +421,7 @@ public class LodNodeGenWorker implements IWorker
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2);
|
||||
|
||||
|
||||
// Issue #35
|
||||
// For some reason Jungle biomes cause incredible lag
|
||||
// the features here must be interacting with each other
|
||||
@@ -447,26 +436,26 @@ public class LodNodeGenWorker implements IWorker
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean allowUnstableFeatures = LodConfig.CLIENT.allowUnstableFeatureGeneration.get();
|
||||
|
||||
|
||||
// generate all the features related to this chunk.
|
||||
// this may or may not be thread safe
|
||||
for (Biome biome : biomes)
|
||||
{
|
||||
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.generationSettings.features();
|
||||
|
||||
|
||||
for(int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++)
|
||||
{
|
||||
for(Supplier<ConfiguredFeature<?, ?>> featureSupplier : featuresForState.get(featureStateToGenerate))
|
||||
{
|
||||
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
|
||||
|
||||
|
||||
if (!allowUnstableFeatures &&
|
||||
configuredFeaturesToAvoid.containsKey(configuredFeature.hashCode()))
|
||||
configuredFeaturesToAvoid.containsKey(configuredFeature.hashCode()))
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
|
||||
@@ -477,18 +466,18 @@ public class LodNodeGenWorker implements IWorker
|
||||
// except pray that it doesn't effect the normal world generation
|
||||
// in any harmful way.
|
||||
// Update: this can cause crashes and high CPU usage.
|
||||
|
||||
|
||||
// Issue #35
|
||||
// I tried cloning the config for each feature, but that
|
||||
// path was blocked since I can't clone lambda methods.
|
||||
// I tried using a deep cloning library and discovered
|
||||
// the problem there.
|
||||
// ( https://github.com/kostaskougios/cloning
|
||||
// ( https://github.com/kostaskougios/cloning
|
||||
// and
|
||||
// https://github.com/EsotericSoftware/kryo )
|
||||
|
||||
|
||||
if (!allowUnstableFeatures)
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
catch(UnsupportedOperationException e)
|
||||
@@ -496,45 +485,41 @@ public class LodNodeGenWorker implements IWorker
|
||||
// This will happen when the LodServerWorld
|
||||
// isn't able to return something that a feature
|
||||
// generator needs
|
||||
|
||||
|
||||
if (!allowUnstableFeatures)
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// I'm not sure what happened, print to the log
|
||||
|
||||
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
e.printStackTrace();
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
|
||||
|
||||
if (!allowUnstableFeatures)
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generate a Lod like normal
|
||||
|
||||
List<LodQuadTreeNode> nodeList = lodNodeBuilder.generateLodNodeFromChunk(chunk, new LodBuilderConfig());
|
||||
|
||||
for(LodQuadTreeNode node : nodeList) {
|
||||
lodDim.addNode(node);
|
||||
}
|
||||
lodNodeBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(DistanceGenerationMode.FEATURES));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* on pre generated chunks 0 - 1 ms
|
||||
* on un generated chunks 0 - 50 ms
|
||||
* on un generated chunks 0 - 50 ms
|
||||
* with the median seeming to hover around 15 - 30 ms
|
||||
* and outliers in the 100 - 200 ms range
|
||||
*
|
||||
*
|
||||
* Note this should not be multithreaded and does cause server/simulation lag
|
||||
* (Higher lag for generating than loading)
|
||||
*/
|
||||
@@ -542,31 +527,31 @@ public class LodNodeGenWorker implements IWorker
|
||||
{
|
||||
lodNodeBuilder.generateLodNodeAsync(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), ClientProxy.getLodWorld(), serverWorld);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// Unused methods //
|
||||
//================//
|
||||
|
||||
|
||||
// Sadly I wasn't able to get these to work,
|
||||
// they are here for documentation purposes
|
||||
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
|
||||
private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config)
|
||||
{
|
||||
IPlacementConfig placementConfig = null;
|
||||
|
||||
|
||||
Class oldConfigClass = config.decorator.config().getClass();
|
||||
|
||||
|
||||
if (oldConfigClass == FeatureSpreadConfig.class)
|
||||
{
|
||||
FeatureSpreadConfig oldPlacementConfig = (FeatureSpreadConfig) config.decorator.config();
|
||||
FeatureSpread oldSpread = oldPlacementConfig.count();
|
||||
|
||||
|
||||
placementConfig = new FeatureSpreadConfig(oldSpread);
|
||||
}
|
||||
else if(oldConfigClass == DecoratedPlacementConfig.class)
|
||||
@@ -584,29 +569,29 @@ public class LodNodeGenWorker implements IWorker
|
||||
// ClientProxy.LOGGER.debug("unkown decorated placement config: \"" + config.decorator.config().getClass() + "\"");
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ConfiguredPlacement<?> newPlacement = new ConfiguredPlacement(config.decorator.decorator, placementConfig);
|
||||
return new DecoratedFeatureConfig(config.feature, newPlacement);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config)
|
||||
{
|
||||
WeightedBlockStateProvider provider = new WeightedBlockStateProvider();
|
||||
for(Entry<BlockState> state : ((WeightedBlockStateProvider) config.stateProvider).weightedList.entries)
|
||||
provider.weightedList.entries.add(state);
|
||||
|
||||
|
||||
HashSet<Block> whitelist = new HashSet<>();
|
||||
for(Block block : config.whitelist)
|
||||
whitelist.add(block);
|
||||
|
||||
|
||||
HashSet<BlockState> blacklist = new HashSet<>();
|
||||
for(BlockState state : config.blacklist)
|
||||
blacklist.add(state);
|
||||
|
||||
|
||||
blacklist.add(state);
|
||||
|
||||
|
||||
BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer);
|
||||
builder.whitelist(whitelist);
|
||||
builder.blacklist(blacklist);
|
||||
@@ -617,43 +602,43 @@ public class LodNodeGenWorker implements IWorker
|
||||
if(config.needWater) { builder.needWater(); }
|
||||
if(config.project) { builder.noProjection(); }
|
||||
builder.tries(config.tries);
|
||||
|
||||
|
||||
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stops the current genThreads if they are running
|
||||
* and then recreates the Executer service. <br><br>
|
||||
*
|
||||
* This is done to clear any outstanding tasks
|
||||
* that may exist after the player leaves their current world.
|
||||
* If this isn't done unfinished tasks may be left in the queue
|
||||
* preventing new LodChunks form being generated.
|
||||
*/
|
||||
public static void restartExecuterService()
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stops the current genThreads if they are running
|
||||
* and then recreates the Executer service. <br><br>
|
||||
*
|
||||
* This is done to clear any outstanding tasks
|
||||
* that may exist after the player leaves their current world.
|
||||
* If this isn't done unfinished tasks may be left in the queue
|
||||
* preventing new LodChunks form being generated.
|
||||
*/
|
||||
public static void restartExecuterService()
|
||||
{
|
||||
if (genThreads != null && !genThreads.isShutdown())
|
||||
{
|
||||
genThreads.shutdownNow();
|
||||
}
|
||||
if (genThreads != null && !genThreads.isShutdown())
|
||||
{
|
||||
genThreads.shutdownNow();
|
||||
}
|
||||
genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* performance/generation tests related to
|
||||
* serverWorld.getChunk(x, z, ChunkStatus. *** )
|
||||
|
||||
|
||||
true/false is whether they generated blocks or not
|
||||
the time is how long it took to generate
|
||||
|
||||
|
||||
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
|
||||
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
|
||||
ChunkStatus.BIOMES 1 - 10 ms false (no height)
|
||||
@@ -666,11 +651,11 @@ public class LodNodeGenWorker implements IWorker
|
||||
ChunkStatus.LIGHT 20 - 40 ms true
|
||||
ChunkStatus.FULL 30 - 50 ms true
|
||||
ChunkStatus.SPAWN 50 - 80 ms true
|
||||
|
||||
|
||||
|
||||
|
||||
At this point I would suggest using FEATURES, as it generates snow and trees
|
||||
(and any other object that is needed to make biomes distinct)
|
||||
|
||||
|
||||
Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ public class LodConfig
|
||||
/** 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;
|
||||
|
||||
@@ -146,8 +150,8 @@ public class LodConfig
|
||||
+ " " + 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"
|
||||
@@ -155,7 +159,7 @@ public class LodConfig
|
||||
+ " 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"
|
||||
|
||||
+186
-195
@@ -17,6 +17,11 @@
|
||||
*/
|
||||
package com.seibel.lod.handlers;
|
||||
|
||||
import com.seibel.lod.objects.*;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
@@ -26,110 +31,110 @@ import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.objects.LodQuadTree;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
|
||||
/**
|
||||
* This object handles creating LodRegions
|
||||
* from files and saving LodRegion objects
|
||||
* to file.
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-14-2021
|
||||
*/
|
||||
public class LodQuadTreeDimensionFileHandler
|
||||
{
|
||||
/** This is what separates each piece of data */
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
|
||||
|
||||
private LodQuadTreeDimension loadedDimension = null;
|
||||
public long regionLastWriteTime[][];
|
||||
|
||||
private File dimensionDataSaveFolder;
|
||||
|
||||
/** lod */
|
||||
private static final String FILE_NAME_PREFIX = "lod";
|
||||
/** .txt */
|
||||
private static final String FILE_EXTENSION = ".txt";
|
||||
/** .tmp <br>
|
||||
* Added to the end of the file path when saving to prevent
|
||||
* nulling a currently existing file. <br>
|
||||
* After the file finishes saving it will end with
|
||||
* FILE_EXTENSION. */
|
||||
private static final String TMP_FILE_EXTENSION = ".tmp";
|
||||
|
||||
/** This is the file version currently accepted by this
|
||||
* file handler, older versions (smaller numbers) will be deleted and overwritten,
|
||||
* newer versions (larger numbers) will be ignored and won't be read. */
|
||||
public static final int LOD_SAVE_FILE_VERSION = 3;
|
||||
|
||||
/** This is the string written before the file version */
|
||||
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
|
||||
|
||||
/** Allow saving asynchronously, but never try to save multiple regions
|
||||
* at a time */
|
||||
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||
|
||||
|
||||
public LodQuadTreeDimensionFileHandler(File newSaveFolder, LodQuadTreeDimension newLoadedDimension)
|
||||
{
|
||||
if (newSaveFolder == null)
|
||||
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
|
||||
|
||||
dimensionDataSaveFolder = newSaveFolder;
|
||||
|
||||
loadedDimension = newLoadedDimension;
|
||||
// these two variable are used in sync with the LodDimension
|
||||
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
|
||||
for(int i = 0; i < loadedDimension.getWidth(); i++)
|
||||
for(int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
regionLastWriteTime[i][j] = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// read from file //
|
||||
//================//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the LodQuadTree region at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodQuadTree loadRegionFromFile(RegionPos regionPos)
|
||||
{
|
||||
int regionX = regionPos.x;
|
||||
int regionZ = regionPos.z;
|
||||
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ);
|
||||
if(FILE_EXTENSION == ".bin"){
|
||||
try {
|
||||
ObjectInputStream is = new ObjectInputStream(new FileInputStream(fileName));
|
||||
List<LodQuadTreeNode> dataList = (List<LodQuadTreeNode>) is.readObject();
|
||||
//LodQuadTree region = (LodQuadTree) is.readObject();
|
||||
is.close();
|
||||
return new LodQuadTree(dataList, regionX, regionZ);
|
||||
//return region;
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new LodQuadTree(new ArrayList<>(), regionX, regionZ);
|
||||
//return null;
|
||||
}
|
||||
public class LodDimensionFileHandler {
|
||||
/**
|
||||
* This is what separates each piece of data
|
||||
*/
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
|
||||
|
||||
private LodDimension loadedDimension = null;
|
||||
public long regionLastWriteTime[][];
|
||||
|
||||
private File dimensionDataSaveFolder;
|
||||
|
||||
/**
|
||||
* lod
|
||||
*/
|
||||
private static final String FILE_NAME_PREFIX = "lod";
|
||||
/**
|
||||
* .txt
|
||||
*/
|
||||
private static final String FILE_EXTENSION = ".bin";
|
||||
/**
|
||||
* .tmp <br>
|
||||
* Added to the end of the file path when saving to prevent
|
||||
* nulling a currently existing file. <br>
|
||||
* After the file finishes saving it will end with
|
||||
* FILE_EXTENSION.
|
||||
*/
|
||||
private static final String TMP_FILE_EXTENSION = ".tmp";
|
||||
|
||||
/**
|
||||
* This is the file version currently accepted by this
|
||||
* file handler, older versions (smaller numbers) will be deleted and overwritten,
|
||||
* newer versions (larger numbers) will be ignored and won't be read.
|
||||
*/
|
||||
public static final int LOD_SAVE_FILE_VERSION = 3;
|
||||
|
||||
/**
|
||||
* This is the string written before the file version
|
||||
*/
|
||||
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
|
||||
|
||||
/**
|
||||
* Allow saving asynchronously, but never try to save multiple regions
|
||||
* at a time
|
||||
*/
|
||||
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||
|
||||
|
||||
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension) {
|
||||
if (newSaveFolder == null)
|
||||
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
|
||||
|
||||
dimensionDataSaveFolder = newSaveFolder;
|
||||
|
||||
loadedDimension = newLoadedDimension;
|
||||
// these two variable are used in sync with the LodDimension
|
||||
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
|
||||
for (int i = 0; i < loadedDimension.getWidth(); i++)
|
||||
for (int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
regionLastWriteTime[i][j] = -1;
|
||||
}
|
||||
|
||||
|
||||
//================//
|
||||
// read from file //
|
||||
//================//
|
||||
|
||||
|
||||
/**
|
||||
* Return the LodRegion region at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(RegionPos regionPos) {
|
||||
int regionX = regionPos.x;
|
||||
int regionZ = regionPos.z;
|
||||
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ);
|
||||
if (FILE_EXTENSION == ".bin") {
|
||||
try {
|
||||
System.out.println(fileName);
|
||||
ObjectInputStream is = new ObjectInputStream(new FileInputStream(fileName));
|
||||
|
||||
|
||||
LevelContainer levelContainer = (LevelContainer) is.readObject();
|
||||
is.close();
|
||||
return new LodRegion(levelContainer, regionPos);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
/*
|
||||
if(FILE_EXTENSION == ".txt") {
|
||||
File f = new File(fileName);
|
||||
|
||||
@@ -210,71 +215,65 @@ public class LodQuadTreeDimensionFileHandler
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// Save to File //
|
||||
//==============//
|
||||
|
||||
/**
|
||||
* Save all dirty regions in this LodDimension to file.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
saveRegionToFile(loadedDimension.regions[i][j]);
|
||||
loadedDimension.isRegionDirty[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a specific region to disk.<br>
|
||||
* Note: <br>
|
||||
* 1. If a file already exists for a newer version
|
||||
* the file won't be written.<br>
|
||||
* 2. This will save to the LodDimension that this
|
||||
* handler is associated with.
|
||||
*/
|
||||
private void saveRegionToFile(LodQuadTree region)
|
||||
{
|
||||
// convert to region coordinates
|
||||
int x = region.getLodNodeData().posX;
|
||||
int z = region.getLodNodeData().posZ;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
File oldFile = new File(getFileNameAndPathForRegion(x, z));
|
||||
//==============//
|
||||
// Save to File //
|
||||
//==============//
|
||||
|
||||
if(FILE_EXTENSION == ".bin"){
|
||||
try {
|
||||
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(getFileNameAndPathForRegion(x, z)));
|
||||
os.writeObject(region.getNodeListWithMask(LodQuadTreeDimension.FULL_COMPLEXITY_MASK, false, true));
|
||||
//os.writeObject(region);
|
||||
os.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Save all dirty regions in this LodDimension to file.
|
||||
*/
|
||||
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) {
|
||||
saveRegionToFile(loadedDimension.regions[i][j]);
|
||||
loadedDimension.isRegionDirty[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a specific region to disk.<br>
|
||||
* Note: <br>
|
||||
* 1. If a file already exists for a newer version
|
||||
* the file won't be written.<br>
|
||||
* 2. This will save to the LodDimension that this
|
||||
* handler is associated with.
|
||||
*/
|
||||
private void saveRegionToFile(LodRegion region) {
|
||||
// convert to region coordinates
|
||||
int x = region.regionPosX;
|
||||
int z = region.regionPosZ;
|
||||
|
||||
// get minimum level
|
||||
|
||||
byte minDetailLevel = region.getMinDetailLevel();
|
||||
|
||||
File oldFile = new File(getFileNameAndPathForRegion(x, z));
|
||||
if (!oldFile.getParentFile().exists())
|
||||
oldFile.getParentFile().mkdirs();
|
||||
if (FILE_EXTENSION == ".bin") {
|
||||
try {
|
||||
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(getFileNameAndPathForRegion(x, z)));
|
||||
os.writeObject(region.getLevel((byte) 0));
|
||||
os.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
/*
|
||||
if(FILE_EXTENSION == ".txt") {
|
||||
try {
|
||||
// make sure the file and folder exists
|
||||
@@ -340,41 +339,33 @@ public class LodQuadTreeDimensionFileHandler
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't ready to read and write. <br><br>
|
||||
*
|
||||
* example: "lod.0.0.txt"
|
||||
*/
|
||||
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)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't ready to read and write. <br><br>
|
||||
* <p>
|
||||
* example: "lod.0.0.txt"
|
||||
*/
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class LevelContainer implements Serializable {
|
||||
|
||||
public final byte detailLevel;
|
||||
|
||||
public final byte[][][] colors;
|
||||
|
||||
public final short[][] height;
|
||||
|
||||
public final short[][] depth;
|
||||
|
||||
public final byte[][] generationType;
|
||||
|
||||
public final 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;
|
||||
this.depth = depth;
|
||||
this.generationType = generationType;
|
||||
this.dataExistence = dataExistence;
|
||||
}
|
||||
/*
|
||||
public LevelContainer(String data, byte detailLevel){
|
||||
int size = detailLevel*detailLevel;
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
public class LevelPos implements Cloneable{
|
||||
public final byte detailLevel;
|
||||
public final int posX;
|
||||
public final int posZ;
|
||||
|
||||
public LevelPos(byte detailLevel, int posX, int posZ){
|
||||
this.posX = posX;
|
||||
this.posZ = posZ;
|
||||
this.detailLevel = detailLevel;
|
||||
}
|
||||
|
||||
public LevelPos convert( byte newDetailLevel){
|
||||
if(newDetailLevel >= detailLevel) {
|
||||
return new LevelPos(
|
||||
newDetailLevel,
|
||||
Math.floorDiv(posX, (int) Math.pow(2, newDetailLevel - detailLevel)),
|
||||
Math.floorDiv(posZ, (int) Math.pow(2, newDetailLevel - detailLevel)));
|
||||
}else{
|
||||
return new LevelPos(
|
||||
newDetailLevel,
|
||||
posX * (int) Math.pow(2, detailLevel - newDetailLevel),
|
||||
posZ * (int) Math.pow(2, detailLevel - newDetailLevel));
|
||||
}
|
||||
}
|
||||
|
||||
public LevelPos regionModule(){
|
||||
return new LevelPos(
|
||||
detailLevel,
|
||||
Math.floorMod(posX, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)),
|
||||
Math.floorMod(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)));
|
||||
}
|
||||
|
||||
public RegionPos getRegionPos(){
|
||||
return new RegionPos(
|
||||
Math.floorDiv(posX, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)),
|
||||
Math.floorDiv(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)));
|
||||
}
|
||||
|
||||
|
||||
public LevelPos clone(){
|
||||
return new LevelPos(detailLevel,posX,posZ);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
String s = (detailLevel + " " + posX + " " + posZ);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import java.awt.*;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler;
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
/**
|
||||
@@ -34,7 +34,7 @@ import com.seibel.lod.util.LodUtil;
|
||||
public class LodDataPoint implements Serializable
|
||||
{
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
private static final char DATA_DELIMITER = LodQuadTreeDimensionFileHandler.DATA_DELIMITER;
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This object holds all loaded LOD regions
|
||||
* for a given dimension.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 8-8-2021
|
||||
*/
|
||||
public class LodDimension
|
||||
{
|
||||
|
||||
public final DimensionType dimension;
|
||||
|
||||
/** measured in regions */
|
||||
private volatile int width;
|
||||
/** measured in regions */
|
||||
private volatile int halfWidth;
|
||||
|
||||
|
||||
public volatile LodRegion regions[][];
|
||||
public volatile boolean isRegionDirty[][];
|
||||
|
||||
private volatile RegionPos center;
|
||||
|
||||
private LodDimensionFileHandler fileHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Creates the dimension centered at (0,0)
|
||||
*
|
||||
* @param newWidth in regions
|
||||
*/
|
||||
public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newWidth)
|
||||
{
|
||||
dimension = newDimension;
|
||||
width = newWidth;
|
||||
halfWidth = (int)Math.floor(width / 2);
|
||||
|
||||
if(newDimension != null && lodWorld != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
File saveDir;
|
||||
if (mc.hasSingleplayerServer())
|
||||
{
|
||||
// local world
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
|
||||
|
||||
// provider needs a separate variable to prevent
|
||||
// the compiler from complaining
|
||||
ServerChunkProvider provider = serverWorld.getChunkSource();
|
||||
saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod");
|
||||
}
|
||||
else
|
||||
{
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level));
|
||||
}
|
||||
|
||||
fileHandler = new LodDimensionFileHandler(saveDir, this);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// the file handler wasn't able to be created
|
||||
// we won't be able to read or write any files
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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++)
|
||||
isRegionDirty[i][j] = false;
|
||||
|
||||
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>
|
||||
*
|
||||
* Synchronized to prevent multiple moves happening on top of each other.
|
||||
*/
|
||||
public synchronized void move(RegionPos regionOffset)
|
||||
{
|
||||
int xOffset = regionOffset.x;
|
||||
int zOffset = regionOffset.z;
|
||||
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total size, just delete the current data
|
||||
// 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 z = 0; z < width; z++)
|
||||
{
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// X
|
||||
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 z = 0; z < width; z++)
|
||||
{
|
||||
if(x + xOffset < width)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything over to the right (as the center moves to the left)
|
||||
for(int x = width - 1; x >= 0; x--)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
if(x + xOffset >= 0)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Z
|
||||
if(zOffset > 0)
|
||||
{
|
||||
// move everything up (as the center moves down)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
if(z + zOffset < width)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything down (as the center moves up)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = width - 1; z >= 0; z--)
|
||||
{
|
||||
if(z + zOffset >= 0)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the region at the given X and Z
|
||||
* <br>
|
||||
* Returns null if the region doesn't exist
|
||||
* or is outside the loaded area.
|
||||
*/
|
||||
public LodRegion getRegion(RegionPos regionPos)
|
||||
{
|
||||
int xIndex = (regionPos.x - center.x) + halfWidth;
|
||||
int zIndex = (regionPos.z - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(regionPos.x, regionPos.z))
|
||||
// out of range
|
||||
return null;
|
||||
|
||||
if (regions[xIndex][zIndex] == null)
|
||||
{
|
||||
|
||||
regions[xIndex][zIndex] = getRegionFromFile(regionPos);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the LodRegion at the location of newRegion with newRegion.
|
||||
*
|
||||
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
|
||||
*/
|
||||
public void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
|
||||
{
|
||||
int xIndex = (newRegion.regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (center.z - newRegion.regionPosZ) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ))
|
||||
// out of range
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
|
||||
regions[xIndex][zIndex] = newRegion;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*this method creates all null regions
|
||||
*/
|
||||
public void initializeNullRegions()
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
RegionPos regionPos;
|
||||
LodRegion region;
|
||||
|
||||
for(int x = 0; x < regions.length; x++)
|
||||
{
|
||||
for(int z = 0; z < regions.length; z++)
|
||||
{
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
regionPos = new RegionPos(regionX,regionZ);
|
||||
region = getRegion(regionPos);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
// if no region exists, create it
|
||||
region = new LodRegion((byte) 0,regionPos);
|
||||
addOrOverwriteRegion(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the given LOD to this dimension at the coordinate
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinates it will be overwritten.
|
||||
*/
|
||||
public synchronized Boolean addData(LevelPos levelPos, LodDataPoint lodDataPoint, DistanceGenerationMode generationMode, boolean update, boolean dontSave)
|
||||
{
|
||||
// don't continue if the region can't be saved
|
||||
RegionPos regionPos = levelPos.getRegionPos();
|
||||
if (!regionIsInRange(regionPos.x, regionPos.z))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LodRegion region = getRegion(regionPos);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
// if no region exists, create it
|
||||
region = new LodRegion((byte) 0,regionPos);
|
||||
addOrOverwriteRegion(region);
|
||||
}
|
||||
boolean nodeAdded = region.setData(levelPos,lodDataPoint,(byte) generationMode.complexity,true);
|
||||
// only save valid LODs to disk
|
||||
if (!dontSave && fileHandler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (regionPos.x - center.x) + halfWidth;
|
||||
int zIndex = (regionPos.z - center.z) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
}
|
||||
catch(ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
// This method was probably called when the dimension was changing size.
|
||||
// Hopefully this shouldn't be an issue.
|
||||
}
|
||||
}
|
||||
return nodeAdded;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the LodNodeData at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public synchronized LodDataPoint getData(ChunkPos chunkPos)
|
||||
{
|
||||
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z);
|
||||
return getData(levelPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data point at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public synchronized LodDataPoint getData(LevelPos levelPos)
|
||||
{
|
||||
if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodRegion region = getRegion(levelPos.getRegionPos());
|
||||
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return region.getData(levelPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if and only if the node at that position exist
|
||||
*/
|
||||
public synchronized boolean hasThisPositionBeenGenerated(ChunkPos chunkPos)
|
||||
{
|
||||
LodRegion region = getRegion(LodUtil.convertGenericPosToRegionPos(chunkPos.x, chunkPos.z, LodUtil.CHUNK_DETAIL_LEVEL));
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return region.hasDataBeenGenerated(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if and only if the node at that position exist
|
||||
*/
|
||||
|
||||
public synchronized boolean hasThisPositionBeenGenerated(LevelPos levelPos)
|
||||
{
|
||||
LodRegion region = getRegion(levelPos.getRegionPos());
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return region.hasDataBeenGenerated(levelPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if and only if the node at that position exist
|
||||
*/
|
||||
public synchronized boolean doesDataExist(LevelPos levelPos)
|
||||
{
|
||||
LodRegion region = getRegion(levelPos.getRegionPos());
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return region.doesDataExist(levelPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if and only if the node at that position exist
|
||||
*/
|
||||
public synchronized DistanceGenerationMode getGenerationMode(LevelPos levelPos)
|
||||
{
|
||||
LodRegion region = getRegion(levelPos.getRegionPos());
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return DistanceGenerationMode.NONE;
|
||||
}
|
||||
|
||||
return region.getGenerationMode(levelPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the region at the given X and Z coordinates from the
|
||||
* RegionFileHandler.
|
||||
*/
|
||||
public synchronized LodRegion getRegionFromFile(RegionPos regionPos)
|
||||
{
|
||||
if (fileHandler != null)
|
||||
return fileHandler.loadRegionFromFile(regionPos);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all dirty regions in this LodDimension to file.
|
||||
*/
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileHandler.saveDirtyRegionsToFileAsync();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the region at the given X and Z coordinates
|
||||
* is within the loaded range.
|
||||
*/
|
||||
public boolean regionIsInRange(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - center.x) + halfWidth;
|
||||
int zIndex = (regionZ - center.z) + halfWidth;
|
||||
|
||||
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public int getCenterX()
|
||||
{
|
||||
return center.x;
|
||||
}
|
||||
|
||||
public int getCenterZ()
|
||||
{
|
||||
return center.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO Double check that this method works as expected
|
||||
*
|
||||
* Returns how many non-null LodChunks
|
||||
* are stored in this LodDimension.
|
||||
*/
|
||||
public int getNumberOfLods()
|
||||
{
|
||||
/**TODO **/
|
||||
int numbLods = 0;
|
||||
return numbLods;
|
||||
}
|
||||
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
if (regions != null)
|
||||
{
|
||||
// we want to get the length directly from the
|
||||
// source to make sure it is in sync with region
|
||||
// and isRegionDirty
|
||||
return regions.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
public void setRegionWidth(int newWidth)
|
||||
{
|
||||
width = newWidth;
|
||||
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++)
|
||||
isRegionDirty[i][j] = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "";
|
||||
|
||||
s += "dim: " + dimension.toString() + "\t";
|
||||
s += "(" + center.x + "," + center.z + ")";
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1,665 +0,0 @@
|
||||
/*
|
||||
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
/**
|
||||
* This object contains all data useful to render LodBlock in a region (32x32 chunk to 512x512 block)
|
||||
* for every node it contains the border of said node, its size, its block position in the world,
|
||||
* color, height and depth.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 8-7-2021
|
||||
*/
|
||||
public class LodQuadTree implements Serializable
|
||||
{
|
||||
// note:
|
||||
// The term node correspond to a LodQuadTree object
|
||||
|
||||
/*
|
||||
Example on how a LodQuadTreeRegion would be rendered (the number corresponds to the level of the LodNodeData)
|
||||
.___.___._______._______________.
|
||||
|6|6| 7 | | |
|
||||
|6|6|___| 8 | |
|
||||
| 7 | 7 | | |
|
||||
|___|___|_______| 9 |
|
||||
| | | |
|
||||
| 8 | 8 | |
|
||||
| | | |
|
||||
|_______|_______|_______________|
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| 9 | 9 |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
|_______________|_______________|
|
||||
*/
|
||||
|
||||
|
||||
/** If true SetNodesAtLowerLevel will update the color and height of all higher level nodes */
|
||||
public static boolean UPDATE_HIGHER_LEVEL = true;
|
||||
|
||||
//data useful to render
|
||||
//if children are present then lodNodeData should be a combination of the lodData of the child. This can be
|
||||
//turned off by deselecting the recursive update in all update method.
|
||||
private LodQuadTreeNode lodNode;
|
||||
|
||||
/*
|
||||
.____.____.
|
||||
| NW | NE | |
|
||||
|____|____| Z
|
||||
| SW | SE | |
|
||||
|____|____| V
|
||||
-----X---->
|
||||
|
||||
North - negative z
|
||||
South - positive z
|
||||
West - negative x
|
||||
east - positive x
|
||||
*/
|
||||
|
||||
|
||||
/** treeFull is true if and only if all child are not null */
|
||||
private boolean treeFull;
|
||||
private boolean treeEmpty;
|
||||
|
||||
/**
|
||||
* The four child are based on the four diagonal cardinal directions.
|
||||
* The first index is for North and South and the second index is for East and West. <br>
|
||||
* Children should always be null for level 0.
|
||||
*/
|
||||
private final LodQuadTree[][] children;
|
||||
|
||||
//parent should always be null for level 9, and always not null for other levels.
|
||||
private final LodQuadTree parent;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for level 0 without LodNodeData (region level constructor)
|
||||
*
|
||||
* @param regionPos indicate the region position of the node
|
||||
*/
|
||||
//maybe the use of useLevelCoordinate could be changed. I could use a builder to do all this work.
|
||||
public LodQuadTree(RegionPos regionPos)
|
||||
{
|
||||
this(null, new LodQuadTreeNode(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for generic world without LOD data
|
||||
*
|
||||
* @param parent parent of this node
|
||||
* @param level level of this note
|
||||
* @param regionPos position of the node
|
||||
*/
|
||||
public LodQuadTree(LodQuadTree parent, byte level, RegionPos regionPos)
|
||||
{
|
||||
this(parent, new LodQuadTreeNode(level, regionPos.x, regionPos.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor using a dataList
|
||||
*
|
||||
* @param dataList list of LodNodeData to put in this LodQuadTree
|
||||
* @param regionX x region coordinate
|
||||
* @param regionZ z region coordinate
|
||||
*/
|
||||
public LodQuadTree(List<LodQuadTreeNode> dataList, int regionX, int regionZ)
|
||||
{
|
||||
this(null, new LodQuadTreeNode(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ));
|
||||
setNodesAtLowerLevel(dataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a generic LodQuadTree using a LodNodeData
|
||||
*
|
||||
* @param newLodNode LodQuadTreeNode containing all the information of this node
|
||||
*/
|
||||
public LodQuadTree(LodQuadTree newParent, LodQuadTreeNode newLodNode)
|
||||
{
|
||||
parent = newParent;
|
||||
lodNode = newLodNode;
|
||||
children = new LodQuadTree[2][2];
|
||||
treeEmpty = true;
|
||||
treeFull = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param dataList list of data to put in the node
|
||||
*/
|
||||
public void setNodesAtLowerLevel(List<LodQuadTreeNode> dataList)
|
||||
{
|
||||
for (LodQuadTreeNode lodQuadTreeNode : dataList)
|
||||
{
|
||||
this.setNodeAtLowerLevel(lodQuadTreeNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newLowerLodNode data to put in the node
|
||||
* @return true only if the QuadTree has been changed
|
||||
*/
|
||||
public boolean setNodeAtLowerLevel(LodQuadTreeNode newLowerLodNode)
|
||||
{
|
||||
byte targetLevel = newLowerLodNode.detailLevel;
|
||||
byte currentLevel = lodNode.detailLevel;
|
||||
|
||||
if (targetLevel >= currentLevel)
|
||||
{
|
||||
// we can't add a node that has a equal or higher
|
||||
// detail level than this region
|
||||
return false;
|
||||
}
|
||||
|
||||
short widthRatio = (short) (lodNode.width / (2 * newLowerLodNode.width));
|
||||
int WE = Math.abs(Math.floorDiv(newLowerLodNode.posX , widthRatio) % 2);
|
||||
int NS = Math.abs(Math.floorDiv(newLowerLodNode.posZ , widthRatio) % 2);
|
||||
|
||||
if (getChild(NS, WE) == null)
|
||||
{
|
||||
// if this child doesn't exist, create an empty one
|
||||
setChild(NS, WE);
|
||||
}
|
||||
|
||||
LodQuadTree child = getChild(NS, WE);
|
||||
if (lodNode.compareComplexity(newLowerLodNode) > 0)
|
||||
{
|
||||
// the node we want to introduce is less complex than the current node
|
||||
// we don't want to override higher complexity with lower complexity
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetLevel == currentLevel - 1)
|
||||
{
|
||||
// we are at the level we want to add the newLowerLodNode
|
||||
child.setLodNodeData(newLowerLodNode);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// recurse until we reach the level we want to add the newLowerLodNode
|
||||
return child.setNodeAtLowerLevel(newLowerLodNode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the LodQuadTreeNode at the given chunkPos.
|
||||
* Returns null if no such LodQuadTreeNode exists.
|
||||
*/
|
||||
public LodQuadTreeNode getNodeAtChunkPos(ChunkPos chunkPos)
|
||||
{
|
||||
return getNodeAtPos(chunkPos.x, chunkPos.z, LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the LodQuadTreeNode at the given generic pos and detailLevel.
|
||||
* Returns null if no such LodQuadTreeNode exists.
|
||||
*/
|
||||
public LodQuadTreeNode getNodeAtPos(int posX, int posZ, int detailLevel)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getNodeAtChunkPos given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
|
||||
byte currentDetailLevel = lodNode.detailLevel;
|
||||
if (detailLevel == currentDetailLevel)
|
||||
{
|
||||
return lodNode;
|
||||
}
|
||||
else if (detailLevel < currentDetailLevel)
|
||||
{
|
||||
// the detail level we need is lower, go down a layer
|
||||
short widthRatio = (short) (lodNode.width / (2 * Math.pow(2, detailLevel)));
|
||||
int WE = Math.abs(Math.floorDiv(posX , widthRatio) % 2);
|
||||
int NS = Math.abs(Math.floorDiv(posZ , widthRatio) % 2);
|
||||
if (getChild(NS, WE) == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
LodQuadTree child = getChild(NS, WE);
|
||||
return child.getNodeAtPos(posX, posZ, detailLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the detail level was higher than this region's
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the LodQuadTree at the given generic pos and detailLevel.
|
||||
* Returns null if no such LodQuadTreeNode exists.
|
||||
*/
|
||||
public LodQuadTree getLevelAtPos(int posX, int posZ, int detailLevel)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getNodeAtChunkPos given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
|
||||
byte currentDetailLevel = lodNode.detailLevel;
|
||||
if (detailLevel == currentDetailLevel)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
else if (detailLevel < currentDetailLevel)
|
||||
{
|
||||
// the detail level we need is lower, go down a layer
|
||||
short widthRatio = (short) (lodNode.width / (2 * Math.pow(2, detailLevel)));
|
||||
int WE = Math.abs(Math.floorDiv(posX , widthRatio) % 2);
|
||||
int NS = Math.abs(Math.floorDiv(posZ , widthRatio) % 2);
|
||||
if (getChild(NS, WE) == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
LodQuadTree child = getChild(NS, WE);
|
||||
return child.getLevelAtPos(posX, posZ, detailLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the detail level was higher than this region's
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a child with the given data into the given position.
|
||||
*
|
||||
* @param newLodNode data to put in the child
|
||||
*/
|
||||
public void setChild(LodQuadTreeNode newLodNode)
|
||||
{
|
||||
// the child must be 1 detail level lower than this region
|
||||
if (newLodNode.detailLevel == lodNode.detailLevel - 1)
|
||||
{
|
||||
int WE = newLodNode.posX % lodNode.posX;
|
||||
int NS = newLodNode.posZ % lodNode.posZ;
|
||||
children[NS][WE] = new LodQuadTree(this, lodNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an empty child in the given position.
|
||||
*
|
||||
* @param NS North-South position
|
||||
* @param WE West-East position
|
||||
*/
|
||||
public void setChild(int NS, int WE)
|
||||
{
|
||||
// TODO is this correctly converting to a regionPos?
|
||||
int childRegionX = lodNode.posX * 2 + WE;
|
||||
int childRegionZ = lodNode.posZ * 2 + NS;
|
||||
|
||||
children[NS][WE] = new LodQuadTree(this, (byte) (lodNode.detailLevel - 1), new RegionPos(childRegionX, childRegionZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all the children
|
||||
*/
|
||||
public void deleteChildren()
|
||||
{
|
||||
for (int NS = 0; NS <= 1; NS++) {
|
||||
for (int WE = 0; WE <= 1; WE++) {
|
||||
children[NS][WE] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut the tree at the given target level
|
||||
*/
|
||||
public void cutTreeAtLevel(byte targetLevel) {
|
||||
if (targetLevel <= lodNode.detailLevel) {
|
||||
deleteChildren();
|
||||
} else {
|
||||
for (int NS = 0; NS <= 1; NS++) {
|
||||
for (int WE = 0; WE <= 1; WE++) {
|
||||
if (getChild(NS, WE) != null) {
|
||||
getChild(NS, WE).cutTreeAtLevel(targetLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this region's data, specifically levelFull and lodNodeData.
|
||||
*
|
||||
* @param recursiveUpdate if recursive is true the update will rise up to the level 0
|
||||
*/
|
||||
private void updateRegion(boolean recursiveUpdate)
|
||||
{
|
||||
boolean isFull = true;
|
||||
boolean isEmpty = true;
|
||||
|
||||
// determine if this region is empty or full
|
||||
List<LodQuadTreeNode> dataList = new ArrayList<>();
|
||||
for (int NS = 0; NS <= 1; NS++)
|
||||
{
|
||||
for (int WE = 0; WE <= 1; WE++)
|
||||
{
|
||||
if (getChild(NS,WE) != null)
|
||||
{
|
||||
dataList.add(getChild(NS,WE).getLodNodeData());
|
||||
isEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isFull = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
treeFull = isFull;
|
||||
treeEmpty = isEmpty;
|
||||
|
||||
// update this regions
|
||||
lodNode.combineData(dataList);
|
||||
|
||||
// update sub regions if requested
|
||||
if (lodNode.detailLevel < LodUtil.REGION_DETAIL_LEVEL && recursiveUpdate)
|
||||
{
|
||||
this.parent.updateRegion(recursiveUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nodes that match the given mask.
|
||||
*
|
||||
* @param complexityMask holds the DistanceGenerationModes to accept
|
||||
* @param getOnlyDirty if true it will return only dirty nodes
|
||||
* @param getOnlyLeaf if true it will return only leaf nodes
|
||||
* @return list of nodes
|
||||
*/
|
||||
public List<LodQuadTreeNode> getNodeListWithMask(Set<DistanceGenerationMode> complexityMask, boolean getOnlyDirty,
|
||||
boolean getOnlyLeaf)
|
||||
{
|
||||
List<LodQuadTreeNode> nodeList = new ArrayList<>();
|
||||
|
||||
if (hasChildren())
|
||||
{
|
||||
//There is at least 1 child
|
||||
|
||||
// this detail level's node
|
||||
if (!getOnlyLeaf && !(getOnlyDirty && !lodNode.isDirty())
|
||||
&& complexityMask.contains(lodNode.complexity))
|
||||
{
|
||||
nodeList.add(lodNode);
|
||||
}
|
||||
|
||||
// search the children for valid nodes
|
||||
for (int NS = 0; NS <= 1; NS++)
|
||||
{
|
||||
for (int WE = 0; WE <= 1; WE++)
|
||||
{
|
||||
LodQuadTree child = children[NS][WE];
|
||||
|
||||
if (child != null)
|
||||
{
|
||||
nodeList.addAll(child.getNodeListWithMask(complexityMask, getOnlyDirty, getOnlyLeaf));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This tree has no children
|
||||
|
||||
if (!(getOnlyDirty && !lodNode.isDirty()) && (complexityMask.contains(lodNode.complexity)))
|
||||
{
|
||||
nodeList.add(lodNode);
|
||||
}
|
||||
}
|
||||
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return all the nodes that can be rendered
|
||||
*
|
||||
* @param playerPos position of the player
|
||||
* @param targetLevel minimum level that can be rendered
|
||||
* @param maxDistance maximum distance from the player
|
||||
* @param minDistance minimum distance from the player
|
||||
* @return
|
||||
*/
|
||||
public List<LodQuadTreeNode> getNodeToRender(BlockPos playerPos, int targetLevel,
|
||||
Set<DistanceGenerationMode> complexityMask, int maxDistance, int minDistance)
|
||||
{
|
||||
int x = playerPos.getX();
|
||||
int z = playerPos.getZ();
|
||||
|
||||
List<Integer> distances = new ArrayList<>();
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2)));
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2)));
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2)));
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2)));
|
||||
|
||||
int min = distances.stream().mapToInt(Integer::intValue).min().getAsInt();
|
||||
int max = distances.stream().mapToInt(Integer::intValue).max().getAsInt();
|
||||
List<LodQuadTreeNode> nodeList = new ArrayList<>();
|
||||
|
||||
|
||||
if (targetLevel <= lodNode.detailLevel && ((min <= maxDistance && max >= minDistance)))
|
||||
{
|
||||
// TODO why is !isNodeFull() here? Becouse if a node is not full then at least one child is missing.
|
||||
// if one child is missing then there would be a hole if you try to render all the other child
|
||||
if (targetLevel == lodNode.detailLevel || !isNodeFull())
|
||||
{
|
||||
// we have either reached the right detail level or this tree isn't full
|
||||
|
||||
if (!lodNode.isVoidNode() && complexityMask.contains(lodNode.complexity))
|
||||
{
|
||||
// this node isn't void and has the complexity level we are looking for
|
||||
nodeList.add(lodNode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// look for the correct targetLevel
|
||||
for (int NS = 0; NS <= 1; NS++)
|
||||
{
|
||||
for (int WE = 0; WE <= 1; WE++)
|
||||
{
|
||||
LodQuadTree child = getChild(NS, WE);
|
||||
if (child != null)
|
||||
{
|
||||
nodeList.addAll(child.getNodeToRender(playerPos, targetLevel, complexityMask, maxDistance, minDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns nodes that should be generated. <br>
|
||||
* A node is generated only if it has child, is higher than the target level, and in the distance range.
|
||||
*/
|
||||
public List<AbstractMap.SimpleEntry<LodQuadTreeNode, Integer>> getNodesToGenerate(BlockPos playerPos, byte targetLevel,
|
||||
DistanceGenerationMode complexityToGenerate, int maxDistance, int minDistance)
|
||||
{
|
||||
int x = playerPos.getX();
|
||||
int z = playerPos.getZ();
|
||||
|
||||
List<Integer> distances = new ArrayList<>();
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2)));
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getStart().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2)));
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getStart().getZ(), 2)));
|
||||
distances.add((int) Math.sqrt(Math.pow(x - lodNode.getEnd().getX(), 2) + Math.pow(z - lodNode.getEnd().getZ(), 2)));
|
||||
|
||||
int min = distances.stream().mapToInt(Integer::intValue).min().getAsInt();
|
||||
int max = distances.stream().mapToInt(Integer::intValue).max().getAsInt();
|
||||
List<AbstractMap.SimpleEntry<LodQuadTreeNode, Integer>> nodeList = new ArrayList<>();
|
||||
|
||||
|
||||
// TODO what is the purpose of isCoordianteInLevel?
|
||||
if (targetLevel <= lodNode.detailLevel && ((min <= maxDistance && max >= minDistance) || isCoordinateInQuadTree(playerPos)))
|
||||
{
|
||||
// TODO shouldn't tagetLevel be != lodNode.detailLevel?
|
||||
if(!hasChildren() || targetLevel == lodNode.detailLevel)
|
||||
{
|
||||
if (this.lodNode.complexity.compareTo(complexityToGenerate) <= 0 )
|
||||
{
|
||||
nodeList.add(new AbstractMap.SimpleEntry<LodQuadTreeNode, Integer>(this.lodNode, min));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if there are nodes further down that need generation
|
||||
|
||||
for (int NS = 0; NS <= 1; NS++)
|
||||
{
|
||||
for (int WE = 0; WE <= 1; WE++)
|
||||
{
|
||||
if (getChild(NS, WE) == null)
|
||||
{
|
||||
setChild(NS, WE);
|
||||
}
|
||||
|
||||
nodeList.addAll(getChild(NS, WE).getNodesToGenerate(playerPos, targetLevel, complexityToGenerate, maxDistance, minDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for lodNodeData, to maintain a correct relationship between worlds
|
||||
* this method forces an update on all parent nodes.
|
||||
*
|
||||
* @param newLodQuadTreeNode data to set
|
||||
*/
|
||||
public void setLodNodeData(LodQuadTreeNode newLodQuadTreeNode)
|
||||
{
|
||||
if (this.lodNode == null)
|
||||
{
|
||||
this.lodNode = newLodQuadTreeNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.lodNode.updateData(newLodQuadTreeNode);
|
||||
}
|
||||
|
||||
//a recursive update is necessary to change the higher levels
|
||||
if (parent != null && UPDATE_HIGHER_LEVEL)
|
||||
{
|
||||
parent.updateRegion(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given BlockPos is within the boundary of
|
||||
* this LodQuadTree.
|
||||
*/
|
||||
public boolean isCoordinateInQuadTree(BlockPos pos)
|
||||
{
|
||||
return (lodNode.getStart().getX() * lodNode.width <= pos.getX() &&
|
||||
lodNode.getStart().getZ() * lodNode.width <= pos.getZ() &&
|
||||
lodNode.getEnd().getX() * lodNode.width >= pos.getX() &&
|
||||
lodNode.getEnd().getZ() * lodNode.width >= pos.getZ());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// simple getters //
|
||||
//================//
|
||||
|
||||
public LodQuadTree getChild(int NS, int WE)
|
||||
{
|
||||
return children[NS][WE];
|
||||
}
|
||||
|
||||
public LodQuadTreeNode getLodNodeData()
|
||||
{
|
||||
return lodNode;
|
||||
}
|
||||
|
||||
public boolean isNodeFull()
|
||||
{
|
||||
return treeFull;
|
||||
}
|
||||
|
||||
public boolean hasChildren()
|
||||
{
|
||||
return !treeEmpty;
|
||||
}
|
||||
|
||||
public boolean isRenderable()
|
||||
{
|
||||
return (lodNode != null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = lodNode.toString();
|
||||
|
||||
s += treeFull ? "Full and " : "";
|
||||
s += treeEmpty ? "Empty " : "";
|
||||
|
||||
if (lodNode != null)
|
||||
s += "detail: " + lodNode.detailLevel;
|
||||
|
||||
/*
|
||||
if(hasChildren())
|
||||
{
|
||||
for (int NS = 0; NS <= 1; NS++)
|
||||
{
|
||||
for (int WE = 0; WE <= 1; WE++)
|
||||
{
|
||||
LodQuadTree child = children[NS][WE];
|
||||
if (child != null) {
|
||||
s += '\n' + child.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1,670 +0,0 @@
|
||||
/*
|
||||
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
||||
/**
|
||||
* This object holds all loaded LOD regions
|
||||
* for a given dimension.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 8-8-2021
|
||||
*/
|
||||
public class LodQuadTreeDimension
|
||||
{
|
||||
/**TODO a dimension should support two different type of quadTree.
|
||||
* The ones that are near from the player should always be saved and can be fully generated (even at block level)
|
||||
* The ones that are far from the player should always be non-savable and at a high level
|
||||
* If this is not done then you could see how heavy a fully generated 64 region dimension can get.
|
||||
* IDEA : use a mask like the "isRegionDirty" to achieve this*/
|
||||
|
||||
public final DimensionType dimension;
|
||||
|
||||
/** measured in regions */
|
||||
private volatile int width;
|
||||
/** measured in regions */
|
||||
private volatile int halfWidth;
|
||||
|
||||
/** */
|
||||
public static final Set<DistanceGenerationMode> FULL_COMPLEXITY_MASK = new HashSet<DistanceGenerationMode>();
|
||||
static
|
||||
{
|
||||
// I moved the setup here because eclipse was complaining
|
||||
FULL_COMPLEXITY_MASK.add(DistanceGenerationMode.BIOME_ONLY);
|
||||
FULL_COMPLEXITY_MASK.add(DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT);
|
||||
FULL_COMPLEXITY_MASK.add(DistanceGenerationMode.SURFACE);
|
||||
FULL_COMPLEXITY_MASK.add(DistanceGenerationMode.FEATURES);
|
||||
FULL_COMPLEXITY_MASK.add(DistanceGenerationMode.SERVER);
|
||||
}
|
||||
|
||||
|
||||
public volatile LodQuadTree regions[][];
|
||||
public volatile boolean isRegionDirty[][];
|
||||
|
||||
private volatile RegionPos center;
|
||||
|
||||
private LodQuadTreeDimensionFileHandler fileHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Creates the dimension centered at (0,0)
|
||||
*
|
||||
* @param newWidth in regions
|
||||
*/
|
||||
public LodQuadTreeDimension(DimensionType newDimension, LodQuadTreeWorld lodWorld, int newWidth)
|
||||
{
|
||||
dimension = newDimension;
|
||||
width = newWidth;
|
||||
halfWidth = (int)Math.floor(width / 2);
|
||||
|
||||
if(newDimension != null && lodWorld != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
File saveDir;
|
||||
if (mc.hasSingleplayerServer())
|
||||
{
|
||||
// local world
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
|
||||
|
||||
// provider needs a separate variable to prevent
|
||||
// the compiler from complaining
|
||||
ServerChunkProvider provider = serverWorld.getChunkSource();
|
||||
saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod");
|
||||
}
|
||||
else
|
||||
{
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level));
|
||||
}
|
||||
|
||||
fileHandler = new LodQuadTreeDimensionFileHandler(saveDir, this);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// the file handler wasn't able to be created
|
||||
// we won't be able to read or write any files
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
regions = new LodQuadTree[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
|
||||
// populate isRegionDirty
|
||||
for(int i = 0; i < width; i++)
|
||||
for(int j = 0; j < width; j++)
|
||||
isRegionDirty[i][j] = false;
|
||||
|
||||
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>
|
||||
*
|
||||
* Synchronized to prevent multiple moves happening on top of each other.
|
||||
*/
|
||||
public synchronized void move(RegionPos regionOffset)
|
||||
{
|
||||
int xOffset = regionOffset.x;
|
||||
int zOffset = regionOffset.z;
|
||||
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total size, just delete the current data
|
||||
// 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 z = 0; z < width; z++)
|
||||
{
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// X
|
||||
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 z = 0; z < width; z++)
|
||||
{
|
||||
if(x + xOffset < width)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything over to the right (as the center moves to the left)
|
||||
for(int x = width - 1; x >= 0; x--)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
if(x + xOffset >= 0)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Z
|
||||
if(zOffset > 0)
|
||||
{
|
||||
// move everything up (as the center moves down)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
if(z + zOffset < width)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything down (as the center moves up)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = width - 1; z >= 0; z--)
|
||||
{
|
||||
if(z + zOffset >= 0)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the region at the given X and Z
|
||||
* <br>
|
||||
* Returns null if the region doesn't exist
|
||||
* or is outside the loaded area.
|
||||
*/
|
||||
public LodQuadTree getRegion(RegionPos regionPos)
|
||||
{
|
||||
int xIndex = (regionPos.x - center.x) + halfWidth;
|
||||
int zIndex = (regionPos.z - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(regionPos.x, regionPos.z))
|
||||
// out of range
|
||||
return null;
|
||||
|
||||
if (regions[xIndex][zIndex] == null)
|
||||
{
|
||||
regions[xIndex][zIndex] = getRegionFromFile(regionPos);
|
||||
if (regions[xIndex][zIndex] == null)
|
||||
{
|
||||
regions[xIndex][zIndex] = new LodQuadTree(regionPos);
|
||||
}
|
||||
}
|
||||
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the LodRegion at the location of newRegion with newRegion.
|
||||
*
|
||||
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
|
||||
*/
|
||||
public void addOrOverwriteRegion(LodQuadTree newRegion) throws ArrayIndexOutOfBoundsException
|
||||
{
|
||||
int xIndex = (newRegion.getLodNodeData().posX - center.x) + halfWidth;
|
||||
int zIndex = (center.z - newRegion.getLodNodeData().posZ) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(newRegion.getLodNodeData().posX, newRegion.getLodNodeData().posZ))
|
||||
// out of range
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
|
||||
regions[xIndex][zIndex] = newRegion;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*this method creates all null regions
|
||||
*/
|
||||
public void initializeNullRegions()
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
RegionPos regionPos;
|
||||
LodQuadTree region;
|
||||
|
||||
for(int x = 0; x < regions.length; x++)
|
||||
{
|
||||
for(int z = 0; z < regions.length; z++)
|
||||
{
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
regionPos = new RegionPos(regionX,regionZ);
|
||||
region = getRegion(regionPos);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
// if no region exists, create it
|
||||
region = new LodQuadTree(regionPos);
|
||||
addOrOverwriteRegion(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the given LOD to this dimension at the coordinate
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinates it will be overwritten.
|
||||
*/
|
||||
public Boolean addNode(LodQuadTreeNode lodNode)
|
||||
{
|
||||
// don't continue if the region can't be saved
|
||||
RegionPos regionPos = LodUtil.convertGenericPosToRegionPos(lodNode.posX, lodNode.posZ, lodNode.detailLevel);
|
||||
if (!regionIsInRange(regionPos.x, regionPos.z))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LodQuadTree region = getRegion(regionPos);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
// if no region exists, create it
|
||||
region = new LodQuadTree(regionPos);
|
||||
addOrOverwriteRegion(region);
|
||||
}
|
||||
boolean nodeAdded = region.setNodeAtLowerLevel(lodNode);
|
||||
|
||||
// only save valid LODs to disk
|
||||
if (!lodNode.dontSave && fileHandler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (regionPos.x - center.x) + halfWidth;
|
||||
int zIndex = (regionPos.z - center.z) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
}
|
||||
catch(ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
// This method was probably called when the dimension was changing size.
|
||||
// Hopefully this shouldn't be an issue.
|
||||
}
|
||||
}
|
||||
return nodeAdded;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the LodNodeData at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public LodQuadTreeNode getLodFromCoordinates(ChunkPos chunkPos)
|
||||
{
|
||||
return getLodFromCoordinates(chunkPos, LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LodNodeData at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public LodQuadTreeNode getLodFromCoordinates(ChunkPos chunkPos, int detailLevel)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodQuadTree region = getRegion(LodUtil.convertGenericPosToRegionPos(chunkPos.x, chunkPos.z, LodUtil.CHUNK_DETAIL_LEVEL));
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return region.getNodeAtChunkPos(chunkPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LodNodeData at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public LodQuadTreeNode getLodFromCoordinates(int posX, int posZ, int detailLevel)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodQuadTree region = getRegion(LodUtil.convertGenericPosToRegionPos(posX, posZ, detailLevel));
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return region.getNodeAtPos(posX, posZ, detailLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LodNodeData at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public LodQuadTree getLevelFromPos(int posX, int posZ, int detailLevel)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodQuadTree region = getRegion(LodUtil.convertGenericPosToRegionPos(posX, posZ, detailLevel));
|
||||
|
||||
if(region == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return region.getLevelAtPos(posX, posZ, detailLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if and only if the node at that position exist
|
||||
*/
|
||||
public boolean hasThisPositionBeenGenerated(ChunkPos chunkPos, int level)
|
||||
{
|
||||
if (level > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + level + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
return getLodFromCoordinates(chunkPos, level).detailLevel == level;
|
||||
}
|
||||
|
||||
/**
|
||||
* method to get all the nodes that have to be rendered based on the position of the player
|
||||
* @return list of nodes
|
||||
*/
|
||||
public List<LodQuadTreeNode> getNodesToRender(BlockPos playerPos, int detailLevel,
|
||||
Set<DistanceGenerationMode> complexityMask, int maxDistance, int minDistance)
|
||||
{
|
||||
List<LodQuadTreeNode> listOfData = new ArrayList<>();
|
||||
|
||||
// go through every region we have stored
|
||||
for(int i = 0; i < regions.length; i++)
|
||||
{
|
||||
for(int j = 0; j < regions.length; j++)
|
||||
{
|
||||
listOfData.addAll(regions[i][j].getNodeToRender(playerPos, detailLevel, complexityMask, maxDistance, minDistance));
|
||||
}
|
||||
}
|
||||
|
||||
return listOfData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all LodQuadTreeNodes that need to be generated based on the position of the player
|
||||
* @return list of quadTrees
|
||||
*/
|
||||
public List<LodQuadTreeNode> getNodesToGenerate(BlockPos playerPos, byte level, DistanceGenerationMode complexity,
|
||||
int maxDistance, int minDistance)
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
LodQuadTree region;
|
||||
RegionPos regionPos;
|
||||
List<Map.Entry<LodQuadTreeNode,Integer>> listOfQuadTree = new ArrayList<>();
|
||||
|
||||
// go through every region we have stored
|
||||
for(int xIndex = 0; xIndex < regions.length; xIndex++)
|
||||
{
|
||||
for(int zIndex = 0; zIndex < regions.length; zIndex++)
|
||||
{
|
||||
regionX = (xIndex + center.x) - halfWidth;
|
||||
regionZ = (zIndex + center.z) - halfWidth;
|
||||
regionPos = new RegionPos(regionX,regionZ);
|
||||
region = getRegion(regionPos);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
region = new LodQuadTree(regionPos);
|
||||
addOrOverwriteRegion(region);
|
||||
}
|
||||
|
||||
listOfQuadTree.addAll(region.getNodesToGenerate(playerPos, level, complexity, maxDistance, minDistance));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO why are we sorting the list?
|
||||
Collections.sort(listOfQuadTree, Map.Entry.comparingByValue());
|
||||
return listOfQuadTree.stream().map(entry -> entry.getKey()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see LodQuadTree#getNodeListWithMask
|
||||
*/
|
||||
public List<LodQuadTreeNode> getNodesWithMask(Set<DistanceGenerationMode> complexityMask, boolean getOnlyDirty, boolean getOnlyLeaf)
|
||||
{
|
||||
List<LodQuadTreeNode> listOfNodes = new ArrayList<>();
|
||||
|
||||
int xIndex;
|
||||
int zIndex;
|
||||
LodQuadTree region;
|
||||
|
||||
// go through every region we have stored
|
||||
for(int xRegion = 0; xRegion < regions.length; xRegion++)
|
||||
{
|
||||
for(int zRegion = 0; zRegion < regions.length; zRegion++)
|
||||
{
|
||||
xIndex = (xRegion + center.x) - halfWidth;
|
||||
zIndex = (zRegion + center.z) - halfWidth;
|
||||
region = getRegion(new RegionPos(xIndex,zIndex));
|
||||
|
||||
// Recursively add any children
|
||||
if (region != null)
|
||||
{
|
||||
listOfNodes.addAll(region.getNodeListWithMask(complexityMask, getOnlyDirty, getOnlyLeaf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return listOfNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the region at the given X and Z coordinates from the
|
||||
* RegionFileHandler.
|
||||
*/
|
||||
public LodQuadTree getRegionFromFile(RegionPos regionPos)
|
||||
{
|
||||
if (fileHandler != null)
|
||||
return fileHandler.loadRegionFromFile(regionPos);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all dirty regions in this LodDimension to file.
|
||||
*/
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileHandler.saveDirtyRegionsToFileAsync();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the region at the given X and Z coordinates
|
||||
* is within the loaded range.
|
||||
*/
|
||||
public boolean regionIsInRange(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - center.x) + halfWidth;
|
||||
int zIndex = (regionZ - center.z) + halfWidth;
|
||||
|
||||
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public int getCenterX()
|
||||
{
|
||||
return center.x;
|
||||
}
|
||||
|
||||
public int getCenterZ()
|
||||
{
|
||||
return center.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO Double check that this method works as expected
|
||||
*
|
||||
* Returns how many non-null LodChunks
|
||||
* are stored in this LodDimension.
|
||||
*/
|
||||
public int getNumberOfLods()
|
||||
{
|
||||
int numbLods = 0;
|
||||
for (LodQuadTree[] regions : regions)
|
||||
{
|
||||
if(regions == null)
|
||||
continue;
|
||||
|
||||
for (LodQuadTree region : regions)
|
||||
{
|
||||
if(region == null)
|
||||
continue;
|
||||
|
||||
for(LodQuadTreeNode node : region.getNodeListWithMask(FULL_COMPLEXITY_MASK,false,true))
|
||||
{
|
||||
if (node != null && !node.voidNode)
|
||||
numbLods++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numbLods;
|
||||
}
|
||||
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
if (regions != null)
|
||||
{
|
||||
// we want to get the length directly from the
|
||||
// source to make sure it is in sync with region
|
||||
// and isRegionDirty
|
||||
return regions.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
public void setRegionWidth(int newWidth)
|
||||
{
|
||||
width = newWidth;
|
||||
halfWidth = (int)Math.floor(width / 2);
|
||||
|
||||
regions = new LodQuadTree[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
|
||||
// populate isRegionDirty
|
||||
for(int i = 0; i < width; i++)
|
||||
for(int j = 0; j < width; j++)
|
||||
isRegionDirty[i][j] = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "";
|
||||
|
||||
s += "dim: " + dimension.toString() + "\t";
|
||||
s += "(" + center.x + "," + center.z + ")";
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1,412 +0,0 @@
|
||||
/*
|
||||
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
|
||||
/**
|
||||
* This object contains position, size,
|
||||
* and color data for an LOD object.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 8-8-2021
|
||||
*/
|
||||
public class LodQuadTreeNode implements Serializable
|
||||
{
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
private static final char DATA_DELIMITER = LodQuadTreeDimensionFileHandler.DATA_DELIMITER;
|
||||
|
||||
/** If we ever have to use a heightmap for any reason, use this one. */
|
||||
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
|
||||
|
||||
/** this is how many pieces of data are exported when toData is called */
|
||||
public static final int NUMBER_OF_DELIMITERS = 10;
|
||||
|
||||
|
||||
|
||||
/** If this is set to true then toData will return
|
||||
* the empty string */
|
||||
public boolean dontSave = false;
|
||||
|
||||
/** X position relative to the Quad tree. */
|
||||
public final int posX;
|
||||
/** Z position relative to the Quad tree */
|
||||
public final int posZ;
|
||||
|
||||
//Complexity indicate how the block was built. This is important because we could use
|
||||
public DistanceGenerationMode complexity;
|
||||
|
||||
/** Indicates how complicated this node is. <br>
|
||||
* Goes from 0 to 9; 0 being the deepest (block size) and 9 being the highest (region size) */
|
||||
public final byte detailLevel;
|
||||
|
||||
/** Indicates the width in blocks of this node. <br>
|
||||
* Goes from 1 to 512 */
|
||||
public final short width;
|
||||
|
||||
|
||||
/** holds the height, depth, and color data for this Node. */
|
||||
private LodDataPoint lodDataPoint;
|
||||
|
||||
/** if true this node doesn't have any data */
|
||||
public boolean voidNode;
|
||||
/** if dirty is true, then this node have unsaved changes */
|
||||
public boolean dirty;
|
||||
|
||||
|
||||
/**TODO There should be a check for the level. Level must be positive, i could use runtime exception or simple if*/
|
||||
/**TODO There should be a good way to create node that must not be saved
|
||||
* For example loading a 64 region wide dimension that is fully generated is too much memory heavy.
|
||||
* There should be a way to create Node that are approximated and at region level, so you could load those
|
||||
* for far region, and then when you get closer you load the actual region from the file or you generate it.
|
||||
* */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates and empty LodDataPoint
|
||||
* This LodDataPoint only contains the position data
|
||||
* @param detailLevel of the node
|
||||
* @param posX position x in the level
|
||||
* @param posZ position z in the level
|
||||
*/
|
||||
public LodQuadTreeNode(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
|
||||
this.posX = posX;
|
||||
this.posZ = posZ;
|
||||
|
||||
width = (short) Math.pow(2, detailLevel);
|
||||
|
||||
lodDataPoint = new LodDataPoint();
|
||||
|
||||
complexity = DistanceGenerationMode.NONE;
|
||||
|
||||
dirty = true;
|
||||
voidNode = true;
|
||||
dontSave = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a LodNodeData
|
||||
* @param level
|
||||
* @param posX
|
||||
* @param posZ
|
||||
* @param height
|
||||
* @param depth
|
||||
* @param color
|
||||
* @param complexity
|
||||
*/
|
||||
public LodQuadTreeNode(byte level, int posX, int posZ, short height, short depth , Color color, DistanceGenerationMode complexity)
|
||||
{
|
||||
this(level, posX, posZ, new LodDataPoint(height,depth,color), complexity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a LodNodeData
|
||||
* @param level
|
||||
* @param posX
|
||||
* @param posZ
|
||||
* @param height
|
||||
* @param depth
|
||||
* @param color
|
||||
* @param complexity
|
||||
*/
|
||||
public LodQuadTreeNode(byte level, int posX, int posZ, int height, int depth, Color color, DistanceGenerationMode complexity)
|
||||
{
|
||||
this(level, posX, posZ, new LodDataPoint(height,depth,color), complexity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a LodNodeData
|
||||
* @param detailLevel level of this
|
||||
* @param posX
|
||||
* @param posZ
|
||||
* @param lodDataPoint
|
||||
* @param complexity
|
||||
*/
|
||||
public LodQuadTreeNode(byte detailLevel, int posX, int posZ, LodDataPoint lodDataPoint, DistanceGenerationMode complexity)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
|
||||
this.posX = posX;
|
||||
this.posZ = posZ;
|
||||
|
||||
width = (short) Math.pow(2, detailLevel);
|
||||
|
||||
|
||||
this.lodDataPoint = lodDataPoint;
|
||||
this.complexity = complexity;
|
||||
|
||||
dirty = true;
|
||||
voidNode = false;
|
||||
dontSave = false;
|
||||
}
|
||||
|
||||
public BlockPos getStart(){
|
||||
return new BlockPos(posX * width, 0, posZ * width);
|
||||
}
|
||||
|
||||
public BlockPos getEnd(){
|
||||
return new BlockPos(posX * (width + 1) - 1, 0, posZ * (width + 1) - 1);
|
||||
}
|
||||
|
||||
public BlockPos getCenter(){
|
||||
return new BlockPos(posX * width + width/2, 0, posZ * width + width/2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the data string doesn't have the correct number of delimited entries
|
||||
*/
|
||||
public LodQuadTreeNode(String data) throws IllegalArgumentException
|
||||
{
|
||||
// make sure there are the correct number of entries
|
||||
// in the data string
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < data.length(); i++)
|
||||
if(data.charAt(i) == DATA_DELIMITER)
|
||||
count++;
|
||||
|
||||
if(count != NUMBER_OF_DELIMITERS)
|
||||
throw new IllegalArgumentException("LodQuadTreeNode constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + NUMBER_OF_DELIMITERS + ".");
|
||||
|
||||
|
||||
// start reading the data string
|
||||
|
||||
int index = 0;
|
||||
int lastIndex = 0;
|
||||
|
||||
index = data.indexOf(DATA_DELIMITER, 0);
|
||||
this.detailLevel = (byte) Integer.parseInt(data.substring(0,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.posX = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.posZ = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.complexity = DistanceGenerationMode.valueOf(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
short height = (short) Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
short depth = (short) Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int red = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int green = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int blue = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int alpha = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
Color color = new Color(red,green,blue,alpha);
|
||||
lodDataPoint = new LodDataPoint(height,depth,color);
|
||||
|
||||
int isVoid = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
this.voidNode = (isVoid == 1);
|
||||
|
||||
|
||||
width = (short) Math.pow(2, detailLevel);
|
||||
|
||||
dirty = false;
|
||||
dontSave = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// data processes //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* Replaces the data in this object.
|
||||
*/
|
||||
public void updateData(LodQuadTreeNode lodQuadTreeNode)
|
||||
{
|
||||
if (lodQuadTreeNode == null)
|
||||
return;
|
||||
|
||||
lodDataPoint = lodQuadTreeNode.lodDataPoint;
|
||||
complexity = lodQuadTreeNode.complexity;
|
||||
voidNode = lodQuadTreeNode.voidNode;
|
||||
dontSave = lodQuadTreeNode.dontSave;
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and averages the data from a list of LodQuadTreeNodes into this node.
|
||||
*/
|
||||
public void combineData(List<LodQuadTreeNode> dataList)
|
||||
{
|
||||
if(dataList.isEmpty())
|
||||
{
|
||||
lodDataPoint = new LodDataPoint();
|
||||
}
|
||||
else
|
||||
{
|
||||
// get the lowest height from the all the given LodQuadTreeNodes
|
||||
short height = (short) (dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().height).sum() / dataList.size());
|
||||
// get the highest depth
|
||||
short depth = (short) (dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().depth).sum() / dataList.size());
|
||||
|
||||
// get the average color
|
||||
int red = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getRed()).sum() / dataList.size();
|
||||
int green = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getGreen()).sum() / dataList.size();
|
||||
int blue = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getBlue()).sum() / dataList.size();
|
||||
Color color = new Color(red,green,blue);
|
||||
|
||||
lodDataPoint = new LodDataPoint(height, depth, color);
|
||||
|
||||
|
||||
// the new complexity is equal to the lowest complexity of the list
|
||||
DistanceGenerationMode minComplexity = DistanceGenerationMode.SERVER;
|
||||
for(LodQuadTreeNode node: dataList)
|
||||
{
|
||||
if (minComplexity.compareTo(node.complexity) > 0)
|
||||
{
|
||||
minComplexity = node.complexity;
|
||||
}
|
||||
}
|
||||
|
||||
complexity = minComplexity;
|
||||
|
||||
voidNode = lodDataPoint.isEmpty();
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
dontSave = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// basic comparisons //
|
||||
//===================//
|
||||
|
||||
public int compareComplexity(LodQuadTreeNode other)
|
||||
{
|
||||
return this.complexity.compareTo(other.complexity);
|
||||
}
|
||||
|
||||
public boolean equals(LodQuadTreeNode other)
|
||||
{
|
||||
return (this.complexity == other.complexity
|
||||
&& this.detailLevel == other.detailLevel
|
||||
&& this.posX == other.posX
|
||||
&& this.posZ == other.posZ
|
||||
&& this.lodDataPoint.equals(other.lodDataPoint)
|
||||
&& this.complexity == other.complexity
|
||||
&& this.voidNode == other.voidNode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================//
|
||||
// basic setters / getters //
|
||||
//=========================//
|
||||
|
||||
public LodDataPoint getLodDataPoint()
|
||||
{
|
||||
return lodDataPoint;
|
||||
}
|
||||
|
||||
public void setLodDataPoint(LodDataPoint newLodDataPoint)
|
||||
{
|
||||
lodDataPoint = newLodDataPoint;
|
||||
|
||||
// update if this is node is currently void
|
||||
voidNode = (lodDataPoint == null);
|
||||
}
|
||||
|
||||
public boolean isVoidNode()
|
||||
{
|
||||
return voidNode;
|
||||
}
|
||||
|
||||
public boolean isDirty()
|
||||
{
|
||||
return dirty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// output //
|
||||
//========//
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(this.complexity, this.detailLevel, this.posX, this.posZ, this.lodDataPoint, this.voidNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs all data in a csv format
|
||||
*/
|
||||
public String toData()
|
||||
{
|
||||
if (dontSave)
|
||||
return "";
|
||||
|
||||
String s = Integer.toString(detailLevel) + DATA_DELIMITER
|
||||
+ Integer.toString(posX) + DATA_DELIMITER
|
||||
+ Integer.toString(posZ) + DATA_DELIMITER
|
||||
+ complexity.toString() + DATA_DELIMITER
|
||||
+ Integer.toString((lodDataPoint.height)) + DATA_DELIMITER
|
||||
+ Integer.toString((lodDataPoint.depth)) + DATA_DELIMITER
|
||||
+ Integer.toString(lodDataPoint.color.getRed()) + DATA_DELIMITER
|
||||
+ Integer.toString(lodDataPoint.color.getGreen()) + DATA_DELIMITER
|
||||
+ Integer.toString(lodDataPoint.color.getBlue()) + DATA_DELIMITER
|
||||
+ Integer.toString(lodDataPoint.color.getAlpha()) + DATA_DELIMITER
|
||||
+ Integer.toString(voidNode ? 1 : 0);
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.toData();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,24 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**STANDARD TO FOLLOW
|
||||
/**
|
||||
* STANDARD TO FOLLOW
|
||||
* every coordinate called posX or posZ is a relative coordinate and not and absolute coordinate
|
||||
* if an array contain coordinate the order is the following
|
||||
* 0 for x, 1 for z in 2D
|
||||
* 0 for x, 1 for y, 2 for z in 3D
|
||||
*/
|
||||
|
||||
public class LodRegion{
|
||||
public class LodRegion implements Serializable {
|
||||
//x coord,
|
||||
private byte minLevelOfDetail;
|
||||
private byte minDetailLevel;
|
||||
private static final byte POSSIBLE_LOD = 10;
|
||||
private int numberOfPoints;
|
||||
|
||||
@@ -32,186 +36,346 @@ public class LodRegion{
|
||||
//a node with 1 is node
|
||||
private byte[][][] generationType;
|
||||
|
||||
private int regionPosX;
|
||||
private int regionPosZ;
|
||||
private boolean[][][] dataExistence;
|
||||
|
||||
public LodRegion(byte minimumLevelOfDetail, RegionPos regionPos){
|
||||
public final int regionPosX;
|
||||
public final int regionPosZ;
|
||||
|
||||
public LodRegion(LevelContainer levelContainer, RegionPos regionPos) {
|
||||
this.regionPosX = regionPos.x;
|
||||
this.regionPosZ = regionPos.z;
|
||||
this.minDetailLevel = levelContainer.detailLevel;
|
||||
|
||||
//Array of matrices of arrays
|
||||
colors = new byte[POSSIBLE_LOD][][][];
|
||||
colors = new byte[POSSIBLE_LOD][][][];
|
||||
|
||||
//Arrays of matrices
|
||||
height = new short[POSSIBLE_LOD][][];
|
||||
depth = new short[POSSIBLE_LOD][][];
|
||||
generationType = new byte[POSSIBLE_LOD][][];
|
||||
height = new short[POSSIBLE_LOD][][];
|
||||
depth = new short[POSSIBLE_LOD][][];
|
||||
generationType = new byte[POSSIBLE_LOD][][];
|
||||
dataExistence = new boolean[POSSIBLE_LOD][][];
|
||||
|
||||
colors[minDetailLevel] = levelContainer.colors;
|
||||
height[minDetailLevel] = levelContainer.height;
|
||||
depth[minDetailLevel] = levelContainer.depth;
|
||||
generationType[minDetailLevel] = levelContainer.generationType;
|
||||
dataExistence[minDetailLevel] = levelContainer.dataExistence;
|
||||
|
||||
//Initialize all the different matrices
|
||||
for(byte lod = minimumLevelOfDetail; 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];
|
||||
depth[lod] = new short[size][size];
|
||||
generationType[lod] = new byte[size][size];
|
||||
dataExistence[lod] = new boolean[size][size];
|
||||
}
|
||||
int sizeDiff = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL - (minDetailLevel + 1));
|
||||
LevelPos levelPos;
|
||||
for(int x = 0; x < sizeDiff; x++){
|
||||
for(int z = 0; z < sizeDiff; z++){
|
||||
levelPos = new LevelPos((byte) (minDetailLevel+1), x, z);
|
||||
update(levelPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LodRegion(byte minDetailLevel, RegionPos regionPos) {
|
||||
this.minDetailLevel = minDetailLevel;
|
||||
this.regionPosX = regionPos.x;
|
||||
this.regionPosZ = regionPos.z;
|
||||
|
||||
//Array of matrices of arrays
|
||||
colors = new byte[POSSIBLE_LOD][][][];
|
||||
|
||||
//Arrays of matrices
|
||||
height = new short[POSSIBLE_LOD][][];
|
||||
depth = new short[POSSIBLE_LOD][][];
|
||||
generationType = new byte[POSSIBLE_LOD][][];
|
||||
dataExistence = new boolean[POSSIBLE_LOD][][];
|
||||
|
||||
|
||||
//Initialize all the different matrices
|
||||
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];
|
||||
depth[lod] = new short[size][size];
|
||||
generationType[lod] = new byte[size][size];
|
||||
dataExistence[lod] = new boolean[size][size];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to insert data into the LodRegion
|
||||
* @param lod
|
||||
* @param posX
|
||||
* @param posZ
|
||||
*
|
||||
* @param levelPos
|
||||
* @param dataPoint
|
||||
* @param generationType
|
||||
* @param update
|
||||
* @return
|
||||
*/
|
||||
public boolean setData(byte lod, int posX, int posZ, LodDataPoint dataPoint, byte generationType, boolean update){
|
||||
return setData(lod, posX, posZ, (byte) (dataPoint.color.getRed() - 128), (byte) (dataPoint.color.getGreen() - 128), (byte) (dataPoint.color.getBlue() - 128), dataPoint.height, dataPoint.depth, generationType, update);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to insert data into the LodRegion
|
||||
* @param lod
|
||||
* @param posX
|
||||
* @param posZ
|
||||
* @param red
|
||||
* @param green
|
||||
* @param blue
|
||||
* @param height
|
||||
* @param depth
|
||||
* @param generationType
|
||||
* @param update
|
||||
* @return
|
||||
*/
|
||||
public boolean setData(byte lod, int posX, int posZ, byte red, byte green, byte blue, short height, short depth, byte generationType, boolean update){
|
||||
posX = Math.floorMod(posX, (int) Math.pow(2,lod));
|
||||
posZ = Math.floorMod(posZ, (int) Math.pow(2,lod));
|
||||
if( (this.generationType[lod][posX][posZ] == 0) || (generationType < this.generationType[lod][posX][posZ]) ) {
|
||||
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])) {
|
||||
|
||||
//update the number of node present
|
||||
if (this.generationType[lod][posX][posZ] == 0) numberOfPoints++ ;
|
||||
//if (this.generationType[lod][posX][posZ] == 0) numberOfPoints++;
|
||||
|
||||
//add the node data
|
||||
this.colors[lod][posX][posZ][0] = red;
|
||||
this.colors[lod][posX][posZ][1] = green;
|
||||
this.colors[lod][posX][posZ][2] = blue;
|
||||
this.height[lod][posX][posZ] = height;
|
||||
this.depth[lod][posX][posZ] = depth;
|
||||
this.generationType[lod][posX][posZ] = generationType;
|
||||
|
||||
//update all the higher level
|
||||
int tempPosX = posX;
|
||||
int tempPosZ = posZ;
|
||||
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint.color.getRed() - 128);
|
||||
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (dataPoint.color.getGreen() - 128);
|
||||
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (dataPoint.color.getBlue() - 128);
|
||||
this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.height;
|
||||
this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.depth;
|
||||
this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = generationType;
|
||||
this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true;
|
||||
|
||||
//update could be stopped and a single big update could be done at the end
|
||||
if(update) {
|
||||
for (byte tempLod = (byte) (lod + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
|
||||
tempPosX = Math.floorDiv(tempPosX, 2);
|
||||
tempPosZ = Math.floorDiv(tempPosZ, 2);
|
||||
update(tempLod, tempPosX, tempPosZ);
|
||||
LevelPos tempLevelPos = levelPos;
|
||||
if (update) {
|
||||
for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
|
||||
tempLevelPos = tempLevelPos.convert(tempLod);
|
||||
update(tempLevelPos);
|
||||
}
|
||||
}
|
||||
return true; //added
|
||||
}else{
|
||||
return false; //not added
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public LodDataPoint getData(ChunkPos chunkPos) {
|
||||
return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the data in the position relative to the level of detail
|
||||
*
|
||||
* @param lod
|
||||
* @param posX
|
||||
* @param posZ
|
||||
* @return the data at the relative pos and level
|
||||
*/
|
||||
public LodDataPoint getData(byte lod, int posX, int posZ){
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the data in the position relative to the level of detail
|
||||
*
|
||||
* @param levelPos
|
||||
* @return the data at the relative pos and level
|
||||
*/
|
||||
public LodDataPoint getData(LevelPos levelPos) {
|
||||
levelPos = levelPos.regionModule();
|
||||
return new LodDataPoint(
|
||||
height[lod][posX][posZ],
|
||||
depth[lod][posX][posZ],
|
||||
new Color(colors[lod][posX][posZ][0] + 128,
|
||||
colors[lod][posX][posZ][1] + 128,
|
||||
colors[lod][posX][posZ][2] + 128
|
||||
height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
|
||||
depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
|
||||
new Color(colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128,
|
||||
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128,
|
||||
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128
|
||||
)
|
||||
);
|
||||
}
|
||||
/*
|
||||
private void updateArea(byte lod, int posX, int posZ){
|
||||
}
|
||||
*/
|
||||
private void update(byte lod, int posX, int posZ){
|
||||
posX = Math.floorMod(posX, (int) Math.pow(2,lod));
|
||||
posZ = Math.floorMod(posZ, (int) Math.pow(2,lod));
|
||||
boolean[][] children = getChildren(lod, posX, posZ);
|
||||
int numberOfChild = 0;
|
||||
|
||||
for(int x = 0; x <= 1; x++) {
|
||||
/**TODO a method to update a whole area, to be used as a single big update*/
|
||||
/**
|
||||
* @param levelPos
|
||||
*/
|
||||
private void updateArea(LevelPos levelPos) {
|
||||
/*
|
||||
LevelPos tempLevelPos = levelPos;
|
||||
int sizeDiff;
|
||||
int startX;
|
||||
int startZ;
|
||||
for(int bottom = minLevelOfDetail + 1 ; bottom < levelPos.detailLevel ; bottom ++){
|
||||
tempLevelPos = levelPos.convert(bottom);
|
||||
startX = tempLevelPos.posX;
|
||||
startZ = tempLevelPos.posZ;
|
||||
sizeDiff = (int) Math.pow(2, levelPos.detailLevel - bottom);
|
||||
for(int x = 0; x < sizeDiff; x++){
|
||||
for(int z = 0; z < sizeDiff; z++) {
|
||||
update(new LevelPos(bottom, startX+x, startZ+z));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @param levelPos
|
||||
*/
|
||||
private void update(LevelPos levelPos) {
|
||||
|
||||
levelPos = levelPos.regionModule();
|
||||
boolean[][] children = getChildren(levelPos);
|
||||
int numberOfChildren = 0;
|
||||
|
||||
/**TODO add the ability to change how the heigth and depth are determinated (for example min or max)**/
|
||||
byte minGenerationType = 10;
|
||||
int tempRed = 0;
|
||||
int tempGreen = 0;
|
||||
int tempBlue = 0;
|
||||
int tempHeight = 0;
|
||||
int tempDepth = 0;
|
||||
int newPosX;
|
||||
int newPosZ;
|
||||
byte newLod;
|
||||
LevelPos childPos;
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
if(children[x][z]){
|
||||
numberOfChild++;
|
||||
newPosX = 2 * levelPos.posX + x;
|
||||
newPosZ = 2 * levelPos.posZ + z;
|
||||
newLod = (byte) (levelPos.detailLevel - 1);
|
||||
childPos = new LevelPos(newLod, newPosX, newPosZ);
|
||||
if (hasDataBeenGenerated(childPos)) {
|
||||
numberOfChildren++;
|
||||
|
||||
tempRed += colors[newLod][newPosX][newPosZ][0];
|
||||
tempGreen += colors[newLod][newPosX][newPosZ][1];
|
||||
tempBlue += colors[newLod][newPosX][newPosZ][2];
|
||||
tempHeight += height[newLod][newPosX][newPosZ];
|
||||
tempDepth += depth[newLod][newPosX][newPosZ];
|
||||
minGenerationType = (byte) Math.min(minGenerationType, generationType[newLod][newPosX][newPosZ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(numberOfChild>0) {
|
||||
|
||||
//int minDepth = Integer.MAX_VALUE;
|
||||
//int maxDepth = Integer.MIN_VALUE;
|
||||
//int minHeight = Integer.MAX_VALUE;
|
||||
//int maxHeight = Integer.MIN_VALUE;
|
||||
|
||||
byte minGenerationType = 0;
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
if (children[x][z]) {
|
||||
int newPosX = 2 * posX + x;
|
||||
int newPosZ = 2 * posZ + z;
|
||||
for (int col = 0; col <= 2; col++) {
|
||||
colors[lod][posX][posZ][col] += (byte) (colors[lod - 1][newPosX][newPosZ][col] / numberOfChild);
|
||||
}
|
||||
|
||||
//TODO ability to change between mean, max and min.
|
||||
|
||||
height[lod][posX][posZ] += (short) (height[lod - 1][newPosX][newPosZ] / numberOfChild);
|
||||
//minHeight = Math.min( height[lod - 1][newPosX][newPosZ] , maxHeight);
|
||||
//maxHeight = Math.max( height[lod - 1][newPosX][newPosZ] , minHeight);
|
||||
|
||||
depth[lod][posX][posZ] += (short) (depth[lod - 1][newPosX][newPosZ] / numberOfChild);
|
||||
//minDepth = Math.min( depth[lod - 1][newPosX][newPosZ] , maxDepth);
|
||||
//maxDepth = Math.max( depth[lod - 1][newPosX][newPosZ] , minDepth);
|
||||
|
||||
minGenerationType = (byte) Math.max(minGenerationType, generationType[lod - 1][newPosX][newPosZ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//height[lod][posX][posZ] = minHeight;
|
||||
//depth[lod][posX][posZ] = maxDepth;
|
||||
generationType[lod][posX][posZ] = minGenerationType;
|
||||
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);
|
||||
height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = (short) (tempHeight / numberOfChildren);
|
||||
depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = (short) (tempDepth / numberOfChildren);
|
||||
generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = minGenerationType;
|
||||
dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean[][] getChildren(byte lod, int posX, int posZ){
|
||||
posX = Math.floorMod(posX, (int) Math.pow(2,lod));
|
||||
posZ = Math.floorMod(posZ, (int) Math.pow(2,lod));
|
||||
/**
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
private boolean[][] getChildren(LevelPos levelPos) {
|
||||
levelPos = levelPos.regionModule();
|
||||
boolean[][] children = new boolean[2][2];
|
||||
int numberOfChild=0;
|
||||
if(minLevelOfDetail == lod){
|
||||
int numberOfChild = 0;
|
||||
if (minDetailLevel == levelPos.detailLevel) {
|
||||
return children;
|
||||
}
|
||||
for(int x = 0; x <= 1; x++) {
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
children[x][z] = (generationType[lod-1][2*posX+x][2*posZ+z] != 0);
|
||||
children[x][z] = (dataExistence[levelPos.detailLevel - 1][2 * levelPos.posX + x][2 * levelPos.posZ + z]);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
private void removeDetailLevel(byte lod, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType){
|
||||
/**
|
||||
* @param chunkPos
|
||||
* @return
|
||||
*/
|
||||
public boolean doesDataExist(ChunkPos chunkPos) {
|
||||
return doesDataExist(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
|
||||
}
|
||||
|
||||
private void addDetailLevel(byte lod, int posX, int posZ){
|
||||
/**
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
public boolean doesDataExist(LevelPos levelPos) {
|
||||
levelPos = levelPos.regionModule();
|
||||
return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
public DistanceGenerationMode getGenerationMode(LevelPos levelPos) {
|
||||
levelPos = levelPos.regionModule();
|
||||
DistanceGenerationMode generationMode = DistanceGenerationMode.NONE;
|
||||
switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) {
|
||||
case 0:
|
||||
generationMode = DistanceGenerationMode.NONE;
|
||||
break;
|
||||
case 1:
|
||||
generationMode = DistanceGenerationMode.BIOME_ONLY;
|
||||
break;
|
||||
case 2:
|
||||
generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
break;
|
||||
case 3:
|
||||
generationMode = DistanceGenerationMode.SURFACE;
|
||||
break;
|
||||
case 4:
|
||||
generationMode = DistanceGenerationMode.FEATURES;
|
||||
break;
|
||||
case 5:
|
||||
generationMode = DistanceGenerationMode.SERVER;
|
||||
break;
|
||||
default:
|
||||
generationMode = DistanceGenerationMode.NONE;
|
||||
break;
|
||||
|
||||
}
|
||||
return generationMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
public boolean hasDataBeenGenerated(LevelPos levelPos) {
|
||||
levelPos = levelPos.regionModule();
|
||||
return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0);
|
||||
}
|
||||
|
||||
public byte getMinDetailLevel() {
|
||||
return minDetailLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be used to save a level
|
||||
*
|
||||
* @param lod
|
||||
* @return
|
||||
*/
|
||||
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) {
|
||||
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;
|
||||
colors[levelContainer.detailLevel] = levelContainer.colors;
|
||||
height[levelContainer.detailLevel] = levelContainer.height;
|
||||
depth[levelContainer.detailLevel] = levelContainer.depth;
|
||||
generationType[levelContainer.detailLevel] = levelContainer.generationType;
|
||||
dataExistence[levelContainer.detailLevel] = levelContainer.dataExistence;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lod
|
||||
*/
|
||||
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];
|
||||
generationType[tempLod] = new byte[0][0];
|
||||
dataExistence[tempLod] = new boolean[0][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -31,11 +31,11 @@ import net.minecraft.world.DimensionType;
|
||||
* @author Leonardo Amato
|
||||
* @version 8-17-2021
|
||||
*/
|
||||
public class LodQuadTreeWorld
|
||||
public class LodWorld
|
||||
{
|
||||
private String worldName;
|
||||
|
||||
private Map<DimensionType, LodQuadTreeDimension> lodDimensions;
|
||||
private Map<DimensionType, LodDimension> lodDimensions;
|
||||
|
||||
/** If true then the LOD world is setup and ready to use */
|
||||
private boolean isWorldLoaded = false;
|
||||
@@ -44,7 +44,7 @@ public class LodQuadTreeWorld
|
||||
|
||||
|
||||
|
||||
public LodQuadTreeWorld()
|
||||
public LodWorld()
|
||||
{
|
||||
worldName = NO_WORLD_LOADED;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class LodQuadTreeWorld
|
||||
return;
|
||||
|
||||
worldName = newWorldName;
|
||||
lodDimensions = new Hashtable<DimensionType, LodQuadTreeDimension>();
|
||||
lodDimensions = new Hashtable<DimensionType, LodDimension>();
|
||||
isWorldLoaded = true;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class LodQuadTreeWorld
|
||||
* Adds newStorage to this world, if a LodQuadTreeDimension
|
||||
* already exists for the given dimension it is replaced.
|
||||
*/
|
||||
public void addLodDimension(LodQuadTreeDimension newStorage)
|
||||
public void addLodDimension(LodDimension newStorage)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return;
|
||||
@@ -101,7 +101,7 @@ public class LodQuadTreeWorld
|
||||
/**
|
||||
* Returns null if no LodQuadTreeDimension exists for the given dimension
|
||||
*/
|
||||
public LodQuadTreeDimension getLodDimension(DimensionType dimension)
|
||||
public LodDimension getLodDimension(DimensionType dimension)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return null;
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package com.seibel.lod.proxy;
|
||||
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -29,8 +31,6 @@ import com.seibel.lod.enums.FogDrawOverride;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.ShadingMode;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeWorld;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.render.LodNodeRenderer;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
@@ -54,7 +54,7 @@ public class ClientProxy
|
||||
{
|
||||
public static final Logger LOGGER = LogManager.getLogger("LOD");
|
||||
|
||||
private static LodQuadTreeWorld lodWorld = new LodQuadTreeWorld();
|
||||
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);
|
||||
@@ -89,11 +89,11 @@ public class ClientProxy
|
||||
{
|
||||
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
|
||||
|
||||
|
||||
viewDistanceChangedEvent();
|
||||
|
||||
LodQuadTreeDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
|
||||
|
||||
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
@@ -142,14 +142,14 @@ public class ClientProxy
|
||||
LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL);
|
||||
LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL);
|
||||
|
||||
LodConfig.CLIENT.lodChunkRadiusMultiplier.set(20);
|
||||
LodConfig.CLIENT.lodChunkRadiusMultiplier.set(16);
|
||||
LodConfig.CLIENT.fogDistance.set(FogDistance.FAR);
|
||||
LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.NEVER_DRAW_FOG);
|
||||
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.distanceGenerationMode.set(DistanceGenerationMode.FEATURES);
|
||||
LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
|
||||
|
||||
LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16);
|
||||
@@ -224,7 +224,7 @@ public class ClientProxy
|
||||
/**
|
||||
* Re-centers the given LodDimension if it needs to be.
|
||||
*/
|
||||
private void playerMoveEvent(LodQuadTreeDimension lodDim)
|
||||
private void playerMoveEvent(LodDimension lodDim)
|
||||
{
|
||||
// make sure the dimension is centered
|
||||
RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition());
|
||||
@@ -237,15 +237,16 @@ public class ClientProxy
|
||||
//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 = (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
|
||||
|
||||
@@ -262,13 +263,13 @@ public class ClientProxy
|
||||
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// public getters //
|
||||
//================//
|
||||
|
||||
public static LodQuadTreeWorld getLodWorld()
|
||||
public static LodWorld getLodWorld()
|
||||
{
|
||||
return lodWorld;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.seibel.lod.objects.LevelPos;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.NVFogDistance;
|
||||
@@ -35,8 +36,7 @@ import com.seibel.lod.enums.FogDrawOverride;
|
||||
import com.seibel.lod.enums.FogQuality;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.handlers.ReflectionHandler;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.NearFarFogSettings;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
@@ -146,7 +146,7 @@ public class LodNodeRenderer
|
||||
* @param partialTicks how far into the current tick this method was called.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void drawLODs(LodQuadTreeDimension lodDim, float partialTicks, IProfiler newProfiler)
|
||||
public void drawLODs(LodDimension lodDim, float partialTicks, IProfiler newProfiler)
|
||||
{
|
||||
if (lodDim == null)
|
||||
{
|
||||
@@ -213,10 +213,12 @@ public class LodNodeRenderer
|
||||
|
||||
|
||||
// determine how far the game's render distance is currently set
|
||||
farPlaneBlockDistance = mc.options.renderDistance * LodUtil.CHUNK_WIDTH;
|
||||
//farPlaneBlockDistance = mc.options.renderDistance * LodUtil.CHUNK_WIDTH;
|
||||
farPlaneBlockDistance = 8 * LodUtil.CHUNK_WIDTH;
|
||||
|
||||
// set how how far the LODs will go
|
||||
int numbChunksWide = mc.options.renderDistance * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get();
|
||||
//int numbChunksWide = mc.options.renderDistance * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get();
|
||||
int numbChunksWide = 8 * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + 1;
|
||||
|
||||
// determine which LODs should not be rendered close to the player
|
||||
HashSet<ChunkPos> chunkPosToSkip = getNearbyLodChunkPosToSkip(lodDim, player.blockPosition());
|
||||
@@ -578,7 +580,7 @@ public class LodNodeRenderer
|
||||
* setup the lighting to be used for the LODs
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setupLighting(LodQuadTreeDimension lodDimension, float partialTicks)
|
||||
private void setupLighting(LodDimension lodDimension, float partialTicks)
|
||||
{
|
||||
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.level.getSkyDarken(partialTicks) : 0.2f;
|
||||
float gammaMultiplyer = (float)mc.options.gamma - 0.5f;
|
||||
@@ -771,7 +773,7 @@ public class LodNodeRenderer
|
||||
* Get a HashSet of all ChunkPos within the normal render distance
|
||||
* that should not be rendered.
|
||||
*/
|
||||
private HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodQuadTreeDimension lodDim, BlockPos playerPos)
|
||||
private HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
|
||||
{
|
||||
int chunkRenderDist = mc.options.renderDistance;
|
||||
int blockRenderDist = chunkRenderDist * 16;
|
||||
@@ -786,11 +788,12 @@ public class LodNodeRenderer
|
||||
{
|
||||
for(int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
|
||||
{
|
||||
LodQuadTreeNode lod = lodDim.getLodFromCoordinates(new ChunkPos(x, z), 4);
|
||||
if (lod != null)
|
||||
|
||||
LevelPos levelPos = new LevelPos((byte) 4, x, z);
|
||||
if (lodDim.doesDataExist(levelPos))
|
||||
{
|
||||
short lodHighestPoint = lod.getLodDataPoint().height;
|
||||
|
||||
short lodHighestPoint = lodDim.getData(levelPos).height;
|
||||
|
||||
if (playerPos.getY() < lodHighestPoint)
|
||||
{
|
||||
// don't draw Lod's that are taller than the player
|
||||
|
||||
Reference in New Issue
Block a user