Compare commits

..

31 Commits

Author SHA1 Message Date
James Seibel b1bcdb24f0 comment out the config override for release a1.4.1 2021-09-08 18:30:37 -05:00
James Seibel cbd7f4300e Improve logging wording 2021-09-08 18:17:36 -05:00
James Seibel 4d8e30121e fix a spelling mistake 2021-09-08 18:17:17 -05:00
James Seibel fbe39542fc update the version number to a1.4.1 2021-09-08 18:13:25 -05:00
James Seibel 6a1f448b41 comment out testing code 2021-09-08 18:13:06 -05:00
James Seibel e13b23832c Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-08 18:09:15 -05:00
James Seibel 4c9e0edf32 Add barrier blocks to the list of blocks to ignore 2021-09-08 18:08:59 -05:00
Leonardo 8664a06d3b fixed a small bug that was causing visual artifacts 2021-09-08 19:38:17 +02:00
James Seibel 18b8359f0d rename the wrapper package to wrappers 2021-09-07 21:12:58 -05:00
James Seibel 7de4c7c72a Make the reflection handler a singleton 2021-09-07 21:12:16 -05:00
James Seibel b4bbabae42 enable debugging settings in ClientProxy 2021-09-07 19:52:27 -05:00
James Seibel 40321d6e21 Remove unused variables, imports, etc. 2021-09-07 19:52:06 -05:00
James Seibel cf1702bd0c Fix file reading/writing not working, Closes #61 2021-09-07 19:38:41 -05:00
James Seibel a9b50cdb32 remove invalid imports / organize imports 2021-09-07 19:10:30 -05:00
James Seibel 7eed472029 Fix stuttering caused by a merge 2021-09-07 19:09:50 -05:00
James Seibel 0262e452c7 Improve FileHandler info message 2021-09-07 19:04:48 -05:00
James Seibel 30287d5a7d Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-07 18:54:41 -05:00
James Seibel 83346fed1f Improve the error messages in the LodDimensionFileHandler 2021-09-07 18:54:24 -05:00
Leonardo e897c3edba fixed generation 2021-09-08 00:51:39 +02:00
Leonardo bd9e2acaf6 Changed the default heigth and depth and small fix to the culling 2021-09-08 00:23:44 +02:00
Leonardo 1e15d372c4 Added box caching 2021-09-07 21:22:12 +02:00
Leonardo 849d563425 Added box to replace AxisAlignedBB 2021-09-07 20:07:39 +02:00
Leonardo aa5a8aa3b8 small change 2021-09-07 19:05:58 +02:00
Leonardo b096cc53aa Working version 2021-09-07 19:02:51 +02:00
Leonardo 94d994e761 re-added the commit 2021-09-07 18:52:58 +02:00
Leonardo f76fa17e25 Removed level pos and data arrays 2021-09-07 17:23:22 +02:00
James Seibel 53a66268cb Close issue #37 (z-fighting far from the origin)
I'm not certain why in LodRenderer swapBuffers has to be called after the frame has been rendered, but otherwise it causes stuttering / rubber banding.
2021-09-06 22:51:46 -05:00
James Seibel 139867d4b8 auto-indent 2021-09-06 12:17:49 -05:00
James Seibel 81cf05ba19 fix a issue where nothing renders in the compiled version 2021-09-06 11:55:44 -05:00
James Seibel 13a1e7ed56 Add the MinecraftWrapper 2021-09-06 11:20:32 -05:00
James Seibel 42bd0fbde9 Delete 1.4 release notes.txt 2021-09-05 17:25:46 -05:00
32 changed files with 1253 additions and 1701 deletions
Binary file not shown.
+1 -1
View File
@@ -21,7 +21,7 @@ apply plugin: 'eclipse'
apply plugin: 'maven-publish'
apply plugin: 'java' // needed for compileJava
version = 'a1.4'
version = 'a1.4.1'
group = 'com.seibel.lod'
archivesBaseName = 'lod_1.16.5'
@@ -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,21 @@ 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 org.lwjgl.opengl.GL11;
import com.seibel.lod.builders.lodTemplates.Box;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LevelPosUtil;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.PosToRenderContainer;
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;
@@ -105,9 +106,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()
{
@@ -169,17 +182,19 @@ public class LodBufferBuilder
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];
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));
}
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;
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
{
@@ -193,117 +208,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 = 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, playerBlockPos, 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;
};
@@ -367,104 +369,6 @@ public class LodBufferBuilder
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 //
//===============================//
@@ -568,10 +472,11 @@ public class LodBufferBuilder
}
/**
* Get the newly created VBOs
*/
public VertexBuffer[][] getVertexBuffers()
public VertexBuffersAndOffset getVertexBuffers()
{
// don't wait for the lock to open
// since this is called on the main render thread
@@ -581,12 +486,28 @@ public class LodBufferBuilder
drawableVbos = buildableVbos;
buildableVbos = tmp;
drawableCenterChunkPos = buildableCenterChunkPos;
// the vbos have been swapped
switchVbos = false;
bufferLock.unlock();
}
return drawableVbos;
return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos);
}
/**
* A simple container to pass multiple objects back in the getVertexBuffers method.
*/
public class VertexBuffersAndOffset
{
public VertexBuffer[][] vbos;
public ChunkPos drawableCenterChunkPos;
public VertexBuffersAndOffset(VertexBuffer[][] newVbos, ChunkPos newDrawableCenterChunkPos)
{
vbos = newVbos;
drawableCenterChunkPos = newDrawableCenterChunkPos;
}
}
/**
@@ -21,17 +21,18 @@ import java.awt.Color;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LevelPosUtil;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.objects.LevelPos.LevelPos;
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.wrappers.MinecraftWrapper;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.BlockState;
@@ -41,7 +42,6 @@ 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;
@@ -55,7 +55,7 @@ import net.minecraft.world.gen.Heightmap;
*
* @author Leonardo Amato
* @author James Seibel
* @version 8-29-2021
* @version 9-7-2021
*/
public class LodBuilder
{
@@ -68,11 +68,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
@@ -111,7 +111,7 @@ public class LodBuilder
int playerPosX;
int playerPosZ;
if (Minecraft.getInstance().player == null)
if (MinecraftWrapper.INSTANCE.getPlayer() == null)
{
playerPosX = chunk.getPos().getMinBlockX();
playerPosZ = chunk.getPos().getMinBlockZ();
@@ -132,7 +132,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
@@ -162,7 +161,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");
@@ -170,17 +168,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];
@@ -200,23 +203,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();
}
}
@@ -353,8 +356,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();
@@ -428,8 +431,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);
}
/**
@@ -440,7 +442,9 @@ public class LodBuilder
int colorInt = 0;
// block special cases
if (blockState == Blocks.AIR.defaultBlockState() || blockState == Blocks.CAVE_AIR.defaultBlockState())
if (blockState == Blocks.AIR.defaultBlockState()
|| blockState == Blocks.CAVE_AIR.defaultBlockState()
|| blockState == Blocks.BARRIER.defaultBlockState())
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
@@ -559,7 +563,8 @@ public class LodBuilder
{
if (chunkSections[sectionIndex].getBlockState(x, y, z) != null
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.CAVE_AIR)
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.CAVE_AIR
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.BARRIER)
{
return true;
}
@@ -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,14 +21,12 @@ 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;
/**
@@ -40,59 +38,65 @@ import net.minecraft.util.math.BlockPos;
public class CubicLodTemplate extends AbstractLodTemplate
{
private final int CULL_OFFSET = 16;
public CubicLodTemplate()
{
}
@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)
{
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, playerBlockPos, adjData);
addBoundingBoxToBuffer(buffer, box, color, bufferCenterBlockPos, adjData);
}
}
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset)
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++;
}
return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset);
// 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();;
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;
@@ -100,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)
{
@@ -116,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);
@@ -130,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, maxY);
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, maxY);
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, maxY);
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, maxY);
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!");
}
@@ -32,7 +32,6 @@ import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
@@ -69,7 +68,7 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker;
* This is used to generate a LodChunk at a given ChunkPos.
*
* @author James Seibel
* @version 8-26-2021
* @version 9-7-2021
*/
public class LodNodeGenWorker implements IWorker
{
@@ -86,7 +85,7 @@ public class LodNodeGenWorker implements IWorker
public LodNodeGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode, LodRenderer newLodRenderer,
public LodNodeGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
LodBuilder newLodBuilder,
LodDimension newLodDimension, ServerWorld newServerWorld)
{
@@ -94,9 +93,6 @@ public class LodNodeGenWorker implements IWorker
if (newPos == null)
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
if (newLodRenderer == null)
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null LodRenderer");
if (newLodBuilder == null)
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
@@ -108,7 +104,7 @@ public class LodNodeGenWorker implements IWorker
thread = new LodChunkGenThread(newPos, newGenerationMode, newLodRenderer,
thread = new LodChunkGenThread(newPos, newGenerationMode,
newLodBuilder,
newLodDimension, newServerWorld);
}
@@ -159,17 +155,15 @@ public class LodNodeGenWorker implements IWorker
public final LodDimension lodDim;
public final DistanceGenerationMode generationMode;
public final LodBuilder lodBuilder;
public final LodRenderer lodRenderer;
private ChunkPos pos;
public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode, LodRenderer newLodRenderer,
public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
LodBuilder newLodBuilder,
LodDimension newLodDimension, ServerWorld newServerWorld)
{
pos = newPos;
generationMode = newGenerationMode;
lodRenderer = newLodRenderer;
lodBuilder = newLodBuilder;
lodDim = newLodDimension;
serverWorld = newServerWorld;
@@ -1,33 +1,23 @@
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.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.LevelPosUtil;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.objects.PosToGenerateContainer;
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.wrappers.MinecraftWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.WorldWorkerManager;
@@ -40,7 +30,7 @@ import net.minecraftforge.common.WorldWorkerManager;
*/
public class LodWorldGenerator
{
public Minecraft mc = Minecraft.getInstance();
public MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/**
* This holds the thread used to generate new LODs off the main thread.
@@ -75,10 +65,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 +94,9 @@ public class LodWorldGenerator
try
{
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(mc.player.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 +104,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 +139,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), lodBuilder, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
@@ -155,7 +155,7 @@ public class LodConfig
lodChunkRenderDistance = builder
.comment("\n\n"
+ " This is the render distance of the mod \n")
.defineInRange("lodChunkRenderDistane", 64, 32, 512);
.defineInRange("lodChunkRenderDistance", 64, 32, 512);
shadingMode = builder
.comment("\n\n"
@@ -42,7 +42,7 @@ import com.seibel.lod.util.LodUtil;
* to file.
*
* @author James Seibel
* @version 8-24-2021
* @version 9-7-2021
*/
public class LodDimensionFileHandler
{
@@ -65,10 +65,6 @@ public class LodDimensionFileHandler
* .txt
*/
private static final String FILE_EXTENSION = ".txt";
/**
* lod/
*/
private static final String LOD_FOLDER_NAME = "lod";
/**
* detail-#
*/
@@ -88,7 +84,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
@@ -133,14 +129,14 @@ public class LodDimensionFileHandler
LodRegion region = new LodRegion(LodUtil.REGION_DETAIL_LEVEL,regionPos, generationMode);
for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--)
{
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel);
try
{
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel);
// if the fileName was null that means the folder is inaccessible
// for some reason
if (fileName == null)
throw new IllegalArgumentException("Game folder is not accessible");
throw new IllegalArgumentException("Unable to read region [" + regionX + ", " + regionZ + "] file, no fileName.");
File f = new File(fileName);
@@ -176,7 +172,7 @@ public class LodDimensionFileHandler
// close the reader and delete the file.
bufferedReader.close();
f.delete();
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version found: " + fileVersion +
", version requested: " + LOD_SAVE_FILE_VERSION +
" File was been deleted.");
@@ -187,7 +183,7 @@ public class LodDimensionFileHandler
// close the reader and ignore the file, we don't
// want to accidently delete anything the user may want.
bufferedReader.close();
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version found: " + fileVersion +
", version requested: " + LOD_SAVE_FILE_VERSION +
" this region will not be written to in order to protect the newer file.");
@@ -209,7 +205,7 @@ public class LodDimensionFileHandler
{
// the buffered reader encountered a
// problem reading the file
ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: ");
e.printStackTrace();
}
}
@@ -274,7 +270,7 @@ public class LodDimensionFileHandler
// for some reason
if (fileName == null)
{
ClientProxy.LOGGER.warn("Unable to save region [" + x + ", " + z + "] to file.");
ClientProxy.LOGGER.warn("Unable to save region [" + x + ", " + z + "] to file, no fileName.");
return;
}
@@ -342,7 +338,7 @@ public class LodDimensionFileHandler
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e)
{
ClientProxy.LOGGER.error("LOD file write error: ");
ClientProxy.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: ");
e.printStackTrace();
}
}
@@ -18,10 +18,10 @@
package com.seibel.lod.handlers;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.seibel.lod.enums.FogQuality;
import net.minecraft.client.Minecraft;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
* This object is used to get variables from methods
@@ -29,27 +29,32 @@ import net.minecraft.client.Minecraft;
* in Optifine.
*
* @author James Seibel
* @version 7-03-2021
* @version 9-7-2021
*/
public class ReflectionHandler
{
private Minecraft mc = Minecraft.getInstance();
public static final ReflectionHandler INSTANCE = new ReflectionHandler();
private MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
public Field ofFogField = null;
public ReflectionHandler()
public Method vertexBufferUploadMethod = null;
private ReflectionHandler()
{
setupFogField();
}
/**
* Similar to setupFovMethod.
* finds the Optifine fog type field
*/
private void setupFogField()
{
// get every variable from the entity renderer
Field[] optionFields = mc.options.getClass().getDeclaredFields();
Field[] optionFields = mc.getOptions().getClass().getDeclaredFields();
// try and find the ofFogType variable in gameSettings
for (Field field : optionFields)
{
@@ -59,14 +64,15 @@ public class ReflectionHandler
return;
}
}
// we didn't find the field,
// either optifine isn't installed, or
// optifine changed the name of the variable
ofFogField = null;
}
/**
* Get what type of fog optifine is currently set to render.
*/
@@ -79,35 +85,34 @@ public class ReflectionHandler
// the setup method wasn't called yet.
return FogQuality.FANCY;
}
int returnNum = 0;
try
{
returnNum = (int) ofFogField.get(mc.options);
returnNum = (int) ofFogField.get(mc.getOptions());
} catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
switch (returnNum)
{
// optifine's "default" option,
// it should never be called in this case
case 0:
return FogQuality.FAST;
// optifine's "default" option,
// it should never be called in this case
case 0:
return FogQuality.FAST;
// normal options
case 1:
return FogQuality.FAST;
case 2:
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
default:
return FogQuality.FAST;
case 1:
return FogQuality.FAST;
case 2:
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
default:
return FogQuality.FAST;
}
}
}
@@ -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();
}
}
@@ -7,65 +7,40 @@ import com.seibel.lod.util.LodUtil;
public class LevelContainer implements Serializable
{
/** This is here so that Eclipse doesn't complain */
private static final long serialVersionUID = -4930855068717998385L;
public static final char DATA_DELIMITER = ',';
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 +50,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 +57,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);
}
@@ -1,9 +1,6 @@
package com.seibel.lod.objects;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.ChunkPos;
import java.util.Comparator;
public class LevelPosUtil
{
@@ -37,11 +34,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 +120,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 +159,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 +218,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 +246,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);
}
}
@@ -19,22 +19,17 @@ package com.seibel.lod.objects;
import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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.wrappers.MinecraftWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerChunkProvider;
@@ -67,8 +62,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;
@@ -89,7 +86,7 @@ public class LodDimension
dimension = newDimension;
width = newWidth;
halfWidth = (int) Math.floor(width / 2);
Minecraft mc = Minecraft.getInstance();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
if (newDimension != null && lodWorld != null)
{
try
@@ -110,8 +107,8 @@ public class LodDimension
{
// connected to server
saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() +
File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level));
saveDir = new File(mc.getGameDirectory().getCanonicalFile().getPath() +
File.separatorChar + "lod server data" + File.separatorChar + mc.getCurrentDimensionId());
}
fileHandler = new LodDimensionFileHandler(saveDir, this);
@@ -243,8 +240,6 @@ public class LodDimension
*/
public int getMinMemoryNeeded()
{
int regionX;
int regionZ;
int count = 0;
LodRegion region;
@@ -262,25 +257,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];
}
@@ -290,15 +289,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];
}
@@ -310,7 +311,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
@@ -325,7 +326,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)
@@ -338,7 +339,6 @@ public class LodDimension
int minDistance;
byte detail;
byte levelToCut;
LevelPos levelPos = new LevelPos();
for (int x = 0; x < regions.length; x++)
{
@@ -351,8 +351,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)
@@ -375,7 +374,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);
@@ -390,20 +389,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)
@@ -439,32 +435,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.
}
@@ -472,7 +467,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;
@@ -483,32 +479,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;
}
/**
@@ -516,33 +506,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);
}
/**
@@ -552,26 +520,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);
}
@@ -582,40 +543,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);
}
/**
@@ -696,9 +651,6 @@ public class LodDimension
@Override
public String toString()
{
int regionX;
int regionZ;
LevelPos levelPos;
LodRegion region;
StringBuilder stringBuilder = new StringBuilder();
@@ -721,7 +673,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(" ");
@@ -2,39 +2,73 @@ 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,15 +76,32 @@ 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];
}
@Override
public String toString()
{
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]);
@@ -26,20 +26,16 @@ import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker;
import com.seibel.lod.builders.worldGeneration.LodWorldGenerator;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceCalculatorType;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.enums.ShadingMode;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.client.event.InputEvent;
@@ -68,7 +64,7 @@ public class ClientProxy
private boolean configOverrideReminderPrinted = false;
Minecraft mc = Minecraft.getInstance();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/**
@@ -104,12 +100,12 @@ public class ClientProxy
public void renderLods(float partialTicks)
{
DetailDistanceUtil.updateSettings();
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
if (mc == null || mc.getPlayer() == null || !lodWorld.getIsWorldLoaded())
return;
viewDistanceChangedEvent();
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
LodDimension lodDim = lodWorld.getLodDimension(mc.getCurrentDimension());
if (lodDim == null)
return;
@@ -118,8 +114,8 @@ public class ClientProxy
//System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte");
//System.out.println(lodDim);
lodDim.treeCutter((int) mc.player.getX(), (int) mc.player.getZ());
lodDim.treeGenerator((int) mc.player.getX(), (int) mc.player.getZ());
lodDim.treeCutter((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
lodDim.treeGenerator((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
// comment out when creating a release
@@ -141,7 +137,7 @@ public class ClientProxy
// these can't be set until after the buffers are built (in renderer.drawLODs)
// otherwise the buffers may be set to the wrong size, or not changed at all
previousChunkRenderDistance = mc.options.renderDistance;
previousChunkRenderDistance = mc.getRenderDistance();
previousLodRenderDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get();
}
@@ -151,7 +147,7 @@ public class ClientProxy
// remind the developer(s). that config override is active
if (!configOverrideReminderPrinted)
{
mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID());
mc.getPlayer().sendMessage(new StringTextComponent("Debug settings enabled!"), mc.getPlayer().getUUID());
configOverrideReminderPrinted = true;
}
@@ -161,21 +157,21 @@ public class ClientProxy
LodConfig.CLIENT.graphics.maxDrawDetail.set(LodDetail.FULL);
LodConfig.CLIENT.worldGenerator.maxGenerationDetail.set(LodDetail.FULL);
LodConfig.CLIENT.graphics.fogDistance.set(FogDistance.FAR);
LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES);
LodConfig.CLIENT.graphics.brightnessMultiplier.set(1.0);
LodConfig.CLIENT.graphics.saturationMultiplier.set(1.0);
// LodConfig.CLIENT.graphics.fogDistance.set(FogDistance.FAR);
// LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
// LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES);
// LodConfig.CLIENT.graphics.brightnessMultiplier.set(1.0);
// LodConfig.CLIENT.graphics.saturationMultiplier.set(1.0);
LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
LodConfig.CLIENT.graphics.lodChunkRenderDistance.set(256);
LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
LodConfig.CLIENT.graphics.lodQuality.set(3);
LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false);
// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
// LodConfig.CLIENT.graphics.lodChunkRenderDistance.set(256);
// LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
// LodConfig.CLIENT.graphics.lodQuality.set(3);
// LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false);
LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.set(2000); // 2000
LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.set(1000); // 1000
LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.set(50); // 5000
// LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.set(2000); // 2000
// LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.set(1000); // 1000
// LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.set(50); // 5000
LodConfig.CLIENT.debugging.enableDebugKeybinding.set(true);
}
@@ -188,10 +184,10 @@ public class ClientProxy
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
if (mc == null || mc.getPlayer() == null || !lodWorld.getIsWorldLoaded())
return;
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
LodDimension lodDim = lodWorld.getLodDimension(mc.getPlayer().level.dimensionType());
if (lodDim == null)
return;
@@ -225,7 +221,6 @@ public class ClientProxy
public void worldUnloadEvent(WorldEvent.Unload event)
{
// the player just unloaded a world/dimension
if (mc.getConnection().getLevel() == null)
{
// if this isn't done unfinished tasks may be left in the queue
@@ -280,7 +275,7 @@ public class ClientProxy
private void playerMoveEvent(LodDimension lodDim)
{
// make sure the dimension is centered
RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition());
RegionPos playerRegionPos = new RegionPos(mc.getPlayer().blockPosition());
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
@@ -31,30 +31,33 @@ import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.builders.LodBufferBuilder;
import com.seibel.lod.builders.LodBufferBuilder.VertexBuffersAndOffset;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.FogQuality;
import com.seibel.lod.handlers.ReflectionHandler;
import com.seibel.lod.objects.LevelPosUtil;
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;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
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.MathHelper;
import net.minecraft.util.math.vector.Matrix4f;
@@ -67,7 +70,7 @@ import net.minecraft.util.math.vector.Vector3f;
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 8-31-2021
* @version 9-7-2021
*/
public class LodRenderer
{
@@ -102,11 +105,10 @@ public class LodRenderer
*/
public DebugMode previousDebugMode = DebugMode.OFF;
private Minecraft mc;
private MinecraftWrapper mc;
private GameRenderer gameRender;
private IProfiler profiler;
private int farPlaneBlockDistance;
private ReflectionHandler reflectionHandler;
/**
@@ -119,12 +121,12 @@ public class LodRenderer
*/
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;
@@ -153,10 +155,9 @@ public class LodRenderer
public LodRenderer(LodBufferBuilder newLodNodeBufferBuilder)
{
mc = Minecraft.getInstance();
gameRender = mc.gameRenderer;
mc = MinecraftWrapper.INSTANCE;
gameRender = mc.getGameRenderer();
reflectionHandler = new ReflectionHandler();
lodBufferBuilder = newLodNodeBufferBuilder;
}
@@ -198,7 +199,7 @@ public class LodRenderer
}
}
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// starting here...
determineIfLodsShouldRegenerate(lodDim);
@@ -215,7 +216,7 @@ public class LodRenderer
if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvaliable())
{
// generate the LODs on a separate thread to prevent stuttering or freezing
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.player.blockPosition(), true);
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayer().blockPosition(), true);
// the regen process has been started,
// it will be done when lodBufferBuilder.newBuffersAvaliable()
@@ -223,20 +224,12 @@ public class LodRenderer
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 //
//===========================//
@@ -247,7 +240,7 @@ public class LodRenderer
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);
@@ -263,15 +256,15 @@ public class LodRenderer
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);
@@ -287,8 +280,10 @@ public class LodRenderer
if (vbos != null)
{
Vector3d cameraDir = mc.cameraEntity.getLookAngle().normalize();
cameraDir = mc.options.getCameraType().isMirrored() ? cameraDir.reverse() : cameraDir;
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
@@ -300,7 +295,7 @@ public class LodRenderer
for (int j = 0; j < vbos.length; j++)
{
RegionPos vboPos = new RegionPos(i + lodDim.getCenterX() - lodDim.getWidth() / 2, j + lodDim.getCenterZ() - lodDim.getWidth() / 2);
if (RenderUtil.isRegionInViewFrustum(mc.cameraEntity.blockPosition(), cameraDir, vboPos.blockPos()))
if (RenderUtil.isRegionInViewFrustum(cameraEntity.blockPosition(), cameraDir, vboPos.blockPos()))
{
if ((i > halfWidth - quarterWidth && i < halfWidth + quarterWidth) && (j > halfWidth - quarterWidth && j < halfWidth + quarterWidth))
setupFog(fogSettings.near.distance, fogSettings.near.quality);
@@ -344,11 +339,23 @@ public class LodRenderer
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.
if (lodBufferBuilder.newBuffersAvaliable())
{
// this has to be called after the VBOs have been drawn
// otherwise rubber banding may occur
swapBuffers();
}
// end of internal LOD profiling
profiler.pop();
}
/**
* This is where the actual drawing happens.
*/
@@ -381,12 +388,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)
@@ -399,7 +406,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)
@@ -408,7 +415,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
@@ -434,16 +441,16 @@ public class LodRenderer
{
if (fogQuality == FogQuality.FANCY)
{
RenderSystem.fogEnd(mc.options.renderDistance * 16 * 1.41f);
RenderSystem.fogStart(mc.options.renderDistance * 16 * 1.6f);
RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.41f);
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.6f);
}
else if (fogQuality == FogQuality.FAST)
{
RenderSystem.fogEnd(mc.options.renderDistance * 16 * 1.0f);
RenderSystem.fogStart(mc.options.renderDistance * 16 * 1.5f);
RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.0f);
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.5f);
}
}
GL11.glEnable(GL11.GL_FOG);
RenderSystem.enableFog();
RenderSystem.setupNvFogDistance();
@@ -466,8 +473,8 @@ public class LodRenderer
// 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);
}
@@ -481,17 +488,24 @@ public class LodRenderer
private Matrix4f generateModelViewMatrix(float partialTicks)
{
// get all relevant camera info
ActiveRenderInfo renderInfo = mc.gameRenderer.getMainCamera();
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
Vector3d projectedView = renderInfo.getPosition();
// generate the model view matrix
MatrixStack matrixStack = new MatrixStack();
matrixStack.pushPose();
// translate and rotate to the current camera location
// rotate to the current camera's direction
matrixStack.mulPose(Vector3f.XP.rotationDegrees(renderInfo.getXRot()));
matrixStack.mulPose(Vector3f.YP.rotationDegrees(renderInfo.getYRot() + 180));
matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z);
// translate the camera relative to the regions' center
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
// accuracy vs the model view matrix, which only uses floats)
BlockPos bufferPos = vbosCenter.getWorldPosition();
Vector3d eyePos = mc.getPlayer().getEyePosition(partialTicks);
double xDiff = eyePos.x - bufferPos.getX();
double zDiff = eyePos.z - bufferPos.getZ();
matrixStack.translate(-xDiff, -projectedView.y, -zDiff);
return matrixStack.last().pose();
}
@@ -519,16 +533,16 @@ public class LodRenderer
matrixStack.pushPose();
gameRender.bobHurt(matrixStack, partialTicks);
if (this.mc.options.bobView)
if (this.mc.getOptions().bobView)
{
gameRender.bobView(matrixStack, partialTicks);
}
// potion and nausea effects
float f = MathHelper.lerp(partialTicks, this.mc.player.oPortalTime, this.mc.player.portalTime) * this.mc.options.screenEffectScale * this.mc.options.screenEffectScale;
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)
{
int i = this.mc.player.hasEffect(Effects.CONFUSION) ? 7 : 20;
int i = this.mc.getPlayer().hasEffect(Effects.CONFUSION) ? 7 : 20;
float f1 = 5.0F / (f * f + 5.0F) - f * 0.04F;
f1 = f1 * f1;
Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_OF_TWO / 2.0F, MathHelper.SQRT_OF_TWO / 2.0F);
@@ -548,7 +562,7 @@ public class LodRenderer
// it is possible to see the near clip plane, but
// you have to be flying quickly in spectator mode through ungenerated
// terrain, so I don't think it is much of an issue.
mc.options.renderDistance,
mc.getRenderDistance(),
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2);
// add the screen space distortions
@@ -565,9 +579,9 @@ public class LodRenderer
{
// Determine if the player has night vision
boolean playerHasNightVision = false;
if (this.mc.player != null)
if (this.mc.getPlayer() != null)
{
Iterator<EffectInstance> iterator = this.mc.player.getActiveEffects().iterator();
Iterator<EffectInstance> iterator = this.mc.getPlayer().getActiveEffects().iterator();
while (iterator.hasNext())
{
EffectInstance instance = iterator.next();
@@ -580,9 +594,9 @@ public class LodRenderer
}
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.level.getSkyDarken(partialTicks) : 0.2f;
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f;
sunBrightness = playerHasNightVision ? 1.0f : sunBrightness;
float gammaMultiplyer = (float) mc.options.gamma - 0.5f;
float gammaMultiplyer = (float) mc.getOptions().gamma - 0.5f;
float lightStrength = ((sunBrightness / 2f) - 0.2f) + (gammaMultiplyer * 0.3f);
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
@@ -636,13 +650,18 @@ public class LodRenderer
/**
* Replace the current Vertex Buffers with the newly
* created buffers from the lodBufferBuilder.
* 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...
*/
private void swapBuffers()
{
// replace the drawable buffers with
// the newly created buffers from the lodBufferBuilder
vbos = lodBufferBuilder.getVertexBuffers();
VertexBuffersAndOffset result = lodBufferBuilder.getVertexBuffers();
vbos = result.vbos;
vbosCenter = result.drawableCenterChunkPos;
}
/**
@@ -656,7 +675,7 @@ public class LodRenderer
private double getFov(float partialTicks, boolean useFovSetting)
{
return mc.gameRenderer.getFov(mc.gameRenderer.getMainCamera(), partialTicks, useFovSetting);
return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting);
}
@@ -668,7 +687,7 @@ public class LodRenderer
NearFarFogSettings fogSettings = new NearFarFogSettings();
FogQuality quality = reflectionHandler.getFogQuality();
FogQuality quality = ReflectionHandler.INSTANCE.getFogQuality();
FogDrawOverride override = LodConfig.CLIENT.graphics.fogDrawOverride.get();
@@ -770,65 +789,65 @@ public class LodRenderer
return fogSettings;
}
/**
* Determines if the LODs should have a fullRegen or partialRegen
*/
@SuppressWarnings("unchecked")
private void determineIfLodsShouldRegenerate(LodDimension lodDim)
{
short renderDistance = (short) mc.options.renderDistance;
short renderDistance = (short) mc.getRenderDistance();
//=============//
// full regens //
//=============//
// check if the view distance changed
if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.lodChunkRenderDistance.get()
|| mc.options.renderDistance != 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.player.xChunk, mc.player.zChunk);
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
prevFogDistance = LodConfig.CLIENT.graphics.fogDistance.get();
prevRenderDistance = mc.options.renderDistance;
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.player.xChunk != previousPos.posX
|| mc.player.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.player.xChunk, mc.player.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())
{
@@ -840,8 +859,8 @@ public class LodRenderer
}
prevVanillaChunkTime = newTime;
}
// check if there is any newly generated terrain to show
if (newTime - prevChunkTime > LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.get())
{
@@ -852,22 +871,22 @@ 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.player.blockPosition());
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition());
int chunkX;
int chunkZ;
for (ChunkPos pos : chunkPosToSkip)
{
chunkX = pos.x - mc.player.xChunk + renderDistance + 1;
chunkZ = pos.z - mc.player.zChunk + renderDistance + 1;
chunkX = pos.x - mc.getPlayer().xChunk + renderDistance + 1;
chunkZ = pos.z - mc.getPlayer().zChunk + renderDistance + 1;
try
{
if (!vanillaRenderedChunks[chunkX][chunkZ])
@@ -881,14 +900,14 @@ public class LodRenderer
e.printStackTrace();
}
}
// if the player is high enough, draw all LODs
if(chunkPosToSkip.isEmpty() && mc.player.position().y > 256)
if(chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256)
{
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
vanillaRenderedChunksChanged = true;
}
}
}
}
@@ -20,8 +20,8 @@ package com.seibel.lod.render;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.LodTemplate;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.vector.Vector3d;
@@ -35,7 +35,7 @@ import net.minecraft.util.math.vector.Vector3d;
*/
public class RenderUtil
{
private static final Minecraft mc = Minecraft.getInstance();
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/**
@@ -44,28 +44,24 @@ public class RenderUtil
*/
public static boolean isChunkPosInLoadedArea(ChunkPos pos, ChunkPos center)
{
Minecraft mc = Minecraft.getInstance();
return (pos.x >= center.x - mc.options.renderDistance
&& pos.x <= center.x + mc.options.renderDistance)
return (pos.x >= center.x - mc.getRenderDistance()
&& pos.x <= center.x + mc.getRenderDistance())
&&
(pos.z >= center.z - mc.options.renderDistance
&& pos.z <= center.z + mc.options.renderDistance);
(pos.z >= center.z - mc.getRenderDistance()
&& pos.z <= center.z + mc.getRenderDistance());
}
/**
* Returns if the given coordinate is in the loaded area of the world.
* @param centerCoordinate the center of the loaded world
*/
public static boolean isCoordinateInLoadedArea(int i, int j, int centerCoordinate)
public static boolean isCoordinateInLoadedArea(int x, int z, int centerCoordinate)
{
Minecraft mc = Minecraft.getInstance();
return (i >= centerCoordinate - mc.options.renderDistance
&& i <= centerCoordinate + mc.options.renderDistance)
return (x >= centerCoordinate - mc.getRenderDistance()
&& x <= centerCoordinate + mc.getRenderDistance())
&&
(j >= centerCoordinate - mc.options.renderDistance
&& j <= centerCoordinate + mc.options.renderDistance);
(z >= centerCoordinate - mc.getRenderDistance()
&& z <= centerCoordinate + mc.getRenderDistance());
}
@@ -109,7 +105,7 @@ public class RenderUtil
int maxNumberOfLods = LodRenderer.MAX_ALOCATEABLE_DIRECT_MEMORY / lodTemplate.getBufferMemoryForSingleLod();
int numbLodsWide = (int) Math.sqrt(maxNumberOfLods);
return numbLodsWide / (2 * mc.options.renderDistance);
return numbLodsWide / (2 * mc.getRenderDistance());
}
@@ -4,8 +4,7 @@ import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.RegionPos;
import net.minecraft.client.Minecraft;
import com.seibel.lod.wrappers.MinecraftWrapper;
public class DetailDistanceUtil
{
@@ -20,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,
@@ -34,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);
@@ -59,7 +59,7 @@ public class DetailDistanceUtil
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
return (int) (Math.pow(base, detail) * initial);
case RENDER_DEPENDANT:
int realRenderDistance = Minecraft.getInstance().options.renderDistance * 16;
int realRenderDistance = MinecraftWrapper.INSTANCE.getRenderDistance() * 16;
int border = 64;
byte detailAtBorder = (byte) 4;
if (detail > detailAtBorder)
@@ -154,10 +154,10 @@ public class DetailDistanceUtil
{
if (detail < minGenDetail)
{
return lodDetails[minGenDetail];
return lodGenDetails[minGenDetail];
} else
{
return lodDetails[detail];
return lodGenDetails[detail];
}
}
@@ -166,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;
}
}
+38 -26
View File
@@ -21,13 +21,15 @@ 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.wrappers.MinecraftWrapper;
import net.minecraft.client.Minecraft;
import it.unimi.dsi.fastutil.objects.ObjectList;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.WorldRenderer.LocalRenderInformationContainer;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
@@ -43,11 +45,11 @@ import net.minecraft.world.server.ServerWorld;
* This class holds methods and constants that may be used in multiple places.
*
* @author James Seibel
* @version 8-29-2021
* @version 9-7-2021
*/
public class LodUtil
{
private static Minecraft mc = Minecraft.getInstance();
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** alpha used when drawing chunks in debug mode */
@@ -98,6 +100,9 @@ public class LodUtil
/** If we ever need to use a heightmap for any reason, use this one. */
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
/** This regex finds any characters that are invalid for use in a windows
* (and by extension mac and linux) file path */
public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\\\/:*?\\\"<>|]";
@@ -207,10 +212,7 @@ public class LodUtil
}
else
{
ServerData server = mc.getCurrentServer();
return server.name + ", IP " +
server.ip + ", GameVersion " +
server.version.getString();
return getServerId();
}
}
@@ -219,15 +221,13 @@ public class LodUtil
/**
* If on single player this will return the name of the user's
* world and the dimensional save folder, if in multiplayer
* it will return the server name, game version, and dimension.<br>
* it will return the server name, ip, game version, and dimension.<br>
* <br>
* This can be used to determine where to save files for a given
* dimension.
*/
public static String getDimensionIDFromWorld(IWorld world)
{
Minecraft mc = Minecraft.getInstance();
if(mc.hasSingleplayerServer())
{
// this will return the world save location
@@ -245,14 +245,25 @@ public class LodUtil
}
else
{
ServerData server = mc.getCurrentServer();
return server.name + ", IP " +
server.ip + ", GameVersion " +
server.version.getString() + File.separatorChar
+ "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar;
return getServerId() + File.separatorChar + "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar;
}
}
/**
* returns the server name, IP and game version.
*/
public static String getServerId()
{
ServerData server = mc.getCurrentServer();
String serverName = server.name.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverIp = server.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverMcVersion = server.version.getString().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverId = serverName + ", IP " + serverIp + ", GameVersion " + serverMcVersion;
return serverId;
}
/**
@@ -313,7 +324,7 @@ public class LodUtil
*/
public static HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
{
int chunkRenderDist = mc.options.renderDistance;
int chunkRenderDist = mc.getRenderDistance();
ChunkPos centerChunk = new ChunkPos(playerPos);
// skip chunks that are already going to be rendered by Minecraft
@@ -324,13 +335,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
@@ -355,12 +366,12 @@ public class LodUtil
{
HashSet<ChunkPos> loadedPos = new HashSet<>();
Minecraft mc = Minecraft.getInstance();
// Wow those are some long names!
// go through every RenderInfo to get the compiled chunks
for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : mc.levelRenderer.renderChunks)
WorldRenderer renderer = mc.getLevelRenderer();
ObjectList<LocalRenderInformationContainer> chunks = renderer.renderChunks;
for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : chunks)
{
if (!worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk().hasNoRenderableLayers())
{
@@ -371,6 +382,7 @@ public class LodUtil
}
}
return loadedPos;
}
@@ -0,0 +1,137 @@
package com.seibel.lod.wrappers;
import java.io.File;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.GameSettings;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.network.play.ClientPlayNetHandler;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.profiler.IProfiler;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
import net.minecraft.world.DimensionType;
/**
* A singleton that wraps the Minecraft class
* to allow for easier movement between Minecraft versions.
*
* @author James Seibel
* @version 9-6-2021
*/
public class MinecraftWrapper
{
public static MinecraftWrapper INSTANCE = new MinecraftWrapper();
private Minecraft mc = Minecraft.getInstance();
private MinecraftWrapper()
{
}
//=================//
// method wrappers //
//=================//
public float getShading(Direction direction)
{
return mc.level.getShade(Direction.UP, true);
}
public boolean hasSingleplayerServer()
{
return mc.hasSingleplayerServer();
}
public DimensionType getCurrentDimension()
{
return mc.player.level.dimensionType();
}
public String getCurrentDimensionId()
{
return LodUtil.getDimensionIDFromWorld(mc.level);
}
//=============//
// Simple gets //
//=============//
public ClientPlayerEntity getPlayer()
{
return mc.player;
}
public GameSettings getOptions()
{
return mc.options;
}
/** Measured in chunks */
public int getRenderDistance()
{
return mc.options.renderDistance;
}
public File getGameDirectory()
{
return mc.gameDirectory;
}
public IProfiler getProfiler()
{
return mc.getProfiler();
}
public ClientPlayNetHandler getConnection()
{
return mc.getConnection();
}
public GameRenderer getGameRenderer()
{
return mc.gameRenderer;
}
public Entity getCameraEntity()
{
return mc.cameraEntity;
}
public MainWindow getWindow()
{
return mc.getWindow();
}
public float getSkyDarken(float partialTicks)
{
return mc.level.getSkyDarken(partialTicks);
}
public IntegratedServer getSingleplayerServer()
{
return mc.getSingleplayerServer();
}
public ServerData getCurrentServer()
{
return mc.getCurrentServer();
}
public WorldRenderer getLevelRenderer()
{
return mc.levelRenderer;
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ modId="lod" #mandatory
#// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
#//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
#// see the associated build.gradle script for how to populate this completely automatically during a build
version="a1.4" #mandatory
version="a1.4.1" #mandatory
#// A display name for the mod
displayName="LOD" #mandatory