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

This commit is contained in:
James Seibel
2021-09-07 18:54:41 -05:00
23 changed files with 1095 additions and 1710 deletions
@@ -1,28 +0,0 @@
package com.seibel.lod.builders;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.ChunkPos;
/**
* @author Leonardo Amato
* @version 22-08-2021
*/
public class GenerationRequest
{
public final LevelPos levelPos;
public final DistanceGenerationMode generationMode;
public GenerationRequest(LevelPos levelPos, DistanceGenerationMode generationMode)
{
this.levelPos = levelPos;
this.generationMode = generationMode;
}
public ChunkPos getChunkPos()
{
LevelPos chunkLevelPos = levelPos.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL);
return new ChunkPos(chunkLevelPos.posX, chunkLevelPos.posZ);
}
}
@@ -20,20 +20,16 @@ package com.seibel.lod.builders;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang3.mutable.MutableBoolean;
import com.seibel.lod.builders.lodTemplates.Box;
import com.seibel.lod.objects.*;
import org.lwjgl.opengl.GL11;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.LodThreadFactory;
@@ -49,7 +45,7 @@ import net.minecraft.util.math.ChunkPos;
* This object is used to create NearFarBuffer objects.
*
* @author James Seibel
* @version 9-6-2021
* @version 8-24-2021
*/
public class LodBufferBuilder
{
@@ -105,15 +101,21 @@ public class LodBufferBuilder
*/
private ReentrantLock bufferLock = new ReentrantLock();
private volatile Object[][] setsToRender;
private static final int NUMBER_OF_DIRECTION = 4;
//in order -x, +x, -z, +z
private static final int[][] ADJ_DIRECTION = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
private volatile Box[][] boxCache;
private volatile PosToRenderContainer[][] setsToRender;
private volatile RegionPos center;
/** This is the ChunkPos the player was at the last time the buffers were built.
* IE the center of the buffers last time they were built */
private volatile ChunkPos drawableCenterChunkPos = new ChunkPos(0,0);
private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0,0);
public LodBufferBuilder()
{
@@ -165,27 +167,29 @@ public class LodBufferBuilder
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
startBuffers(fullRegen, lodDim);
// =====================//
// RENDERING PART //
// =====================//
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
if (center == null)
center = playerRegionPos;
if (setsToRender == null)
setsToRender = new Object[lodDim.regions.length][lodDim.regions.length];
setsToRender = new PosToRenderContainer[lodDim.regions.length][lodDim.regions.length];
if (setsToRender.length != lodDim.regions.length)
setsToRender = new Object[lodDim.regions.length][lodDim.regions.length];
setsToRender = new PosToRenderContainer[lodDim.regions.length][lodDim.regions.length];
if (boxCache == null)
boxCache = new Box[lodDim.regions.length][lodDim.regions.length];
if (boxCache.length != lodDim.regions.length)
boxCache = new Box[lodDim.regions.length][lodDim.regions.length];
// this will be the center of the VBOs once they have been built
buildableCenterChunkPos = playerChunkPos;
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
move(worldRegionOffset, Math.floorDiv(lodDim.getWidth(), 2));
}
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
{
@@ -199,117 +203,104 @@ public class LodBufferBuilder
// local position in the vbo and bufferBuilder arrays
BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion];
LodRegion region = lodDim.getRegion(regionPos.x, regionPos.z);
if (region == null) continue;
byte minDetail = region.getMinDetailLevel();
// make sure the buffers weren't
// changed while we were running this method
if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building()))
return;
//previous setToRender chache
if (setsToRender[xRegion][zRegion] == null)
{
setsToRender[xRegion][zRegion] = new ConcurrentHashMap<LevelPos, MutableBoolean>();
}
ConcurrentMap<LevelPos, MutableBoolean> nodeToRender = (ConcurrentMap<LevelPos, MutableBoolean>) setsToRender[xRegion][zRegion];
final int xR = xRegion;
final int zR = zRegion;
Callable<Boolean> dataToRenderThread = () ->
{
//previous setToRender chache
if (setsToRender[xR][zR] == null)
{
setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z);
}
if (boxCache[xR][zR] == null)
{
boxCache[xR][zR] = new Box();
}
PosToRenderContainer posToRender = (PosToRenderContainer) setsToRender[xR][zR];
posToRender.clear(minDetail, regionPos.x, regionPos.z);
lodDim.getDataToRender(
nodeToRender,
posToRender,
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
byte detailLevel;
int posX;
int posZ;
byte detailLevel;
int xAdj;
int zAdj;
int chunkXdist;
int chunkZdist;
short gameChunkRenderDistance = (short) (renderer.vanillaRenderedChunks.length/2 - 1);
for (LevelPos posToRender : nodeToRender.keySet())
short gameChunkRenderDistance = (short) (renderer.vanillaRenderedChunks.length / 2 - 1);
long dataPoint;
long[] adjData;
for (int index = 0; index < posToRender.getNumberOfPos(); index++)
{
if (!nodeToRender.get(posToRender).booleanValue())
detailLevel = posToRender.getNthDetailLevel(index);
posX = posToRender.getNthPosX(index);
posZ = posToRender.getNthPosZ(index);
// skip any chunks that Minecraft is going to render
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z;
if (gameChunkRenderDistance >= Math.abs(chunkXdist)
&& gameChunkRenderDistance >= Math.abs(chunkZdist)
&& detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL
&& renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])
{
nodeToRender.remove(posToRender);
continue;
}
nodeToRender.get(posToRender).setFalse();
// skip any chunks that Minecraft is going to render
chunkXdist = posToRender.getChunkPosX() - playerChunkPos.x;
chunkZdist = posToRender.getChunkPosZ() - playerChunkPos.z;
if(gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist) && posToRender.detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL)
{
if (renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])
{
continue;
}
}
posX = posToRender.posX;
posZ = posToRender.posZ;
detailLevel = posToRender.detailLevel;
// skip any chunks that Minecraft is going to render
try
{
boolean disableFix = false;
if (lodDim.doesDataExist(posToRender.clone()))
if (lodDim.doesDataExist(detailLevel, posX, posZ))
{
short[] lodData = lodDim.getData(posToRender);
short[][][] adjData = new short[2][2][];
/**TODO The following two for are too complex*/
for (int x : new int[]{0, 1})
dataPoint = lodDim.getData(detailLevel, posX, posZ);
if(DataPoint.getHeight(dataPoint) == LodBuilder.DEFAULT_HEIGHT && DataPoint.getDepth(dataPoint) == LodBuilder.DEFAULT_DEPTH)
continue;
adjData = new long[NUMBER_OF_DIRECTION];
for (int direction = 0; direction < NUMBER_OF_DIRECTION; direction++)
{
posToRender.changeParameters(detailLevel, posX + x * 2 - 1, posZ);
chunkXdist = posToRender.getChunkPosX() - playerChunkPos.x;
chunkZdist = posToRender.getChunkPosZ() - playerChunkPos.z;
if(gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist))
xAdj = posX + ADJ_DIRECTION[direction][0];
zAdj = posZ + ADJ_DIRECTION[direction][1];
chunkXdist = LevelPosUtil.getChunkPos(detailLevel,xAdj) - playerChunkPos.x;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel,zAdj) - playerChunkPos.z;
if (gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist))
{
if (!renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]
&& (nodeToRender.containsKey(posToRender) || disableFix))
&& posToRender.contains(detailLevel, xAdj, zAdj))
{
adjData[0][x] = lodDim.getData(posToRender);
adjData[direction]= lodDim.getData(detailLevel, xAdj, zAdj);
}
}else{
if (nodeToRender.containsKey(posToRender) || disableFix)
{
adjData[0][x] = lodDim.getData(posToRender);
}
}
}
for (int z : new int[]{0, 1})
{
posToRender.changeParameters(detailLevel, posX, posZ + z * 2 - 1);
chunkXdist = posToRender.getChunkPosX() - playerChunkPos.x;
chunkZdist = posToRender.getChunkPosZ() - playerChunkPos.z;
if(gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist))
} else
{
if (!renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance+ 1]
&& (nodeToRender.containsKey(posToRender) || disableFix))
if (posToRender.contains(detailLevel, xAdj, zAdj))
{
adjData[1][z] = lodDim.getData(posToRender);
}
}else{
if (nodeToRender.containsKey(posToRender) || disableFix)
{
adjData[1][z] = lodDim.getData(posToRender);
adjData[direction] = lodDim.getData(detailLevel, xAdj, zAdj);
}
}
}
posToRender.changeParameters(detailLevel, posX, posZ);
LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPosRounded, lodData, adjData,
posToRender, renderer.previousDebugMode);
LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPosRounded, dataPoint, adjData,
detailLevel, posX, posZ, boxCache[xR][zR],renderer.previousDebugMode);
}
} catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
return false;
}
}// for pos to in list to render
// the thread executed successfully
return true;
};
@@ -344,7 +335,7 @@ public class LodBufferBuilder
// ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' +
// "Tree cutting time: " + treeTime + " ms" + '\n' +
// "Rendering time: " + renderingTime + " ms");
// mark that the buildable buffers as ready to swap
switchVbos = true;
} catch (Exception e)
@@ -367,110 +358,12 @@ public class LodBufferBuilder
}
});
mainGenThread.execute(thread);
return;
}
/**
* Move the center of this LodDimension and move all owned
* regions over by the given x and z offset. <br><br>
* <p>
* Synchronized to prevent multiple moves happening on top of each other.
*/
public void move(RegionPos regionOffset, int width)
{
int xOffset = regionOffset.x;
int zOffset = regionOffset.z;
// if the x or z offset is equal to or greater than
// the total size, just delete the current data
// and update the centerX and/or centerZ
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
{
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
setsToRender[x][z] = null;
}
}
// update the new center
center.x += xOffset;
center.z += zOffset;
return;
}
// X
if (xOffset > 0)
{
// move everything over to the left (as the center moves to the right)
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
if (x + xOffset < width)
setsToRender[x][z] = setsToRender[x + xOffset][z];
else
setsToRender[x][z] = null;
}
}
} else
{
// move everything over to the right (as the center moves to the left)
for (int x = width - 1; x >= 0; x--)
{
for (int z = 0; z < width; z++)
{
if (x + xOffset >= 0)
setsToRender[x][z] = setsToRender[x + xOffset][z];
else
setsToRender[x][z] = null;
}
}
}
// Z
if (zOffset > 0)
{
// move everything up (as the center moves down)
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
if (z + zOffset < width)
setsToRender[x][z] = setsToRender[x][z + zOffset];
else
setsToRender[x][z] = null;
}
}
} else
{
// move everything down (as the center moves up)
for (int x = 0; x < width; x++)
{
for (int z = width - 1; z >= 0; z--)
{
if (z + zOffset >= 0)
setsToRender[x][z] = setsToRender[x][z + zOffset];
else
setsToRender[x][z] = null;
}
}
}
// update the new center
center.x += xOffset;
center.z += zOffset;
}
//===============================//
// BufferBuilder related methods //
//===============================//
@@ -574,6 +467,7 @@ public class LodBufferBuilder
}
/**
* Get the newly created VBOs
*/
@@ -586,9 +480,9 @@ public class LodBufferBuilder
VertexBuffer[][] tmp = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmp;
drawableCenterChunkPos = buildableCenterChunkPos;
// the vbos have been swapped
switchVbos = false;
bufferLock.unlock();
@@ -603,7 +497,7 @@ public class LodBufferBuilder
{
public VertexBuffer[][] vbos;
public ChunkPos drawableCenterChunkPos;
public VertexBuffersAndOffset(VertexBuffer[][] newVbos, ChunkPos newDrawableCenterChunkPos)
{
vbos = newVbos;
@@ -20,19 +20,18 @@ package com.seibel.lod.builders;
import java.awt.Color;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.objects.*;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrapper.MinecraftWrapper;
import com.seibel.lod.wrapper.MinecraftWrapper;
import com.sun.org.apache.xpath.internal.operations.Bool;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
@@ -41,6 +40,7 @@ import net.minecraft.block.GrassBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.client.Minecraft;
import net.minecraft.world.DimensionType;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.Biome;
@@ -67,11 +67,11 @@ public class LodBuilder
/**
* If no blocks are found in the area in determineBottomPointForArea return this
*/
public static final short DEFAULT_DEPTH = -1;
public static final short DEFAULT_DEPTH = 0;
/**
* If no blocks are found in the area in determineHeightPointForArea return this
*/
public static final short DEFAULT_HEIGHT = -1;
public static final short DEFAULT_HEIGHT = 0;
/**
* How wide LodDimensions should be in regions
@@ -131,7 +131,6 @@ public class LodBuilder
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode));
} catch (IllegalArgumentException | NullPointerException e)
{
System.out.println("Chunk pos " + chunk.getPos());
e.printStackTrace();
// if the world changes while LODs are being generated
// they will throw errors as they try to access things that no longer
@@ -161,7 +160,6 @@ public class LodBuilder
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config)
throws IllegalArgumentException
{
if (chunk == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
@@ -169,17 +167,22 @@ public class LodBuilder
int startZ;
int endX;
int endZ;
short[] color;
int color;
short height;
short depth;
short[] data;
LevelPos levelPos = new LevelPos((byte) 0, 0, 0);
levelPos.changeParameters(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
levelPos.convert(LodUtil.REGION_DETAIL_LEVEL);
long data;
try
{
byte minDetailLevel = lodDim.getRegion(levelPos).getMinDetailLevel();
LodDetail detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
LodDetail detail;
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
if (region == null)
return;
byte minDetailLevel = region.getMinDetailLevel();
detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
byte detailLevel = detail.detailLevel;
int posX;
int posZ;
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
{
startX = detail.startX[i];
@@ -199,23 +202,23 @@ public class LodBuilder
startZ, endX, endZ);
depth = 0;
}
levelPos.changeParameters((byte) 0,
chunk.getPos().x * 16 + startX,
chunk.getPos().z * 16 + startZ);
levelPos.convert(detail.detailLevel);
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().x * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().z * 16 + startZ, detail.detailLevel);
boolean isServer = config.distanceGenerationMode == DistanceGenerationMode.SERVER;
data = DataPoint.createDataPoint(height, depth, color[0], color[1], color[2]);
lodDim.addData(levelPos,
data = DataPoint.createDataPoint(height, depth, ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color));
lodDim.addData(detailLevel,
posX,
posZ,
data,
false,
isServer);
}
levelPos.changeParameters(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
lodDim.updateData(levelPos);
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
} catch (Exception e)
{
//e.printStackTrace();
e.printStackTrace();
}
}
@@ -352,8 +355,8 @@ public class LodBuilder
* otherwise only use the block's
* material color
*/
private short[] generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
private int generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
{
ChunkSection[] chunkSections = chunk.getSections();
@@ -427,8 +430,7 @@ public class LodBuilder
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new short[]{(short) red, (short) green, (short) blue};
return ColorUtil.rgbToInt(red,green,blue);
}
/**
@@ -18,7 +18,6 @@
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.objects.LevelPos.LevelPos;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
@@ -32,8 +31,8 @@ import net.minecraft.util.math.BlockPos;
*/
public abstract class AbstractLodTemplate
{
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, DebugMode debugging);
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging);
/**
* add the given position and color to the buffer
@@ -0,0 +1,64 @@
package com.seibel.lod.builders.lodTemplates;
public class Box
{
public static final int DOWN = 0;
public static final int UP = 1;
public static final int EAST = 2;
public static final int WEST = 3;
public static final int SOUTH = 4;
public static final int NORTH = 5;
public static final int OFFSET = 0;
public static final int WIDTH = 1;
public static final int X = 0;
public static final int Y = 1;
public static final int Z = 2;
public int[][] box;
public Box(){
box = new int[2][3];
}
public void set(int xWidth, int yWidth, int zWidth){
box[OFFSET][X] = 0;
box[OFFSET][Y] = 0;
box[OFFSET][Z] = 0;
box[WIDTH][X] = xWidth;
box[WIDTH][Y] = yWidth;
box[WIDTH][Z] = zWidth;
}
public void move(int xOffset, int yOffset, int zOffset){
box[OFFSET][X] = xOffset;
box[OFFSET][Y] = yOffset;
box[OFFSET][Z] = zOffset;
}
public int getMinX(){
return box[OFFSET][X];
}
public int getMaxX(){
return box[OFFSET][X] + box[WIDTH][X];
}
public int getMinY(){
return box[OFFSET][Y];
}
public int getMaxY(){
return box[OFFSET][Y] + box[WIDTH][Y];
}
public int getMinZ(){
return box[OFFSET][Z];
}
public int getMaxZ(){
return box[OFFSET][Z] + box[WIDTH][Z];
}
}
@@ -21,85 +21,82 @@ import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.ShadingMode;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
/**
* Builds LODs as rectangular prisms.
*
* @author James Seibel
* @version 9-6-2021
* @version 8-10-2021
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
private final int CULL_OFFSET = 16;
public CubicLodTemplate()
{
}
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, DebugMode debugging)
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging)
{
AxisAlignedBB bbox;
int width = 1 << levelPos.detailLevel;
int width = 1 << detailLevel;
// add each LOD for the detail level
bbox = generateBoundingBox(
generateBoundingBox(
box,
DataPoint.getHeight(data),
DataPoint.getDepth(data),
width,
levelPos.posX * width,
posX * width,
0,
levelPos.posZ * width,
posZ * width,
bufferCenterBlockPos);
int color = DataPoint.getColor(data);
if (debugging != DebugMode.OFF)
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel].getRGB();
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
}
if (bbox != null)
if (box != null)
{
addBoundingBoxToBuffer(buffer, bbox, color, bufferCenterBlockPos, adjData);
addBoundingBoxToBuffer(buffer, box, color, bufferCenterBlockPos, adjData);
}
}
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset, BlockPos bufferCenterBlockPos)
private void generateBoundingBox(Box box, int height, int depth, int width, double xOffset, double yOffset, double zOffset, BlockPos bufferCenterBlockPos)
{
// don't add an LOD if it is empty
if (height == -1 && depth == -1)
return null;
return;
if (depth == height)
{
// if the top and bottom points are at the same height
// render this LOD as 1 block thick
height++;
}
// offset the AABB by it's x/z position in the world since
// it uses doubles to specify its location, unlike the model view matrix
// which only uses floats
double x = -bufferCenterBlockPos.getX();
double z = -bufferCenterBlockPos.getZ();
return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset).move(x, 0, z);
double z = -bufferCenterBlockPos.getZ();;
box.set(width, height - depth, width);
box.move((int) (xOffset + x), (int) (yOffset + depth), (int) (zOffset + z));
}
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, int c, BlockPos playerBlockPos, short[][][] adjData)
private void addBoundingBoxToBuffer(BufferBuilder buffer, Box box, int c, BlockPos playerBlockPos, long[] adjData)
{
int topColor = c;
int bottomColor = c;
@@ -107,7 +104,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
int southColor = c;
int westColor = c;
int eastColor = c;
// darken the bottom and side colors if requested
if (LodConfig.CLIENT.graphics.shadingMode.get() == ShadingMode.DARKEN_SIDES)
{
@@ -123,11 +120,11 @@ public class CubicLodTemplate extends AbstractLodTemplate
westColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.WEST, true));
eastColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.EAST, true));
}
// apply the user specified saturation and brightness
float saturationMultiplier = LodConfig.CLIENT.graphics.saturationMultiplier.get().floatValue();
float brightnessMultiplier = LodConfig.CLIENT.graphics.brightnessMultiplier.get().floatValue();
if (saturationMultiplier != 1 || brightnessMultiplier != 1)
{
topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier);
@@ -137,199 +134,249 @@ public class CubicLodTemplate extends AbstractLodTemplate
westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier);
eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier);
}
int minX;
int maxX;
int minY;
int maxY;
short[] data;
int minZ;
int maxZ;
long data;
int tempMinY;
int tempMaxY;
int red;
int green;
int blue;
int alpha;
boolean disableCulling = true;
/**TODO make all of this more automatic if possible*/
if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET || disableCulling)
if (playerBlockPos.getY() > box.getMaxY() - CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(topColor);
green = ColorUtil.getGreen(topColor);
blue = ColorUtil.getBlue(topColor);
alpha = ColorUtil.getAlpha(topColor);
// top (facing up)
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
}
if (playerBlockPos.getY() < bb.minY + CULL_OFFSET || disableCulling)
if (playerBlockPos.getY() < box.getMinY() + CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(bottomColor);
green = ColorUtil.getGreen(bottomColor);
blue = ColorUtil.getBlue(bottomColor);
alpha = ColorUtil.getAlpha(bottomColor);
// bottom (facing down)
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
}
if (playerBlockPos.getZ() > bb.minZ - CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(northColor);
green = ColorUtil.getGreen(northColor);
blue = ColorUtil.getBlue(northColor);
alpha = ColorUtil.getAlpha(northColor);
// south (facing -Z)
data = adjData[1][1];
if (data == null)
{
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
}
else
{
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
{
minY = (int) Math.max(maxY, bb.minY);
addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha);
}
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
{
maxY = (int) Math.min(minY, bb.maxY);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getZ() < bb.maxZ + CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(southColor);
green = ColorUtil.getGreen(southColor);
blue = ColorUtil.getBlue(southColor);
alpha = ColorUtil.getAlpha(southColor);
data = adjData[1][0];
// north (facing +Z)
if (data == null)
{
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
}
else
{
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
{
minY = (int) Math.max(maxY, bb.minY);
addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha);
}
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
{
maxY = (int) Math.min(minY, bb.maxY);
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getX() < bb.maxX + CULL_OFFSET || disableCulling)
if (playerBlockPos.getX() < box.getMaxX() + CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(westColor);
green = ColorUtil.getGreen(westColor);
blue = ColorUtil.getBlue(westColor);
alpha = ColorUtil.getAlpha(westColor);
// west (facing -X)
data = adjData[0][0];
if (data == null)
data = adjData[0];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
if (data == 0)
{
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
}
else
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
}else
{
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = (int) Math.max(maxY, bb.minY);
addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
}
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = (int) Math.min(minY, bb.maxY);
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha);
maxY = Math.min(tempMinY, maxX);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getX() > bb.minX - CULL_OFFSET || disableCulling)
if (playerBlockPos.getX() > box.getMinX() - CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(eastColor);
green = ColorUtil.getGreen(eastColor);
blue = ColorUtil.getBlue(eastColor);
alpha = ColorUtil.getAlpha(eastColor);
// east (facing +X)
data = adjData[0][1];
if (data == null)
data = adjData[1];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
if (data == 0)
{
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
else
{
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = (int) Math.max(maxY, bb.minY);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha);
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = (int) Math.min(minY, bb.maxY);
addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
maxY = Math.min(tempMinY, maxX);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getZ() > box.getMinZ() - CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(northColor);
green = ColorUtil.getGreen(northColor);
blue = ColorUtil.getBlue(northColor);
alpha = ColorUtil.getAlpha(northColor);
data = adjData[3];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
// north (facing +Z)
if (data == 0)
{
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
}
else
{
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
}
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = Math.min(tempMinY, maxX);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getZ() < box.getMaxZ() + CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(southColor);
green = ColorUtil.getGreen(southColor);
blue = ColorUtil.getBlue(southColor);
alpha = ColorUtil.getAlpha(southColor);
data = adjData[2];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
// south (facing -Z)
if (data == 0)
{
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
else
{
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = Math.min(tempMinY, maxX);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
}
}
}
@Override
public int getBufferMemoryForSingleNode()
{
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints)))
return (6 * 4 * (3 + 4));
}
}
@@ -18,7 +18,6 @@
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.objects.LevelPos.LevelPos;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
@@ -35,8 +34,8 @@ import net.minecraft.util.math.BlockPos;
public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, DebugMode debugging)
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
}
@@ -18,7 +18,6 @@
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.objects.LevelPos.LevelPos;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
@@ -33,8 +32,8 @@ import net.minecraft.util.math.BlockPos;
public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, DebugMode debugging)
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
}
@@ -1,33 +1,24 @@
package com.seibel.lod.builders.worldGeneration;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.mutable.MutableBoolean;
import com.seibel.lod.objects.LevelPosUtil;
import com.seibel.lod.objects.PosToGenerateContainer;
import com.seibel.lod.builders.GenerationRequest;
import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrapper.MinecraftWrapper;
import net.minecraft.util.math.BlockPos;
import com.seibel.lod.wrapper.MinecraftWrapper;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.WorldWorkerManager;
@@ -75,10 +66,6 @@ public class LodWorldGenerator
*/
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
public volatile ConcurrentMap<LevelPos, MutableBoolean> nodeToGenerate;
SortedSet<LevelPos> nodeToGenerateListNear;
SortedSet<LevelPos> nodeToGenerateListFar;
private LodWorldGenerator()
{
@@ -108,14 +95,9 @@ public class LodWorldGenerator
try
{
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(mc.getPlayer().blockPosition());
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
int playerPosX = mc.getPlayer().blockPosition().getX();
int playerPosZ = mc.getPlayer().blockPosition().getZ();
// used when determining which chunks are closer when queuing distance
// generation
int minChunkDist = Integer.MAX_VALUE;
ArrayList<GenerationRequest> chunksToGen = new ArrayList<>(maxChunkGenRequests);
// if we don't have a full number of chunks to generate in chunksToGen
// we can top it off from this reserve
@@ -123,92 +105,34 @@ public class LodWorldGenerator
//=======================================//
// create the generation Request objects //
//=======================================//
List<GenerationRequest> generationRequestList = new ArrayList<>(maxChunkGenRequests);
if (nodeToGenerate == null)
nodeToGenerate = new ConcurrentHashMap<>();
Comparator<LevelPos> posNearComparator = LevelPos.getPosComparator(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
Comparator<LevelPos> posFarComparator = LevelPos.getPosAndDetailComparator(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
nodeToGenerateListNear = new TreeSet(posNearComparator);
nodeToGenerateListFar = new TreeSet(posFarComparator);
lodDim.getDataToGenerate(
nodeToGenerate,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
byte farDetail = (byte) 8;
PosToGenerateContainer posToGenerate = lodDim.getDataToGenerate(
farDetail,
maxChunkGenRequests,
0.25,
playerPosX,
playerPosZ);
//System.out.println(posToGenerate);
//here we prepare two sorted set
//the first contains the near pos to render
//the second contain the far pos to render
byte farDetail = (byte) 7;
for (LevelPos pos : nodeToGenerate.keySet())
byte detailLevel;
int posX;
int posZ;
int[] levelPos;
for (int index = 0; index < posToGenerate.getNumberOfPos(); index++)
{
if (!nodeToGenerate.get(pos).booleanValue())
{
nodeToGenerate.remove(pos);
} else
{
if (pos.detailLevel > farDetail){
nodeToGenerateListFar.add(pos);
}
nodeToGenerateListNear.add(pos);
nodeToGenerate.get(pos).setFalse();
}
}
levelPos = posToGenerate.getNthPos(index);
if(levelPos[0] == 0)
continue;
detailLevel = (byte) (levelPos[0] -1);
posX = levelPos[1];
posZ = levelPos[2];
int maxDistance;
byte circle;
LevelPos levelPos;
int requesting = maxChunkGenRequests;
int requestingFar = maxChunkGenRequests / 4;
while (requesting > 0 && !nodeToGenerateListNear.isEmpty())
{
levelPos = nodeToGenerateListNear.first();
//.out.println(levelPos);
nodeToGenerate.remove(levelPos);
nodeToGenerateListNear.remove(levelPos);
nodeToGenerateListFar.remove(levelPos);
//maxDistance = levelPos.maxDistance(
// playerBlockPosRounded.getX(),
// playerBlockPosRounded.getZ());
//circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance);
generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(levelPos.detailLevel)));
requesting--;
if (requestingFar > 0 && !nodeToGenerateListFar.isEmpty())
{
levelPos = nodeToGenerateListFar.first();
nodeToGenerate.remove(levelPos);
nodeToGenerateListNear.remove(levelPos);
nodeToGenerateListFar.remove(levelPos);
if (levelPos.detailLevel >= farDetail)
{
//maxDistance = levelPos.maxDistance( playerBlockPosRounded.getX(), playerBlockPosRounded.getZ());
//circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance);
generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(levelPos.detailLevel)));
requestingFar--;
requesting--;
}
}
}
//====================================//
// get the closet generation requests //
//====================================//
// determine which points in the posListToGenerate
// should actually be queued to generate
for (GenerationRequest generationRequest : generationRequestList)
{
ChunkPos chunkPos = generationRequest.getChunkPos();
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel,posX), LevelPosUtil.getChunkPos(detailLevel,posZ));
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
// prevent generating the same chunk multiple times
@@ -216,32 +140,16 @@ public class LodWorldGenerator
{
continue;
}
chunksToGen.add(generationRequest);
}
} // lod null and can generate more chunks
} // positions to generate
//=============================//
// start the LodNodeGenWorkers //
//=============================//
// issue #19
// TODO add a way for a server side mod to generate chunks requested here
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
// start chunk generation
for (GenerationRequest generationRequest : generationRequestList)
{
// don't add null chunkPos (which shouldn't happen anyway)
// or add more to the generation queue
ChunkPos chunkPos = generationRequest.getChunkPos();
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
continue;
positionWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, generationRequest.generationMode, renderer, lodBuilder, lodDim, serverWorld);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), renderer, lodBuilder, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
@@ -88,7 +88,7 @@ public class LodDimensionFileHandler
* file handler, older versions (smaller numbers) will be deleted and overwritten,
* newer versions (larger numbers) will be ignored and won't be read.
*/
public static final int LOD_SAVE_FILE_VERSION = 4;
public static final int LOD_SAVE_FILE_VERSION = 5;
/**
* This is the string written before the file version
@@ -2,47 +2,96 @@ package com.seibel.lod.objects;
public class DataPoint
{
public final static int HEIGHT_SHIFT = 54;
public final static int DEPTH_SHIFT = 44;
public final static int RED_SHIFT = 36;
public final static int GREEN_SHIFT = 28;
public final static int BLUE_SHIFT = 20;
public final static int GEN_TYPE_SHIFT = 17;
public final static int LIGHT_SHIFT = 13;
public final static int EXISTENCE_SHIFT = 0;
public static short[] createDataPoint(int height, int depth, int red, int green, int blue)
public final static long HEIGHT_MASK = Long.parseUnsignedLong("1111111111", 2);
public final static long DEPTH_MASK = Long.parseUnsignedLong("1111111111", 2);
public final static long RED_MASK = Long.parseUnsignedLong("11111111", 2);
public final static long GREEN_MASK = Long.parseUnsignedLong("11111111", 2);
public final static long BLUE_MASK = Long.parseUnsignedLong("11111111", 2);
public final static long GEN_TYPE_MASK = Long.parseUnsignedLong("111", 2);
public final static long LIGHT_MASK = Long.parseUnsignedLong("1111", 2);
public final static long EXISTENCE_MASK = 1;
public static long createDataPoint(int height, int depth, int color)
{
return new short[]{(short) height, (short) depth, (short) red, (short) green, (short) blue};
int red = (getRed(color) << 16) & 0x00FF0000;
int green = (getGreen(color) << 8) & 0x0000FF00;
int blue = getBlue(color)& 0x000000FF;
return createDataPoint(height, depth, red, green, blue);
}
public static long createDataPoint(int height, int depth, int red, int green, int blue)
{
long dataPoint = 0;
dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT;
dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT;
dataPoint += (red & RED_MASK) << RED_SHIFT;
dataPoint += (green & GREEN_MASK) << GREEN_SHIFT;
dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT;
dataPoint += 1;
return dataPoint;
}
public static short getHeight(short[] dataPoint)
public static short getHeight(long dataPoint)
{
return dataPoint[0];
return (short) ((dataPoint >> HEIGHT_SHIFT) & HEIGHT_MASK);
}
public static short getDepth(short[] dataPoint)
public static short getDepth(long dataPoint)
{
return dataPoint[1];
return (short) ((dataPoint >> DEPTH_SHIFT) & DEPTH_MASK);
}
public static short getRed(short[] dataPoint)
public static short getRed(long dataPoint)
{
return dataPoint[2];
return (short) ((dataPoint >> RED_SHIFT) & RED_MASK);
}
public static short getGreen(short[] dataPoint)
public static short getGreen(long dataPoint)
{
return dataPoint[3];
return (short) ((dataPoint >> GREEN_SHIFT) & GREEN_MASK);
}
public static short getBlue(short[] dataPoint)
public static short getBlue(long dataPoint)
{
return dataPoint[4];
return (short) ((dataPoint >> BLUE_SHIFT) & BLUE_MASK);
}
public static short[] getHeightDepth(short[] dataPoint)
public static boolean doesItExist(long dataPoint)
{
return new short[]{dataPoint[0], dataPoint[1]};
return ((dataPoint & EXISTENCE_MASK) == 1);
}
public static int getColor(short[] dataPoint)
public static int getColor(long dataPoint)
{
int R = (dataPoint[2] << 16) & 0x00FF0000;
int G = (dataPoint[3] << 8) & 0x0000FF00;
int B = dataPoint[4] & 0x000000FF;
int R = (getRed(dataPoint) << 16) & 0x00FF0000;
int G = (getGreen(dataPoint) << 8) & 0x0000FF00;
int B = getBlue(dataPoint)& 0x000000FF;
return 0xFF000000 | R | G | B;
}
public static String toString(long dataPoint)
{
StringBuilder s = new StringBuilder();
s.append(getHeight(dataPoint));
s.append(" ");
s.append(getDepth(dataPoint));
s.append(" ");
s.append(getRed(dataPoint));
s.append(" ");
s.append(getBlue(dataPoint));
s.append(" ");
s.append(getGreen(dataPoint));
s.append('\n');
return s.toString();
}
}
@@ -11,61 +11,33 @@ public class LevelContainer implements Serializable
public final byte detailLevel;
public final byte[][][] colors;
public final long[][] data;
public final short[][] height;
public final short[][] depth;
public final boolean[][] dataExistence;
public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, boolean[][] dataExistence)
public LevelContainer(byte detailLevel, long[][] data)
{
this.detailLevel = detailLevel;
this.colors = colors;
this.height = height;
this.depth = depth;
this.dataExistence = dataExistence;
this.data = data;
}
public LevelContainer(String data)
public LevelContainer(String inputString)
{
int index = 0;
int lastIndex = 0;
index = data.indexOf(DATA_DELIMITER, 0);
this.detailLevel = (byte) Integer.parseInt(data.substring(0, index));
index = inputString.indexOf(DATA_DELIMITER, 0);
this.detailLevel = (byte) Integer.parseInt(inputString.substring(0, index));
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
this.colors = new byte[size][size][3];
this.height = new short[size][size];
this.depth = new short[size][size];
this.dataExistence = new boolean[size][size];
int intCol;
this.data = new long[size][size];
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
intCol = Integer.parseInt(data.substring(lastIndex + 1, index), 16);
colors[x][z][0] = (byte) ((intCol >> 16) - 128);
colors[x][z][1] = (byte) ((intCol >> 8) - 128);
colors[x][z][2] = (byte) (intCol - 128);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
height[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
depth[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex + 1, index));
index = inputString.indexOf(DATA_DELIMITER, lastIndex + 1);
data[x][z] = Long.parseLong(inputString.substring(lastIndex + 1, index), 16);
}
}
@@ -75,7 +47,6 @@ public class LevelContainer implements Serializable
public String toString()
{
StringBuilder stringBuilder = new StringBuilder();
int combinedCol;
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
stringBuilder.append(detailLevel);
stringBuilder.append(DATA_DELIMITER);
@@ -83,15 +54,8 @@ public class LevelContainer implements Serializable
{
for (int z = 0; z < size; z++)
{
//Converting the colors to intColor and then to HEX
combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128));
stringBuilder.append(Integer.toHexString(combinedCol));
stringBuilder.append(DATA_DELIMITER);
stringBuilder.append(Integer.toHexString(height[x][z] & 0xffff));
stringBuilder.append(DATA_DELIMITER);
stringBuilder.append(Integer.toHexString(depth[x][z] & 0xffff));
stringBuilder.append(DATA_DELIMITER);
stringBuilder.append(dataExistence[x][z]);
//Converting the dataToHex
stringBuilder.append(Long.toHexString(data[x][z]));
stringBuilder.append(DATA_DELIMITER);
}
}
@@ -1,7 +0,0 @@
package com.seibel.lod.objects.LevelPos;
public interface ImmutableLevelPos
{
public LevelPos getConvertedLevelPos(byte newDetailLevel);
public LevelPos getRegionModuleLevelPos();
}
@@ -1,415 +0,0 @@
package com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.ChunkPos;
import java.util.Comparator;
public class LevelPos implements Cloneable, ImmutableLevelPos, MutableLevelPos, Comparable<LevelPos>
{
public byte detailLevel;
public int posX;
public int posZ;
public boolean flag;
public LevelPos()
{
}
public LevelPos(byte detailLevel, int posX, int posZ)
{
this.posX = posX;
this.posZ = posZ;
this.detailLevel = detailLevel;
}
/**
* this operation does not change the state
*/
public LevelPos getConvertedLevelPos(byte newDetailLevel)
{
if (newDetailLevel >= detailLevel)
{
int width = 1 << (newDetailLevel - detailLevel);
return new LevelPos(
newDetailLevel,
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
} else
{
int width = 1 << (detailLevel - newDetailLevel);
return new LevelPos(
newDetailLevel,
posX * width,
posZ * width);
}
}
/**
* this operation does not change the state
*/
public LevelPos getRegionModuleLevelPos()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return new LevelPos(
detailLevel,
Math.floorMod(posX, width),
Math.floorMod(posZ, width));
}
/**
* this operation changes the state
*/
public void convert(byte newDetailLevel)
{
if (newDetailLevel >= detailLevel)
{
int width = 1 << (newDetailLevel - detailLevel);
detailLevel = newDetailLevel;
posX = Math.floorDiv(posX, width);
posZ = Math.floorDiv(posZ, width);
} else
{
int width = 1 << (detailLevel - newDetailLevel);
detailLevel = newDetailLevel;
posX = posX * width;
posZ = posZ * width;
}
}
/**
* this operation changes the state
*/
public void performRegionModule()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
posX = Math.floorMod(posX, width);
posZ = Math.floorMod(posZ, width);
}
/**
* this operation changes the state
*/
public void applyOffset(int xOffset, int zOffset)
{
posX = posX + xOffset;
posX = posZ + zOffset;
}
/**
* this operation changes the state
*/
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ)
{
detailLevel = newDetailLevel;
posX = newPosX;
posZ = newPosZ;
}
public RegionPos getRegionPos()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return new RegionPos(
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
}
public int getRegionPosX()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return Math.floorDiv(posX, width);
}
public int getRegionPosZ()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return Math.floorDiv(posZ, width);
}
public int getChunkPosX()
{
if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel)
{
int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel);
return Math.floorDiv(posX, width);
} else
{
int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL);
return posX * width;
}
}
public int getChunkPosZ()
{
if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel)
{
int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel);
return Math.floorDiv(posZ, width);
} else
{
int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL);
return posZ * width;
}
}
public ChunkPos getChunkPos()
{
if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel)
{
int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel);
return new ChunkPos(
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
} else
{
int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL);
return new ChunkPos(
posX * width,
posZ * width);
}
}
/**
* TODO fix the region disappearing for a second
*/
public int maxDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ)
{
int width = 1 << detailLevel;
int startPosX = regionPosX * 512 + posX * width;
int startPosZ = regionPosZ * 512 + posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return maxDistance;
}
public int maxDistance(int playerPosX, int playerPosZ)
{
int width = 1 << detailLevel;
int startPosX = posX * width;
int startPosZ = posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return maxDistance;
}
public int minDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ)
{
int width = 1 << detailLevel;
int startPosX = regionPosX * 512 + posX * width;
int startPosZ = regionPosZ * 512 + posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX;
boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ;
if (inXArea && inZArea)
{
return 0;
} else if (inXArea)
{
return Math.min(
Math.abs(playerPosZ - startPosZ),
Math.abs(playerPosZ - endPosZ)
);
} else if (inZArea)
{
return Math.min(
Math.abs(playerPosX - startPosX),
Math.abs(playerPosX - endPosX)
);
} else
{
int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return minDistance;
}
}
public int minDistance(int playerPosX, int playerPosZ)
{
int width = 1 << detailLevel;
int startPosX = posX * width;
int startPosZ = posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX;
boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ;
if (inXArea && inZArea)
{
return 0;
} else if (inXArea)
{
return Math.min(
Math.abs(playerPosZ - startPosZ),
Math.abs(playerPosZ - endPosZ)
);
} else if (inZArea)
{
return Math.min(
Math.abs(playerPosX - startPosX),
Math.abs(playerPosX - endPosX)
);
} else
{
int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return minDistance;
}
}
public static LevelPosDistanceComparator getPosComparator(int playerPosX, int playerPosZ)
{
return new LevelPosDistanceComparator(playerPosX, playerPosZ);
}
public static LevelPosDetailComparator getPosAndDetailComparator(int playerPosX, int playerPosZ)
{
return new LevelPosDetailComparator(playerPosX, playerPosZ);
}
public static class LevelPosDistanceComparator implements Comparator<LevelPos>
{
int playerPosX;
int playerPosZ;
public LevelPosDistanceComparator(int playerPosX, int playerPosZ)
{
this.playerPosX = playerPosX;
this.playerPosZ = playerPosZ;
}
@Override
public int compare(LevelPos first, LevelPos second)
{
return Integer.compare(
first.minDistance(playerPosX, playerPosZ),
second.minDistance(playerPosX, playerPosZ));
}
}
public static class LevelPosDetailComparator implements Comparator<LevelPos>
{
int playerPosX;
int playerPosZ;
public LevelPosDetailComparator(int playerPosX, int playerPosZ)
{
this.playerPosX = playerPosX;
this.playerPosZ = playerPosZ;
}
@Override
public int compare(LevelPos first, LevelPos second)
{
int compareResult = Integer.compare(second.detailLevel, first.detailLevel);
if (compareResult == 0)
{
compareResult = Integer.compare(
first.minDistance(playerPosX, playerPosZ),
second.minDistance(playerPosX, playerPosZ));
}
return compareResult;
}
}
public static LevelPosComparator getComparator()
{
return new LevelPosComparator();
}
public static class LevelPosComparator implements Comparator<LevelPos>
{
@Override
public int compare(LevelPos first, LevelPos second)
{
int compareResult = Integer.compare(first.detailLevel, second.detailLevel);
if (compareResult == 0)
{
compareResult = Integer.compare(
first.posX,
second.posX);
}
if (compareResult == 0)
{
compareResult = Integer.compare(
first.posZ,
second.posZ);
}
return compareResult;
}
}
@Override
public int compareTo(LevelPos other)
{
int compareResult = Integer.compare(this.detailLevel, other.detailLevel);
if (compareResult == 0)
{
compareResult = Integer.compare(
this.posX,
other.posX);
}
if (compareResult == 0)
{
compareResult = Integer.compare(
this.posZ,
other.posZ);
}
return compareResult;
}
@Override
public LevelPos clone()
{
return new LevelPos(detailLevel, posX, posZ);
}
@Override
public int hashCode()
{
int hash = 7;
hash = 31 * hash + (int) detailLevel;
hash = 31 * hash + posX;
hash = 31 * hash + posZ;
return hash;
}
@Override
public boolean equals(Object other)
{
return (this.detailLevel == ((LevelPos) other).detailLevel &&
this.posX == ((LevelPos) other).posX &&
this.posZ == ((LevelPos) other).posZ);
}
@Override
public String toString()
{
String s = (detailLevel + " " + posX + " " + posZ);
return s;
}
}
@@ -1,14 +0,0 @@
package com.seibel.lod.objects.LevelPos;
import com.seibel.lod.util.LodUtil;
public interface MutableLevelPos
{
public void convert(byte newDetailLevel);
public void performRegionModule();
public void applyOffset(int xOffset, int zOffset);
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ);
}
@@ -37,11 +37,29 @@ public class LevelPosUtil
return new int[]{detailLevel, posX, posZ};
}
public static int[] createLevelPos(byte detailLevel, int posX, int posZ, int distance)
public static int convert(byte detailLevel, int pos, byte newDetailLevel)
{
return new int[]{detailLevel, posX, posZ, distance};
int width;
if (newDetailLevel >= detailLevel)
{
width = 1 << (newDetailLevel - detailLevel);
return Math.floorDiv(pos, width);
} else
{
width = 1 << (detailLevel - newDetailLevel);
return pos * width;
}
}
public static int getRegion(byte detailLevel, int pos)
{
return Math.floorDiv(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
}
public static int getRegionModule(byte detailLevel, int pos)
{
return Math.floorMod(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
}
public static byte getDetailLevel(int[] levelPos)
{
@@ -105,6 +123,11 @@ public class LevelPosUtil
return Math.floorDiv(getPosZ(levelPos), width);
}
public static int getChunkPos(byte detailLevel, int pos)
{
return convert(detailLevel,pos, LodUtil.CHUNK_DETAIL_LEVEL);
}
public static int getChunkPosX(int[] levelPos)
{
levelPos = convert(levelPos, LodUtil.CHUNK_DETAIL_LEVEL);
@@ -139,6 +162,23 @@ public class LevelPosUtil
return maxDistance;
}
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion)
{
int width = 1 << detailLevel;
int startPosX = xRegion * 512 + posX * width;
int startPosZ = zRegion * 512 + posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return maxDistance;
}
public static int minDistance(int[] levelPos, int playerPosX, int playerPosZ)
{
@@ -181,40 +221,26 @@ public class LevelPosUtil
}
}
public static int compareDistance(int posX, int posZ, int[] first, int[] second)
public static int compareDistance(int firstDistance, int secondDistance)
{
return Integer.compare(
minDistance(first, posX, posZ),
minDistance(second, posX, posZ));
firstDistance,
secondDistance);
}
public static int compareDistance(int[] first, int[] second)
{
return Integer.compare(
getDistance(first),
getDistance(second));
}
public static int compareLevelAndDistance(int[] first, int[] second)
public static int compareLevelAndDistance(byte firstDetail, int firstDistance, byte secondDetail, int secondDistance)
{
int compareResult = Integer.compare(getDetailLevel(second), getDetailLevel(first));
int compareResult = Integer.compare(
secondDetail,
firstDetail);
System.out.println("comparing level "+ firstDetail + " " + secondDetail + " " + compareResult);
if (compareResult == 0)
{
compareResult = Integer.compare(
getDistance(first),
getDistance(second));
}
return compareResult;
}
public static int compareLevelAndDistance(int posX, int posZ, int[] first, int[] second)
{
int compareResult = Integer.compare(getDetailLevel(second), getDetailLevel(first));
if (compareResult == 0)
{
compareResult = Integer.compare(
minDistance(first, posX, posZ),
minDistance(second, posX, posZ));
compareResult = compareDistance(
firstDistance,
secondDistance);
System.out.println("Equal level "+ firstDistance + " " + secondDistance + " " + compareResult);
}
return compareResult;
}
@@ -223,4 +249,8 @@ public class LevelPosUtil
{
return (getDetailLevel(levelPos) + " " + getPosX(levelPos) + " " + getPosZ(levelPos));
}
public static String toString(byte detailLevel, int posX, int posZ)
{
return (detailLevel + " " + posX + " " + posZ);
}
}
@@ -24,21 +24,22 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.wrapper.MinecraftWrapper;
import org.apache.commons.lang3.mutable.MutableBoolean;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrapper.MinecraftWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
import org.lwjgl.system.CallbackI;
/**
@@ -47,7 +48,7 @@ import net.minecraft.world.server.ServerWorld;
*
* @author Leonardo Amato
* @author James Seibel
* @version 9-6-2021
* @version 8-8-2021
*/
public class LodDimension
{
@@ -67,8 +68,10 @@ public class LodDimension
public volatile LodRegion regions[][];
public volatile boolean isRegionDirty[][];
public volatile boolean regen[][];
/** if true that means there are regions in this dimension
* that need to have their buffers rebuilt. */
/**
* if true that means there are regions in this dimension
* that need to have their buffers rebuilt.
*/
public volatile boolean regenDimension = false;
private volatile RegionPos center;
@@ -243,6 +246,8 @@ public class LodDimension
*/
public int getMinMemoryNeeded()
{
int regionX;
int regionZ;
int count = 0;
LodRegion region;
@@ -260,25 +265,29 @@ public class LodDimension
return count;
}
/**
* Gets the region at the given X and Z
* <br>
* Returns null if the region doesn't exist
* or is outside the loaded area.
*/
public LodRegion getRegion(LevelPos levelPos)
public LodRegion getRegion(byte detailLevel, int posX, int posZ)
{
int xRegion = LevelPosUtil.getRegion(detailLevel, posX);
int zRegion = LevelPosUtil.getRegion(detailLevel, posZ);
int xIndex = (xRegion - center.x) + halfWidth;
int zIndex = (zRegion - center.z) + halfWidth;
RegionPos regionPos = levelPos.getRegionPos();
int xIndex = (regionPos.x - center.x) + halfWidth;
int zIndex = (regionPos.z - center.z) + halfWidth;
if (!regionIsInRange(regionPos.x, regionPos.z))
throw new ArrayIndexOutOfBoundsException("Region for level pos " + levelPos + " out of range");
if (!regionIsInRange(xRegion, zRegion))
return null;
//throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range");
else if (regions[xIndex][zIndex] == null)
throw new InvalidParameterException("Region for level pos " + levelPos + " not currently initialized");
else if (regions[xIndex][zIndex].getMinDetailLevel() > levelPos.detailLevel)
throw new InvalidParameterException("Region for level pos " + levelPos + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel());
return null;
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " not currently initialized");
else if (regions[xIndex][zIndex].getMinDetailLevel() > detailLevel)
return null;
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel());
return regions[xIndex][zIndex];
}
@@ -288,15 +297,17 @@ public class LodDimension
* Returns null if the region doesn't exist
* or is outside the loaded area.
*/
public LodRegion getRegion(RegionPos regionPos)
public LodRegion getRegion(int regionPosX, int regionPosZ)
{
int xIndex = (regionPos.x - center.x) + halfWidth;
int zIndex = (regionPos.z - center.z) + halfWidth;
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
if (!regionIsInRange(regionPos.x, regionPos.z))
throw new ArrayIndexOutOfBoundsException("Region " + regionPos + " out of range");
if (!regionIsInRange(regionPosX, regionPosZ))
return null;
//throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range");
else if (regions[xIndex][zIndex] == null)
throw new InvalidParameterException("Region " + regionPos + " not currently initialized");
return null;
//throw new InvalidParameterException("Region " + regionPosX + " " + regionPosZ + " not currently initialized");
return regions[xIndex][zIndex];
}
@@ -308,7 +319,7 @@ public class LodDimension
public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
{
int xIndex = (newRegion.regionPosX - center.x) + halfWidth;
int zIndex = (center.z - newRegion.regionPosZ) + halfWidth;
int zIndex = (newRegion.regionPosZ - center.z) + halfWidth;
if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ))
// out of range
@@ -323,7 +334,7 @@ public class LodDimension
*/
public void treeCutter(int playerPosX, int playerPosZ)
{
ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos();
ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
if (lastCutChunk == null)
lastCutChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1);
if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z)
@@ -336,7 +347,6 @@ public class LodDimension
int minDistance;
byte detail;
byte levelToCut;
LevelPos levelPos = new LevelPos();
for (int x = 0; x < regions.length; x++)
{
@@ -349,8 +359,7 @@ public class LodDimension
//if this is not the case w
if (regions[x][z] != null)
{
levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ);
minDistance = levelPos.minDistance(playerPosX, playerPosZ);
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
detail = DetailDistanceUtil.getDistanceTreeCutInverse(minDistance);
levelToCut = DetailDistanceUtil.getCutLodDetail(detail);
if (regions[x][z].getMinDetailLevel() > levelToCut)
@@ -373,7 +382,7 @@ public class LodDimension
public void treeGenerator(int playerPosX, int playerPosZ)
{
DistanceGenerationMode generationMode = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos();
ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
if (lastGenChunk == null)
lastGenChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1);
@@ -388,20 +397,17 @@ public class LodDimension
int minDistance;
byte detail;
byte levelToGen;
LevelPos levelPos = new LevelPos();
for (int x = 0; x < regions.length; x++)
{
for (int z = 0; z < regions.length; z++)
{
regionX = (x + center.x) - halfWidth;
regionZ = (z + center.z) - halfWidth;
levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ);
final RegionPos regionPos = new RegionPos(regionX, regionZ);
region = regions[x][z];
//We require that the region we are checking is loaded with at least this level
levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ);
minDistance = levelPos.minDistance(playerPosX, playerPosZ);
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
detail = DetailDistanceUtil.getDistanceTreeGenInverse(minDistance);
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
if (region == null || region.getGenerationMode() != generationMode)
@@ -437,32 +443,31 @@ public class LodDimension
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
public synchronized Boolean addData(LevelPos levelPos, short[] lodDataPoint, boolean dontSave, boolean serverQuality)
public synchronized Boolean addData(byte detailLevel, int posX, int posZ, long lodDataPoint, boolean dontSave, boolean serverQuality)
{
// don't continue if the region can't be saved
RegionPos regionPos = levelPos.getRegionPos();
if (!regionIsInRange(regionPos.x, regionPos.z))
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
LodRegion region = getRegion(regionPosX, regionPosZ);
if (region == null)
return false;
}
LodRegion region = getRegion(levelPos);
boolean nodeAdded = region.addData(levelPos, lodDataPoint, serverQuality);
boolean nodeAdded = region.addData(detailLevel, posX, posZ, lodDataPoint, serverQuality);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
try
{
// mark the region as dirty so it will be saved to disk
int xIndex = (regionPos.x - center.x) + halfWidth;
int zIndex = (regionPos.z - center.z) + halfWidth;
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
regen[xIndex][zIndex] = true;
regenDimension = true;
} catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// This method was probably called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
@@ -470,7 +475,8 @@ public class LodDimension
return nodeAdded;
}
public void setToRegen(int xRegion, int zRegion){
public void setToRegen(int xRegion, int zRegion)
{
int xIndex = (xRegion - center.x) + halfWidth;
int zIndex = (zRegion - center.z) + halfWidth;
regen[xIndex][zIndex] = true;
@@ -481,32 +487,26 @@ public class LodDimension
*
* @return list of quadTrees
*/
public void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, int playerPosX, int playerPosZ)
public PosToGenerateContainer getDataToGenerate(byte farDetail, int maxDataToGenerate, double farRatio, int playerPosX, int playerPosZ)
{
PosToGenerateContainer posToGenerate = new PosToGenerateContainer(farDetail, maxDataToGenerate, (int) (maxDataToGenerate * farRatio), playerPosX, playerPosZ);
int n = regions.length;
int xIndex;
int zIndex;
LodRegion region;
RegionPos regionPos;
for (int xRegion = 0; xRegion < n; xRegion++)
{
for (int zRegion = 0; zRegion < n; zRegion++)
{
try
{
xIndex = (xRegion + center.x) - halfWidth;
zIndex = (zRegion + center.z) - halfWidth;
regionPos = new RegionPos(xIndex, zIndex);
region = getRegion(regionPos);
region.getDataToGenerate(dataToGenerate, playerPosX, playerPosZ);
xIndex = (xRegion + center.x) - halfWidth;
zIndex = (zRegion + center.z) - halfWidth;
region = getRegion(xIndex, zIndex);
if (region != null)
region.getDataToGenerate(posToGenerate, playerPosX, playerPosZ);
} catch (Exception e)
{
//e.printStackTrace();
}
}
}
return posToGenerate;
}
/**
@@ -514,33 +514,11 @@ public class LodDimension
*
* @return list of nodes
*/
public void getDataToRender(ConcurrentMap<LevelPos, MutableBoolean> dataToRender, RegionPos regionPos, int playerPosX, int playerPosZ)
public void getDataToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX, int playerPosZ)
{
try
{
LodRegion region = getRegion(regionPos);
region.getDataToRender(dataToRender, playerPosX, playerPosZ);
} catch (NullPointerException e)
{
System.out.println(regionPos);
e.printStackTrace();
} catch (Exception e)
{
//e.printStackTrace();
}
}
/**
* Get the LodNodeData at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public short[] getData(ChunkPos chunkPos)
{
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z);
return getData(levelPos);
LodRegion region = getRegion(regionPos.x, regionPos.z);
if (region != null)
region.getDataToRender(posToRender, playerPosX, playerPosZ);
}
/**
@@ -550,26 +528,19 @@ public class LodDimension
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public short[] getData(LevelPos levelPos)
public long getData(byte detailLevel, int posX, int posZ)
{
if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
try
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
{
LodRegion region = getRegion(levelPos);
if (region == null)
{
return null;
}
return region.getData(levelPos);
} catch (Exception e)
{
return null;
return 0;
}
return region.getData(detailLevel, posX, posZ);
}
@@ -580,40 +551,34 @@ public class LodDimension
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public void updateData(LevelPos levelPos)
public void updateData(byte detailLevel, int posX, int posZ)
{
if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(levelPos);
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
{
return;
}
region.updateArea(levelPos);
region.updateArea(detailLevel, posX, posZ);
}
/**
* return true if and only if the node at that position exist
*/
public boolean doesDataExist(LevelPos levelPos)
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
{
try
{
LodRegion region = getRegion(levelPos);
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
{
return false;
}
return region.doesDataExist(levelPos.clone());
} catch (Exception e)
if (region == null)
{
return false;
}
return region.doesDataExist(detailLevel, posX, posZ);
}
/**
@@ -716,7 +681,6 @@ public class LodDimension
}
stringBuilder.append("\n");
}
System.out.println(stringBuilder);
return stringBuilder.toString();
}
}
@@ -1,14 +1,8 @@
package com.seibel.lod.objects;
import java.io.Serializable;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.lang3.mutable.MutableBoolean;
import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
@@ -20,24 +14,19 @@ import com.seibel.lod.util.LodUtil;
* 0 for x, 1 for y, 2 for z in 3D
*/
public class LodRegion implements Serializable
public class LodRegion
{
//x coord,
private byte minDetailLevel;
private static final byte POSSIBLE_LOD = 10;
private int numberOfPoints;
//private int numberOfPoints;
private DistanceGenerationMode generationMode;
//For each of the following field the first slot is for the level of detail
//Important: byte have a [-128, 127] range. When converting from or to int a 128 should be added or removed
//If there is a bug with color then it's probably caused by this.
//in the future other fields like transparency and light level could be added
private byte[][][][] colors;
private short[][][] height;
private short[][][] depth;
private boolean[][][] dataExistence;
private long[][][] data;
public final int regionPosX;
@@ -50,42 +39,18 @@ public class LodRegion implements Serializable
this.regionPosZ = regionPos.z;
this.minDetailLevel = levelContainer.detailLevel;
//Array of matrices of arrays
colors = new byte[POSSIBLE_LOD][][][];
//Arrays of matrices
height = new short[POSSIBLE_LOD][][];
depth = new short[POSSIBLE_LOD][][];
dataExistence = new boolean[POSSIBLE_LOD][][];
data = new long[POSSIBLE_LOD][][];
colors[minDetailLevel] = levelContainer.colors;
height[minDetailLevel] = levelContainer.height;
depth[minDetailLevel] = levelContainer.depth;
dataExistence[minDetailLevel] = levelContainer.dataExistence;
data[minDetailLevel] = levelContainer.data;
//Initialize all the different matrices
for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
colors[lod] = new byte[size][size][3];
height[lod] = new short[size][size];
depth[lod] = new short[size][size];
dataExistence[lod] = new boolean[size][size];
}
int width;
LevelPos levelPos = new LevelPos();
for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
{
width = 1 << (LodUtil.REGION_DETAIL_LEVEL - tempLod);
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
levelPos.changeParameters(tempLod, x, z);
update(levelPos);
}
}
data[lod] = new long[size][size];
}
updateArea(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ);
}
public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode)
@@ -95,23 +60,14 @@ public class LodRegion implements Serializable
this.regionPosX = regionPos.x;
this.regionPosZ = regionPos.z;
//Array of matrices of arrays
colors = new byte[POSSIBLE_LOD][][][];
//Arrays of matrices
height = new short[POSSIBLE_LOD][][];
depth = new short[POSSIBLE_LOD][][];
dataExistence = new boolean[POSSIBLE_LOD][][];
data = new long[POSSIBLE_LOD][][];
//Initialize all the different matrices
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
colors[lod] = new byte[size][size][3];
height[lod] = new short[size][size];
depth[lod] = new short[size][size];
dataExistence[lod] = new boolean[size][size];
data[lod] = new long[size][size];
}
}
@@ -119,26 +75,21 @@ public class LodRegion implements Serializable
/**
* This method can be used to insert data into the LodRegion
*
* @param levelPos
* @param dataPoint
* @return
*/
public boolean addData(LevelPos levelPos, short[] dataPoint, boolean serverQuality)
public boolean addData(byte detailLevel, int posX, int posZ, long dataPoint, boolean serverQuality)
{
levelPos.performRegionModule();
if (!doesDataExist(levelPos) || serverQuality)
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
if (!doesDataExist(detailLevel, posX, posZ) || serverQuality)
{
//update the number of node present
if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++;
//if (!doesDataExist(detailLevel, posX, posZ)) numberOfPoints++;
//add the node data
this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getHeight(dataPoint);
this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getDepth(dataPoint);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (DataPoint.getRed(dataPoint) - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (DataPoint.getGreen(dataPoint) - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (DataPoint.getBlue(dataPoint) - 128);
this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true;
this.data[detailLevel][posX][posZ] = dataPoint;
return true;
} else
{
@@ -149,18 +100,13 @@ public class LodRegion implements Serializable
/**
* This method will return the data in the position relative to the level of detail
*
* @param levelPos
* @return the data at the relative pos and level
*/
public short[] getData(LevelPos levelPos)
public long getData(byte detailLevel, int posX, int posZ)
{
levelPos = levelPos.getRegionModuleLevelPos();
return new short[]{height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128),
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128),
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128)
};
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return data[detailLevel][posX][posZ];
}
/**
@@ -168,25 +114,21 @@ public class LodRegion implements Serializable
*
* @return
*/
public void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, int playerPosX, int playerPosZ)
public void getDataToGenerate(PosToGenerateContainer posToGenerate, int playerPosX, int playerPosZ)
{
LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0);
getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ);
getDataToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ);
}
private void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, LevelPos levelPos, int playerPosX, int playerPosZ)
private void getDataToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel);
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
//here i calculate the the LevelPos is in range
//This is important to avoid any kind of hole in the generation
int minDistance = levelPos.minDistance(playerPosX, playerPosZ, regionPosX, regionPosZ);
//nt minDistance = LevelPosUtil.minDistance(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size, playerPosX, playerPosZ);
int maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
int posX = levelPos.posX;
int posZ = levelPos.posZ;
byte detailLevel = levelPos.detailLevel;
byte childDetailLevel = (byte) (detailLevel - 1);
int childPosX = posX * 2;
int childPosZ = posZ * 2;
@@ -194,21 +136,14 @@ public class LodRegion implements Serializable
int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel);
//we have reached the target detail level
if (DetailDistanceUtil.getDistanceGenerationInverse(minDistance) > detailLevel)
if (DetailDistanceUtil.getDistanceGenerationInverse(maxDistance) > detailLevel)
{
return;
} else if (DetailDistanceUtil.getDistanceGenerationInverse(minDistance) == detailLevel)
} else if (DetailDistanceUtil.getDistanceGenerationInverse(maxDistance) == detailLevel)
{
if (!doesDataExist(levelPos))
if (!doesDataExist(detailLevel, posX, posZ))
{
levelPos.changeParameters(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
if (dataToGenerate.containsKey(levelPos))
{
dataToGenerate.get(levelPos).setTrue();
} else
{
dataToGenerate.put(levelPos.clone(), new MutableBoolean(true));
}
posToGenerate.addPosToGenerate(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
}
} else
{
@@ -221,19 +156,11 @@ public class LodRegion implements Serializable
{
for (int z = 0; z <= 1; z++)
{
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
if (!doesDataExist(levelPos))
if (!doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
{
num++;
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
if (dataToGenerate.containsKey(levelPos))
{
dataToGenerate.get(levelPos).setTrue();
} else
{
dataToGenerate.put(levelPos.clone(), new MutableBoolean(true));
}
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
}
}
}
@@ -245,8 +172,7 @@ public class LodRegion implements Serializable
{
for (int z = 0; z <= 1; z++)
{
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ);
getDataToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
}
}
}
@@ -255,21 +181,12 @@ public class LodRegion implements Serializable
{
if (DetailDistanceUtil.getLodGenDetail(childDetailLevel).detailLevel <= (childDetailLevel))
{
levelPos.changeParameters(detailLevel, posX, posZ);
levelPos.convert(childDetailLevel);
if (!doesDataExist(levelPos))
if (!doesDataExist(childDetailLevel, childPosX, childPosZ))
{
levelPos.changeParameters(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize);
if (dataToGenerate.containsKey(levelPos))
{
dataToGenerate.get(levelPos).setTrue();
} else
{
dataToGenerate.put(levelPos.clone(), new MutableBoolean(true));
}
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + regionPosX * childSize, childPosZ + regionPosZ * childSize);
} else
{
getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ);
getDataToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
}
}
}
@@ -280,60 +197,41 @@ public class LodRegion implements Serializable
/**
* @return
*/
public void getDataToRender(ConcurrentMap<LevelPos, MutableBoolean> dataToRender, int playerPosX, int playerPosZ)
public void getDataToRender(PosToRenderContainer posToRender, int playerPosX, int playerPosZ)
{
LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0);
getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ);
getDataToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ);
}
/**
* @return
*/
private void getDataToRender(ConcurrentMap<LevelPos, MutableBoolean> dataToRender, LevelPos levelPos, int playerPosX, int playerPosZ)
private void getDataToRender(PosToRenderContainer posToRender, byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel);
int posX = levelPos.posX;
int posZ = levelPos.posZ;
byte detailLevel = levelPos.detailLevel;
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
//here i calculate the the LevelPos is in range
//This is important to avoid any kind of hole in the rendering
int maxDistance = levelPos.maxDistance(playerPosX, playerPosZ, regionPosX, regionPosZ);
int maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
byte supposedLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDistanceRenderingInverse(maxDistance));
if (supposedLevel > detailLevel)
return;
else if (supposedLevel == detailLevel)
{
if (dataToRender.containsKey(levelPos))
{
levelPos.changeParameters(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
try
{
dataToRender.get(levelPos).setTrue();
}catch (Exception e){
/*TODO Fix this exception*/
// This seems to happen more often when using an elytra in an amplified world
// maybe it has something to do with the dimensions moving?
ClientProxy.LOGGER.error("getDataToRender had a error at " + levelPos.getRegionPos() + ". Exception: " + e.getMessage());
dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new MutableBoolean(true));
}
} else
{
dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new MutableBoolean(true));
}
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
} else //case where (detailLevel > supposedLevel)
{
int childPosX = posX * 2;
int childPosZ = posZ * 2;
byte childDetailLevel = (byte) (detailLevel - 1);
int childrenCount = 0;
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
if (doesDataExist(levelPos)) childrenCount++;
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z)) childrenCount++;
}
}
@@ -344,68 +242,50 @@ public class LodRegion implements Serializable
{
for (int z = 0; z <= 1; z++)
{
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ);
getDataToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
}
}
} else
{
levelPos.changeParameters(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
if (dataToRender.containsKey(levelPos))
{
if (dataToRender.get(levelPos) == null)
dataToRender.replace(levelPos, new MutableBoolean());
dataToRender.get(levelPos).setTrue();
} else
{
dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new MutableBoolean(true));
}
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
}
}
}
/**
* @param levelPos
*/
public void updateArea(LevelPos levelPos)
public void updateArea(byte detailLevel, int posX, int posZ)
{
int width;
int startX;
int startZ;
byte detailLevel = levelPos.detailLevel;
int posX = levelPos.posX;
int posZ = levelPos.posZ;
for (byte bottom = (byte) (minDetailLevel + 1); bottom <= detailLevel; bottom++)
{
levelPos.convert(bottom);
startX = levelPos.posX;
startZ = levelPos.posZ;
startX = LevelPosUtil.convert(detailLevel, posX, bottom);
startZ = LevelPosUtil.convert(detailLevel, posZ, bottom);
width = 1 << (detailLevel - bottom);
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
levelPos.changeParameters(bottom, startX + x, startZ + z);
update(levelPos);
update(bottom, startX + x, startZ + z);
}
}
levelPos.changeParameters(detailLevel, posX, posZ);
}
for (byte tempLod = (byte) (detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
for (byte up = (byte) (detailLevel + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++)
{
levelPos.convert(tempLod);
update(levelPos);
update(up,
LevelPosUtil.convert(detailLevel, posX, up),
LevelPosUtil.convert(detailLevel, posZ, up));
}
}
/**
* @param levelPos
*/
private void update(LevelPos levelPos)
private void update(byte detailLevel, int posX, int posZ)
{
levelPos.performRegionModule();
int numberOfChildren = 0;
int numberOfVoidChildren = 0;
@@ -414,32 +294,30 @@ public class LodRegion implements Serializable
int tempBlue = 0;
int tempHeight = 0;
int tempDepth = 0;
int newPosX;
int newPosZ;
byte newDetailLevel;
int detailLevel = levelPos.detailLevel;
int posX = levelPos.posX;
int posZ = levelPos.posZ;
int childPosX;
int childPosZ;
byte childDetailLevel;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
newPosX = 2 * posX + x;
newPosZ = 2 * posZ + z;
newDetailLevel = (byte) (detailLevel - 1);
levelPos.changeParameters(newDetailLevel, newPosX, newPosZ);
if (doesDataExist(levelPos))
childPosX = 2 * posX + x;
childPosZ = 2 * posZ + z;
childDetailLevel = (byte) (detailLevel - 1);
if (doesDataExist(childDetailLevel, childPosX, childPosZ))
{
if (height[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_HEIGHT
&& depth[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_DEPTH)
if (!(DataPoint.getHeight(data[childDetailLevel][childPosX][childPosZ]) == LodBuilder.DEFAULT_HEIGHT
&& DataPoint.getDepth(data[childDetailLevel][childPosX][childPosZ]) == LodBuilder.DEFAULT_DEPTH))
{
numberOfChildren++;
tempRed += colors[newDetailLevel][newPosX][newPosZ][0];
tempGreen += colors[newDetailLevel][newPosX][newPosZ][1];
tempBlue += colors[newDetailLevel][newPosX][newPosZ][2];
tempHeight += height[newDetailLevel][newPosX][newPosZ];
tempDepth += depth[newDetailLevel][newPosX][newPosZ];
tempRed += DataPoint.getRed(data[childDetailLevel][childPosX][childPosZ]);
tempGreen += DataPoint.getGreen(data[childDetailLevel][childPosX][childPosZ]);
tempBlue += DataPoint.getBlue(data[childDetailLevel][childPosX][childPosZ]);
tempHeight += DataPoint.getHeight(data[childDetailLevel][childPosX][childPosZ]);
tempDepth += DataPoint.getDepth(data[childDetailLevel][childPosX][childPosZ]);
} else
{
// void children have the default height (most likely -1)
@@ -449,42 +327,34 @@ public class LodRegion implements Serializable
}
}
}
if (numberOfChildren > 0)
{
colors[detailLevel][posX][posZ][0] = (byte) (tempRed / numberOfChildren);
colors[detailLevel][posX][posZ][1] = (byte) (tempGreen / numberOfChildren);
colors[detailLevel][posX][posZ][2] = (byte) (tempBlue / numberOfChildren);
height[detailLevel][posX][posZ] = (short) (tempHeight / numberOfChildren);
depth[detailLevel][posX][posZ] = (short) (tempDepth / numberOfChildren);
dataExistence[detailLevel][posX][posZ] = true;
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempHeight = tempHeight / numberOfChildren;
tempDepth = tempDepth / numberOfChildren;
} else if (numberOfVoidChildren > 0)
{
colors[detailLevel][posX][posZ][0] = (byte) 0;
colors[detailLevel][posX][posZ][1] = (byte) 0;
colors[detailLevel][posX][posZ][2] = (byte) 0;
height[detailLevel][posX][posZ] = LodBuilder.DEFAULT_HEIGHT;
depth[detailLevel][posX][posZ] = LodBuilder.DEFAULT_DEPTH;
dataExistence[detailLevel][posX][posZ] = true;
tempRed = (byte) 0;
tempGreen = (byte) 0;
tempBlue = (byte) 0;
tempHeight = LodBuilder.DEFAULT_HEIGHT;
tempDepth = LodBuilder.DEFAULT_DEPTH;
}
data[detailLevel][posX][posZ] = DataPoint.createDataPoint(tempHeight, tempDepth, tempRed, tempGreen, tempBlue);
}
/**
* @param levelPos
* @return
*/
public boolean doesDataExist(LevelPos levelPos)
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
{
try
{
levelPos = levelPos.getRegionModuleLevelPos();
return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ];
} catch (NullPointerException e)
{
return false;
}
if(detailLevel < minDetailLevel) return false;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return DataPoint.doesItExist(data[detailLevel][posX][posZ]);
}
/**
@@ -512,7 +382,7 @@ public class LodRegion implements Serializable
{
throw new IllegalArgumentException("getLevel asked for a level that does not exist: minimum " + minDetailLevel + " level requested " + detailLevel);
}
return new LevelContainer(detailLevel, colors[detailLevel], height[detailLevel], depth[detailLevel], dataExistence[detailLevel]);
return new LevelContainer(detailLevel, data[detailLevel]);
}
/**
@@ -525,10 +395,7 @@ public class LodRegion implements Serializable
throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 ");
}
if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel;
colors[levelContainer.detailLevel] = levelContainer.colors;
height[levelContainer.detailLevel] = levelContainer.height;
depth[levelContainer.detailLevel] = levelContainer.depth;
dataExistence[levelContainer.detailLevel] = levelContainer.dataExistence;
data[levelContainer.detailLevel] = levelContainer.data;
}
@@ -541,10 +408,7 @@ public class LodRegion implements Serializable
{
for (byte tempLod = 0; tempLod < detailLevel; tempLod++)
{
colors[tempLod] = new byte[0][0][0];
height[tempLod] = new short[0][0];
depth[tempLod] = new short[0][0];
dataExistence[tempLod] = new boolean[0][0];
data[tempLod] = new long[0][0];
}
minDetailLevel = detailLevel;
}
@@ -560,10 +424,7 @@ public class LodRegion implements Serializable
for (byte tempLod = detailLevel; tempLod < minDetailLevel; tempLod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod);
colors[tempLod] = new byte[size][size][3];
height[tempLod] = new short[size][size];
depth[tempLod] = new short[size][size];
dataExistence[tempLod] = new boolean[size][size];
data[tempLod] = new long[size][size];
}
minDetailLevel = detailLevel;
}
@@ -1,7 +1,5 @@
package com.seibel.lod.objects;
import org.lwjgl.system.CallbackI;
public class PosToGenerateContainer
{
private int playerPosX;
@@ -27,20 +25,10 @@ public class PosToGenerateContainer
posToGenerate = new int[maxDataToGenerate][4];
}
public void addPosToGenerate(int[] levelPos)
{
addPosToGenerate(LevelPosUtil.getDetailLevel(levelPos), LevelPosUtil.getPosX(levelPos), LevelPosUtil.getPosZ(levelPos));
}
public void addPosToGenerate(byte detailLevel, int posX, int posZ)
{
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
int index;
int[] tempPos = new int[]{
detailLevel,
posX,
posZ,
distance};
if (detailLevel >= farMinDetail)
{//We are introducing a position in the far array
if (farSize < maxFarSize)
@@ -53,26 +41,42 @@ public class PosToGenerateContainer
maxNearSize--;
}
index = posToGenerate.length - farSize;
while (index < posToGenerate.length - 1 && LevelPosUtil.compareLevelAndDistance(tempPos, posToGenerate[index + 1]) <= 0)
while (index < posToGenerate.length - 1 && LevelPosUtil.compareLevelAndDistance(detailLevel, distance, (byte) (posToGenerate[index + 1][0] - 1), posToGenerate[index + 1][3]) <= 0)
{
posToGenerate[index] = posToGenerate[index + 1];
posToGenerate[index][0] = posToGenerate[index + 1][0];
posToGenerate[index][1] = posToGenerate[index + 1][1];
posToGenerate[index][2] = posToGenerate[index + 1][2];
posToGenerate[index][3] = posToGenerate[index + 1][3];
index++;
}
if (index <= posToGenerate.length - 1)
posToGenerate[index] = tempPos;
{
posToGenerate[index][0] = detailLevel + 1;
posToGenerate[index][1] = posX;
posToGenerate[index][2] = posZ;
posToGenerate[index][3] = distance;
}
} else
{//We are introducing a position in the near array
if (nearSize < maxNearSize)
nearSize++;
index = nearSize - 1;
while (index > 0 && LevelPosUtil.compareDistance(tempPos, posToGenerate[index - 1]) <= 0)
while (index > 0 && LevelPosUtil.compareDistance(distance, posToGenerate[index - 1][3]) <= 0)
{
posToGenerate[index] = posToGenerate[index - 1];
posToGenerate[index][0] = posToGenerate[index - 1][0];
posToGenerate[index][1] = posToGenerate[index - 1][1];
posToGenerate[index][2] = posToGenerate[index - 1][2];
posToGenerate[index][3] = posToGenerate[index - 1][3];
index--;
}
if (index >= 0)
posToGenerate[index] = tempPos;
{
posToGenerate[index][0] = detailLevel + 1;
posToGenerate[index][1] = posX;
posToGenerate[index][2] = posZ;
posToGenerate[index][3] = distance;
}
}
}
@@ -84,6 +88,10 @@ public class PosToGenerateContainer
public int[] getNthPos(int n)
{
/*if(n < farSize)
return posToGenerate[maxSize - n - 1];
else
return posToGenerate[n - farSize];*/
int index;
if (n > farSize * 2)
index = n - farSize;
@@ -111,7 +119,7 @@ public class PosToGenerateContainer
builder.append('\n');
for (int i = 0; i < nearSize; i++)
{
builder.append(posToGenerate[i][0]);
builder.append(posToGenerate[i][0]-1);
builder.append(" ");
builder.append(posToGenerate[i][1]);
builder.append(" ");
@@ -125,7 +133,7 @@ public class PosToGenerateContainer
builder.append('\n');
for (int i = maxSize - 1; i >= maxSize - farSize; i--)
{
builder.append(posToGenerate[i][0]);
builder.append(posToGenerate[i][0]-1);
builder.append(" ");
builder.append(posToGenerate[i][1]);
builder.append(" ");
@@ -1,40 +1,75 @@
package com.seibel.lod.objects;
import com.seibel.lod.util.LodUtil;
import java.util.Arrays;
public class PosToRenderContainer
{
private byte minDetail;
public byte minDetail;
private int regionPosX;
private int regionPosZ;
private int numberOfPosToRender;
private int[][] posToRender;
/*TODO this population matrix could be converted to boolean to improve memory use*/
private byte[][] population;
public PosToRenderContainer(byte minDetail)
public PosToRenderContainer(byte minDetail, int regionPosX, int regionPosZ)
{
this.minDetail = minDetail;
this.numberOfPosToRender = 0;
posToRender = new int[1][4];
this.regionPosX = regionPosX;
this.regionPosZ = regionPosZ;
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
posToRender = new int[size*size][3];
population = new byte[size][size];
}
public void addPosToRender(int[] levelPos)
public void addPosToRender(byte detailLevel, int posX, int posZ)
{
if(numberOfPosToRender >= posToRender.length)
posToRender = Arrays.copyOf(posToRender, posToRender.length*2);
posToRender[numberOfPosToRender] = levelPos;
//if(numberOfPosToRender >= posToRender.length)
// posToRender = Arrays.copyOf(posToRender, posToRender.length*2);
posToRender[numberOfPosToRender][0] = detailLevel;
posToRender[numberOfPosToRender][1] = posX;
posToRender[numberOfPosToRender][2] = posZ;
numberOfPosToRender++;
int[] newLevelPos = LevelPosUtil.getRegionModule(LevelPosUtil.convert(levelPos, minDetail));
population[LevelPosUtil.getPosZ(newLevelPos)][LevelPosUtil.getPosZ(newLevelPos)] = (byte) (LevelPosUtil.getDetailLevel(levelPos) + 1);
population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posX,minDetail))]
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posZ,minDetail))] = (byte) (detailLevel + 1);
}
public boolean contains(int[] levelPos)
public boolean contains(byte detailLevel, int posX, int posZ)
{
int[] newLevelPos = LevelPosUtil.convert(LevelPosUtil.getRegionModule(levelPos), minDetail);
return (population[LevelPosUtil.getPosZ(newLevelPos)][LevelPosUtil.getPosZ(newLevelPos)] == (LevelPosUtil.getDetailLevel(levelPos) + 1));
if(LevelPosUtil.getRegion(detailLevel, posX) == regionPosX && LevelPosUtil.getRegion(detailLevel, posZ) == regionPosZ)
{
return (population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posX,minDetail))]
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posZ,minDetail))] == (detailLevel + 1));
}else
{
return false;
}
}
public void clear(byte minDetail, int regionPosX, int regionPosZ){
this.numberOfPosToRender = 0;
this.regionPosX = regionPosX;
this.regionPosZ = regionPosZ;
if(this.minDetail == minDetail)
{
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
posToRender[0][0] = 0;
posToRender[0][1] = 0;
posToRender[0][2] = 0;
population[x][z] = 0;
}
}
}else{
this.minDetail = minDetail;
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
posToRender = new int[size*size][3];
population = new byte[size][size];
}
}
public int getNumberOfPos()
@@ -42,6 +77,19 @@ public class PosToRenderContainer
return numberOfPosToRender;
}
public byte getNthDetailLevel(int n)
{
return (byte) posToRender[n][0];
}
public int getNthPosX(int n)
{
return posToRender[n][1];
}
public int getNthPosZ(int n)
{
return posToRender[n][2];
}
public int[] getNthPos(int n)
{
return posToRender[n];
@@ -51,6 +99,9 @@ public class PosToRenderContainer
{
StringBuilder builder = new StringBuilder();
builder.append("To render ");
builder.append(numberOfPosToRender);
builder.append('\n');
for(int i = 0; i < numberOfPosToRender; i++)
{
builder.append(posToRender[i][0]);
@@ -23,6 +23,7 @@ import java.nio.FloatBuffer;
import java.util.HashSet;
import java.util.Iterator;
import com.seibel.lod.objects.LevelPosUtil;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.NVFogDistance;
@@ -41,7 +42,6 @@ import com.seibel.lod.handlers.ReflectionHandler;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.NearFarFogSettings;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
@@ -57,8 +57,8 @@ import net.minecraft.entity.Entity;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
@@ -70,7 +70,7 @@ import net.minecraft.util.math.vector.Vector3f;
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 9-6-2021
* @version 8-31-2021
*/
public class LodRenderer
{
@@ -79,7 +79,7 @@ public class LodRenderer
* it should be something different than what is used by Minecraft
*/
private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2;
/**
* 64 MB by default is the maximum amount of memory that
* can be directly allocated. <br><br>
@@ -92,78 +92,78 @@ public class LodRenderer
* https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx
*/
public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
/**
* Does this computer's GPU support fancy fog?
*/
private static Boolean fancyFogAvailable = null;
/**
* If true the LODs colors will be replaced with
* a checkerboard, this can be used for debugging.
*/
public DebugMode previousDebugMode = DebugMode.OFF;
private MinecraftWrapper mc;
private GameRenderer gameRender;
private IProfiler profiler;
private int farPlaneBlockDistance;
private ReflectionHandler reflectionHandler;
/**
* This is used to generate the buildable buffers
*/
private LodBufferBuilder lodBufferBuilder;
/**
* Each VertexBuffer represents 1 region
*/
private VertexBuffer[][] vbos;
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
private ChunkPos vbosCenter = new ChunkPos(0,0);
/**
* This is used to determine if the LODs should be regenerated
*/
private LevelPos previousPos = new LevelPos((byte) 0, 0, 0);
private int[] previousPos = new int[]{0,0,0};
private int prevRenderDistance = 0;
private long prevPlayerPosTime = 0;
private long prevVanillaChunkTime = 0;
private long prevChunkTime = 0;
/**
* This is used to determine if the LODs should be regenerated
*/
private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR;
/**
* if this is true the LOD buffers should be regenerated,
* provided they aren't already being regenerated.
*/
private volatile boolean partialRegen = false;
private volatile boolean fullRegen = true;
/**
* This HashSet contains every chunk that Vanilla Minecraft
* is going to render
*/
public boolean[][] vanillaRenderedChunks;
public boolean vanillaRenderedChunksChanged;
public LodRenderer(LodBufferBuilder newLodNodeBufferBuilder)
{
mc = MinecraftWrapper.INSTANCE;
gameRender = mc.getGameRenderer();
reflectionHandler = new ReflectionHandler();
lodBufferBuilder = newLodNodeBufferBuilder;
}
/**
* Besides drawing the LODs this method also starts
* the async process of generating the Buffers that hold those LODs.
@@ -179,37 +179,37 @@ public class LodRenderer
// don't try drawing anything
return;
}
//===============//
// initial setup //
//===============//
profiler = newProfiler;
profiler.push("LOD setup");
// only check the GPU capability's once
if (fancyFogAvailable == null)
{
// see if this GPU can run fancy fog
fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
if (!fancyFogAvailable)
{
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that fancy fog options will not be available.");
}
}
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// starting here...
determineIfLodsShouldRegenerate(lodDim);
//=================//
// create the LODs //
//=================//
// only regenerate the LODs if:
// 1. we want to regenerate LODs
// 2. we aren't already regenerating the LODs
@@ -219,78 +219,87 @@ public class LodRenderer
{
// generate the LODs on a separate thread to prevent stuttering or freezing
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayer().blockPosition(), true);
// the regen process has been started,
// it will be done when lodBufferBuilder.newBuffersAvaliable()
// is true
fullRegen = false;
partialRegen = false;
}
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// ...ending here
// replace the buffers used to draw and build,
// this is only done when the createLodBufferGenerationThread
// has finished executing on a parallel thread.
if (lodBufferBuilder.newBuffersAvaliable())
{
swapBuffers();
}
//===========================//
// GL settings for rendering //
//===========================//
// set the required open GL settings
if (LodConfig.CLIENT.debugging.debugMode.get() == DebugMode.SHOW_DETAIL_WIREFRAME)
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
else
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glEnable(GL11.GL_DEPTH_TEST);
// disable the lights Minecraft uses
GL11.glDisable(GL11.GL_LIGHT0);
GL11.glDisable(GL11.GL_LIGHT1);
// get the default projection matrix so we can
// reset it after drawing the LODs
float[] defaultProjMatrix = new float[16];
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, defaultProjMatrix);
Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks);
// required for setupFog and setupProjectionMatrix
farPlaneBlockDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
setupProjectionMatrix(partialTicks);
setupLighting(lodDim, partialTicks);
NearFarFogSettings fogSettings = determineFogSettings();
// determine the current fog settings so they can be
// reset after drawing the LODs
float defaultFogStartDist = GL11.glGetFloat(GL11.GL_FOG_START);
float defaultFogEndDist = GL11.glGetFloat(GL11.GL_FOG_END);
int defaultFogMode = GL11.glGetInteger(GL11.GL_FOG_MODE);
int defaultFogDistance = GL11.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV);
//===========//
// rendering //
//===========//
profiler.popPush("LOD draw");
if (vbos != null)
{
Entity cameraEntity = mc.getCameraEntity();
Vector3d cameraDir = cameraEntity.getLookAngle().normalize();
cameraDir = mc.getOptions().getCameraType().isMirrored() ? cameraDir.reverse() : cameraDir;
// used to determine what type of fog to render
int halfWidth = vbos.length / 2;
int quarterWidth = vbos.length / 4;
for (int i = 0; i < vbos.length; i++)
{
for (int j = 0; j < vbos.length; j++)
@@ -302,21 +311,21 @@ public class LodRenderer
setupFog(fogSettings.near.distance, fogSettings.near.quality);
else
setupFog(fogSettings.far.distance, fogSettings.far.quality);
sendLodsToGpuAndDraw(vbos[i][j], modelViewMatrix);
}
}
}
}
//=========//
// cleanup //
//=========//
profiler.popPush("LOD cleanup");
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(LOD_GL_LIGHT_NUMBER);
@@ -324,23 +333,23 @@ public class LodRenderer
GL11.glEnable(GL11.GL_LIGHT0);
GL11.glEnable(GL11.GL_LIGHT1);
RenderSystem.disableLighting();
// reset the fog settings so the normal chunks
// will be drawn correctly
cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance);
// reset the projection matrix so anything drawn after
// the LODs will use the correct projection matrix
Matrix4f mvm = new Matrix4f(defaultProjMatrix);
mvm.transpose();
gameRender.resetProjectionMatrix(mvm);
// clear the depth buffer so anything drawn is drawn
// over the LODs
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
// replace the buffers used to draw and build,
// this is only done when the createLodBufferGenerationThread
// has finished executing on a parallel thread.
@@ -350,13 +359,13 @@ public class LodRenderer
// otherwise rubber banding may occur
swapBuffers();
}
// end of internal LOD profiling
profiler.pop();
}
/**
* This is where the actual drawing happens.
*/
@@ -364,22 +373,22 @@ public class LodRenderer
{
if (vbo == null)
return;
vbo.bind();
// 0L is the starting pointer
LOD_VERTEX_FORMAT.setupBufferState(0L);
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
VertexBuffer.unbind();
LOD_VERTEX_FORMAT.clearBufferState();
}
//=================//
// Setup Functions //
//=================//
@SuppressWarnings("deprecation")
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
{
@@ -389,12 +398,12 @@ public class LodRenderer
RenderSystem.disableFog();
return;
}
if (fogDistance == FogDistance.NEAR_AND_FAR)
{
throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance.");
}
// determine the fog distance mode to use
int glFogDistanceMode;
if (fogQuality == FogQuality.FANCY)
@@ -407,7 +416,7 @@ public class LodRenderer
// fast fog (frustum distance based fog)
glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV;
}
// the multipliers are percentages
// of the regular view distance.
if (fogDistance == FogDistance.FAR)
@@ -416,7 +425,7 @@ public class LodRenderer
// is because we are using fog backwards to how
// it is normally used, with it hiding near objects
// instead of far objects.
if (fogQuality == FogQuality.FANCY)
{
// for more realistic fog when using FAR
@@ -451,37 +460,37 @@ public class LodRenderer
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.5f);
}
}
GL11.glEnable(GL11.GL_FOG);
RenderSystem.enableFog();
RenderSystem.setupNvFogDistance();
RenderSystem.fogMode(GlStateManager.FogMode.LINEAR);
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode);
}
/**
* Revert any changes that were made to the fog.
*/
private void cleanupFog(NearFarFogSettings fogSettings,
float defaultFogStartDist, float defaultFogEndDist,
int defaultFogMode, int defaultFogDistance)
float defaultFogStartDist, float defaultFogEndDist,
int defaultFogMode, int defaultFogDistance)
{
RenderSystem.fogStart(defaultFogStartDist);
RenderSystem.fogEnd(defaultFogEndDist);
RenderSystem.fogMode(defaultFogMode);
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, defaultFogDistance);
// disable fog if Minecraft wasn't rendering fog
// but we were
if (!fogSettings.vanillaIsRenderingFog &&
(fogSettings.near.quality != FogQuality.OFF ||
fogSettings.far.quality != FogQuality.OFF))
(fogSettings.near.quality != FogQuality.OFF ||
fogSettings.far.quality != FogQuality.OFF))
{
GL11.glDisable(GL11.GL_FOG);
}
}
/**
* Create the model view matrix to move the LODs
* from object space into world space.
@@ -491,8 +500,8 @@ public class LodRenderer
// get all relevant camera info
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
Vector3d projectedView = renderInfo.getPosition();
// generate the model view matrix
MatrixStack matrixStack = new MatrixStack();
matrixStack.pushPose();
@@ -507,11 +516,11 @@ public class LodRenderer
double xDiff = eyePos.x - bufferPos.getX();
double zDiff = eyePos.z - bufferPos.getZ();
matrixStack.translate(-xDiff, -projectedView.y, -zDiff);
return matrixStack.last().pose();
}
/**
* create a new projection matrix and send it over to the GPU
* <br><br>
@@ -529,16 +538,16 @@ public class LodRenderer
// Note: if the LOD objects don't distort correctly
// compared to regular minecraft terrain, make sure
// all the transformations in renderWorld are here too
MatrixStack matrixStack = new MatrixStack();
matrixStack.pushPose();
gameRender.bobHurt(matrixStack, partialTicks);
if (this.mc.getOptions().bobView)
{
gameRender.bobView(matrixStack, partialTicks);
}
// potion and nausea effects
float f = MathHelper.lerp(partialTicks, this.mc.getPlayer().oPortalTime, this.mc.getPlayer().portalTime) * this.mc.getOptions().screenEffectScale * this.mc.getOptions().screenEffectScale;
if (f > 0.0F)
@@ -552,8 +561,8 @@ public class LodRenderer
float f2 = -(gameRender.tick + partialTicks) * i;
matrixStack.mulPose(vector3f.rotationDegrees(f2));
}
// this projection matrix allows us to see past the normal
// world render distance
Matrix4f projectionMatrix =
@@ -565,14 +574,14 @@ public class LodRenderer
// terrain, so I don't think it is much of an issue.
mc.getRenderDistance(),
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2);
// add the screen space distortions
projectionMatrix.multiply(matrixStack.last().pose());
gameRender.resetProjectionMatrix(projectionMatrix);
return;
}
/**
* setup the lighting to be used for the LODs
*/
@@ -593,27 +602,27 @@ public class LodRenderer
}
}
}
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f;
sunBrightness = playerHasNightVision ? 1.0f : sunBrightness;
float gammaMultiplyer = (float) mc.getOptions().gamma - 0.5f;
float lightStrength = ((sunBrightness / 2f) - 0.2f) + (gammaMultiplyer * 0.3f);
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
// can be used for debugging
// if (partialTicks < 0.005)
// ClientProxy.LOGGER.debug(lightStrength);
ByteBuffer temp = ByteBuffer.allocateDirect(16);
temp.order(ByteOrder.nativeOrder());
GL11.glLightfv(LOD_GL_LIGHT_NUMBER, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
GL11.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
RenderSystem.enableLighting();
}
/**
* Create all buffers that will be used.
*/
@@ -621,24 +630,24 @@ public class LodRenderer
{
// calculate the max amount of memory needed (in bytes)
int bufferMemory = RenderUtil.getBufferMemoryForRegion();
// if the required memory is greater than the
// MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier
// to fit.
if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY)
{
ClientProxy.LOGGER.warn("setupBuffers tried to allocate too much memory for the BufferBuilders."
+ " It tried to allocate \"" + bufferMemory + "\" bytes, when \"" + MAX_ALOCATEABLE_DIRECT_MEMORY + "\" is the max.");
+ " It tried to allocate \"" + bufferMemory + "\" bytes, when \"" + MAX_ALOCATEABLE_DIRECT_MEMORY + "\" is the max.");
}
lodBufferBuilder.setupBuffers(numbRegionsWide, bufferMemory);
}
//======================//
// Other Misc Functions //
//======================//
/**
* If this is called then the next time "drawLODs" is called
* the LODs will be regenerated; the same as if the player moved.
@@ -647,12 +656,12 @@ public class LodRenderer
{
fullRegen = true;
}
/**
* Replace the current Vertex Buffers with the newly
* created buffers from the lodBufferBuilder. <br><br>
*
*
* For some reason this has to be called after the frame has been rendered,
* otherwise visual stuttering/rubber banding may happen. I'm not sure why...
*/
@@ -664,7 +673,7 @@ public class LodRenderer
vbos = result.vbos;
vbosCenter = result.drawableCenterChunkPos;
}
/**
* Calls the BufferBuilder's destroyBuffers method.
*/
@@ -672,182 +681,182 @@ public class LodRenderer
{
lodBufferBuilder.destroyBuffers();
}
private double getFov(float partialTicks, boolean useFovSetting)
{
return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting);
}
/**
* Return what fog settings should be used when rendering.
*/
private NearFarFogSettings determineFogSettings()
{
NearFarFogSettings fogSettings = new NearFarFogSettings();
FogQuality quality = reflectionHandler.getFogQuality();
FogDrawOverride override = LodConfig.CLIENT.graphics.fogDrawOverride.get();
if (quality == FogQuality.OFF)
fogSettings.vanillaIsRenderingFog = false;
else
fogSettings.vanillaIsRenderingFog = true;
// use any fog overrides the user may have set
switch (override)
{
case ALWAYS_DRAW_FOG_FANCY:
quality = FogQuality.FANCY;
break;
case NEVER_DRAW_FOG:
quality = FogQuality.OFF;
break;
case ALWAYS_DRAW_FOG_FAST:
quality = FogQuality.FAST;
break;
case USE_OPTIFINE_FOG_SETTING:
// don't override anything
break;
case ALWAYS_DRAW_FOG_FANCY:
quality = FogQuality.FANCY;
break;
case NEVER_DRAW_FOG:
quality = FogQuality.OFF;
break;
case ALWAYS_DRAW_FOG_FAST:
quality = FogQuality.FAST;
break;
case USE_OPTIFINE_FOG_SETTING:
// don't override anything
break;
}
// only use fancy fog if the user's GPU can deliver
if (!fancyFogAvailable && quality == FogQuality.FANCY)
{
quality = FogQuality.FAST;
}
// how different distances are drawn depends on the quality set
switch (quality)
{
case FANCY:
fogSettings.near.quality = FogQuality.FANCY;
fogSettings.far.quality = FogQuality.FANCY;
switch (LodConfig.CLIENT.graphics.fogDistance.get())
{
case NEAR_AND_FAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.FAR;
case FANCY:
fogSettings.near.quality = FogQuality.FANCY;
fogSettings.far.quality = FogQuality.FANCY;
switch (LodConfig.CLIENT.graphics.fogDistance.get())
{
case NEAR_AND_FAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.FAR;
break;
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
break;
}
break;
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
case FAST:
fogSettings.near.quality = FogQuality.FAST;
fogSettings.far.quality = FogQuality.FAST;
// fast fog setting should only have one type of
// fog, since the LODs are separated into a near
// and far portion; and fast fog is rendered from the
// frustrum's perspective instead of the camera
switch (LodConfig.CLIENT.graphics.fogDistance.get())
{
case NEAR_AND_FAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
break;
}
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
case OFF:
fogSettings.near.quality = FogQuality.OFF;
fogSettings.far.quality = FogQuality.OFF;
break;
}
break;
case FAST:
fogSettings.near.quality = FogQuality.FAST;
fogSettings.far.quality = FogQuality.FAST;
// fast fog setting should only have one type of
// fog, since the LODs are separated into a near
// and far portion; and fast fog is rendered from the
// frustrum's perspective instead of the camera
switch (LodConfig.CLIENT.graphics.fogDistance.get())
{
case NEAR_AND_FAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
break;
}
break;
case OFF:
fogSettings.near.quality = FogQuality.OFF;
fogSettings.far.quality = FogQuality.OFF;
break;
}
return fogSettings;
}
/**
* Determines if the LODs should have a fullRegen or partialRegen
*/
@SuppressWarnings("unchecked")
private void determineIfLodsShouldRegenerate(LodDimension lodDim)
{
short renderDistance = (short) mc.getRenderDistance();
//=============//
// full regens //
//=============//
// check if the view distance changed
if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.lodChunkRenderDistance.get()
|| mc.getRenderDistance() != prevRenderDistance
|| prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get())
|| mc.getRenderDistance() != prevRenderDistance
|| prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get())
{
DetailDistanceUtil.updateSettings();
fullRegen = true;
previousPos.changeParameters((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
prevFogDistance = LodConfig.CLIENT.graphics.fogDistance.get();
prevRenderDistance = mc.getRenderDistance();
//should use this when it's ready
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
}
// did the user change the debug setting?
if (LodConfig.CLIENT.debugging.debugMode.get() != previousDebugMode)
{
previousDebugMode = LodConfig.CLIENT.debugging.debugMode.get();
fullRegen = true;
}
long newTime = System.currentTimeMillis();
// check if the player has moved
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get())
{
if (previousPos.detailLevel == 0
|| mc.getPlayer().xChunk != previousPos.posX
|| mc.getPlayer().zChunk != previousPos.posZ)
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
|| mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos))
{
fullRegen = true;
previousPos.changeParameters((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
//should use this when it's ready
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
}
prevPlayerPosTime = newTime;
}
//================//
// partial regens //
//================//
// check if the vanilla rendered chunks changed
if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.get())
{
@@ -855,12 +864,12 @@ public class LodRenderer
{
partialRegen = true;
vanillaRenderedChunksChanged = false;
}
prevVanillaChunkTime = newTime;
}
// check if there is any newly generated terrain to show
if (newTime - prevChunkTime > LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.get())
{
@@ -871,14 +880,14 @@ public class LodRenderer
}
prevChunkTime = newTime;
}
//==============//
// LOD skipping //
//==============//
// determine which LODs should not be rendered close to the player
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition());
int chunkX;
@@ -900,8 +909,8 @@ public class LodRenderer
e.printStackTrace();
}
}
// if the player is high enough, draw all LODs
if(chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256)
{
@@ -909,5 +918,5 @@ public class LodRenderer
vanillaRenderedChunksChanged = true;
}
}
}
}
@@ -19,7 +19,7 @@ public class DetailDistanceUtil
private static int base = 2;
private static double logBase = Math.log(2);
private static LodDetail[] lodDetails = {
private static LodDetail[] lodGenDetails = {
LodDetail.FULL,
LodDetail.HALF,
LodDetail.QUAD,
@@ -33,6 +33,7 @@ public class DetailDistanceUtil
LodDetail.SINGLE};
public static void updateSettings(){
minGenDetail = LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel;
minDrawDetail = Math.max(LodConfig.CLIENT.graphics.maxDrawDetail.get().detailLevel,LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel);
@@ -153,10 +154,10 @@ public class DetailDistanceUtil
{
if (detail < minGenDetail)
{
return lodDetails[minGenDetail];
return lodGenDetails[minGenDetail];
} else
{
return lodDetails[detail];
return lodGenDetails[detail];
}
}
@@ -165,13 +166,13 @@ public class DetailDistanceUtil
{
if (detail < minGenDetail)
{
return lodDetails[minGenDetail].detailLevel;
return lodGenDetails[minGenDetail].detailLevel;
} else if (detail == maxDetail)
{
return LodUtil.REGION_DETAIL_LEVEL;
} else
{
return lodDetails[detail].detailLevel;
return lodGenDetails[detail].detailLevel;
}
}
@@ -21,9 +21,9 @@ import java.awt.Color;
import java.io.File;
import java.util.HashSet;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.wrapper.MinecraftWrapper;
import it.unimi.dsi.fastutil.objects.ObjectList;
@@ -324,13 +324,13 @@ public class LodUtil
{
for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
{
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, x, z);
if (!lodDim.doesDataExist(levelPos.clone()))
if (!lodDim.doesDataExist(LodUtil.CHUNK_DETAIL_LEVEL, x, z))
continue;
short[] data = lodDim.getData(levelPos);
short lodAverageHeight = data[0];
long data = lodDim.getData(LodUtil.CHUNK_DETAIL_LEVEL, x, z);
short lodAverageHeight = DataPoint.getHeight(data);
if (playerPos.getY() <= lodAverageHeight)
{
// don't draw Lod's that are taller than the player