Correctly removed LodDataPoint and optimized the LevelPos use

This commit is contained in:
Leonardo
2021-08-26 13:18:18 +02:00
parent 98cbc30709
commit 74e4744ff7
18 changed files with 639 additions and 681 deletions
@@ -2,7 +2,7 @@ package com.seibel.lod.builders;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.ChunkPos;
@@ -25,7 +25,7 @@ public class GenerationRequest
public ChunkPos getChunkPos()
{
LevelPos chunkLevelPos = levelPos.convert(LodUtil.CHUNK_DETAIL_LEVEL);
LevelPos chunkLevelPos = levelPos.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL);
return new ChunkPos(chunkLevelPos.posX, chunkLevelPos.posZ);
}
}
@@ -27,11 +27,11 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import net.minecraft.world.chunk.Chunk;
import org.lwjgl.opengl.GL11;
import com.seibel.lod.handlers.LodConfig;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.proxy.ClientProxy;
@@ -53,129 +53,141 @@ import net.minecraft.util.math.ChunkPos;
*/
public class LodBufferBuilder
{
/** This holds the thread used to generate new LODs off the main thread. */
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main"));
/** This holds the threads used to generate buffers. */
private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder"));
/** The buffers that are used to create LODs using far fog */
public volatile BufferBuilder[][] buildableBuffers;
/** Used when building new VBOs */
public volatile VertexBuffer[][] buildableVbos;
/** VBOs that are sent over to the LodNodeRenderer */
public volatile VertexBuffer[][] drawableVbos;
/**
* if this is true the LOD buffers are currently being
* regenerated.
*/
public boolean generatingBuffers = false;
/**
* if this is true new LOD buffers have been generated
* and are waiting to be swapped with the drawable buffers
*/
private boolean switchVbos = false;
/** Size of the buffer builders in bytes last time we created them */
public int previousBufferSize = 0;
/** Width of the dimension in regions last time we created the buffers */
public int previousRegionWidth = 0;
/** this is used to prevent multiple threads creating, destroying, or using the buffers at the same time */
private ReentrantLock bufferLock = new ReentrantLock();
public LodBufferBuilder()
{
}
/**
* Create a thread to asynchronously generate LOD buffers
* centered around the given camera X and Z.
* <br>
* This method will write to the drawable near and far buffers.
* <br>
* After the buildable buffers have been generated they must be
* swapped with the drawable buffers in the LodRenderer to be drawn.
*/
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
BlockPos playerBlockPos, int numbChunksWide)
{
// only allow one generation process to happen at a time
if (generatingBuffers)
return;
if (buildableBuffers == null)
throw new IllegalStateException("\"generateLodBuffersAsync\" was called before the \"setupBuffers\" method was called.");
generatingBuffers = true;
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
Thread thread = new Thread(() ->
{
bufferLock.lock();
/**
* This holds the thread used to generate new LODs off the main thread.
*/
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main"));
/**
* This holds the threads used to generate buffers.
*/
private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder"));
try
{
long treeStart = System.currentTimeMillis();
long treeEnd = System.currentTimeMillis();
/**
* The buffers that are used to create LODs using far fog
*/
public volatile BufferBuilder[][] buildableBuffers;
long startTime = System.currentTimeMillis();
ArrayList<Callable<Boolean>> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
startBuffers();
/**
* Used when building new VBOs
*/
public volatile VertexBuffer[][] buildableVbos;
// =====================//
// RENDERING PART //
// =====================//
/**
* VBOs that are sent over to the LodNodeRenderer
*/
public volatile VertexBuffer[][] drawableVbos;
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
{
for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++)
{
RegionPos regionPos = new RegionPos(xRegion + lodDim.getCenterX() - lodDim.getWidth() / 2, zRegion + lodDim.getCenterZ() - lodDim.getWidth() / 2);
/**
* if this is true the LOD buffers are currently being
* regenerated.
*/
public boolean generatingBuffers = false;
// local position in the vbo and bufferBuilder arrays
BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion];
// make sure the buffers weren't
// changed while we were running this method
if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building()))
return;
/**
* if this is true new LOD buffers have been generated
* and are waiting to be swapped with the drawable buffers
*/
private boolean switchVbos = false;
byte detailLevel = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel;
/**
* Size of the buffer builders in bytes last time we created them
*/
public int previousBufferSize = 0;
Callable<Boolean> bufferBuildingThread = () ->
{
byte detailToRender;
boolean zFix;
Set<LevelPos> setOfPosToRender = new HashSet<>();
/**
* Width of the dimension in regions last time we created the buffers
*/
public int previousRegionWidth = 0;
for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
detailToRender = detail;
zFix = true;
/**
* this is used to prevent multiple threads creating, destroying, or using the buffers at the same time
*/
private ReentrantLock bufferLock = new ReentrantLock();
public LodBufferBuilder()
{
}
/**
* Create a thread to asynchronously generate LOD buffers
* centered around the given camera X and Z.
* <br>
* This method will write to the drawable near and far buffers.
* <br>
* After the buildable buffers have been generated they must be
* swapped with the drawable buffers in the LodRenderer to be drawn.
*/
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
BlockPos playerBlockPos, int numbChunksWide)
{
// only allow one generation process to happen at a time
if (generatingBuffers)
return;
if (buildableBuffers == null)
throw new IllegalStateException("\"generateLodBuffersAsync\" was called before the \"setupBuffers\" method was called.");
generatingBuffers = true;
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
Thread thread = new Thread(() ->
{
bufferLock.lock();
try
{
long treeStart = System.currentTimeMillis();
long treeEnd = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
ArrayList<Callable<Boolean>> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
startBuffers();
// =====================//
// RENDERING PART //
// =====================//
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
{
for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++)
{
RegionPos regionPos = new RegionPos(xRegion + lodDim.getCenterX() - lodDim.getWidth() / 2, zRegion + lodDim.getCenterZ() - lodDim.getWidth() / 2);
// local position in the vbo and bufferBuilder arrays
BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion];
// make sure the buffers weren't
// changed while we were running this method
if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building()))
return;
byte detailLevel = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel;
Callable<Boolean> bufferBuildingThread = () ->
{
byte detailToRender;
boolean zFix;
Set<LevelPos> setOfPosToRender = new HashSet<>();
for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
detailToRender = detail;
zFix = true;
/*
detailToRender = (byte) 4;
if(detail > detailToRender){
@@ -185,269 +197,256 @@ public class LodBufferBuilder
zFix = true;
}
*/
setOfPosToRender.addAll(lodDim.getDataToRender(
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
DetailDistanceUtil.getDistanceRendering(detail),
DetailDistanceUtil.getDistanceRendering(detail + 1),
detailToRender,
zFix));
}
setOfPosToRender.addAll(lodDim.getDataToRender(
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
DetailDistanceUtil.getDistanceRendering(detail),
DetailDistanceUtil.getDistanceRendering(detail + 1),
detailToRender,
zFix));
}
for (LevelPos posToRender : setOfPosToRender)
{
LevelPos chunkPos = posToRender.convert(LodUtil.CHUNK_DETAIL_LEVEL);
// skip any chunks that Minecraft is going to render
for (LevelPos posToRender : setOfPosToRender)
{
LevelPos chunkPos = posToRender.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL);
// skip any chunks that Minecraft is going to render
if (renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkPos.posX, chunkPos.posZ)))
{
continue;
}
if (renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkPos.posX, chunkPos.posZ)))
{
continue;
}
if (lodDim.doesDataExist(posToRender))
{
try
{
LodDataPoint lodData = lodDim.getData(posToRender);
if (lodDim.doesDataExist(posToRender))
{
try
{
ChunkPos adjChunkPos = new ChunkPos(0, 0);
LevelPos adjPos = new LevelPos();
if (lodDim.doesDataExist(posToRender))
{
short[] lodData = lodDim.getData(posToRender);
short[][][] adjData = new short[2][2][];
for (int x : new int[]{0, 1})
{
adjPos.changeParameters(posToRender.detailLevel, posToRender.posX + x * 2 - 1, posToRender.posZ);
if (!renderer.vanillaRenderedChunks.contains(adjPos.getChunkPos())
&& setOfPosToRender.contains(adjPos))
adjData[0][x] = lodDim.getData(adjPos);
}
if (lodData != null)
{
/*We check for adjacent data*/
LodDataPoint[][] adjData = new LodDataPoint[2][2];
LevelPos adjPos;
for(int x : new int[]{0,1}){
adjPos = new LevelPos(posToRender.detailLevel, posToRender.posX + x*2-1, posToRender.posZ);
if (!renderer.vanillaRenderedChunks.contains(new ChunkPos(adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posX, adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posZ)));
adjData[0][x] = lodDim.getData(adjPos);
/*
if(setOfPosToRender.contains(adjPos)){
System.out.println("yup");
adjData[0][x] = lodDim.getData(adjPos);
}*/
}
for (int z : new int[]{0, 1})
{
adjPos.changeParameters(posToRender.detailLevel, posToRender.posX, posToRender.posZ + z * 2 - 1);
if (!renderer.vanillaRenderedChunks.contains(adjPos.getChunkPos())
&& setOfPosToRender.contains(adjPos))
adjData[1][z] = lodDim.getData(adjPos);
}
for(int z : new int[]{0,1}){
adjPos = new LevelPos(posToRender.detailLevel, posToRender.posX, posToRender.posZ + z*2-1);
if (!renderer.vanillaRenderedChunks.contains(new ChunkPos(adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posX, adjPos.convert(LodUtil.CHUNK_DETAIL_LEVEL).posZ)))
adjData[1][z] = lodDim.getData(adjPos);
/*
if(setOfPosToRender.contains(adjPos)){
System.out.println("yup2");
adjData[1][z] = lodDim.getData(adjPos);
}*/
}
LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPos, lodData, adjData,
posToRender, renderer.debugging);
}
} catch (ArrayIndexOutOfBoundsException e)
{
return false;
}
}
LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPos, lodData, adjData,
posToRender, renderer.debugging);
}
}
catch (ArrayIndexOutOfBoundsException e)
{
return false;
}
}
}// for pos to in list to render
}// for pos to in list to render
// the thread executed successfully
return true;
};// buffer builder worker thread
// the thread executed successfully
return true;
};// buffer builder worker thread
builderThreads.add(bufferBuildingThread);
builderThreads.add(bufferBuildingThread);
}// region z
}// region z
}// region z
}// region z
long renderStart = System.currentTimeMillis();
// wait for all threads to finish
List<Future<Boolean>> futures = bufferBuilderThreads.invokeAll(builderThreads);
for(Future<Boolean> future : futures)
{
// the future will be false if its thread failed
if (!future.get())
{
ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over.");
closeBuffers();
return;
}
}
long renderEnd = System.currentTimeMillis();
// finish the buffer building
closeBuffers();
// upload the new buffers
uploadBuffers();
long endTime = System.currentTimeMillis();
long buildTime = endTime - startTime;
long renderStart = System.currentTimeMillis();
// wait for all threads to finish
List<Future<Boolean>> futures = bufferBuilderThreads.invokeAll(builderThreads);
for (Future<Boolean> future : futures)
{
// the future will be false if its thread failed
if (!future.get())
{
ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over.");
closeBuffers();
return;
}
}
long renderEnd = System.currentTimeMillis();
long treeTime = treeEnd - treeStart;
long renderingTime = renderEnd - renderStart;
// finish the buffer building
closeBuffers();
// upload the new buffers
uploadBuffers();
long endTime = System.currentTimeMillis();
long buildTime = endTime - startTime;
long treeTime = treeEnd - treeStart;
long renderingTime = renderEnd - renderStart;
ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' +
"Tree cutting time: " + treeTime + " ms" + '\n' +
"Rendering time: " + renderingTime + " ms");
// mark that the buildable buffers as ready to swap
switchVbos = true;
} catch (Exception e)
{
ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ");
e.printStackTrace();
} finally
{
// regardless of if we successfully created the buffers
// we are done generating.
generatingBuffers = false;
// clean up any potentially open resources
if (buildableBuffers != null)
closeBuffers();
bufferLock.unlock();
}
});
mainGenThread.execute(thread);
return;
}
//===============================//
// BufferBuilder related methods //
//===============================//
/**
* Called from the LodRenderer to create the
* BufferBuilders. <br><br>
* <p>
* May have to wait for the bufferLock to open.
*/
public void setupBuffers(int numbRegionsWide, int bufferMaxCapacity)
{
bufferLock.lock();
previousRegionWidth = numbRegionsWide;
previousBufferSize = bufferMaxCapacity;
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide];
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
for (int x = 0; x < numbRegionsWide; x++)
{
for (int z = 0; z < numbRegionsWide; z++)
{
buildableBuffers[x][z] = new BufferBuilder(bufferMaxCapacity);
buildableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
drawableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
}
}
bufferLock.unlock();
}
/**
* sets the buffers and Vbos to null, forcing them to be recreated. <br><br>
* <p>
* May have to wait for the bufferLock to open.
*/
public void destroyBuffers()
{
bufferLock.lock();
buildableBuffers = null;
buildableVbos = null;
drawableVbos = null;
bufferLock.unlock();
}
/**
* Calls begin on each of the buildable BufferBuilders.
*/
private void startBuffers()
{
for (int x = 0; x < buildableBuffers.length; x++)
for (int z = 0; z < buildableBuffers.length; z++)
buildableBuffers[x][z].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
}
/**
* Calls end on each of the buildable BufferBuilders.
*/
private void closeBuffers()
{
for (int x = 0; x < buildableBuffers.length; x++)
for (int z = 0; z < buildableBuffers.length; z++)
if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building())
buildableBuffers[x][z].end();
}
/**
* Called from the LodRenderer to create the
* BufferBuilders at the right size.
*/
private void uploadBuffers()
{
for (int x = 0; x < buildableVbos.length; x++)
{
for (int z = 0; z < buildableVbos.length; z++)
{
buildableVbos[x][z].upload(buildableBuffers[x][z]);
}
}
}
/**
* Get the newly created VBOs
*/
public VertexBuffer[][] getVertexBuffers()
{
// don't wait for the lock to open
// since this is called on the main render thread
if (bufferLock.tryLock())
{
VertexBuffer[][] tmp = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmp;
// the vbos have been swapped
switchVbos = false;
bufferLock.unlock();
}
return drawableVbos;
}
/**
* If this is true the buildable near and far
* buffers have been generated and are ready to be
* sent to the LodRenderer.
*/
public boolean newBuffersAvaliable()
{
return switchVbos;
}
ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' +
"Tree cutting time: " + treeTime + " ms" + '\n' +
"Rendering time: " + renderingTime + " ms");
// mark that the buildable buffers as ready to swap
switchVbos = true;
}
catch (Exception e)
{
ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ");
e.printStackTrace();
}
finally
{
// regardless of if we successfully created the buffers
// we are done generating.
generatingBuffers = false;
// clean up any potentially open resources
if (buildableBuffers != null)
closeBuffers();
bufferLock.unlock();
}
});
mainGenThread.execute(thread);
return;
}
//===============================//
// BufferBuilder related methods //
//===============================//
/**
* Called from the LodRenderer to create the
* BufferBuilders. <br><br>
*
* May have to wait for the bufferLock to open.
*/
public void setupBuffers(int numbRegionsWide, int bufferMaxCapacity)
{
bufferLock.lock();
previousRegionWidth = numbRegionsWide;
previousBufferSize = bufferMaxCapacity;
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide];
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
for (int x = 0; x < numbRegionsWide; x++)
{
for (int z = 0; z < numbRegionsWide; z++)
{
buildableBuffers[x][z] = new BufferBuilder(bufferMaxCapacity);
buildableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
drawableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
}
}
bufferLock.unlock();
}
/**
* sets the buffers and Vbos to null, forcing them to be recreated. <br><br>
*
* May have to wait for the bufferLock to open.
*/
public void destroyBuffers()
{
bufferLock.lock();
buildableBuffers = null;
buildableVbos = null;
drawableVbos = null;
bufferLock.unlock();
}
/**
* Calls begin on each of the buildable BufferBuilders.
*/
private void startBuffers()
{
for (int x = 0; x < buildableBuffers.length; x++)
for (int z = 0; z < buildableBuffers.length; z++)
buildableBuffers[x][z].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
}
/**
* Calls end on each of the buildable BufferBuilders.
*/
private void closeBuffers()
{
for (int x = 0; x < buildableBuffers.length; x++)
for (int z = 0; z < buildableBuffers.length; z++)
if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building())
buildableBuffers[x][z].end();
}
/**
* Called from the LodRenderer to create the
* BufferBuilders at the right size.
*
*/
private void uploadBuffers()
{
for (int x = 0; x < buildableVbos.length; x++)
{
for (int z = 0; z < buildableVbos.length; z++)
{
buildableVbos[x][z].upload(buildableBuffers[x][z]);
}
}
}
/**
* Get the newly created VBOs
*/
public VertexBuffer[][] getVertexBuffers()
{
// don't wait for the lock to open
// since this is called on the main render thread
if (bufferLock.tryLock())
{
VertexBuffer[][] tmp = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmp;
// the vbos have been swapped
switchVbos = false;
bufferLock.unlock();
}
return drawableVbos;
}
/**
* If this is true the buildable near and far
* buffers have been generated and are ready to be
* sent to the LodRenderer.
*/
public boolean newBuffersAvaliable()
{
return switchVbos;
}
}
@@ -24,8 +24,7 @@ import java.util.concurrent.Executors;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.handlers.LodConfig;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.util.LodThreadFactory;
@@ -150,11 +149,11 @@ public class LodBuilder
int startZ;
int endX;
int endZ;
Color color;
short[] color;
short height;
short depth;
LevelPos levelPos;
LodDataPoint data = null;
LevelPos levelPos = new LevelPos();
short[] data;
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
{
@@ -175,14 +174,14 @@ public class LodBuilder
startZ, endX, endZ);
depth = 0;
}
levelPos = new LevelPos((byte) 0,
levelPos.changeParameters((byte) 0,
chunk.getPos().x * 16 + startX,
chunk.getPos().z * 16 + startZ);
data = new LodDataPoint(height, depth, color);
lodDim.addData(levelPos.convert(detail.detailLevel),
levelPos.convert(detail.detailLevel);
data = new short[]{height, depth, color[0], color[1], color[2]};
lodDim.addData(levelPos,
data,
config.distanceGenerationMode,
false,
false);
}
lodDim.updateData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z));
@@ -332,7 +331,7 @@ public class LodBuilder
* otherwise only use the block's
* material color
*/
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
private short[] generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
{
ChunkSection[] chunkSections = chunk.getSections();
@@ -386,11 +385,9 @@ public class LodBuilder
colorInt = getColorForBlock(x, z, blockState, biome);
}
Color c = LodUtil.intToColor(colorInt);
red += c.getRed();
green += c.getGreen();
blue += c.getBlue();
red += (colorInt)&0xFF;;
green += (colorInt>>8)&0xFF;
blue += (colorInt>>16)&0xFF;
numbOfBlocks++;
@@ -410,7 +407,7 @@ public class LodBuilder
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new Color(red, green, blue);
return new short[]{(short) red, (short) green, (short) blue};
}
/**
@@ -18,12 +18,8 @@
package com.seibel.lod.builders.lodTemplates;
import java.awt.Color;
import java.util.Set;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.renderer.BufferBuilder;
@@ -38,8 +34,8 @@ import net.minecraft.util.math.BlockPos;
*/
public abstract class AbstractLodTemplate
{
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData,
LevelPos levelPos, boolean debugging);
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging);
/** add the given position and color to the buffer */
protected void addPosAndColor(BufferBuilder buffer,
@@ -21,8 +21,8 @@ import java.awt.Color;
import com.seibel.lod.enums.ShadingMode;
import com.seibel.lod.handlers.LodConfig;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.renderer.BufferBuilder;
@@ -45,23 +45,23 @@ public class CubicLodTemplate extends AbstractLodTemplate
}
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData,
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging)
{
AxisAlignedBB bbox;
int width = (int) Math.pow(2, levelPos.detailLevel);
int width = 1 << levelPos.detailLevel;
// add each LOD for the detail level
bbox = generateBoundingBox(
data.height,
data.depth,
DataPoint.getHeight(data),
DataPoint.getDepth(data),
width,
levelPos.posX * width,
0,
levelPos.posZ * width);
Color color = data.color;
Color color = new Color(DataPoint.getColor(data));
if (LodConfig.CLIENT.debugMode.get())
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel];
@@ -105,7 +105,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset);
}
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c, BlockPos playerBlockPos, LodDataPoint[][] adjData)
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c, BlockPos playerBlockPos, short[][][] adjData)
{
Color topColor = c;
Color northSouthColor = c;
@@ -136,7 +136,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
bottomColor = applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier);
int minY;
int maxY;
LodDataPoint data;
short[] data;
/**TODO make all of this more automatic if possible*/
if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET)
{
@@ -168,7 +168,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
} else
{
maxY = data.height;
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
{
minY = (int) Math.max(maxY, bb.minY);
@@ -177,7 +177,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
addPosAndColor(buffer, bb.minX, minY, bb.maxZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
}
minY = data.depth;
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
{
maxY = (int) Math.min(minY, bb.maxX);
@@ -201,7 +201,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
} else
{
maxY = data.height;
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
{
minY = (int) Math.max(maxY, bb.minY);
@@ -210,7 +210,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
addPosAndColor(buffer, bb.maxX, minY, bb.minZ, northSouthColor.getRed(), northSouthColor.getGreen(), northSouthColor.getBlue(), northSouthColor.getAlpha());
}
minY = data.depth;
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
{
maxY = (int) Math.min(minY, bb.maxX);
@@ -234,7 +234,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
} else
{
maxY = data.height;
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
{
minY = (int) Math.max(maxY, bb.minY);
@@ -243,7 +243,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
}
minY = data.depth;
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
{
maxY = (int) Math.min(minY, bb.maxX);
@@ -267,7 +267,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
} else
{
maxY = data.height;
maxY = DataPoint.getHeight(data);
if (maxY < bb.maxY)
{
minY = (int) Math.max(maxY, bb.minY);
@@ -276,7 +276,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
addPosAndColor(buffer, bb.maxX, minY, bb.minZ, eastWestColor.getRed(), eastWestColor.getGreen(), eastWestColor.getBlue(), eastWestColor.getAlpha());
}
minY = data.depth;
minY = DataPoint.getDepth(data);
if (minY > bb.minY)
{
maxY = (int) Math.min(minY, bb.maxX);
@@ -17,15 +17,11 @@
*/
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LevelPos.LevelPos;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
import java.util.Set;
/**
* TODO DynamicLodTemplate
* Chunks smoothly transition between
@@ -38,7 +34,7 @@ import java.util.Set;
public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData,
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
@@ -17,15 +17,11 @@
*/
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LevelPos.LevelPos;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
import java.util.Set;
/**
* TODO #21 TriangularLodTemplate
* Builds each LOD chunk as a singular rectangular prism.
@@ -36,7 +32,7 @@ import java.util.Set;
public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, LodDataPoint data, LodDataPoint[][] adjData,
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
@@ -13,7 +13,7 @@ import com.seibel.lod.builders.GenerationRequest;
import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.handlers.LodConfig;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.DetailDistanceUtil;
@@ -131,14 +131,13 @@ public class LodWorldGenerator
//=======================================//
// start by generating half-region sized blocks...
int farRequesting = maxChunkGenRequests/2;
int farRequesting = maxChunkGenRequests/4;
//we firstly make sure that the world is filled with half region wide block
/*
for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++)
{
if (farRequesting <= 0){
farRequesting=0;
break;
}
levelPosListToGen = lodDim.getDataToGenerate(
@@ -147,20 +146,19 @@ public class LodWorldGenerator
DetailDistanceUtil.getDistanceGeneration(detailGen),
DetailDistanceUtil.getDistanceGeneration(detailGen + 1),
DetailDistanceUtil.getDistanceGenerationMode(detailGen).complexity,
(byte) 7,
(byte) 8,
farRequesting);
for(LevelPos levelPos : levelPosListToGen){
generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen)));
}
farRequesting = farRequesting - levelPosListToGen.size();
}*/
}
// ...then once the world is filled with half-region sized blocks
// fill in the rest
int t = generationRequestList.size();
int nearRequesting = maxChunkGenRequests - maxChunkGenRequests/2 + farRequesting;
System.out.println("clear slot " + nearRequesting);
int nearRequesting = maxChunkGenRequests - maxChunkGenRequests/4 + farRequesting;
//we then fill the world with the rest of the block
for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++)
{
@@ -177,9 +175,7 @@ public class LodWorldGenerator
generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen)));
}
nearRequesting = nearRequesting - levelPosListToGen.size();
System.out.println("adding " + levelPosListToGen.size());
}
System.out.println("generating " + generationRequestList.size() + " and " + t);
//====================================//
@@ -20,7 +20,6 @@ package com.seibel.lod.enums;
import java.util.ArrayList;
import java.util.Collections;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.util.LodUtil;
/**
@@ -63,10 +62,7 @@ public enum LodDetail
public final int[] endX;
public final int[] endZ;
/** This is how many pieces of data should be expected
* when creating a LodChunk with this detail level */
public final int lodChunkStringDelimiterCount;
/**
* 1st dimension: LodDetail.detailLevel <br>
@@ -106,9 +102,6 @@ public enum LodDetail
}
}
lodChunkStringDelimiterCount = 2 + (dataPointLengthCount * dataPointLengthCount * LodDataPoint.NUMBER_OF_DELIMITERS);
}// constructor
@@ -0,0 +1,40 @@
package com.seibel.lod.objects;
public class DataPoint
{
public static short[] createDataPoint(int height, int depth, int red, int green, int blue){
return new short[]{(short) height, (short) depth, (short) red, (short) green, (short) blue};
}
public static short getHeight(short[] dataPoint){
return dataPoint[0];
}
public static short getDepth(short[] dataPoint){
return dataPoint[1];
}
public static short getRed(short[] dataPoint){
return dataPoint[2];
}
public static short getGreen(short[] dataPoint){
return dataPoint[3];
}
public static short getBlue(short[] dataPoint){
return dataPoint[4];
}
public static short[] getHeightDepth(short[] dataPoint){
return new short[]{dataPoint[0], dataPoint[1]};
}
public static int getColor(short[] dataPoint){
int R = (dataPoint[2] << 16) & 0x00FF0000;
int G = (dataPoint[3] << 8) & 0x0000FF00;
int B = dataPoint[4] & 0x000000FF;
return 0xFF000000 | R | G | B;
}
}
@@ -0,0 +1,7 @@
package com.seibel.lod.objects.LevelPos;
public interface ImmutableLevelPos
{
public LevelPos getConvertedLevelPos(byte newDetailLevel);
public LevelPos getRegionModuleLevelPos();
}
@@ -1,15 +1,23 @@
package com.seibel.lod.objects;
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 net.minecraft.world.chunk.Chunk;
import java.util.Comparator;
import java.util.Map;
public class LevelPos implements Cloneable
public class LevelPos implements Cloneable, ImmutableLevelPos, MutableLevelPos
{
public final byte detailLevel;
public final int posX;
public final int posZ;
public byte detailLevel;
public int posX;
public int posZ;
public LevelPos()
{
}
public LevelPos(byte detailLevel, int posX, int posZ)
{
@@ -18,46 +26,117 @@ public class LevelPos implements Cloneable
this.detailLevel = detailLevel;
}
public LevelPos convert(byte newDetailLevel)
/**
* 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, (int) Math.pow(2, newDetailLevel - detailLevel)),
Math.floorDiv(posZ, (int) Math.pow(2, newDetailLevel - detailLevel)));
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
} else
{
int width = 1 << (detailLevel - newDetailLevel);
return new LevelPos(
newDetailLevel,
posX * (int) Math.pow(2, detailLevel - newDetailLevel),
posZ * (int) Math.pow(2, detailLevel - newDetailLevel));
posX * width,
posZ * width);
}
}
public LevelPos regionModule()
/**
* 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, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)),
Math.floorMod(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - 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);
posX = 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;
posX = newPosZ;
}
public RegionPos getRegionPos()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return new RegionPos(
Math.floorDiv(posX, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)),
Math.floorDiv(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)));
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
}
@Override
public LevelPos clone()
public ChunkPos getChunkPos()
{
return new LevelPos(detailLevel, posX, posZ);
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*/
/**
* TODO fix the region disappearing for a second
*/
public int maxDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ)
{
@@ -192,7 +271,7 @@ public class LevelPos implements Cloneable
@Override
public int compare(Map.Entry<LevelPos, Integer> first, Map.Entry<LevelPos, Integer> second)
{
Integer compareResult = Integer.compare(first.getKey().detailLevel, second.getKey().detailLevel);
int compareResult = Integer.compare(first.getKey().detailLevel, second.getKey().detailLevel);
if (compareResult != 0)
{
compareResult = Integer.compare(first.getValue(), second.getValue());
@@ -201,6 +280,14 @@ public class LevelPos implements Cloneable
}
}
@Override
public LevelPos clone()
{
return new LevelPos(detailLevel, posX, posZ);
}
@Override
public int hashCode()
{
int hash = 7;
@@ -210,11 +297,12 @@ public class LevelPos implements Cloneable
return hash;
}
public boolean equals(LevelPos other)
@Override
public boolean equals(Object other)
{
return (this.detailLevel == other.detailLevel &&
this.posX == other.posX &&
this.posZ == other.posZ);
return (this.detailLevel == ((LevelPos) other).detailLevel &&
this.posX == ((LevelPos) other).posX &&
this.posZ == ((LevelPos) other).posZ);
}
@Override
@@ -0,0 +1,14 @@
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,146 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.objects;
import java.awt.*;
import java.io.Serializable;
import java.util.Objects;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.util.LodUtil;
/**
* This stores the height and color
* for a specific area in a LodChunk.
*
* @author James Seibel
* @version 8-8-2021
*/
public class LodDataPoint implements Serializable
{
/**
* This is what separates each piece of data in the toData method
*/
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
/**
* this is how many pieces of data are exported when toData is called
*/
public static final int NUMBER_OF_DELIMITERS = 5;
/**
* a empty data point that can be used for comparisons
*/
public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint();
/**
* highest point
*/
public short height;
/**
* lowest point
*/
public short depth;
/**
* The average color for the 6 cardinal directions
*/
public Color color;
/**
* Creates an empty LodDataPoint
*/
public LodDataPoint()
{
height = -1;
depth = -1;
color = LodUtil.COLOR_INVISIBLE;
}
public LodDataPoint(short newHeight, short newDepth, Color newColor)
{
height = newHeight;
depth = newDepth;
color = newColor;
}
public LodDataPoint(int newHeight, int newDepth, Color newColor)
{
height = (short) newHeight;
depth = (short) newDepth;
color = newColor;
}
@Override
public int hashCode()
{
return Objects.hash(this.height, this.depth, this.color);
}
public boolean equals(LodDataPoint other)
{
return (this.height == other.height
&& this.depth == other.depth
&& this.color == other.color);
}
public boolean isEmpty()
{
return this.equals(EMPTY_DATA_POINT);
}
/**
* Outputs all data in a csv format
* with the given delimiter.
* <br>
* Exports data in the form:
* <br>
* height, depth, rgb color data
*
* <br>
* example output:
* <br>
* 4, 0, 255,255,255,
*/
public String toData()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
@Override
public String toString()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
}
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
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.LodUtil;
@@ -400,7 +401,7 @@ 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, LodDataPoint lodDataPoint, DistanceGenerationMode generationMode, boolean update, boolean dontSave)
public synchronized Boolean addData(LevelPos levelPos, short[] lodDataPoint, DistanceGenerationMode generationMode, boolean dontSave)
{
// don't continue if the region can't be saved
@@ -412,7 +413,7 @@ public class LodDimension
LodRegion region = getRegion(levelPos);
boolean nodeAdded = region.setData(levelPos, lodDataPoint, generationMode.complexity, true);
boolean nodeAdded = region.setData(levelPos, lodDataPoint, generationMode.complexity);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
@@ -432,20 +433,6 @@ public class LodDimension
}
/**
* 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 LodDataPoint getData(ChunkPos chunkPos)
{
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z);
return getData(levelPos);
}
/**
* method to get all the quadtree level that have to be generated based on the position of the player
*
@@ -474,7 +461,7 @@ public class LodDimension
if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) &&
start <= regionLevelPos.maxDistance(playerPosX, playerPosZ))
{
region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).convert(detailLevel));
region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel));
listOfData.addAll(region.getDataToGenerate(playerPosX, playerPosZ, start, end, generation, detailLevel, dataNumber));
}
}catch (Exception e){
@@ -510,7 +497,7 @@ public class LodDimension
if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) &&
start <= regionLevelPos.maxDistance(playerPosX, playerPosZ))
{
LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).convert(detailLevel));
LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel));
listOfData.addAll(region.getDataToRender(playerPosX, playerPosZ, start, end, detailLevel,zFix));
}
}catch (Exception e){
@@ -522,6 +509,19 @@ public class LodDimension
}
}
/**
* 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);
}
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
@@ -529,7 +529,7 @@ public class LodDimension
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public LodDataPoint getData(LevelPos levelPos)
public short[] getData(LevelPos levelPos)
{
if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
@@ -1,16 +1,14 @@
package com.seibel.lod.objects;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.handlers.LodConfig;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import java.awt.*;
import java.io.Serializable;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
/**
* STANDARD TO FOLLOW
@@ -128,12 +126,11 @@ public class LodRegion implements Serializable
* @param levelPos
* @param dataPoint
* @param generationType
* @param update
* @return
*/
public boolean setData(LevelPos levelPos, LodDataPoint dataPoint, byte generationType, boolean update)
public boolean setData(LevelPos levelPos, short[] dataPoint, byte generationType)
{
levelPos = levelPos.regionModule();
levelPos.performRegionModule();
if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]))
{
@@ -141,24 +138,13 @@ public class LodRegion implements Serializable
if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++;
//add the node data
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint.color.getRed() - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (dataPoint.color.getGreen() - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (dataPoint.color.getBlue() - 128);
this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.height;
this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.depth;
this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint[0];
this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint[1];
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint[2] - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (dataPoint[3] - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (dataPoint[4] - 128);
this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = generationType;
this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true;
//update could be stopped and a single big update could be done at the end
LevelPos tempLevelPos = levelPos;
if (update)
{
for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
{
tempLevelPos = tempLevelPos.convert(tempLod);
update(tempLevelPos);
}
}
return true;
} else
{
@@ -167,7 +153,7 @@ public class LodRegion implements Serializable
}
public LodDataPoint getData(ChunkPos chunkPos)
public short[] getData(ChunkPos chunkPos)
{
return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
}
@@ -178,7 +164,7 @@ public class LodRegion implements Serializable
* @param lod
* @return the data at the relative pos and level
*/
public LodDataPoint getData(byte lod, BlockPos blockPos)
public short[] getData(byte lod, BlockPos blockPos)
{
int posX = Math.floorMod(blockPos.getX(), (int) Math.pow(2, lod));
int posZ = Math.floorMod(blockPos.getZ(), (int) Math.pow(2, lod));
@@ -191,17 +177,15 @@ public class LodRegion implements Serializable
* @param levelPos
* @return the data at the relative pos and level
*/
public LodDataPoint getData(LevelPos levelPos)
public short[] getData(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
return new LodDataPoint(
height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
levelPos = levelPos.getRegionModuleLevelPos();
return new short[]{height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
new Color(colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128,
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128,
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128
)
);
(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)
};
}
/**
@@ -296,8 +280,9 @@ public class LodRegion implements Serializable
}
}
} else
//now we keep exploring the top right child
{
childPos = levelPos.convert((byte) (levelPos.detailLevel - 1));
childPos = levelPos.getConvertedLevelPos((byte) (levelPos.detailLevel - 1));
if (generationType[childPos.detailLevel][childPos.posX][childPos.posZ] < generation)
{
minDistance = childPos.minDistance(playerPosX,playerPosZ,regionPosX,regionPosZ);
@@ -403,7 +388,7 @@ public class LodRegion implements Serializable
int startX;
int startZ;
for(byte bottom = (byte) (minDetailLevel + 1); bottom < levelPos.detailLevel ; bottom++){
tempLevelPos = levelPos.convert(bottom);
tempLevelPos = levelPos.getConvertedLevelPos(bottom);
startX = tempLevelPos.posX;
startZ = tempLevelPos.posZ;
sizeDiff = (int) Math.pow(2, levelPos.detailLevel - bottom);
@@ -415,7 +400,7 @@ public class LodRegion implements Serializable
}
for (byte tempLod = levelPos.detailLevel; tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
tempLevelPos = levelPos.convert(tempLod);
tempLevelPos = levelPos.getConvertedLevelPos(tempLod);
update(tempLevelPos);
}
}
@@ -426,7 +411,7 @@ public class LodRegion implements Serializable
private void update(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
int numberOfChildren = 0;
/**TODO add the ability to change how the heigth and depth are determinated (for example min or max)**/
@@ -480,7 +465,7 @@ public class LodRegion implements Serializable
*/
private boolean[][] getChildren(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
boolean[][] children = new boolean[2][2];
int numberOfChild = 0;
if (minDetailLevel == levelPos.detailLevel)
@@ -512,7 +497,7 @@ public class LodRegion implements Serializable
*/
public boolean doesDataExist(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ];
}
@@ -522,7 +507,7 @@ public class LodRegion implements Serializable
*/
public DistanceGenerationMode getGenerationMode(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
DistanceGenerationMode generationMode = DistanceGenerationMode.NONE;
switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])
{
@@ -558,7 +543,7 @@ public class LodRegion implements Serializable
*/
public boolean hasDataBeenGenerated(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0);
}
@@ -95,9 +95,8 @@ public class ClientProxy
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
return;
applyConfigOverrides();
viewDistanceChangedEvent();
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
@@ -125,7 +124,9 @@ public class ClientProxy
profiler.pop(); // end LOD
profiler.push("terrain"); // restart "terrain"
applyConfigOverrides();
// 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
@@ -156,10 +157,9 @@ public class ClientProxy
// LodConfig.CLIENT.saturationMultiplier.set(1.0);
LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
LodConfig.CLIENT.lodChunkRenderDistance.set(256);
LodConfig.CLIENT.lodChunkRenderDistance.set(128);
LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
LodConfig.CLIENT.lodQuality.set(1);
LodConfig.CLIENT.lodQuality.set(2);
LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors());
@@ -21,8 +21,7 @@ import java.awt.Color;
import java.io.File;
import java.util.HashSet;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.RegionPos;
@@ -325,11 +324,9 @@ public class LodUtil
if (!lodDim.doesDataExist(levelPos))
continue;
LodDataPoint data = lodDim.getData(levelPos);
if (data == null)
continue;
short[] data = lodDim.getData(levelPos);
short lodAverageHeight = data.height;
short lodAverageHeight = data[0];
if (playerPos.getY() <= lodAverageHeight)
{
// don't draw Lod's that are taller than the player