Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5

This commit is contained in:
James Seibel
2021-10-07 21:25:05 -05:00
24 changed files with 882 additions and 797 deletions
@@ -77,7 +77,7 @@ public class LodBufferBuilder
public volatile boolean fullRegeneration = false;
/** the capacity of each buffer in bytes */
public volatile int[][] bufferCount;
public volatile int[][] bufferSize;
/** Used when building the vbos */
public volatile BufferBuilder[][][] buildableBuffers;
@@ -254,7 +254,7 @@ public class LodBufferBuilder
PosToRenderContainer posToRender = setsToRender[xR][zR];
posToRender.clear(minDetail, regionPos.x, regionPos.z);
lodDim.getDataToRender(
lodDim.getPosToRender(
posToRender,
regionPos,
playerBlockPosRounded.getX(),
@@ -428,7 +428,7 @@ public class LodBufferBuilder
int numberOfBuffers;
previousRegionWidth = numbRegionsWide;
bufferCount = new int[numbRegionsWide][numbRegionsWide];
bufferSize = new int[numbRegionsWide][numbRegionsWide];
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide][];
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][];
@@ -447,7 +447,7 @@ public class LodBufferBuilder
// if the memory required is greater than the max buffer capacity divide the memory across multiple buffers
if (memoryRequired < LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY)
{
bufferCount[x][z] = 1;
bufferSize[x][z] = 1;
buildableBuffers[x][z] = new BufferBuilder[1];
buildableVbos[x][z] = new VertexBuffer[1];
drawableVbos[x][z] = new VertexBuffer[1];
@@ -456,14 +456,14 @@ public class LodBufferBuilder
{
numberOfBuffers = (int) Math.ceil(memoryRequired / LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY) + 1;
memoryRequired = LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY;
bufferCount[x][z] = numberOfBuffers;
bufferSize[x][z] = numberOfBuffers;
buildableBuffers[x][z] = new BufferBuilder[numberOfBuffers];
buildableVbos[x][z] = new VertexBuffer[numberOfBuffers];
drawableVbos[x][z] = new VertexBuffer[numberOfBuffers];
}
for (int i = 0; i < bufferCount[x][z]; i++)
for (int i = 0; i < bufferSize[x][z]; i++)
{
buildableBuffers[x][z][i] = new BufferBuilder((int) memoryRequired);
@@ -15,18 +15,18 @@
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
package com.seibel.lod.builders.bufferBuilding.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import java.util.Map;
/**
* This is the abstract class used to create different
* BufferBuilders.
@@ -1,20 +1,19 @@
package com.seibel.lod.builders.bufferBuilding.lodTemplates;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3i;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Similar to Minecraft's AxisAlignedBoundingBox.
*
@@ -37,57 +36,56 @@ public class Box
public static final int VOID_FACE = 0;
/** The six cardinal directions */
public static final Direction[] DIRECTIONS = new Direction[]{
public static final Direction[] DIRECTIONS = new Direction[] {
Direction.UP,
Direction.DOWN,
Direction.WEST,
Direction.EAST,
Direction.NORTH,
Direction.SOUTH};
Direction.SOUTH };
/** North, South, East, West */
public static final Direction[] ADJ_DIRECTIONS = new Direction[]{
public static final Direction[] ADJ_DIRECTIONS = new Direction[] {
Direction.EAST,
Direction.WEST,
Direction.SOUTH,
Direction.NORTH};
Direction.NORTH };
/**
* All the faces and vertices of a cube. This is used to extract the vertex from the column
*/
@SuppressWarnings("serial")
public static final Map<Direction, int[][]> DIRECTION_VERTEX_MAP = new HashMap<Direction, int[][]>()
{{
put(Direction.UP, new int[][]{
{0, 1, 0},
{0, 1, 1},
{1, 1, 1},
{1, 1, 0}});
put(Direction.DOWN, new int[][]{
{1, 0, 0},
{1, 0, 1},
{0, 0, 1},
{0, 0, 0}});
put(Direction.EAST, new int[][]{
{1, 1, 0},
{1, 1, 1},
{1, 0, 1},
{1, 0, 0}});
put(Direction.WEST, new int[][]{
{0, 0, 0},
{0, 0, 1},
{0, 1, 1},
{0, 1, 0}});
put(Direction.SOUTH, new int[][]{
{1, 0, 1},
{1, 1, 1},
{0, 1, 1},
{0, 0, 1}});
put(Direction.NORTH, new int[][]{
{0, 0, 0},
{0, 1, 0},
{1, 1, 0},
{1, 0, 0}});
put(Direction.UP, new int[][] {
{ 0, 1, 0 },
{ 0, 1, 1 },
{ 1, 1, 1 },
{ 1, 1, 0 } });
put(Direction.DOWN, new int[][] {
{ 1, 0, 0 },
{ 1, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 } });
put(Direction.EAST, new int[][] {
{ 1, 1, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 0, 0 } });
put(Direction.WEST, new int[][] {
{ 0, 0, 0 },
{ 0, 0, 1 },
{ 0, 1, 1 },
{ 0, 1, 0 } });
put(Direction.SOUTH, new int[][] {
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 1, 1 },
{ 0, 0, 1 } });
put(Direction.NORTH, new int[][] {
{ 0, 0, 0 },
{ 0, 1, 0 },
{ 1, 1, 0 },
{ 1, 0, 0 } });
}};
@@ -95,15 +93,14 @@ public class Box
* This indicate which position is invariable in the DIRECTION_VERTEX_MAP.
* Is used to extract the vertex
*/
@SuppressWarnings("serial")
public static final Map<Direction, int[]> FACE_DIRECTION = new HashMap<Direction, int[]>()
{{
put(Direction.UP, new int[]{Y, MAX});
put(Direction.DOWN, new int[]{Y, MIN});
put(Direction.EAST, new int[]{X, MAX});
put(Direction.WEST, new int[]{X, MIN});
put(Direction.SOUTH, new int[]{Z, MAX});
put(Direction.NORTH, new int[]{Z, MIN});
put(Direction.UP, new int[] { Y, MAX });
put(Direction.DOWN, new int[] { Y, MIN });
put(Direction.EAST, new int[] { X, MAX });
put(Direction.WEST, new int[] { X, MIN });
put(Direction.SOUTH, new int[] { Z, MAX });
put(Direction.NORTH, new int[] { Z, MIN });
}};
@@ -159,7 +156,6 @@ public class Box
/** creates a empty box */
@SuppressWarnings("serial")
public Box()
{
boxOffset = new int[3];
@@ -248,7 +244,7 @@ public class Box
playerPos = MinecraftWrapper.INSTANCE.getPlayer().blockPosition();
for (Direction direction : DIRECTIONS)
{
if (direction == Direction.DOWN || direction == Direction.WEST || direction == Direction.NORTH)
if (direction == Direction.DOWN || direction == Direction.WEST || direction == Direction.NORTH)
{
culling[DIRECTION_INDEX.get(direction)] = playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance;
}
@@ -304,7 +300,7 @@ public class Box
{
singleAdjDataPoint = dataPoint[i];
if(DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
break;
height = DataPointUtil.getHeight(singleAdjDataPoint);
@@ -385,7 +381,7 @@ public class Box
}
}
if(allAbove)
if (allAbove)
{
adjHeight.get(direction)[0] = getMaxY();
adjDepth.get(direction)[0] = getMinY();
@@ -15,19 +15,19 @@
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
package com.seibel.lod.builders.bufferBuilding.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import java.util.Map;
/**
* Builds LODs as rectangular prisms.
*
@@ -39,7 +39,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
public CubicLodTemplate()
{
}
@Override
@@ -73,11 +73,11 @@ public class CubicLodTemplate extends AbstractLodTemplate
addBoundingBoxToBuffer(buffer, box);
}
private void generateBoundingBox(Box box,
int height, int depth, int width,
double xOffset, double yOffset, double zOffset,
BlockPos bufferCenterBlockPos,
Map<Direction, long[]> adjData,
private void generateBoundingBox(Box box,
int height, int depth, int width,
double xOffset, double yOffset, double zOffset,
BlockPos bufferCenterBlockPos,
Map<Direction, long[]> adjData,
int color,
boolean[] adjShadeDisabled)
{
@@ -130,7 +130,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
public int getBufferMemoryForSingleNode(int maxVerticalData)
{
// TODO, what do these magic numbers mean
return 2 * 4 * (3 + 4) + 4 * 4 * Math.max((maxVerticalData+1)/2,1) * (3 + 4);
return 2 * 4 * (3 + 4) + 4 * 4 * Math.max((maxVerticalData + 1) / 2, 1) * (3 + 4);
}
}
@@ -15,18 +15,18 @@
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
package com.seibel.lod.builders.bufferBuilding.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import java.util.Map;
/**
* TODO DynamicLodTemplate
* Chunks smoothly transition between
@@ -40,11 +40,11 @@ public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
@Override
public int getBufferMemoryForSingleNode(int maxVerticalData)
{
@@ -15,18 +15,18 @@
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
package com.seibel.lod.builders.bufferBuilding.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import java.util.Map;
/**
* TODO #21 TriangularLodTemplate
* Builds each LOD chunk as a singular rectangular prism.
@@ -38,11 +38,11 @@ public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
@Override
public int getBufferMemoryForSingleNode(int maxVerticalData)
{
@@ -15,15 +15,8 @@
* 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.builders.lodBuilding;
import java.awt.Color;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
package com.seibel.lod.builders.lodBuilding;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
@@ -32,26 +25,9 @@ import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
import com.seibel.lod.util.*;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.AbstractTopPlantBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.BushBlock;
import net.minecraft.block.FlowerBlock;
import net.minecraft.block.GrassBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.TallGrassBlock;
import net.minecraft.block.*;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
@@ -71,6 +47,14 @@ import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap;
import net.minecraftforge.client.model.data.ModelDataMap;
import java.awt.*;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* This object is in charge of creating Lod related objects. (specifically: Lod
* World, Dimension, and Region objects)
@@ -81,9 +65,9 @@ import net.minecraftforge.client.model.data.ModelDataMap;
*/
public class LodBuilder
{
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
@@ -115,19 +99,14 @@ public class LodBuilder
public LodBuilder()
{
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
{
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
{
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
@@ -212,7 +191,7 @@ public class LodBuilder
// determine how many LODs to generate vertically
VerticalQuality verticalQuality = LodConfig.CLIENT.worldGenerator.lodQualityMode.get();
VerticalQuality verticalQuality = LodConfig.CLIENT.worldGenerator.verticalQuality.get();
byte detailLevel = detail.detailLevel;
@@ -231,43 +210,25 @@ public class LodBuilder
long[] data;
switch (verticalQuality)
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ, endX, endZ);
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight, DetailDistanceUtil.getMaxVerticalData(detailLevel));
//lodDim.clear(detailLevel, posX, posZ);
if (data != null && data.length != 0)
{
default:
case HEIGHTMAP:
long singleData;
long[] dataToMergeSingle = createSingleDataToMerge(detail, chunk, config, startX, startZ, endX, endZ);
singleData = DataPointUtil.mergeSingleData(dataToMergeSingle);
lodDim.addData(detailLevel,
posX,
posZ,
0,
singleData,
false);
break;
case VOXEL:
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ, endX, endZ);
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight, DetailDistanceUtil.getMaxVerticalData(detailLevel));
//lodDim.clear(detailLevel, posX, posZ);
if (data != null && data.length != 0)
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
{
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
{
if (!DataPointUtil.doesItExist(data[verticalIndex]))
break;
lodDim.addData(detailLevel,
posX,
posZ,
verticalIndex,
data[verticalIndex],
false);
}
if (!DataPointUtil.doesItExist(data[verticalIndex]))
break;
lodDim.addData(detailLevel,
posX,
posZ,
verticalIndex,
data[verticalIndex],
false);
}
break;
}
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
@@ -366,35 +327,35 @@ public class LodBuilder
private short determineBottomPointFrom(IChunk chunk, LodBuilderConfig config, int xRel, int zRel, int yAbs, BlockPos.Mutable blockPos)
{
short depth = DEFAULT_DEPTH;
if (config.useHeightmap)
/*if (config.useHeightmap)
{
// when using the generated heightmap there is no data about the lowest point
depth = 0;
depth = 0; //DEFAULT_DEPTH == 0
}
else
{*/
boolean voidData = true;
ChunkSection[] chunkSections = chunk.getSections();
for (int sectionIndex = chunkSections.length - 1; sectionIndex >= 0; sectionIndex--)
{
boolean voidData = true;
ChunkSection[] chunkSections = chunk.getSections();
for (int sectionIndex = chunkSections.length - 1; sectionIndex >= 0; sectionIndex--)
for (int yRel = CHUNK_DATA_WIDTH - 1; yRel >= 0; yRel--)
{
for (int yRel = CHUNK_DATA_WIDTH - 1; yRel >= 0; yRel--)
{
if (sectionIndex * CHUNK_DATA_WIDTH + yRel > yAbs)
continue;
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (!isLayerValidLodPoint(chunk, blockPos))
{
depth = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel + 1);
voidData = false;
break;
}
}
if (sectionIndex * CHUNK_DATA_WIDTH + yRel > yAbs)
continue;
if (!voidData)
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (!isLayerValidLodPoint(chunk, blockPos))
{
depth = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel + 1);
voidData = false;
break;
}
}
if (!voidData)
break;
}
//}
return depth;
}
@@ -405,9 +366,7 @@ public class LodBuilder
{
short height = DEFAULT_HEIGHT;
if (config.useHeightmap)
{
height = (short) chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
}
else
{
boolean voidData = true;
@@ -509,30 +468,30 @@ public class LodBuilder
{
ChunkSection[] chunkSections = chunk.getSections();
short depth = DEFAULT_DEPTH;
if (config.useHeightmap)
/*if (config.useHeightmap)
{
depth = 0;
depth = 0; //DEFAULT_DEPTH == 0
}
else
{*/
boolean found = false;
for (int sectionIndex = 0; sectionIndex < chunkSections.length; sectionIndex++)
{
boolean found = false;
for (int sectionIndex = 0; sectionIndex < chunkSections.length; sectionIndex++)
for (int yRel = 0; yRel < CHUNK_DATA_WIDTH; yRel++)
{
for (int yRel = 0; yRel < CHUNK_DATA_WIDTH; yRel++)
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (isLayerValidLodPoint(chunk, blockPos))
{
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (isLayerValidLodPoint(chunk, blockPos))
{
depth = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel);
found = true;
break;
}
}
if (found)
depth = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel);
found = true;
break;
}
}
if (found)
break;
}
//}
return depth;
}
@@ -544,9 +503,7 @@ public class LodBuilder
{
short height = DEFAULT_HEIGHT;
if (config.useHeightmap)
{
height = (short) chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
}
else
{
boolean voidData = true;
@@ -610,7 +567,7 @@ public class LodBuilder
private int getLightValue(IChunk chunk, BlockPos.Mutable blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
{
int skyLight;
int blockLight = 0;
int blockLight;
if (mc.getClientWorld() == null)
return 0;
@@ -627,19 +584,13 @@ public class LodBuilder
if (!hasSkyLight && hasCeiling)
{
skyLight = 0;
}
else if (topBlock)
{
skyLight = DEFAULT_MAX_LIGHT;
}
else
{
if (chunk.isLightCorrect())
{
skyLight = world.getBrightness(LightType.SKY, blockPos);
}
else
{
// we don't know what the light here is,
@@ -747,10 +698,8 @@ public class LodBuilder
if (count == 0)
{
// this block is entirely transparent
color = 0;
}
else
{
// determine the average color
@@ -762,7 +711,7 @@ public class LodBuilder
}
// determine if this block should use the biome color tint
if (block instanceof TallGrassBlock || (useGrassTint(block) || useLeafTint(block) || useWaterTint(block)) && numberOfGreyPixel / count > 0.75f)
if (block instanceof TallGrassBlock || (useGrassTint(block) || useLeafTint(block) || useWaterTint(block)) && (float) numberOfGreyPixel / count > 0.75f)
toTint.replace(block, true);
// add the newly generated block color to the map for later use
@@ -775,19 +724,19 @@ public class LodBuilder
private boolean useGrassTint(Block block)
{
return block instanceof GrassBlock
|| block instanceof BushBlock
|| block instanceof IGrowable
|| block instanceof AbstractPlantBlock
|| block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
|| block instanceof BushBlock
|| block instanceof IGrowable
|| block instanceof AbstractPlantBlock
|| block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean useLeafTint(Block block)
{
return block instanceof LeavesBlock
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's water color */
@@ -816,8 +765,8 @@ public class LodBuilder
// block special cases
// TODO: this needs to be replaced by a config file of some sort
if (blockState == Blocks.AIR.defaultBlockState()
|| blockState == Blocks.CAVE_AIR.defaultBlockState()
|| blockState == Blocks.BARRIER.defaultBlockState())
|| blockState == Blocks.CAVE_AIR.defaultBlockState()
|| blockState == Blocks.BARRIER.defaultBlockState())
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
@@ -825,28 +774,20 @@ public class LodBuilder
}
blockColor = getColorTextureForBlock(blockState, blockPos, true);
if (toTint.get(blockState.getBlock()).booleanValue())
if (toTint.get(blockState.getBlock()))
{
if (useLeafTint(blockState.getBlock()))
{
// leaves
colorInt = ColorUtil.multiplyRGBcolors(biome.getFoliageColor() | 0xFF000000, blockColor);
}
else if (useGrassTint(blockState.getBlock()))
{
// grass and green plants
colorInt = ColorUtil.multiplyRGBcolors(biome.getGrassColor(x, z) | 0xFF000000, blockColor);
}
else if (useWaterTint(blockState.getBlock()))
{
// water
colorInt = ColorUtil.multiplyRGBcolors(biome.getWaterColor() | 0xFF000000, blockColor);
}
}
else
{
colorInt = blockColor;
}
return colorInt;
}
@@ -907,22 +848,23 @@ public class LodBuilder
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
return colorInt;
}
public static final ConcurrentMap<Block, Boolean> notFullBlock = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> smallBlock = new ConcurrentHashMap<>();
/**
* Is the block at the given blockPos a valid LOD point?
*/
private boolean isLayerValidLodPoint(IChunk chunk, BlockPos.Mutable blockPos)
{
BlockState blockState = chunk.getBlockState(blockPos);
if (blockState != null)
{
// TODO this code is dead since avoidSmallBlock and onlyUseFullBlock
@@ -931,9 +873,9 @@ public class LodBuilder
if (avoidSmallBlock || avoidNonFullBlock)
{
if (!smallBlock.containsKey(blockState.getBlock())
|| smallBlock.get(blockState.getBlock()) == null
|| !notFullBlock.containsKey(blockState.getBlock())
|| notFullBlock.get(blockState.getBlock()) == null
|| smallBlock.get(blockState.getBlock()) == null
|| !notFullBlock.containsKey(blockState.getBlock())
|| notFullBlock.get(blockState.getBlock()) == null
)
{
VoxelShape voxelShape = blockState.getShape(chunk, blockPos);
@@ -942,7 +884,7 @@ public class LodBuilder
notFullBlock.put(blockState.getBlock(), false);
smallBlock.put(blockState.getBlock(), false);
}
if (!voxelShape.isEmpty())
{
AxisAlignedBB bbox = voxelShape.bounds();
@@ -953,34 +895,31 @@ public class LodBuilder
notFullBlock.put(blockState.getBlock(), true);
else
notFullBlock.put(blockState.getBlock(), false);
if (xWidth < 0.7 && zWidth < 0.7 && yWidth < 0.7)
smallBlock.put(blockState.getBlock(), true);
else
smallBlock.put(blockState.getBlock(), false);
} else
}
else
{
notFullBlock.put(blockState.getBlock(), false);
smallBlock.put(blockState.getBlock(), false);
}
}
if (notFullBlock.get(blockState.getBlock()) && avoidNonFullBlock)
{
return false;
}
if (smallBlock.get(blockState.getBlock()) && avoidSmallBlock)
{
return false;
}
}
return blockState.getBlock() != Blocks.AIR
&& blockState.getBlock() != Blocks.CAVE_AIR
&& blockState.getBlock() != Blocks.BARRIER;
&& blockState.getBlock() != Blocks.CAVE_AIR
&& blockState.getBlock() != Blocks.BARRIER;
}
return false;
}
}
@@ -15,6 +15,7 @@
* 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.builders.lodBuilding;
import com.seibel.lod.enums.DistanceGenerationMode;
@@ -38,7 +39,7 @@ public class LodBuilderConfig
public boolean useSolidBlocksInColorGen;
/** default: server */
public DistanceGenerationMode distanceGenerationMode;
/**
* default settings for a normal chunk <br>
* useHeightmap = false <br>
@@ -53,7 +54,7 @@ public class LodBuilderConfig
useSolidBlocksInColorGen = true;
distanceGenerationMode = DistanceGenerationMode.SERVER;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
@@ -61,14 +62,14 @@ public class LodBuilderConfig
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors,
boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode)
boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode)
{
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
distanceGenerationMode = newDistanceGenerationMode;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
@@ -81,9 +82,9 @@ public class LodBuilderConfig
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
distanceGenerationMode = newUseHeightmap? DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT : DistanceGenerationMode.BIOME_ONLY;
distanceGenerationMode = newUseHeightmap ? DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT : DistanceGenerationMode.BIOME_ONLY;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
@@ -15,16 +15,8 @@
* 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.builders.worldGeneration;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
package com.seibel.lod.builders.worldGeneration;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.seibel.lod.builders.lodBuilding.LodBuilder;
@@ -33,9 +25,7 @@ import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.ChunkPos;
@@ -48,13 +38,7 @@ import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider;
import net.minecraft.world.gen.feature.BlockClusterFeatureConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.DecoratedFeatureConfig;
import net.minecraft.world.gen.feature.FeatureSpread;
import net.minecraft.world.gen.feature.FeatureSpreadConfig;
import net.minecraft.world.gen.feature.IceAndSnowFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.*;
import net.minecraft.world.gen.placement.ConfiguredPlacement;
import net.minecraft.world.gen.placement.DecoratedPlacementConfig;
import net.minecraft.world.gen.placement.IPlacementConfig;
@@ -64,6 +48,15 @@ import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.server.ServerWorldLightManager;
import net.minecraftforge.common.WorldWorkerManager.IWorker;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
/**
* This is used to generate a LodChunk at a given ChunkPos.
*
@@ -75,13 +68,13 @@ public class LodNodeGenWorker implements IWorker
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
private boolean threadStarted = false;
private LodChunkGenThread thread;
private final 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<>();
private static final ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> configuredFeaturesToAvoid = new ConcurrentHashMap<>();
@@ -149,14 +142,14 @@ public class LodNodeGenWorker implements IWorker
private class LodChunkGenThread implements Runnable
private static class LodChunkGenThread implements Runnable
{
public final ServerWorld serverWorld;
public final LodDimension lodDim;
public final DistanceGenerationMode generationMode;
public final LodBuilder lodBuilder;
private ChunkPos pos;
private final ChunkPos pos;
public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
LodBuilder newLodBuilder,
@@ -177,12 +170,12 @@ public class LodNodeGenWorker implements IWorker
// only generate LodChunks if they can
// be added to the current LodDimension
/**TODO i must disable this if, i will find a way to replace it*/
/* TODO i must disable this if, i will find a way to replace it */
if (lodDim.regionIsInRange(pos.x / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.z / LodUtil.REGION_WIDTH_IN_CHUNKS))
{
// long startTime = System.currentTimeMillis();
switch(generationMode)
switch (generationMode)
{
case NONE:
// don't generate
@@ -217,7 +210,7 @@ public class LodNodeGenWorker implements IWorker
*/
// 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);
@@ -271,20 +264,20 @@ public class LodNodeGenWorker implements IWorker
// 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++)
for (int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++)
{
for(int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++)
for (int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++)
{
if (simulateHeight)
{
// 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())
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;
@@ -300,24 +293,24 @@ public class LodNodeGenWorker implements IWorker
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
// DESERT
// FOREST
// ICY
// MUSHROOM
// SAVANNA
// SWAMP
// TAIGA
// PLAINS
default:
heightmap.setHeight(x, z, seaLevel + 10);
break;
@@ -346,8 +339,8 @@ public class LodNodeGenWorker implements IWorker
// generates the same and it looks really bad.
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(true, true, false));
}
// long startTime = System.currentTimeMillis();
// long endTime = System.currentTimeMillis();
// System.out.println(endTime - startTime);
@@ -445,9 +438,9 @@ public class LodNodeGenWorker implements IWorker
{
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.generationSettings.features();
for(int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++)
for (int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++)
{
for(Supplier<ConfiguredFeature<?, ?>> featureSupplier : featuresForState.get(featureStateToGenerate))
for (Supplier<ConfiguredFeature<?, ?>> featureSupplier : featuresForState.get(featureStateToGenerate))
{
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
@@ -460,7 +453,7 @@ public class LodNodeGenWorker implements IWorker
{
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
}
catch(ConcurrentModificationException e)
catch (ConcurrentModificationException e)
{
// This will happen. I'm not sure what to do about it
// except pray that it doesn't effect the normal world generation
@@ -480,7 +473,7 @@ public class LodNodeGenWorker implements IWorker
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
catch(UnsupportedOperationException e)
catch (UnsupportedOperationException e)
{
// This will happen when the LodServerWorld
// isn't able to return something that a feature
@@ -490,7 +483,7 @@ public class LodNodeGenWorker implements IWorker
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
catch(Exception e)
catch (Exception e)
{
// I'm not sure what happened, print to the log
@@ -538,7 +531,7 @@ public class LodNodeGenWorker implements IWorker
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config)
{
IPlacementConfig placementConfig = null;
IPlacementConfig placementConfig;
Class oldConfigClass = config.decorator.config().getClass();
@@ -549,12 +542,12 @@ public class LodNodeGenWorker implements IWorker
placementConfig = new FeatureSpreadConfig(oldSpread);
}
else if(oldConfigClass == DecoratedPlacementConfig.class)
else if (oldConfigClass == DecoratedPlacementConfig.class)
{
DecoratedPlacementConfig oldPlacementConfig = (DecoratedPlacementConfig) config.decorator.config();
placementConfig = new DecoratedPlacementConfig(oldPlacementConfig.inner(), oldPlacementConfig.outer());
}
else if(oldConfigClass == NoiseDependant.class)
else if (oldConfigClass == NoiseDependant.class)
{
NoiseDependant oldPlacementConfig = (NoiseDependant) config.decorator.config();
placementConfig = new NoiseDependant(oldPlacementConfig.noiseLevel, oldPlacementConfig.belowNoise, oldPlacementConfig.aboveNoise);
@@ -588,9 +581,18 @@ public class LodNodeGenWorker implements IWorker
builder.xspread(config.xspread);
builder.yspread(config.yspread);
builder.zspread(config.zspread);
if(config.canReplace) { builder.canReplace(); }
if(config.needWater) { builder.needWater(); }
if(config.project) { builder.noProjection(); }
if (config.canReplace)
{
builder.canReplace();
}
if (config.needWater)
{
builder.needWater();
}
if (config.project)
{
builder.noProjection();
}
builder.tries(config.tries);
@@ -615,7 +617,7 @@ public class LodNodeGenWorker implements IWorker
{
genThreads.shutdownNow();
}
genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
}
@@ -33,7 +33,7 @@ public class LodWorldGenerator
public MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** This holds the thread used to generate new LODs off the main thread. */
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
private final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
/** we only want to queue up one generator thread at a time */
private boolean generatorThreadRunning = false;
@@ -100,23 +100,23 @@ public class LodWorldGenerator
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
PosToGenerateContainer posToGenerate = lodDim.getDataToGenerate(
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(
maxChunkGenRequests,
playerPosX,
playerPosZ);
byte detailLevel;
int posX;
int posZ;
int nearIndex = 0;
int farIndex = 0;
for (int i = 0; i < posToGenerate.getNumberOfPos(); i++)
{
// I wish there was a way to compress this code, but I'm not aware of
// a easy way to do so.
// add the near positions
if (posToGenerate.getNthDetail(nearIndex, true) != 0 && nearIndex < posToGenerate.getNumberOfNearPos())
{
@@ -126,13 +126,11 @@ public class LodWorldGenerator
nearIndex++;
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
// prevent generating the same chunk multiple times
if (positionsWaitingToBeGenerated.contains(chunkPos))
continue;
}
// prevent generating the same chunk multiple times
if (positionsWaitingToBeGenerated.contains(chunkPos))
continue;
// don't add more to the generation queue then allowed
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
break;
@@ -142,8 +140,8 @@ public class LodWorldGenerator
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
// add the far positions
if (posToGenerate.getNthDetail(farIndex, false) != 0 && farIndex < posToGenerate.getNumberOfFarPos())
{
@@ -153,17 +151,16 @@ public class LodWorldGenerator
farIndex++;
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
// prevent generating the same chunk multiple times
if (positionsWaitingToBeGenerated.contains(chunkPos))
continue;
}
// don't add more to the generation queue then allowed
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
break;
continue;
//break;
// prevent generating the same chunk multiple times
if (positionsWaitingToBeGenerated.contains(chunkPos))
continue;
positionsWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
+203 -229
View File
@@ -15,28 +15,19 @@
* 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.config;
import java.nio.file.Path;
import java.nio.file.Paths;
import com.seibel.lod.enums.*;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import com.seibel.lod.ModInfo;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.DetailDropOff;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.GenerationPriority;
import com.seibel.lod.enums.HorizontalQuality;
import com.seibel.lod.enums.HorizontalResolution;
import com.seibel.lod.enums.HorizontalScale;
import com.seibel.lod.enums.LodTemplate;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.render.LodRenderer.FovTest;
import net.minecraftforge.common.ForgeConfigSpec;
@@ -81,8 +72,6 @@ public class LodConfig
public static class Graphics
{
public ForgeConfigSpec.BooleanValue drawLods;
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
@@ -90,9 +79,6 @@ public class LodConfig
public ForgeConfigSpec.EnumValue<HorizontalResolution> drawResolution;
// public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
public ForgeConfigSpec.EnumValue<DetailDropOff> detailDropOff;
public ForgeConfigSpec.IntValue lodChunkRenderDistance;
@@ -101,6 +87,8 @@ public class LodConfig
public ForgeConfigSpec.BooleanValue alwaysDrawAtMaxQuality;
public ForgeConfigSpec.BooleanValue drawLods;
public ForgeConfigSpec.EnumValue<FovTest> useFovSetting;
@@ -109,80 +97,80 @@ public class LodConfig
builder.comment("These settings control how the LODs look.").push(this.getClass().getSimpleName());
drawLods = builder
.comment("\n\n"
+ " If true, the mod is enabled and LODs will be drawn. \n"
+ " If false, the mod will still generate LODs, \n"
+ " but they won't be rendered. \n")
.define("drawLODs", true);
.comment("\n\n"
+ " If true, the mod is enabled and LODs will be drawn. \n"
+ " If false, the mod will still generate LODs, \n"
+ " but they won't be rendered. \n")
.define("drawLODs", true);
fogDistance = builder
.comment("\n\n"
+ " At what distance should Fog be drawn on the LODs? \n"
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" fog option \n"
+ " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n")
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
.comment("\n\n"
+ " At what distance should Fog be drawn on the LODs? \n"
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" fog option \n"
+ " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n")
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
fogDrawOverride = builder
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY + ". \n"
+ " " + FogDrawOverride.NEVER_DRAW_FOG + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("fogDrawOverride", FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY + ". \n"
+ " " + FogDrawOverride.NEVER_DRAW_FOG + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("fogDrawOverride", FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
lodTemplate = builder
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC + ": LOD Chunks smoothly transition between other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("lodTemplate", LodTemplate.CUBIC);
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC + ": LOD Chunks smoothly transition between other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("lodTemplate", LodTemplate.CUBIC);
detailDropOff = builder
.comment("\n\n"
+ " How smooth should the detail transition for LODs be? \n"
+ DetailDropOff.FANCY + ": quality is determined per-block (best quality option, may cause stuttering when moving)\n"
+ DetailDropOff.FAST + ": quality is determined per-region (performance option)\n")
.defineEnum("detailDropOff", DetailDropOff.FANCY);
.comment("\n\n"
+ " How smooth should the detail transition for LODs be? \n"
+ DetailDropOff.FANCY + ": quality is determined per-block (best quality option, may cause stuttering when moving)\n"
+ DetailDropOff.FAST + ": quality is determined per-region (performance option)\n")
.defineEnum("detailDropOff", DetailDropOff.FANCY);
drawResolution = builder
.comment("\n\n"
+ " What is the maximum detail LODs should be drawn at? \n"
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n")
.defineEnum("Draw resolution", HorizontalResolution.BLOCK);
.comment("\n\n"
+ " What is the maximum detail LODs should be drawn at? \n"
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n")
.defineEnum("Draw resolution", HorizontalResolution.BLOCK);
lodChunkRenderDistance = builder
.comment("\n\n"
+ " The mod's render distance, measured in chunks. \n")
.defineInRange("lodChunkRenderDistance", 64, 32, 1024);
.comment("\n\n"
+ " The mod's render distance, measured in chunks. \n")
.defineInRange("lodChunkRenderDistance", 64, 32, 1024);
useFovSetting = builder
.comment("\n\n"
+ " Experimental text value. \n"
+ " " + FovTest.BOTH + ": is the normal value \n")
.defineEnum("useFovSetting", FovTest.BOTH);
.comment("\n\n"
+ " Experimental text value. \n"
+ " " + FovTest.BOTH + ": is the normal value \n")
.defineEnum("useFovSetting", FovTest.BOTH);
disableDirectionalCulling = builder
.comment("\n\n"
+ " If false LODs that are behind the player's camera \n"
+ " aren't drawn, increasing performance. \n\n"
+ ""
+ " If true all LODs are drawn, even those behind \n"
+ " the player's camera, decreasing performance. \n\n"
+ ""
+ " Disable this if you see LODs disapearing. \n"
+ " (This may happen if you are using a camera mod) \n")
.define("disableDirectionalCulling", false);
.comment("\n\n"
+ " If false LODs that are behind the player's camera \n"
+ " aren't drawn, increasing performance. \n\n"
+ ""
+ " If true all LODs are drawn, even those behind \n"
+ " the player's camera, decreasing performance. \n\n"
+ ""
+ " Disable this if you see LODs disapearing. \n"
+ " (This may happen if you are using a camera mod) \n")
.define("disableDirectionalCulling", false);
// shadingMode = builder
// .comment("\n\n"
// + " What kind of shading should the LODs have? \n"
@@ -196,15 +184,15 @@ public class LodConfig
// .defineEnum("lightingMode", ShadingMode.GAME_SHADING);
alwaysDrawAtMaxQuality = builder
.comment("\n\n"
+ " Disable LOD quality falloff, "
+ " all LODs will be drawn at the highest "
+ " available detail level. "
+ " "
+ " WARNING "
+ " This could cause a Out Of Memory crash on render "
+ " distances higher than 128 \n")
.define("alwaysDrawAtMaxQuality", false);
.comment("\n\n"
+ " Disable LOD quality falloff, "
+ " all LODs will be drawn at the highest "
+ " available detail level. "
+ " "
+ " WARNING "
+ " This could cause a Out Of Memory crash on render "
+ " distances higher than 128 \n")
.define("alwaysDrawAtMaxQuality", false);
builder.pop();
}
@@ -212,7 +200,7 @@ public class LodConfig
public static class WorldGenerator
{
public ForgeConfigSpec.EnumValue<VerticalQuality> lodQualityMode;
public ForgeConfigSpec.EnumValue<VerticalQuality> verticalQuality;
public ForgeConfigSpec.EnumValue<HorizontalResolution> generationResolution;
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
public ForgeConfigSpec.EnumValue<GenerationPriority> generationPriority;
@@ -224,118 +212,120 @@ public class LodConfig
{
builder.comment("These settings control how LODs outside your normal view range are generated.").push(this.getClass().getSimpleName());
lodQualityMode = builder
.comment("\n\n"
+ " Use 3d lods or 2d lods? \n"
+ " " + VerticalQuality.HEIGHTMAP + ": LODs are solid from the lowest point to the highest. Creates pillars for floating islands. Faster \n"
+ " " + VerticalQuality.VOXEL + ": LODs have gaps between vertical blocks. Good for floating islands and caves. Slower \n"
+ " " + "(Yes we know voxels are generally cubes, but voxel sounds better than rectangular_prism) \n")
.defineEnum("lodQualityMode", VerticalQuality.HEIGHTMAP);
verticalQuality = builder
.comment("\n\n"
+ " this indicate how complex the vertical data will be \n"
+ " the higher value will take more memory and lower the performance \n"
+ " " + VerticalQuality.LOW + ": uses at max 2 column per position. (performance option)\n"
+ " " + VerticalQuality.MEDIUM + ": uses at max 4 column per position. \n"
+ " " + VerticalQuality.HIGH + ": uses at max 8 column per position. \n"
+ " " + "(Yes we know voxels are generally cubes, but voxel sounds better than rectangular_prism) \n")
.defineEnum("Vertical Quality", VerticalQuality.MEDIUM);
generationResolution = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be generated at? \n"
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n")
.defineEnum("Generation Resolution", HorizontalResolution.BLOCK);
.comment("\n\n"
+ " What is the maximum detail level that LODs should be generated at? \n"
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n")
.defineEnum("Generation Resolution", HorizontalResolution.BLOCK);
horizontalScale = builder
.comment("\n\n"
+ " This indicates how quickly LODs drop off in quality. \n"
+ " " + HorizontalScale.LOW + ": quality drops every " + HorizontalScale.LOW.distanceUnit / 16 + " chunks. \n"
+ " " + HorizontalScale.MEDIUM + ": quality drops every " + HorizontalScale.MEDIUM.distanceUnit / 16 + " chunks. \n"
+ " " + HorizontalScale.HIGH + ": quality drops every " + HorizontalScale.HIGH.distanceUnit / 16 + " chunks. \n")
.defineEnum("horizontal scale", HorizontalScale.MEDIUM);
.comment("\n\n"
+ " This indicates how quickly LODs drop off in quality. \n"
+ " " + HorizontalScale.LOW + ": quality drops every " + HorizontalScale.LOW.distanceUnit / 16 + " chunks. \n"
+ " " + HorizontalScale.MEDIUM + ": quality drops every " + HorizontalScale.MEDIUM.distanceUnit / 16 + " chunks. \n"
+ " " + HorizontalScale.HIGH + ": quality drops every " + HorizontalScale.HIGH.distanceUnit / 16 + " chunks. \n")
.defineEnum("horizontal scale", HorizontalScale.MEDIUM);
horizontalQuality = builder
.comment("\n\n"
+ " This indicates the exponential base of the quadratic drop-off \n"
+ " " + HorizontalQuality.LINEAR + ": base " + HorizontalQuality.LINEAR.quadraticBase + ". \n"
+ " " + HorizontalQuality.LOW + ": base " + HorizontalQuality.LOW.quadraticBase + ". \n"
+ " " + HorizontalQuality.MEDIUM + ": base " + HorizontalQuality.MEDIUM.quadraticBase + ". \n"
+ " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \n")
.defineEnum("horizontal quality", HorizontalQuality.MEDIUM);
.comment("\n\n"
+ " This indicates the exponential base of the quadratic drop-off \n"
+ " " + HorizontalQuality.LINEAR + ": base " + HorizontalQuality.LINEAR.quadraticBase + ". \n"
+ " " + HorizontalQuality.LOW + ": base " + HorizontalQuality.LOW.quadraticBase + ". \n"
+ " " + HorizontalQuality.MEDIUM + ": base " + HorizontalQuality.MEDIUM.quadraticBase + ". \n"
+ " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \n")
.defineEnum("horizontal quality", HorizontalQuality.MEDIUM);
generationPriority = builder
.comment("\n\n"
+ " " + GenerationPriority.FAR_FIRST + " \n"
+ " LODs are generated from low to high detail\n"
+ " with a small priority for far regions. \n"
+ " This fills in the world fastest. \n"
.comment("\n\n"
+ " " + GenerationPriority.FAR_FIRST + " \n"
+ " LODs are generated from low to high detail\n"
+ " with a small priority for far regions. \n"
+ " This fills in the world fastest. \n"
+ "\n"
+ " " + GenerationPriority.NEAR_FIRST + " \n"
+ " LODs are generated around the player \n"
+ " in a spiral, similar to vanilla minecraft. \n")
.defineEnum("Generation priority", GenerationPriority.NEAR_FIRST);
+ "\n"
+ " " + GenerationPriority.NEAR_FIRST + " \n"
+ " LODs are generated around the player \n"
+ " in a spiral, similar to vanilla minecraft. \n")
.defineEnum("Generation priority", GenerationPriority.NEAR_FIRST);
distanceGenerationMode = builder
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " one of the developer's PC to generate 1 chunk, \n"
+ " and are included so you can compare the \n"
+ " different generation options. Your mileage may vary. \n"
+ "\n"
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " one of the developer's PC to generate 1 chunk, \n"
+ " and are included so you can compare the \n"
+ " different generation options. Your mileage may vary. \n"
+ "\n"
+ " " + DistanceGenerationMode.NONE + " \n"
+ " Don't run the distance generator. \n"
+ " " + DistanceGenerationMode.NONE + " \n"
+ " Don't run the distance generator. \n"
+ " " + DistanceGenerationMode.BIOME_ONLY + " \n"
+ " Only generate the biomes and use the biome's \n"
+ " grass color, water color, or snow color. \n"
+ " Doesn't generate height, everything is shown at sea level. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ " " + DistanceGenerationMode.BIOME_ONLY + " \n"
+ " Only generate the biomes and use the biome's \n"
+ " grass color, water color, or snow color. \n"
+ " Doesn't generate height, everything is shown at sea level. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT + " \n"
+ " Same as BIOME_ONLY, except instead \n"
+ " of always using sea level as the LOD height \n"
+ " different biome types (mountain, ocean, forest, etc.) \n"
+ " use predetermined heights to simulate having height data. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT + " \n"
+ " Same as BIOME_ONLY, except instead \n"
+ " of always using sea level as the LOD height \n"
+ " different biome types (mountain, ocean, forest, etc.) \n"
+ " use predetermined heights to simulate having height data. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SURFACE + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include caves, trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SURFACE + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include caves, trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES + " \n"
+ " Generate everything except structures. \n"
+ " WARNING: This may cause world generation bugs or instability! \n"
+ " Multithreaded - Fast (15-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES + " \n"
+ " Generate everything except structures. \n"
+ " WARNING: This may cause world generation bugs or instability! \n"
+ " Multithreaded - Fast (15-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SERVER + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " This will show player made structures, which can \n"
+ " be useful if you are adding the mod to a pre-existing world. \n"
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
+ "\n"
+ " " + DistanceGenerationMode.SERVER + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " This will show player made structures, which can \n"
+ " be useful if you are adding the mod to a pre-existing world. \n"
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES + " generation mode \n"
+ " some features may not be thread safe, which could \n"
+ " cause instability and crashes. \n"
+ " By default (false) those features are skipped, \n"
+ " improving stability, but decreasing how many features are \n"
+ " actually generated. \n"
+ " (for example: some tree generation is unstable, \n"
+ " so some trees may not be generated.) \n"
+ " By setting this to true, all features will be generated, \n"
+ " but your game will be more unstable and crashes may occur. \n"
+ " \n"
+ " I would love to remove this option and always generate everything, \n"
+ " but I'm not sure how to do that. \n"
+ " If you are a Java wizard, check out the git issue here: \n"
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
.define("allowUnstableFeatureGeneration", false);
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES + " generation mode \n"
+ " some features may not be thread safe, which could \n"
+ " cause instability and crashes. \n"
+ " By default (false) those features are skipped, \n"
+ " improving stability, but decreasing how many features are \n"
+ " actually generated. \n"
+ " (for example: some tree generation is unstable, \n"
+ " so some trees may not be generated.) \n"
+ " By setting this to true, all features will be generated, \n"
+ " but your game will be more unstable and crashes may occur. \n"
+ " \n"
+ " I would love to remove this option and always generate everything, \n"
+ " but I'm not sure how to do that. \n"
+ " If you are a Java wizard, check out the git issue here: \n"
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
.define("allowUnstableFeatureGeneration", false);
builder.pop();
}
@@ -351,27 +341,27 @@ public class LodConfig
builder.comment("These settings control how many CPU threads the mod uses for different tasks.").push(this.getClass().getSimpleName());
numberOfWorldGenerationThreads = builder
.comment("\n\n"
+ " This is how many threads are used when generating LODs outside \n"
+ " the normal render distance. \n"
+ " If you experience stuttering when generating distant LODs, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase this number. \n"
+ " \n"
+ " The maximum value is the number of logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors() / 2, 1, Runtime.getRuntime().availableProcessors());
.comment("\n\n"
+ " This is how many threads are used when generating LODs outside \n"
+ " the normal render distance. \n"
+ " If you experience stuttering when generating distant LODs, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase this number. \n"
+ " \n"
+ " The maximum value is the number of logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors() / 2, 1, Runtime.getRuntime().availableProcessors());
numberOfBufferBuilderThreads = builder
.comment("\n\n"
+ " This is how many threads are used when building vertex buffers \n"
+ " (The things sent to your GPU to draw the LODs). \n"
+ " If you experience high CPU useage when NOT generating distant \n"
+ " LODs, lower this number. \n"
+ " \n"
+ " The maximum value is the number of logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfBufferBuilderThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
.comment("\n\n"
+ " This is how many threads are used when building vertex buffers \n"
+ " (The things sent to your GPU to draw the LODs). \n"
+ " If you experience high CPU useage when NOT generating distant \n"
+ " LODs, lower this number. \n"
+ " \n"
+ " The maximum value is the number of logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfBufferBuilderThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
builder.pop();
}
@@ -387,17 +377,17 @@ public class LodConfig
builder.comment("These settings can be used to look for bugs, or see how certain aspects of the mod work.").push(this.getClass().getSimpleName());
debugMode = builder
.comment("\n\n"
+ " " + DebugMode.OFF + ": LODs will draw with their normal colors. \n"
+ " " + DebugMode.SHOW_DETAIL + ": LOD colors will be based on their detail level. \n"
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME + ": LOD colors will be based on their detail level, drawn as a wireframe. \n")
.defineEnum("debugMode", DebugMode.OFF);
.comment("\n\n"
+ " " + DebugMode.OFF + ": LODs will draw with their normal colors. \n"
+ " " + DebugMode.SHOW_DETAIL + ": LOD colors will be based on their detail level. \n"
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME + ": LOD colors will be based on their detail level, drawn as a wireframe. \n")
.defineEnum("debugMode", DebugMode.OFF);
enableDebugKeybindings = builder
.comment("\n\n"
+ " If true the F4 key can be used to cycle through the different debug modes. \n"
+ " and the F6 key can be used to enable and disable LOD rendering.")
.define("enableDebugKeybinding", false);
.comment("\n\n"
+ " If true the F4 key can be used to cycle through the different debug modes. \n"
+ " and the F6 key can be used to enable and disable LOD rendering.")
.define("enableDebugKeybinding", false);
builder.pop();
}
@@ -405,33 +395,17 @@ public class LodConfig
public static class Buffers
{
public ForgeConfigSpec.IntValue bufferRebuildPlayerMoveTimeout;
public ForgeConfigSpec.IntValue bufferRebuildChunkChangeTimeout;
public ForgeConfigSpec.IntValue bufferRebuildLodChangeTimeout;
public ForgeConfigSpec.EnumValue<BufferRebuildTimes> rebuildTimes;
Buffers(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings affect when Vertex Buffers are built.").push(this.getClass().getSimpleName());
bufferRebuildPlayerMoveTimeout = builder
.comment("\n\n"
+ " How long in milliseconds should we wait to \n"
+ " rebuild the vertex buffers when the player moves \n"
+ " a chunk or more? \n")
.defineInRange("bufferRebuildPlayerMoveTimeout", 2000, 1, 60000);
bufferRebuildChunkChangeTimeout = builder
.comment("\n\n"
+ " How long in milliseconds should we wait to \n"
+ " rebuild the vertex buffers when the vanilla rendered \n"
+ " chunks change? \n")
.defineInRange("bufferRebuildChunkChangeTimeout", 1000, 1, 60000);
bufferRebuildLodChangeTimeout = builder
.comment("\n\n"
+ " How long in milliseconds should we wait to \n"
+ " rebuild the vertex buffers when the LOD regions change? \n")
.defineInRange("bufferRebuildLodChangeTimeout", 5000, 1, 60000);
rebuildTimes = builder
.comment("\n\n"
+ "How frequently we rebuild the buffers?" + "\n"
+ "more frequent rebuild implies more stuttering" + "\n")
.defineEnum("rebuildFrequency", BufferRebuildTimes.NORMAL);
builder.pop();
}
@@ -451,8 +425,8 @@ public class LodConfig
CLIENT_SPEC = specPair.getRight();
CLIENT = specPair.getLeft();
CommentedFileConfig clientConfig = CommentedFileConfig.builder(CONFIG_PATH)
.writingMode(WritingMode.REPLACE)
.build();
.writingMode(WritingMode.REPLACE)
.build();
clientConfig.load();
clientConfig.save();
CLIENT_SPEC.setConfig(clientConfig);
@@ -0,0 +1,49 @@
/*
* 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.enums;
import net.minecraftforge.common.ForgeConfigSpec;
/**
* Near_First <br>
* Far_First <br>
* <br>
* Determines how fast the buffers need to be regenerated
*
* @author Leonardo Amato
* @version 9-25-2021
*/
public enum BufferRebuildTimes
{
FREQUENT(1000, 500, 2500),
NORMAL(2000, 1000, 5000),
RARE(5000, 2000, 10000);
public int playerMoveTimeout;
public int renderdChunkTimeout;
public int chunkChangeTimeout;
BufferRebuildTimes(int playerMoveTimeout, int renderdChunkTimeout, int chunkChangeTimeout)
{
this.playerMoveTimeout = playerMoveTimeout;
this.renderdChunkTimeout = renderdChunkTimeout;
this.chunkChangeTimeout = chunkChangeTimeout;
}
}
@@ -22,13 +22,57 @@ package com.seibel.lod.enums;
* multi_lod <br>
*
* @author Leonardo Amato
* @version 9-27-2021
* @version 10-07-2021
*/
public enum VerticalQuality
{
/** Lods only have height and depth data */
HEIGHTMAP,
/** Lods expand in three dimensions */
VOXEL;
LOW(
new int[]{2,
2,
2,
2,
1,
1,
1,
1,
1,
1,
1}
),
MEDIUM(
new int[]{4,
4,
2,
2,
2,
1,
1,
1,
1,
1,
1}
),
HIGH(
new int[]{
8,
8,
4,
4,
2,
2,
2,
1,
1,
1,
1}
);
public final int[] maxVerticalData;
VerticalQuality(int[] maxVerticalData)
{
this.maxVerticalData = maxVerticalData;
}
}
@@ -15,37 +15,27 @@
* 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.handlers;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.VerticalQuality;
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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.objects.SingleLevelContainer;
import com.seibel.lod.objects.VerticalLevelContainer;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
/**
* This object handles creating LodRegions
* from files and saving LodRegion objects
* to file.
*
*
* @author James Seibel
* @author Cola
* @version 9-25-2021
@@ -84,7 +74,7 @@ public class LodDimensionFileHandler
* Allow saving asynchronously, but never try to save multiple regions
* at a time
*/
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
private final ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
@@ -184,16 +174,7 @@ public class LodDimensionFileHandler
// add the data to our region
switch (region.getVerticalQuality())
{
default:
case HEIGHTMAP:
region.addLevelContainer(new SingleLevelContainer(data));
break;
case VOXEL:
region.addLevelContainer(new VerticalLevelContainer(data));
break;
}
region.addLevelContainer(new VerticalLevelContainer(data));
}
catch (IOException ioEx)
{
@@ -230,7 +211,7 @@ public class LodDimensionFileHandler
fileWritingThreadPool.execute(saveDirtyRegionsThread);
}
private Thread saveDirtyRegionsThread = new Thread(() ->
private final Thread saveDirtyRegionsThread = new Thread(() ->
{
try
{
@@ -238,11 +219,10 @@ public class LodDimensionFileHandler
{
for (int j = 0; j < lodDimension.getWidth(); j++)
{
// TODO shouldn't this use lodDimension.isRegionDirty?
if (lodDimension.doesRegionNeedBufferRegen(i,j) && lodDimension.getRegionByArrayIndex(i,j) != null)
if (lodDimension.GetIsRegionDirty(i, j) && lodDimension.getRegionByArrayIndex(i, j) != null)
{
saveRegionToFile(lodDimension.getRegionByArrayIndex(i,j));
lodDimension.setRegenRegionBufferByArrayIndex(i, j,false);
saveRegionToFile(lodDimension.getRegionByArrayIndex(i, j));
lodDimension.SetIsRegionDirty(i, j, false);
}
}
}
@@ -275,7 +255,7 @@ public class LodDimensionFileHandler
ClientProxy.LOGGER.warn("Unable to save region [" + region.regionPosX + ", " + region.regionPosZ + "] to file, file is inaccessible.");
return;
}
//ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
try
{
@@ -312,7 +292,7 @@ public class LodDimensionFileHandler
// don't write anything, we don't want to accidently
// delete anything the user may want.
return;
}
}
// if we got this far then we are good
// to overwrite the old file
@@ -15,15 +15,16 @@
* 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.handlers;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
package com.seibel.lod.handlers;
import com.seibel.lod.enums.FogQuality;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.wrappers.MinecraftWrapper;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* This object is used to get variables from methods
* where they are private. Specifically the fog setting
@@ -35,7 +36,7 @@ import com.seibel.lod.wrappers.MinecraftWrapper;
public class ReflectionHandler
{
public static final ReflectionHandler INSTANCE = new ReflectionHandler();
private MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
private final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
public Field ofFogField = null;
public Method vertexBufferUploadMethod = null;
@@ -106,9 +107,9 @@ public class ReflectionHandler
// optifine's "default" option,
// it should never be called in this case
return FogQuality.FAST;
// normal options
// normal options
case 1:
return FogQuality.FAST;
case 2:
@@ -2,9 +2,6 @@ package com.seibel.lod.objects;
public interface LevelContainer
{
public static final char VERTICAL_DATA_DELIMITER = '\t';
public static final char DATA_DELIMITER = ' ';
/**With this you can add data to the level container
*
* @param data actual data to add in a array of long format.
@@ -13,7 +10,7 @@ public interface LevelContainer
* @param index z position in the detail level
* @return true if correctly added, false otherwise
*/
public boolean addData(long data, int posX, int posZ, int index);
boolean addData(long data, int posX, int posZ, int index);
/**With this you can add data to the level container
*
@@ -22,7 +19,7 @@ public interface LevelContainer
* @param posZ z position in the detail level
* @return true if correctly added, false otherwise
*/
public boolean addSingleData(long data, int posX, int posZ);
boolean addSingleData(long data, int posX, int posZ);
/**With this you can get data from the level container
*
@@ -30,7 +27,7 @@ public interface LevelContainer
* @param posZ z position in the detail level
* @return the data in long array format
*/
public long getData(int posX, int posZ, int index);
long getData(int posX, int posZ, int index);
/**With this you can get data from the level container
*
@@ -38,31 +35,31 @@ public interface LevelContainer
* @param posZ z position in the detail level
* @return the data in long array format
*/
public long getSingleData(int posX, int posZ);
long getSingleData(int posX, int posZ);
/**
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return true only if the data exist
*/
public boolean doesItExist(int posX, int posZ);
boolean doesItExist(int posX, int posZ);
/**
* @return return the deatilLevel of this level container
*/
public byte getDetailLevel();
byte getDetailLevel();
public int getMaxVerticalData();
int getMaxVerticalData();
/** Clears the dataPoint at the given array index */
public void clear(int posX, int posZ);
void clear(int posX, int posZ);
/**This return a level container with detail level lower than the current level.
* The new level container may use information of this level.
* @return the new level container
*/
public LevelContainer expand();
LevelContainer expand();
/**
*
@@ -70,24 +67,24 @@ public interface LevelContainer
* @param posX x position in the detail level to update
* @param posZ z position in the detail level to update
*/
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ);
void updateData(LevelContainer lowerLevelContainer, int posX, int posZ);
/**
* This will give the data to save in the file
* @return data as a String
*/
public byte[] toDataString();
byte[] toDataString();
/**
* This will give the data to save in the file
* @return data as a String
*/
public int getMaxNumberOfLods();
int getMaxNumberOfLods();
/**
* This will give the data to save in the file
* @return data as a String
*/
public int getMaxMemoryUse();
int getMaxMemoryUse();
}
@@ -15,12 +15,8 @@
* 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.concurrent.ExecutorService;
import java.util.concurrent.Executors;
package com.seibel.lod.objects;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
@@ -28,27 +24,27 @@ import com.seibel.lod.enums.GenerationPriority;
import com.seibel.lod.enums.LodTemplate;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.*;
import com.seibel.lod.wrappers.MinecraftWrapper;
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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* This object holds all loaded LOD regions
* for a given dimension. <Br><Br>
*
*
* <strong>Coordinate Standard: </strong><br>
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
* unless stated otherwise. <br>
*
*
* @author Leonardo Amato
* @author James Seibel
* @version 9-27-2021
@@ -82,13 +78,13 @@ public class LodDimension
private LodDimensionFileHandler fileHandler;
private volatile RegionPos center;
private final RegionPos center;
/** prevents the cutAndExpandThread from expanding at the same location multiple times */
private volatile ChunkPos lastExpandedChunk;
/** prevents the cutAndExpandThread from cutting at the same location multiple times */
private volatile ChunkPos lastCutChunk;
private ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand"));
private final ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand"));
/**
* Creates the dimension centered at (0,0)
@@ -391,7 +387,7 @@ public class LodDimension
{
DistanceGenerationMode generationMode = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
VerticalQuality verticalQuality = LodConfig.CLIENT.worldGenerator.lodQualityMode.get();
VerticalQuality verticalQuality = LodConfig.CLIENT.worldGenerator.verticalQuality.get();
if (lastExpandedChunk == null)
@@ -510,7 +506,7 @@ public class LodDimension
/**
* Returns every position that need to be generated based on the position of the player
*/
public PosToGenerateContainer getDataToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ)
public PosToGenerateContainer getPosToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ)
{
PosToGenerateContainer posToGenerate;
LodRegion region;
@@ -584,8 +580,8 @@ public class LodDimension
z += dz;
}
break;
case FAR_FIRST:
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
@@ -599,7 +595,7 @@ public class LodDimension
region = getRegion(xRegion, zRegion);
if (region != null)
region.getDataToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ);
region.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ);
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z)))
@@ -618,15 +614,15 @@ public class LodDimension
/**
* Returns every node that should be rendered based on the position of the player.
*
*
* TODO why isn't posToRender returned? it would make it a bit more clear what is happening
*/
public void getDataToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX,
public void getPosToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX,
int playerPosZ)
{
LodRegion region = getRegion(regionPos.x, regionPos.z);
if (region != null)
region.getDataToRender(posToRender, playerPosX, playerPosZ, LodConfig.CLIENT.worldGenerator.generationPriority.get() == GenerationPriority.NEAR_FIRST);
region.getPosToRender(posToRender, playerPosX, playerPosZ, LodConfig.CLIENT.worldGenerator.generationPriority.get() == GenerationPriority.NEAR_FIRST);
}
/**
@@ -709,7 +705,7 @@ public class LodDimension
/**
* TODO we aren't currently using this, is there a reason for that?
* is this significantly different than regenRegionBuffer?
*
*
* Returns if the buffer at the given array index needs
* to have its buffer resized.
*/
@@ -762,7 +758,7 @@ public class LodDimension
* Loads the region at the given RegionPos from file,
* if a file exists for that region.
*/
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel,
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel,
DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
{
if (fileHandler != null)
@@ -890,8 +886,18 @@ public class LodDimension
int levelToGen = DetailDistanceUtil.getLodDrawDetail(detail);
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelToGen);
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detail);
long memoryUse = LodUtil.regionRenderingMemoryUse(x,z,template);
long memoryUse = LodUtil.regionRenderingMemoryUse(x, z, template);
//System.out.println(detail + " " + memoryUse + " " + template.getBufferMemoryForSingleLod(maxVerticalData));
return memoryUse;
}
public boolean GetIsRegionDirty(int i, int j)
{
return isRegionDirty[i][j];
}
public void SetIsRegionDirty(int i, int j, boolean val)
{
isRegionDirty[i][j] = val;
}
}
@@ -79,16 +79,7 @@ public class LodRegion
// Initialize all the different matrices
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
switch (verticalQuality)
{
default:
case HEIGHTMAP:
dataContainer[lod] = new SingleLevelContainer(lod);
break;
case VOXEL:
dataContainer[lod] = new VerticalLevelContainer(lod);
break;
}
dataContainer[lod] = new VerticalLevelContainer(lod);
}
}
@@ -109,10 +100,7 @@ public class LodRegion
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
{
if (verticalQuality == VerticalQuality.HEIGHTMAP)
this.dataContainer[detailLevel] = new SingleLevelContainer(detailLevel);
else
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
}
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
@@ -156,10 +144,10 @@ public class LodRegion
* <p>
* TODO why don't we return the posToGenerate, it would make this easier to understand
*/
public void getDataToGenerate(PosToGenerateContainer posToGenerate,
int playerBlockPosX, int playerBlockPosZ)
public void getPosToGenerate(PosToGenerateContainer posToGenerate,
int playerBlockPosX, int playerBlockPosZ)
{
getDataToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ);
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ);
}
@@ -169,8 +157,8 @@ public class LodRegion
* <p>
* TODO why don't we return the posToGenerate, it would make this easier to understand
*/
private void getDataToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel,
int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ)
private void getPosToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel,
int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ)
{
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
@@ -223,7 +211,7 @@ public class LodRegion
if (ungeneratedChildren == 0)
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getDataToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
} else
{
// The detail Level is smaller than a chunk.
@@ -234,7 +222,7 @@ public class LodRegion
if (!doesDataExist(childDetailLevel, childPosX, childPosZ))
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + regionPosX * childSize, childPosZ + regionPosZ * childSize);
else
getDataToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
}
}
}
@@ -247,10 +235,10 @@ public class LodRegion
* <p>
* TODO why don't we return the posToRender, it would make this easier to understand
*/
public void getDataToRender(PosToRenderContainer posToRender,
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
public void getPosToRender(PosToRenderContainer posToRender,
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
{
getDataToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel);
getPosToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel);
}
/**
@@ -260,9 +248,9 @@ public class LodRegion
* TODO why don't we return the posToRender, it would make this easier to understand
* TODO this needs some more comments, James was only able to figure out part of it
*/
private void getDataToRender(PosToRenderContainer posToRender,
byte detailLevel, int posX, int posZ,
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
private void getPosToRender(PosToRenderContainer posToRender,
byte detailLevel, int posX, int posZ,
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
{
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
@@ -302,9 +290,9 @@ public class LodRegion
if (stopNow)
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
} else if (desiredLevel > detailLevel)
{
// we have gone beyond the target Detail level
@@ -312,9 +300,9 @@ public class LodRegion
return;
} else if (desiredLevel == detailLevel)
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
} else //case where (detailLevel > desiredLevel)
{
int childPosX = posX * 2;
@@ -331,7 +319,7 @@ public class LodRegion
if (!requireCorrectDetailLevel)
childrenCount++;
else
getDataToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
}
}
}
@@ -344,12 +332,12 @@ public class LodRegion
{
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getDataToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
} else
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
}
}
}
@@ -152,49 +152,43 @@ public class PosToGenerateContainer
// TOOD is this commented code still useful?
// if so why have it commented out?
/*
StringBuilder builder = new StringBuilder();
builder.append("Number of pos to generate ");
builder.append(farSize + nearSize);
builder.append('\n');
builder.append("Number of near pos to generate ");
builder.append(nearSize);
builder.append('\n');
builder.append("Number of far pos to generate ");
builder.append(farSize);
builder.append('\n');
builder.append('\n');
builder.append("near pos to generate");
builder.append('\n');
for (int i = 0; i < nearSize; i++)
for (int i = 0; i < nearPosToGenerate.length; i++)
{
builder.append(posToGenerate[i][0]-1);
if(nearPosToGenerate[i][0] == 0)
break;
builder.append(nearPosToGenerate[i][0]-1);
builder.append(" ");
builder.append(posToGenerate[i][1]);
builder.append(nearPosToGenerate[i][1]);
builder.append(" ");
builder.append(posToGenerate[i][2]);
builder.append(nearPosToGenerate[i][2]);
builder.append(" ");
builder.append(posToGenerate[i][3]);
builder.append(nearPosToGenerate[i][3]);
builder.append('\n');
}
builder.append('\n');
builder.append("far pos to generate");
builder.append('\n');
for (int i = maxSize - 1; i >= maxSize - farSize; i--)
for (int i = 0; i < farPosToGenerate.length; i++)
{
builder.append(posToGenerate[i][0]-1);
if(farPosToGenerate[i][0] == 0)
break;
builder.append(farPosToGenerate[i][0]-1);
builder.append(" ");
builder.append(posToGenerate[i][1]);
builder.append(farPosToGenerate[i][1]);
builder.append(" ");
builder.append(posToGenerate[i][2]);
builder.append(farPosToGenerate[i][2]);
builder.append(" ");
builder.append(posToGenerate[i][3]);
builder.append(farPosToGenerate[i][3]);
builder.append('\n');
}
builder.append('\n');
return builder.toString();
*/
return " ";
}
}
@@ -231,7 +231,8 @@ public class ClientProxy
public void worldLoadEvent(WorldEvent.Load event)
{
DataPointUtil.worldHeight = event.getWorld().getHeight();
ThreadMapUtil.clearMaps();
//LodNodeGenWorker.restartExecuterService();
//ThreadMapUtil.clearMaps();
// the player just loaded a new world/dimension
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
@@ -254,8 +255,8 @@ public class ClientProxy
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
LodNodeGenWorker.restartExecuterService();
ThreadMapUtil.clearMaps();
//LodNodeGenWorker.restartExecuterService();
//ThreadMapUtil.clearMaps();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
lodWorld.deselectWorld();
@@ -332,7 +333,12 @@ public class ClientProxy
private void viewDistanceChangedEvent()
{
// calculate how wide the dimension(s) should be in regions
int chunksWide = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 2 + 1;
int chunksWide;
if(mc.getClientWorld().dimensionType().hasCeiling())
chunksWide = Math.max(LodConfig.CLIENT.graphics.lodChunkRenderDistance.get(),64) * 2 + 1;
else
chunksWide = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 2 + 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
@@ -366,6 +372,16 @@ public class ClientProxy
firstTimeSetupComplete = true;
}
/**
* this method reset some of the static data everytime we change world
**/
private void resetMod()
{
ThreadMapUtil.clearMaps();
LodNodeGenWorker.restartExecuterService();
}
//================//
// public getters //
@@ -291,7 +291,7 @@ public class LodRenderer
setupFog(fogSettings.far.distance, fogSettings.far.quality);
for (int i = 0; i < lodBufferBuilder.bufferCount[x][z]; i++)
for (int i = 0; i < lodBufferBuilder.bufferSize[x][z]; i++)
{
sendLodsToGpuAndDraw(vbos[x][z][i], storageBufferIds[x][z], modelViewMatrix);
}
@@ -804,7 +804,7 @@ public class LodRenderer
if (LodConfig.CLIENT.graphics.detailDropOff.get() == DetailDropOff.FANCY)
{
// check if the player has moved
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get())
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.rebuildTimes.get().playerMoveTimeout)
{
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
@@ -825,7 +825,7 @@ public class LodRenderer
// check if the vanilla rendered chunks changed
if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.get())
if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.buffers.rebuildTimes.get().renderdChunkTimeout)
{
if (vanillaRenderedChunksChanged)
{
@@ -838,7 +838,7 @@ public class LodRenderer
// check if there is any newly generated terrain to show
if (newTime - prevChunkTime > LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.get())
if (newTime - prevChunkTime > LodConfig.CLIENT.buffers.rebuildTimes.get().chunkChangeTimeout)
{
if (lodDim.regenDimensionBuffers)
{
@@ -4,6 +4,8 @@ import com.seibel.lod.enums.DistanceGenerationMode;
import net.minecraft.client.renderer.texture.NativeImage;
import javax.xml.crypto.Data;
public class DataPointUtil
{
/*
@@ -174,7 +176,13 @@ public class DataPointUtil
{
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111) << 24));
}
/**
* This method apply the lightmap to the color to use
* @param dataPoint
* @param lightMap
* @return
*/
public static int getLightColor(long dataPoint, NativeImage lightMap)
{
int lightBlock = getLightBlock(dataPoint);
@@ -186,7 +194,12 @@ public class DataPointUtil
return ColorUtil.multiplyRGBcolors(getColor(dataPoint), ColorUtil.rgbToInt(red, green, blue));
}
/**
* This is used to convert a dataPoint to string (usefull for the print function)
* @param dataPoint
* @return
*/
public static String toString(long dataPoint)
{
StringBuilder s = new StringBuilder();
@@ -214,7 +227,12 @@ public class DataPointUtil
s.append('\n');
return s.toString();
}
/**
* This method merge column of single data together
* @param dataToMerge
* @return
*/
public static long mergeSingleData(long[] dataToMerge)
{
int numberOfChildren = 0;
@@ -276,12 +294,43 @@ public class DataPointUtil
}
}
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = 0; i < arraySize - start; i++)
{
array[start + i] = array[start + length + i];
//remove comment to not leave garbage at the end
//array[start + packetSize + i] = 0;
}
}
public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = arraySize - start - 1; i >= 0; i--)
{
array[start + length + i] = array[start + i];
array[start + i] = 0;
}
}
/**
* This method merge column of multiple data together
* @param dataToMerge
* @param inputVerticalData vertical size of an input data
* @param maxVerticalData max vertical size of the merged data
* @return
*/
public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData)
{
int size = dataToMerge.length / inputVerticalData;
// We initialize the arrays that are going to be used
short[] projection = ThreadMapUtil.getProjectionArray((worldHeight) / 16 + 1);
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight + 1) * 2);
long[] singleDataToMerge = ThreadMapUtil.getSingleAddDataToMerge(size);
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(worldHeight + 1);
@@ -295,28 +344,119 @@ public class DataPointUtil
short depth;
short height;
int count = 0;
int i;
int ii;
//We collect the indexes of the data, ordered by the depth
for (int index = 0; index < size; index++)
for (int index = 0; index < dataToMerge.length; index++)
{
for (int dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
singleData = dataToMerge[index];
if (doesItExist(singleData))
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData))
genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!isVoid(singleData))
{
genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!isVoid(singleData))
allVoid = false;
depth = getDepth(singleData);
height = getHeight(singleData);
int botPos = -1;
int topPos = -1;
//values fall in between and possibly require extension of array
boolean botExtend = false;
boolean topExtend = false;
for (i = 0; i < count; i++)
{
allVoid = false;
depth = getDepth(singleData);
height = getHeight(singleData);
for (int y = depth; y <= height; y++)
projection[y / 16] |= 1 << (y & 0xf);
if (depth >= heightAndDepth[i * 2] && depth <= heightAndDepth[i * 2 + 1])
{
botPos = i;
break;
}
else if (((i + 1 < count && depth < heightAndDepth[(i + 1) * 2]) || i + 1 == count) && depth > heightAndDepth[i * 2 + 1])
{
botPos = i;
botExtend = true;
break;
}
}
for (i = 0; i < count; i++)
{
if (height >= heightAndDepth[i * 2] && height <= heightAndDepth[i * 2 + 1])
{
topPos = i;
break;
}
else if (((i + 1 < count && height < heightAndDepth[(i + 1) * 2]) || i + 1 == count) && height > heightAndDepth[i * 2 + 1])
{
topPos = i;
topExtend = true;
break;
}
}
if (botPos == -1)
{
if (topPos == -1)
{
//whole block falls below
extendArray(heightAndDepth, 2, 0, 1, count);
heightAndDepth[0] = depth;
heightAndDepth[1] = height;
count++;
}
else if (!topExtend)
{
//only bottom falls below extending it there, while top is inside existing
shrinkArray(heightAndDepth, 2, 0, topPos, count);
heightAndDepth[0] = depth;
count -= topPos;
}
else
{
//top falls between some blocks, extending those as well
shrinkArray(heightAndDepth, 2, 0, topPos, count);
heightAndDepth[0] = depth;
heightAndDepth[1] = height;
count -= topPos;
}
}
else if (!botExtend)
{
if (!topExtend)
//both top and bottom are within some exiting blocks, possibly merging them
heightAndDepth[botPos * 2 + 1] = heightAndDepth[topPos * 2 + 1];
else
//top falls between some blocks, extending it there
heightAndDepth[botPos * 2 + 1] = height;
shrinkArray(heightAndDepth, 2, botPos + 1, topPos - botPos, count);
count -= topPos - botPos;
}
else
{
if (!topExtend)
{
//only top is within some exiting block, extending it
botPos++; //to make it easier
heightAndDepth[botPos * 2] = depth;
heightAndDepth[botPos * 2 + 1] = heightAndDepth[topPos * 2 + 1];
shrinkArray(heightAndDepth, 2, botPos + 1, topPos - botPos, count);
count -= topPos - botPos;
}
else
{
//both top and bottom are outside existing blocks
shrinkArray(heightAndDepth, 2, botPos + 1, topPos - botPos, count);
count -= topPos - botPos;
extendArray(heightAndDepth, 2, botPos + 1, 1, count);
count++;
heightAndDepth[botPos * 2 + 2] = depth;
heightAndDepth[botPos * 2 + 3] = height;
}
}
}
}
}
//We check if there is any data that's not empty or void
if (allEmpty)
{
@@ -327,44 +467,6 @@ public class DataPointUtil
dataPoint[0] = createVoidDataPoint(genMode);
return dataPoint;
}
//We extract the merged data
int count = 0;
int i = 0;
int ii = 0;
while (i < projection.length)
{
while (i < projection.length && projection[i] == 0) i++;
if (i == projection.length)
break; //we reached end of WORLD_HEIGHT and it's nothing more here
while (ii < 15 && ((projection[i] >>> ii) & 1) == 0) ii++;
if (ii >= 15 && ((projection[i] >>> ii) & 1) == 0) //there is nothing more in this chunk
{
ii = 0;
i++;
continue;
}
depth = (short) (i * 16 + ii);
while (ii < 15 && ((projection[i] >>> ii) & 1) == 1) ii++;
if (ii >= 15 && ((projection[i] >>> ii) & 1) == 1) //if end is not in this chunk
{
ii = 0;
i++;
while (i < projection.length && ~(projection[i]) == 0) i++; //check for big solid blocks
if (i == projection.length) //solid to WORLD_HEIGHT
{
heightAndDepth[count * 2] = depth;
heightAndDepth[count * 2 + 1] = (short) (worldHeight - 1);
break;
}
while ((((projection[i] >>> ii) & 1) == 1)) ii++;
}
height = (short) (i * 16 + ii - 1);
heightAndDepth[count * 2] = depth;
heightAndDepth[count * 2 + 1] = height;
count++;
}
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > maxVerticalData)
@@ -405,6 +507,7 @@ public class DataPointUtil
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData) && !isVoid(singleData))
{
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
{
@@ -415,6 +518,10 @@ public class DataPointUtil
}
}
}
if(!doesItExist(singleDataToMerge[index])){
singleData = dataToMerge[index * inputVerticalData];
singleDataToMerge[index] = createVoidDataPoint(getGenerationMode(singleData));
}
}
long data = mergeSingleData(singleDataToMerge);
@@ -422,9 +529,4 @@ public class DataPointUtil
}
return dataPoint;
}
public static long[] compress(long[] data, byte detailLevel)
{
return null;
}
}
@@ -3,7 +3,6 @@ package com.seibel.lod.util;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.HorizontalResolution;
import com.seibel.lod.enums.VerticalQuality;
public class DetailDistanceUtil
{
@@ -167,9 +166,7 @@ public class DetailDistanceUtil
public static int getMaxVerticalData(int detail)
{
if(LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == VerticalQuality.HEIGHTMAP)
return 1;
return maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)];
return LodConfig.CLIENT.worldGenerator.verticalQuality.get().maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)];
}
}
@@ -13,7 +13,7 @@ import java.util.concurrent.ConcurrentMap;
* Holds data used by specific threads so
* the data doesn't have to be recreated every
* time it is needed.
*
*
* @author Leonardo Amato
* @version 9-25-2021
*/
@@ -28,72 +28,74 @@ public class ThreadMapUtil
public static final ConcurrentMap<String, short[]> heightAndDepthMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[]> singleDataToMergeMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[][]> verticalUpdate = new ConcurrentHashMap<>();
//________________________//
// used in BufferBuilder //
//________________________//
public static final ConcurrentMap<String, boolean[]> adjShadeDisabled = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, Map<Direction ,long[]>> adjDataMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, Map<Direction, long[]>> adjDataMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, Box> boxMap = new ConcurrentHashMap<>();
/** returns the array NOT cleared every time
* @return*/
public static boolean[] getAdjShadeDisabledArray()
{
if (!adjShadeDisabled.containsKey(Thread.currentThread().getName())
|| (adjShadeDisabled.get(Thread.currentThread().getName()) == null))
|| (adjShadeDisabled.get(Thread.currentThread().getName()) == null))
{
adjShadeDisabled.put(Thread.currentThread().getName(), new boolean[Box.DIRECTIONS.length]);
}
Arrays.fill(adjShadeDisabled.get(Thread.currentThread().getName()), false);
return adjShadeDisabled.get(Thread.currentThread().getName());
}
/** returns the array NOT cleared every time */
public static Map<Direction ,long[]> getAdjDataArray(int verticalData)
public static Map<Direction, long[]> getAdjDataArray(int verticalData)
{
if (!adjDataMap.containsKey(Thread.currentThread().getName())
|| (adjDataMap.get(Thread.currentThread().getName()) == null)
|| (adjDataMap.get(Thread.currentThread().getName()).get(Direction.UP) == null)
|| (adjDataMap.get(Thread.currentThread().getName()).get(Direction.UP).length != verticalData))
|| (adjDataMap.get(Thread.currentThread().getName()) == null)
|| (adjDataMap.get(Thread.currentThread().getName()).get(Direction.UP) == null)
|| (adjDataMap.get(Thread.currentThread().getName()).get(Direction.UP).length != verticalData))
{
adjDataMap.put(Thread.currentThread().getName(), new HashMap());
for (Direction direction : Box.ADJ_DIRECTIONS)
adjDataMap.get(Thread.currentThread().getName()).put(direction, new long[verticalData]);
}else{
}
else
{
for (Direction direction : Box.ADJ_DIRECTIONS)
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(direction),DataPointUtil.EMPTY_DATA);
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(direction), DataPointUtil.EMPTY_DATA);
}
return adjDataMap.get(Thread.currentThread().getName());
}
public static Box getBox()
{
if (!boxMap.containsKey(Thread.currentThread().getName())
|| (boxMap.get(Thread.currentThread().getName()) == null))
|| (boxMap.get(Thread.currentThread().getName()) == null))
{
boxMap.put(Thread.currentThread().getName(), new Box());
}
boxMap.get(Thread.currentThread().getName()).reset();
return boxMap.get(Thread.currentThread().getName());
}
//________________________//
// used in DataPointUtil //
// mergeVerticalData //
//________________________//
//________________________//
// used in DataPointUtil //
// mergeSingleData //
//________________________//
/** returns the array NOT cleared every time */
public static long[] getSingleUpdateArray()
{