changed indent

This commit is contained in:
Leonardo
2021-08-27 14:34:43 +02:00
parent 9990132db2
commit db2410e7fe
25 changed files with 3853 additions and 3794 deletions
@@ -12,20 +12,20 @@ import net.minecraft.util.math.ChunkPos;
*/
public class GenerationRequest
{
public final LevelPos levelPos;
public final DistanceGenerationMode generationMode;
public final LodDetail detail;
public final LevelPos levelPos;
public final DistanceGenerationMode generationMode;
public final LodDetail detail;
public GenerationRequest(LevelPos levelPos, DistanceGenerationMode generationMode, LodDetail detail)
{
this.levelPos = levelPos;
this.generationMode = generationMode;
this.detail = detail;
}
public GenerationRequest(LevelPos levelPos, DistanceGenerationMode generationMode, LodDetail detail)
{
this.levelPos = levelPos;
this.generationMode = generationMode;
this.detail = detail;
}
public ChunkPos getChunkPos()
{
LevelPos chunkLevelPos = levelPos.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL);
return new ChunkPos(chunkLevelPos.posX, chunkLevelPos.posZ);
}
public ChunkPos getChunkPos()
{
LevelPos chunkLevelPos = levelPos.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL);
return new ChunkPos(chunkLevelPos.posX, chunkLevelPos.posZ);
}
}
@@ -53,431 +53,431 @@ import javax.xml.crypto.Data;
*/
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(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder"));
/**
* 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(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder"));
/**
* The buffers that are used to create LODs using far fog
*/
public volatile BufferBuilder[][] buildableBuffers;
/**
* The buffers that are used to create LODs using far fog
*/
public volatile BufferBuilder[][] buildableBuffers;
/**
* Used when building new VBOs
*/
public volatile VertexBuffer[][] buildableVbos;
/**
* Used when building new VBOs
*/
public volatile VertexBuffer[][] buildableVbos;
/**
* VBOs that are sent over to the LodNodeRenderer
*/
public volatile VertexBuffer[][] drawableVbos;
/**
* 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 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();
try
{
long treeStart = System.currentTimeMillis();
long treeEnd = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
ArrayList<Callable<Boolean>> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
startBuffers();
// =====================//
// RENDERING PART //
// =====================//
Object[][] nodeToRenderMatrix = new Object[lodDim.regions.length][lodDim.regions.length];
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
{
for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++)
{
nodeToRenderMatrix[xRegion][zRegion] = new ConcurrentSkipListMap<>(LevelPos.getComparator());
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;
final int xR = xRegion;
final int zR = zRegion;
Callable<Boolean> dataToRenderThread = () ->
{
byte detailToRender;
boolean zFix;
for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
detailToRender = detail;
lodDim.getDataToRender(
(ConcurrentSkipListMap<LevelPos, List<Integer>>) nodeToRenderMatrix[xR][zR],
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
DetailDistanceUtil.getDistanceRendering(detail),
DetailDistanceUtil.getDistanceRendering(detail + 1),
detailToRender,
true);
}
// the thread executed successfully
return true;
};// buffer builder worker thread
nodeToRenderThreads.add(dataToRenderThread);
}// region z
}// region z
long renderStart = System.currentTimeMillis();
// wait for all threads to finish
List<Future<Boolean>> futures = bufferBuilderThreads.invokeAll(nodeToRenderThreads);
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();
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;
final int xR = xRegion;
final int zR = zRegion;
Callable<Boolean> bufferBuildingThread = () ->
{
LevelPos adjPos = new LevelPos();
for (LevelPos posToRender : ((ConcurrentSkipListMap<LevelPos, List<Integer>>) nodeToRenderMatrix[xR][zR]).keySet())
{
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;
}
try
{
if (lodDim.doesDataExist(posToRender.clone()))
{
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()))
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()))
adjData[1][z] = lodDim.getData(adjPos);
}
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
// the thread executed successfully
return true;
};// buffer builder worker thread
builderThreads.add(bufferBuildingThread);
}// region z
}// region z
long renderBufferStart = System.currentTimeMillis();
// wait for all threads to finish
List<Future<Boolean>> futuresBuffer = bufferBuilderThreads.invokeAll(builderThreads);
for (Future<Boolean> future : futuresBuffer)
{
// 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 renderBufferEnd = System.currentTimeMillis();
// 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;
}
/**
* 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();
try
{
long treeStart = System.currentTimeMillis();
long treeEnd = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
ArrayList<Callable<Boolean>> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
startBuffers();
// =====================//
// RENDERING PART //
// =====================//
Object[][] nodeToRenderMatrix = new Object[lodDim.regions.length][lodDim.regions.length];
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
{
for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++)
{
nodeToRenderMatrix[xRegion][zRegion] = new ConcurrentSkipListMap<>(LevelPos.getComparator());
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;
final int xR = xRegion;
final int zR = zRegion;
Callable<Boolean> dataToRenderThread = () ->
{
byte detailToRender;
boolean zFix;
for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
detailToRender = detail;
lodDim.getDataToRender(
(ConcurrentSkipListMap<LevelPos, List<Integer>>) nodeToRenderMatrix[xR][zR],
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
DetailDistanceUtil.getDistanceRendering(detail),
DetailDistanceUtil.getDistanceRendering(detail + 1),
detailToRender,
true);
}
// the thread executed successfully
return true;
};// buffer builder worker thread
nodeToRenderThreads.add(dataToRenderThread);
}// region z
}// region z
long renderStart = System.currentTimeMillis();
// wait for all threads to finish
List<Future<Boolean>> futures = bufferBuilderThreads.invokeAll(nodeToRenderThreads);
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();
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;
final int xR = xRegion;
final int zR = zRegion;
Callable<Boolean> bufferBuildingThread = () ->
{
LevelPos adjPos = new LevelPos();
for (LevelPos posToRender : ((ConcurrentSkipListMap<LevelPos, List<Integer>>) nodeToRenderMatrix[xR][zR]).keySet())
{
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;
}
try
{
if (lodDim.doesDataExist(posToRender.clone()))
{
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()))
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()))
adjData[1][z] = lodDim.getData(adjPos);
}
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
// the thread executed successfully
return true;
};// buffer builder worker thread
builderThreads.add(bufferBuildingThread);
}// region z
}// region z
long renderBufferStart = System.currentTimeMillis();
// wait for all threads to finish
List<Future<Boolean>> futuresBuffer = bufferBuilderThreads.invokeAll(builderThreads);
for (Future<Boolean> future : futuresBuffer)
{
// 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 renderBufferEnd = System.currentTimeMillis();
// 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;
}
}
@@ -57,496 +57,500 @@ import net.minecraft.world.gen.Heightmap;
*/
public class LodBuilder
{
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH;
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH;
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
/** If no blocks are found in the area in determineBottomPointForArea return this */
public static final short DEFAULT_DEPTH = -1;
/** If no blocks are found in the area in determineHeightPointForArea return this */
/**
* If no blocks are found in the area in determineBottomPointForArea return this
*/
public static final short DEFAULT_DEPTH = -1;
/**
* If no blocks are found in the area in determineHeightPointForArea return this
*/
public static final short DEFAULT_HEIGHT = -1;
/**
* How wide LodDimensions should be in regions
*/
public int defaultDimensionWidthInRegions = 5;
public LodBuilder()
{
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
{
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
{
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
return;
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
if (chunk == null)
return;
Thread thread = new Thread(() ->
{
try
{
DimensionType dim = world.dimensionType();
LodDimension lodDim;
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
lodWorld.addLodDimension(lodDim);
} else
{
lodDim = lodWorld.getLodDimension(dim);
}
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode), LodConfig.CLIENT.maxGenerationDetail.get());
} 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
// exist.
}
});
lodGenThreadPool.execute(thread);
return;
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodDetail detailLevel) throws IllegalArgumentException
{
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(), detailLevel);
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config, LodDetail detail)
throws IllegalArgumentException
{
if (chunk == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
boolean check = false;
int startX;
int startZ;
int endX;
int endZ;
short[] color;
short height;
short depth;
LevelPos levelPos = new LevelPos((byte) 0, 0, 0);
short[] data;
try
{
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
{
startX = detail.startX[i];
startZ = detail.startZ[i];
endX = detail.endX[i];
endZ = detail.endZ[i];
color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
if (!config.useHeightmap)
{
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
} else
{
height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP), startX,
startZ, endX, endZ);
depth = 0;
}
levelPos.changeParameters((byte) 0,
chunk.getPos().x * 16 + startX,
chunk.getPos().z * 16 + startZ);
levelPos.convert(detail.detailLevel);
data = DataPoint.createDataPoint(height, depth, color[0], color[1], color[2]);
lodDim.addData(levelPos,
data,
config.distanceGenerationMode,
false);
}
//levelPos.changeParameters(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
lodDim.updateData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z));
} catch (NullPointerException e)
{
e.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
}
}
// =====================//
// constructor helpers //
// =====================//
/**
* Find the lowest valid point from the bottom.
*/
private short determineBottomPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the bottom up
for (int section = 0; section < CHUNK_DATA_WIDTH; section++)
{
for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return DEFAULT_DEPTH;
}
/**
* Find the lowest valid point from the bottom.
*/
@SuppressWarnings("unused")
private short determineBottomPoint(Heightmap heightmap)
{
// the heightmap only shows how high the blocks go, it
// doesn't have any info about how low they go
return 0;
}
/**
* Find the highest valid point from the Top
*/
private short determineHeightPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the top down
for (int section = chunkSections.length - 1; section >= 0; section--)
{
for (int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + 1 + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return DEFAULT_HEIGHT;
}
/**
* Find the highest point from the Top
*/
private short determineHeightPoint(Heightmap heightmap, int startX, int startZ, int endX, int endZ)
{
short highest = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
short newHeight = (short) heightmap.getFirstAvailable(x, z);
if (newHeight > highest)
highest = newHeight;
}
}
return highest;
}
/**
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*
* @param config_useSolidBlocksInColorGen <br>
* If true we look down from the top of
* the <br>
* chunk until we find a non-invisible
* block, and then use <br>
* its color. If false we generate the
* color immediately for <br>
* each x and z.
* @param config_useBiomeColors <br>
* If true use biome foliage, water, and
* grass colors, <br>
* otherwise only use the block's
* material color
*/
private short[] generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
{
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
int red = 0;
int green = 0;
int blue = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
boolean foundBlock = false;
// go top down
for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--)
{
if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen))
{
for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--)
{
int colorInt = 0;
BlockState blockState = null;
if (chunkSections[i] != null)
{
blockState = chunkSections[i].getBlockState(x, y, z);
colorInt = blockState.materialColor.col;
}
if (colorInt == 0 && config.useSolidBlocksInColorGen)
{
// skip air or invisible blocks
continue;
}
if (config.useBiomeColors)
{
// I have no idea why I need to bit shift to the right, but
// if I don't the biomes don't show up correctly.
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBiome(x, z, biome);
} else
{
// the bit shift is equivalent to dividing by 4
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBlock(x, z, blockState, biome);
}
red += ColorUtil.getRed(colorInt);
green += ColorUtil.getGreen(colorInt);
blue += ColorUtil.getBlue(colorInt);
numbOfBlocks++;
// we found a valid block, skip to the
// next x and z
foundBlock = true;
}
}
}
}
}
if (numbOfBlocks == 0)
numbOfBlocks = 1;
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new short[]{(short) red, (short) green, (short) blue};
}
/**
* Returns a color int for a given block.
*/
private int getColorForBlock(int x, int z, BlockState blockState, Biome biome)
{
int colorInt = 0;
// block special cases
if (blockState == Blocks.AIR.defaultBlockState() || blockState == Blocks.CAVE_AIR.defaultBlockState())
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
} else if (blockState == Blocks.MYCELIUM.defaultBlockState())
{
colorInt = LodUtil.MYCELIUM_COLOR_INT;
}
// plant life
else if (blockState.getBlock() instanceof LeavesBlock || blockState.getBlock() == Blocks.VINE)
{
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
leafColor = leafColor.darker();
colorInt = LodUtil.colorToInt(leafColor);
} else if ((blockState.getBlock() instanceof GrassBlock || blockState.getBlock() instanceof AbstractPlantBlock
|| blockState.getBlock() instanceof BushBlock || blockState.getBlock() instanceof IGrowable)
&& !(blockState.getBlock() == Blocks.BROWN_MUSHROOM || blockState.getBlock() == Blocks.RED_MUSHROOM))
{
Color plantColor = LodUtil.intToColor(biome.getGrassColor(x, z));
plantColor = plantColor.darker();
colorInt = LodUtil.colorToInt(plantColor);
}
// water
else if (blockState.getBlock() == Blocks.WATER)
{
colorInt = biome.getWaterColor();
}
// everything else
else
{
colorInt = blockState.materialColor.col;
}
return colorInt;
}
/**
* Returns a color int for the given biome.
*/
private int getColorForBiome(int x, int z, Biome biome)
{
int colorInt = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col;
break;
case THEEND:
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
break;
case BEACH:
case DESERT:
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
break;
case EXTREME_HILLS:
colorInt = Blocks.STONE.defaultMaterialColor().col;
break;
case MUSHROOM:
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
break;
case ICY:
colorInt = Blocks.SNOW.defaultMaterialColor().col;
break;
case MESA:
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
return colorInt;
}
/**
* Is the layer between the given X, Z, and dataIndex values a valid LOD point?
*/
private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z)
{
if (chunkSections[sectionIndex] == null)
{
// this section doesn't have any blocks,
// it is not a valid section
return false;
} else
{
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)
{
return true;
}
}
return false;
}
/**
* How wide LodDimensions should be in regions
*/
public int defaultDimensionWidthInRegions = 5;
public LodBuilder()
{
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
{
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
{
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
return;
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
if (chunk == null)
return;
Thread thread = new Thread(() ->
{
try
{
DimensionType dim = world.dimensionType();
LodDimension lodDim;
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
lodWorld.addLodDimension(lodDim);
} else
{
lodDim = lodWorld.getLodDimension(dim);
}
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode), LodConfig.CLIENT.maxGenerationDetail.get());
} 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
// exist.
}
});
lodGenThreadPool.execute(thread);
return;
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodDetail detailLevel) throws IllegalArgumentException
{
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(), detailLevel);
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config, LodDetail detail)
throws IllegalArgumentException
{
if (chunk == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
boolean check = false;
int startX;
int startZ;
int endX;
int endZ;
short[] color;
short height;
short depth;
LevelPos levelPos = new LevelPos((byte) 0, 0, 0);
short[] data;
try
{
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
{
startX = detail.startX[i];
startZ = detail.startZ[i];
endX = detail.endX[i];
endZ = detail.endZ[i];
color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
if (!config.useHeightmap)
{
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
} else
{
height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP), startX,
startZ, endX, endZ);
depth = 0;
}
levelPos.changeParameters((byte) 0,
chunk.getPos().x * 16 + startX,
chunk.getPos().z * 16 + startZ);
levelPos.convert(detail.detailLevel);
data = DataPoint.createDataPoint(height, depth, color[0], color[1], color[2]);
lodDim.addData(levelPos,
data,
config.distanceGenerationMode,
false);
}
//levelPos.changeParameters(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
lodDim.updateData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z));
} catch (NullPointerException e)
{
e.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
}
}
// =====================//
// constructor helpers //
// =====================//
/**
* Find the lowest valid point from the bottom.
*/
private short determineBottomPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the bottom up
for (int section = 0; section < CHUNK_DATA_WIDTH; section++)
{
for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return DEFAULT_DEPTH;
}
/**
* Find the lowest valid point from the bottom.
*/
@SuppressWarnings("unused")
private short determineBottomPoint(Heightmap heightmap)
{
// the heightmap only shows how high the blocks go, it
// doesn't have any info about how low they go
return 0;
}
/**
* Find the highest valid point from the Top
*/
private short determineHeightPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the top down
for (int section = chunkSections.length - 1; section >= 0; section--)
{
for (int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + 1 + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return DEFAULT_HEIGHT;
}
/**
* Find the highest point from the Top
*/
private short determineHeightPoint(Heightmap heightmap, int startX, int startZ, int endX, int endZ)
{
short highest = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
short newHeight = (short) heightmap.getFirstAvailable(x, z);
if (newHeight > highest)
highest = newHeight;
}
}
return highest;
}
/**
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*
* @param config_useSolidBlocksInColorGen <br>
* If true we look down from the top of
* the <br>
* chunk until we find a non-invisible
* block, and then use <br>
* its color. If false we generate the
* color immediately for <br>
* each x and z.
* @param config_useBiomeColors <br>
* If true use biome foliage, water, and
* grass colors, <br>
* otherwise only use the block's
* material color
*/
private short[] generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
{
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
int red = 0;
int green = 0;
int blue = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
boolean foundBlock = false;
// go top down
for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--)
{
if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen))
{
for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--)
{
int colorInt = 0;
BlockState blockState = null;
if (chunkSections[i] != null)
{
blockState = chunkSections[i].getBlockState(x, y, z);
colorInt = blockState.materialColor.col;
}
if (colorInt == 0 && config.useSolidBlocksInColorGen)
{
// skip air or invisible blocks
continue;
}
if (config.useBiomeColors)
{
// I have no idea why I need to bit shift to the right, but
// if I don't the biomes don't show up correctly.
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBiome(x, z, biome);
} else
{
// the bit shift is equivalent to dividing by 4
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBlock(x, z, blockState, biome);
}
red += ColorUtil.getRed(colorInt);
green += ColorUtil.getGreen(colorInt);
blue += ColorUtil.getBlue(colorInt);
numbOfBlocks++;
// we found a valid block, skip to the
// next x and z
foundBlock = true;
}
}
}
}
}
if (numbOfBlocks == 0)
numbOfBlocks = 1;
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new short[]{(short) red, (short) green, (short) blue};
}
/**
* Returns a color int for a given block.
*/
private int getColorForBlock(int x, int z, BlockState blockState, Biome biome)
{
int colorInt = 0;
// block special cases
if (blockState == Blocks.AIR.defaultBlockState() || blockState == Blocks.CAVE_AIR.defaultBlockState())
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
} else if (blockState == Blocks.MYCELIUM.defaultBlockState())
{
colorInt = LodUtil.MYCELIUM_COLOR_INT;
}
// plant life
else if (blockState.getBlock() instanceof LeavesBlock || blockState.getBlock() == Blocks.VINE)
{
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
leafColor = leafColor.darker();
colorInt = LodUtil.colorToInt(leafColor);
} else if ((blockState.getBlock() instanceof GrassBlock || blockState.getBlock() instanceof AbstractPlantBlock
|| blockState.getBlock() instanceof BushBlock || blockState.getBlock() instanceof IGrowable)
&& !(blockState.getBlock() == Blocks.BROWN_MUSHROOM || blockState.getBlock() == Blocks.RED_MUSHROOM))
{
Color plantColor = LodUtil.intToColor(biome.getGrassColor(x, z));
plantColor = plantColor.darker();
colorInt = LodUtil.colorToInt(plantColor);
}
// water
else if (blockState.getBlock() == Blocks.WATER)
{
colorInt = biome.getWaterColor();
}
// everything else
else
{
colorInt = blockState.materialColor.col;
}
return colorInt;
}
/**
* Returns a color int for the given biome.
*/
private int getColorForBiome(int x, int z, Biome biome)
{
int colorInt = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col;
break;
case THEEND:
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
break;
case BEACH:
case DESERT:
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
break;
case EXTREME_HILLS:
colorInt = Blocks.STONE.defaultMaterialColor().col;
break;
case MUSHROOM:
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
break;
case ICY:
colorInt = Blocks.SNOW.defaultMaterialColor().col;
break;
case MESA:
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
return colorInt;
}
/**
* Is the layer between the given X, Z, and dataIndex values a valid LOD point?
*/
private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z)
{
if (chunkSections[sectionIndex] == null)
{
// this section doesn't have any blocks,
// it is not a valid section
return false;
} else
{
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)
{
return true;
}
}
return false;
}
}
@@ -30,83 +30,83 @@ import com.seibel.lod.enums.DistanceGenerationMode;
*/
public class LodBuilderConfig
{
/**
* default false
*/
public boolean useHeightmap;
/**
* default false
*/
public boolean useBiomeColors;
/**
* default true
*/
public boolean useSolidBlocksInColorGen;
/**
* default server
*/
public DistanceGenerationMode distanceGenerationMode;
/**
* default false
*/
public boolean useHeightmap;
/**
* default false
*/
public boolean useBiomeColors;
/**
* default true
*/
public boolean useSolidBlocksInColorGen;
/**
* default server
*/
public DistanceGenerationMode distanceGenerationMode;
/**
* default settings for a normal chunk <br>
* useHeightmap = false <br>
* useBiomeColors = false <br>
* useSolidBlocksInColorGen = true <br>
* generationMode = Server <br>
*/
public LodBuilderConfig()
{
useHeightmap = false;
useBiomeColors = false;
useSolidBlocksInColorGen = true;
distanceGenerationMode = DistanceGenerationMode.SERVER;
}
/**
* default settings for a normal chunk <br>
* useHeightmap = false <br>
* useBiomeColors = false <br>
* useSolidBlocksInColorGen = true <br>
* generationMode = Server <br>
*/
public LodBuilderConfig()
{
useHeightmap = false;
useBiomeColors = false;
useSolidBlocksInColorGen = true;
distanceGenerationMode = DistanceGenerationMode.SERVER;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors,
boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode)
{
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
distanceGenerationMode = newDistanceGenerationMode;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors,
boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode)
{
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
distanceGenerationMode = newDistanceGenerationMode;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
{
this();
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
if (newUseHeightmap)
{
distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
} else
{
distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY;
}
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
{
this();
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
if (newUseHeightmap)
{
distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
} else
{
distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY;
}
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode)
{
this();
distanceGenerationMode = newDistanceGenerationMode;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode)
{
this();
distanceGenerationMode = newDistanceGenerationMode;
}
}
@@ -35,17 +35,21 @@ import net.minecraft.util.math.BlockPos;
public abstract class AbstractLodTemplate
{
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging);
LevelPos levelPos, boolean debugging);
/** add the given position and color to the buffer */
/**
* add the given position and color to the buffer
*/
protected void addPosAndColor(BufferBuilder buffer,
double x, double y, double z,
int red, int green, int blue, int alpha)
double x, double y, double z,
int red, int green, int blue, int alpha)
{
buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex();
}
/** Returns in bytes how much buffer memory is required
* for one LOD object */
/**
* Returns in bytes how much buffer memory is required
* for one LOD object
*/
public abstract int getBufferMemoryForSingleNode(int level);
}
@@ -38,89 +38,89 @@ import net.minecraft.util.math.BlockPos;
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
private final int CULL_OFFSET = 16;
private final int CULL_OFFSET = 16;
public CubicLodTemplate()
{
public CubicLodTemplate()
{
}
}
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging)
{
AxisAlignedBB bbox;
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging)
{
AxisAlignedBB bbox;
int width = 1 << levelPos.detailLevel;
int width = 1 << levelPos.detailLevel;
// add each LOD for the detail level
bbox = generateBoundingBox(
DataPoint.getHeight(data),
DataPoint.getDepth(data),
width,
levelPos.posX * width,
0,
levelPos.posZ * width);
// add each LOD for the detail level
bbox = generateBoundingBox(
DataPoint.getHeight(data),
DataPoint.getDepth(data),
width,
levelPos.posX * width,
0,
levelPos.posZ * width);
int color = DataPoint.getColor(data);
if (LodConfig.CLIENT.debugMode.get())
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel].getRGB();
}
int color = DataPoint.getColor(data);
if (LodConfig.CLIENT.debugMode.get())
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel].getRGB();
}
if (bbox != null)
{
addBoundingBoxToBuffer(buffer, bbox, color, playerBlockPos, adjData);
}
if (bbox != null)
{
addBoundingBoxToBuffer(buffer, bbox, color, playerBlockPos, adjData);
}
}
}
/*
* @Override public void addLodToBuffer(BufferBuilder buffer,
* LodQuadTreeDimension lodDim, LodQuadTreeNode lod, double xOffset, double
* yOffset, double zOffset, boolean debugging) { AxisAlignedBB bbox;
*
* bbox = generateBoundingBox( lod.getLodDataPoint().height,
* lod.getLodDataPoint().depth, lod.width, xOffset, yOffset, zOffset);
*
* Color color = lod.getLodDataPoint().color;
*
* if (bbox != null) { addBoundingBoxToBuffer(buffer, bbox, color); }
*
* }
*/
/*
* @Override public void addLodToBuffer(BufferBuilder buffer,
* LodQuadTreeDimension lodDim, LodQuadTreeNode lod, double xOffset, double
* yOffset, double zOffset, boolean debugging) { AxisAlignedBB bbox;
*
* bbox = generateBoundingBox( lod.getLodDataPoint().height,
* lod.getLodDataPoint().depth, lod.width, xOffset, yOffset, zOffset);
*
* Color color = lod.getLodDataPoint().color;
*
* if (bbox != null) { addBoundingBoxToBuffer(buffer, bbox, color); }
*
* }
*/
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset)
{
// don't add an LOD if it is empty
if (height == -1 && depth == -1)
return null;
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset)
{
// don't add an LOD if it is empty
if (height == -1 && depth == -1)
return null;
if (depth == height)
{
// if the top and bottom points are at the same height
// render this LOD as 1 block thick
height++;
}
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);
}
return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset);
}
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, int c, BlockPos playerBlockPos, short[][][] adjData)
{
int topColor = c;
int bottomColor = c;
int northColor = c;
int southColor = c;
int westColor = c;
int eastColor = c;
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, int c, BlockPos playerBlockPos, short[][][] adjData)
{
int topColor = c;
int bottomColor = c;
int northColor = c;
int southColor = c;
int westColor = c;
int eastColor = c;
// darken the bottom and side colors if requested
if (LodConfig.CLIENT.shadingMode.get() == ShadingMode.DARKEN_SIDES)
{
// the side colors are different because
// when using fast lighting in Minecraft the north/south
// and east/west sides are different in a similar way
// darken the bottom and side colors if requested
if (LodConfig.CLIENT.shadingMode.get() == ShadingMode.DARKEN_SIDES)
{
// the side colors are different because
// when using fast lighting in Minecraft the north/south
// and east/west sides are different in a similar way
/*
int northSouthDarkenAmount = -25;
int eastWestDarkenAmount = -50;
@@ -133,217 +133,217 @@ public class CubicLodTemplate extends AbstractLodTemplate
float northSouthDarkenAmount = 0.80f;
float eastWestDarkenAmount = 0.60f;
float bottomDarkenAmount = 0.40f;*/
/**TODO OPTIMIZE THIS STEP*/
topColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.UP,true));
bottomColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.DOWN,true));
northColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.NORTH,true));
southColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.SOUTH,true));
westColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.WEST,true));
eastColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.EAST,true));
}
/**TODO OPTIMIZE THIS STEP*/
topColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.UP, true));
bottomColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.DOWN, true));
northColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.NORTH, true));
southColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.SOUTH, true));
westColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.WEST, true));
eastColor = ColorUtil.applyShade(c, Minecraft.getInstance().level.getShade(Direction.EAST, true));
}
// apply the user specified saturation and brightness
float saturationMultiplier = LodConfig.CLIENT.saturationMultiplier.get().floatValue();
float brightnessMultiplier = LodConfig.CLIENT.brightnessMultiplier.get().floatValue();
// apply the user specified saturation and brightness
float saturationMultiplier = LodConfig.CLIENT.saturationMultiplier.get().floatValue();
float brightnessMultiplier = LodConfig.CLIENT.brightnessMultiplier.get().floatValue();
if(saturationMultiplier != 1 || brightnessMultiplier != 1)
{
topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier);
bottomColor = ColorUtil.applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier);
northColor = ColorUtil.applySaturationAndBrightnessMultipliers(northColor, saturationMultiplier, brightnessMultiplier);
southColor = ColorUtil.applySaturationAndBrightnessMultipliers(southColor, saturationMultiplier, brightnessMultiplier);
westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier);
eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier);
}
int minY;
int maxY;
short[] data;
if (saturationMultiplier != 1 || brightnessMultiplier != 1)
{
topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier);
bottomColor = ColorUtil.applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier);
northColor = ColorUtil.applySaturationAndBrightnessMultipliers(northColor, saturationMultiplier, brightnessMultiplier);
southColor = ColorUtil.applySaturationAndBrightnessMultipliers(southColor, saturationMultiplier, brightnessMultiplier);
westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier);
eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier);
}
int minY;
int maxY;
short[] data;
int red;
int green;
int blue;
int alpha;
/**TODO make all of this more automatic if possible*/
if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET)
{
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);
}
if (playerBlockPos.getY() < bb.minY + CULL_OFFSET)
{
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);
}
int red;
int green;
int blue;
int alpha;
/**TODO make all of this more automatic if possible*/
if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET)
{
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);
}
if (playerBlockPos.getY() < bb.minY + CULL_OFFSET)
{
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);
}
if (playerBlockPos.getZ() > bb.minZ - CULL_OFFSET)
{
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.minZ - CULL_OFFSET)
{
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)
{
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.getZ() < bb.maxZ + CULL_OFFSET)
{
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)
{
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)
{
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
{
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, 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 = 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, 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);
}
}
}
if (playerBlockPos.getX() < bb.maxX + CULL_OFFSET)
{
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)
{
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
{
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, 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 = 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, 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);
}
}
}
if (playerBlockPos.getX() > bb.minX - CULL_OFFSET)
{
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)
{
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);
} else
{
maxY = DataPoint.getHeight(data);
if (maxY < bb.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 = DataPoint.getDepth(data);
if (minY > bb.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);
}
}
}
}
if (playerBlockPos.getX() > bb.minX - CULL_OFFSET)
{
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)
{
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);
} else
{
maxY = DataPoint.getHeight(data);
if (maxY < bb.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 = DataPoint.getDepth(data);
if (minY > bb.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);
}
}
}
}
@Override
public int getBufferMemoryForSingleNode(int detailLevel)
{
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints))) *
// howManyPointsPerLodChunk
return (6 * 4 * (3 + 4));
}
@Override
public int getBufferMemoryForSingleNode(int detailLevel)
{
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints))) *
// howManyPointsPerLodChunk
return (6 * 4 * (3 + 4));
}
}
@@ -27,7 +27,7 @@ import net.minecraft.util.math.BlockPos;
* Chunks smoothly transition between
* each other, unless a neighboring chunk
* is at a significantly different height.
*
*
* @author James Seibel
* @version 06-16-2021
*/
@@ -35,7 +35,7 @@ public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging)
LevelPos levelPos, boolean debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
}
@@ -25,7 +25,7 @@ import net.minecraft.util.math.BlockPos;
/**
* TODO #21 TriangularLodTemplate
* Builds each LOD chunk as a singular rectangular prism.
*
*
* @author James Seibel
* @version 06-16-2021
*/
@@ -33,7 +33,7 @@ public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
LevelPos levelPos, boolean debugging)
LevelPos levelPos, boolean debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
}
@@ -65,259 +65,266 @@ import net.minecraft.world.storage.IWorldInfo;
* This allows us to keep each LodChunk generation independent
* of the actual ServerWorld, allowing us
* to multithread generation.
*
*
* @author James Seibel
* @version 7-26-2021
*/
public class LodServerWorld implements ISeedReader {
public class LodServerWorld implements ISeedReader
{
public HashMap<Heightmap.Type, Heightmap> heightmaps = new HashMap<>();
public IChunk chunk;
public ServerWorld serverWorld;
public LodServerWorld(ServerWorld newServerWorld, IChunk newChunk)
{
chunk = newChunk;
serverWorld = newServerWorld;
}
@Override
public int getHeight(Type heightmapType, int x, int z)
{
// make sure the block position is set relative to the chunk
x = x % LodUtil.CHUNK_WIDTH;
x = (x < 0) ? x + 16 : x;
z = z % LodUtil.CHUNK_WIDTH;
z = (z < 0) ? z + 16 : z;
return chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z);
}
@Override
public Biome getBiome(BlockPos pos)
{
return chunk.getBiomes().getNoiseBiome(pos.getX() >> 2, pos.getY() >> 2, pos.getZ() >> 2);
}
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft)
{
return chunk.setBlockState(pos, state, false) == state;
}
@Override
public BlockState getBlockState(BlockPos pos)
{
return chunk.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos pos)
{
return chunk.getFluidState(pos);
}
@Override
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> state)
{
return state.test(chunk.getBlockState(pos));
}
@Override
public ITickList<Block> getBlockTicks()
{
return EmptyTickList.empty();
}
@Override
public IChunk getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull)
{
return chunk;
}
@Override
public Stream<? extends StructureStart<?>> startsForFeature(SectionPos p_241827_1_, Structure<?> p_241827_2_)
{
return serverWorld.startsForFeature(p_241827_1_, p_241827_2_);
}
@Override
public ITickList<Fluid> getLiquidTicks()
{
return EmptyTickList.empty();
}
@Override
public WorldLightManager getLightEngine()
{
return new WorldLightManager(null, false, false);
}
@Override
public long getSeed()
{
return serverWorld.getSeed();
}
@Override
public DynamicRegistries registryAccess()
{
return serverWorld.registryAccess();
}
/**
*
* All methods below shouldn't be needed
* and thus have been left unimplemented.
*
* and thus have been left unimplemented.
*/
@Override
public Random getRandom() {
public Random getRandom()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void playSound(PlayerEntity player, BlockPos pos, SoundEvent soundIn, SoundCategory category, float volume,
float pitch) {
float pitch)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void addParticle(IParticleData particleData, double x, double y, double z, double xSpeed, double ySpeed,
double zSpeed) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public BiomeManager getBiomeManager() {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSeaLevel() {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public float getShade(Direction p_230487_1_, boolean p_230487_2_) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public WorldBorder getWorldBorder() {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean removeBlock(BlockPos pos, boolean isMoving) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft) {
double zSpeed)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public ServerWorld getLevel() {
public BiomeManager getBiomeManager()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSeaLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public float getShade(Direction p_230487_1_, boolean p_230487_2_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public WorldBorder getWorldBorder()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean removeBlock(BlockPos pos, boolean isMoving)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public AbstractChunkProvider getChunkSource() {
public ServerWorld getLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0) {
public AbstractChunkProvider getChunkSource()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public IWorldInfo getLevelData() {
public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void levelEvent(PlayerEntity arg0, int arg1, BlockPos arg2, int arg3) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<Entity> getEntities(Entity arg0, AxisAlignedBB arg1, Predicate<? super Entity> arg2) {
public IWorldInfo getLevelData()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void levelEvent(PlayerEntity arg0, int arg1, BlockPos arg2, int arg3)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<Entity> getEntities(Entity arg0, AxisAlignedBB arg1, Predicate<? super Entity> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public <T extends Entity> List<T> getEntitiesOfClass(Class<? extends T> arg0, AxisAlignedBB arg1,
Predicate<? super T> arg2) {
Predicate<? super T> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<? extends PlayerEntity> players() {
public List<? extends PlayerEntity> players()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSkyDarken() {
public int getSkyDarken()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) {
public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean isClientSide() {
public boolean isClientSide()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DimensionType dimensionType() {
public DimensionType dimensionType()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public TileEntity getBlockEntity(BlockPos p_175625_1_) {
public TileEntity getBlockEntity(BlockPos p_175625_1_)
{
throw new UnsupportedOperationException("Not Implemented");
}
}
@@ -28,20 +28,24 @@ import net.minecraftforge.common.WorldWorkerManager;
/**
* A singleton that handles all long distance LOD world generation.
*
*
* @author James Seibel
* @version 8-24-2021
*/
public class LodWorldGenerator
{
public Minecraft mc = Minecraft.getInstance();
/** This holds the thread used to generate new LODs off the main thread. */
/**
* This holds the thread used to generate new LODs off the main thread.
*/
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
/** we only want to queue up one generator thread at a time */
/**
* we only want to queue up one generator thread at a time
*/
private boolean generatorThreadRunning = false;
/**
* how many chunks to generate outside of the player's view distance at one
* time. (or more specifically how many requests to make at one time). I
@@ -50,42 +54,44 @@ public class LodWorldGenerator
* possible.
*/
public int maxChunkGenRequests;
/**
* This keeps track of how many chunk generation requests are on going. This is
* to limit how many chunks are queued at once. To prevent chunks from being
* generated for a long time in an area the player is no longer in.
*/
public AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0);
public Set<ChunkPos> positionWaitingToBeGenerated = new HashSet<>();
/** Singleton copy of this object */
/**
* Singleton copy of this object
*/
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
private LodWorldGenerator()
{
}
/**
* Queues up LodNodeGenWorkers for the given lodDimension.
*
*
* @param renderer needed so the LodNodeGenWorkers can flag that the
* buffers need to be rebuilt.
* buffers need to be rebuilt.
*/
public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder)
{
if (LodConfig.CLIENT.distanceGenerationMode.get() != DistanceGenerationMode.NONE
&& !generatorThreadRunning
&& mc.hasSingleplayerServer())
if (LodConfig.CLIENT.distanceGenerationMode.get() != DistanceGenerationMode.NONE
&& !generatorThreadRunning
&& mc.hasSingleplayerServer())
{
// the thread is now running, don't queue up another thread
generatorThreadRunning = true;
// just in case the config changed
maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8;
Thread generatorThread = new Thread(() ->
{
try
@@ -93,23 +99,23 @@ public class LodWorldGenerator
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(mc.player.blockPosition());
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
// used when determining which chunks are closer when queuing distance
// generation
int minChunkDist = Integer.MAX_VALUE;
List<LevelPos> levelPosListToGen;
List<GenerationRequest> generationRequestList = new ArrayList<>();
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
ArrayList<GenerationRequest> chunksToGenReserve = new ArrayList<>(maxChunkGenRequests);
// how many level positions to
int requesting = maxChunkGenRequests;
/** TODO can give a totally different generation */
/*
* for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <=
@@ -122,20 +128,21 @@ public class LodWorldGenerator
* (byte) distancesGenerators[detailGen].complexity, detail, 16));
* System.out.println("HERE"); } }
*/
//=======================================//
// create the generation Request objects //
//=======================================//
// start by generating half-region sized blocks...
int farRequesting = maxChunkGenRequests/4;
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){
if (farRequesting <= 0)
{
break;
}
levelPosListToGen = lodDim.getDataToGenerate(
@@ -146,17 +153,18 @@ public class LodWorldGenerator
DetailDistanceUtil.getDistanceGenerationMode(detailGen).complexity,
(byte) 8,
farRequesting);
for(LevelPos levelPos : levelPosListToGen){
generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen)));
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/4 + farRequesting;
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++)
{
@@ -169,23 +177,24 @@ public class LodWorldGenerator
DetailDistanceUtil.getDistanceGenerationMode(detailGen).complexity,
DetailDistanceUtil.getLodDetail(detailGen).detailLevel,
nearRequesting);
for(LevelPos levelPos : levelPosListToGen){
generationRequestList.add(new GenerationRequest(levelPos,DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen)));
for (LevelPos levelPos : levelPosListToGen)
{
generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(detailGen), DetailDistanceUtil.getLodDetail(detailGen)));
}
nearRequesting = nearRequesting - levelPosListToGen.size();
}
//====================================//
// 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();
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
// prevent generating the same chunk multiple times
@@ -194,17 +203,17 @@ public class LodWorldGenerator
// ClientProxy.LOGGER.debug(pos + " asked to be generated again.");
continue;
}
// determine if this position is closer to the player
// than the previous
int newDistance = playerChunkPos.getChessboardDistance(chunkPos);
if (newDistance < minChunkDist)
{
// this chunk is closer, clear any previous
// positions and update the new minimum distance
minChunkDist = newDistance;
// move all the old chunks into the reserve
ArrayList<GenerationRequest> oldReserve = new ArrayList<>(chunksToGenReserve);
chunksToGenReserve.clear();
@@ -217,11 +226,10 @@ public class LodWorldGenerator
else
break;
}
chunksToGen.clear();
chunksToGen.add(generationRequest);
}
else if (newDistance == minChunkDist)
} else if (newDistance == minChunkDist)
{
// this chunk position as close as the minimum distance
if (chunksToGen.size() < maxChunkGenRequests)
@@ -230,17 +238,16 @@ public class LodWorldGenerator
// add this position to the list
chunksToGen.add(generationRequest);
}
}
else
} else
{
// this chunk is farther away than the minimum distance,
// add it to the reserve to make sure we always have a full reserve
chunksToGenReserve.add(generationRequest);
}
} // lod null and can generate more chunks
} // positions to generate
// fill up chunksToGen from the reserve if it isn't full
// already
if (chunksToGen.size() < maxChunkGenRequests)
@@ -251,18 +258,16 @@ public class LodWorldGenerator
chunksToGen.add(reserveIterator.next());
}
}
//=============================//
// 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)
{
@@ -271,27 +276,25 @@ public class LodWorldGenerator
ChunkPos chunkPos = generationRequest.getChunkPos();
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
continue;
positionWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, generationRequest.generationMode, generationRequest.detail, renderer, lodBuilder, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
}
catch (Exception e)
} catch (Exception e)
{
// this shouldn't ever happen, but just in case
e.printStackTrace();
}
finally
} finally
{
generatorThreadRunning = false;
}
});
mainGenThread.execute(generatorThread);
} // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning
}
}
@@ -6,12 +6,18 @@ package com.seibel.lod.enums;
*/
public enum DistanceCalculatorType
{
/** different Lod detail render and generate linearly to the distance */
LINEAR,
/**
* different Lod detail render and generate linearly to the distance
*/
LINEAR,
/** different Lod detail render and generate quadratically to the distance */
QUADRATIC,
/**
* different Lod detail render and generate quadratically to the distance
*/
QUADRATIC,
/** we calculate the distance based on game render distance and mod render distance*/
RENDER_DEPENDANT;
/**
* we calculate the distance based on game render distance and mod render distance
*/
RENDER_DEPENDANT;
}
@@ -24,25 +24,29 @@ package com.seibel.lod.enums;
* SURFACE <br>
* FEATURES <br>
* SERVER <br><br>
*
* <p>
* In order of fastest to slowest.
*
*
* @author James Seibel
* @author Leonardo Amato
* @version 8-7-2021
*/
public enum DistanceGenerationMode
{
/** Don't generate anything */
/**
* Don't generate anything
*/
NONE((byte) 0),
/** Only generate the biomes and use biome
/**
* Only generate the biomes and use biome
* grass/foliage color, water color, or ice color
* to generate the color.
* Doesn't generate height, everything is shown at sea level.
* Multithreaded - Fastest (2-5 ms) */
* Doesn't generate height, everything is shown at sea level.
* Multithreaded - Fastest (2-5 ms)
*/
BIOME_ONLY((byte) 1),
/**
* Same as BIOME_ONLY, except instead
* of always using sea level as the LOD height
@@ -50,30 +54,38 @@ public enum DistanceGenerationMode
* use predetermined heights to simulate having height data.
*/
BIOME_ONLY_SIMULATE_HEIGHT((byte) 2),
/** Generate the world surface,
/**
* Generate the world surface,
* this does NOT include caves, trees,
* or structures.
* Multithreaded - Faster (10-20 ms) */
* or structures.
* Multithreaded - Faster (10-20 ms)
*/
SURFACE((byte) 3),
/** Generate everything except structures.
/**
* Generate everything except structures.
* NOTE: This may cause world generation bugs or instability,
* since some features cause concurrentModification exceptions.
* Multithreaded - Fast (15-20 ms) */
* Multithreaded - Fast (15-20 ms)
*/
FEATURES((byte) 4),
/** Ask the server to generate/load each chunk.
/**
* Ask the server to generate/load each chunk.
* This is the most compatible, but causes server/simulation lag.
* This will also show player made structures if you
* are adding the mod to a pre-existing world.
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) */
* are adding the mod to a pre-existing world.
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms)
*/
SERVER((byte) 5);
/** The higher the number the more complete the generation is. */
/**
* The higher the number the more complete the generation is.
*/
public final byte complexity;
DistanceGenerationMode(byte complexity)
{
this.complexity = complexity;
@@ -42,281 +42,281 @@ import net.minecraftforge.fml.config.ModConfig;
@Mod.EventBusSubscriber
public class LodConfig
{
public static class Client
{
public ForgeConfigSpec.BooleanValue drawLODs;
public static class Client
{
public ForgeConfigSpec.BooleanValue drawLODs;
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
public ForgeConfigSpec.BooleanValue debugMode;
public ForgeConfigSpec.BooleanValue debugMode;
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
public ForgeConfigSpec.EnumValue<DistanceCalculatorType> lodDistanceCalculatorType;
public ForgeConfigSpec.EnumValue<DistanceCalculatorType> lodDistanceCalculatorType;
public ForgeConfigSpec.IntValue lodQuality;
public ForgeConfigSpec.IntValue lodQuality;
public ForgeConfigSpec.IntValue lodChunkRenderDistance;
public ForgeConfigSpec.IntValue lodChunkRenderDistance;
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
public ForgeConfigSpec.DoubleValue saturationMultiplier;
public ForgeConfigSpec.DoubleValue saturationMultiplier;
Client(ForgeConfigSpec.Builder builder)
{
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
Client(ForgeConfigSpec.Builder builder)
{
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
drawLODs = builder
.comment("\n\n"
+ " If false LODs will not be drawn, \n"
+ " however they will still be generated \n"
+ " and saved to file for later use. \n")
.define("drawLODs", true);
drawLODs = builder
.comment("\n\n"
+ " If false LODs will not be drawn, \n"
+ " however they will still be generated \n"
+ " and saved to file for later use. \n")
.define("drawLODs", true);
fogDistance = builder
.comment("\n\n"
+ " At what distance should Fog be drawn on the LODs? \n"
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
.defineEnum("fogDistance", FogDistance.FAR);
fogDistance = builder
.comment("\n\n"
+ " At what distance should Fog be drawn on the LODs? \n"
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
.defineEnum("fogDistance", FogDistance.FAR);
fogDrawOverride = builder
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
fogDrawOverride = builder
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
debugMode = builder
.comment("\n\n"
+ " If false the LODs will draw with their normal colors. \n"
+ " If true LODs colors will be based on their detail \n"
+ " level. \n")
.define("debugMode", false);
debugMode = builder
.comment("\n\n"
+ " If false the LODs will draw with their normal colors. \n"
+ " If true LODs colors will be based on their detail \n"
+ " level. \n")
.define("debugMode", false);
lodTemplate = builder
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("lodTemplate", LodTemplate.CUBIC);
lodTemplate = builder
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("lodTemplate", LodTemplate.CUBIC);
maxDrawDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be drawn at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodDrawQuality", LodDetail.FULL);
maxDrawDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be drawn at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodDrawQuality", LodDetail.FULL);
maxGenerationDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be generated at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodGenerationQuality", LodDetail.FULL);
maxGenerationDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be generated at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodGenerationQuality", LodDetail.FULL);
lodDistanceCalculatorType = builder
.comment("\n\n"
+ " " + DistanceCalculatorType.LINEAR + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " linearly to the distance of the player \n"
lodDistanceCalculatorType = builder
.comment("\n\n"
+ " " + DistanceCalculatorType.LINEAR + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " linearly to the distance of the player \n"
+ "\n"
+ " " + DistanceCalculatorType.QUADRATIC + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " quadratically to the distance of the player \n"
+ "\n"
+ " " + DistanceCalculatorType.QUADRATIC + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " quadratically to the distance of the player \n"
+ "\n"
+ " " + DistanceCalculatorType.RENDER_DEPENDANT + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " quadratically to the distance of the player \n")
.defineEnum("lodDistanceComputation", DistanceCalculatorType.LINEAR);
+ "\n"
+ " " + DistanceCalculatorType.RENDER_DEPENDANT + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " quadratically to the distance of the player \n")
.defineEnum("lodDistanceComputation", DistanceCalculatorType.LINEAR);
lodQuality = builder
.comment("\n\n"
+ " this value is multiplied by 128 and determine \n"
+ " how much the quality decrease over distance \n")
.defineInRange("lodQuality", 1, 1, 4);
lodQuality = builder
.comment("\n\n"
+ " this value is multiplied by 128 and determine \n"
+ " how much the quality decrease over distance \n")
.defineInRange("lodQuality", 1, 1, 4);
lodChunkRenderDistance = builder
.comment("\n\n"
+ " This is the render distance of the mod \n")
.defineInRange("lodChunkRenderDistane", 128, 32, 256);
lodChunkRenderDistance = builder
.comment("\n\n"
+ " This is the render distance of the mod \n")
.defineInRange("lodChunkRenderDistane", 128, 32, 256);
distanceGenerationMode = builder
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " the developer's PC to generate 1 chunk, \n"
+ " and are included so you can compare the \n"
+ " different generation options. Your mileage may vary. \n"
+ "\n"
distanceGenerationMode = builder
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " the developer's PC to generate 1 chunk, \n"
+ " and are included so you can compare the \n"
+ " different generation options. Your mileage may vary. \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
+ " Only generate the biomes and use biome \n"
+ " grass/foliage color, water color, or snow color \n"
+ " to generate the color. \n"
+ " Doesn't generate height, everything is shown at sea level. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
+ " Only generate the biomes and use biome \n"
+ " grass/foliage color, water color, or snow color \n"
+ " to generate the color. \n"
+ " Doesn't generate height, everything is shown at sea level. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
+ " Same as BIOME_ONLY, except instead \n"
+ " of always using sea level as the LOD height \n"
+ " different biome types (mountain, ocean, forest, etc.) \n"
+ " use predetermined heights to simulate having height data. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
+ " Same as BIOME_ONLY, except instead \n"
+ " of always using sea level as the LOD height \n"
+ " different biome types (mountain, ocean, forest, etc.) \n"
+ " use predetermined heights to simulate having height data. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include caves, trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include caves, trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
+ " Generate everything except structures. \n"
+ " WARNING: This may cause world generation bugs or instability! \n"
+ " Multithreaded - Fast (15-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
+ " Generate everything except structures. \n"
+ " WARNING: This may cause world generation bugs or instability! \n"
+ " Multithreaded - Fast (15-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " This will also show player made structures if you \n"
+ " are adding the mod to a pre-existing world. \n"
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
+ "\n"
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " This will also show player made structures if you \n"
+ " are adding the mod to a pre-existing world. \n"
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
+ " some features may not be thread safe, which could \n"
+ " cause instability and crashes. \n"
+ " By default (false) those features are skipped, \n"
+ " improving stability, but decreasing how many features are \n"
+ " actually generated. \n"
+ " (for example: some tree generation is unstable, \n"
+ " so some trees may not be generated.) \n"
+ " By setting this to true, all features will be generated, \n"
+ " but your game will be more unstable and crashes may occur. \n"
+ " \n"
+ " I would love to remove this option and always generate everything, \n"
+ " but I'm not sure how to do that. \n"
+ " If you are a Java wizard, check out the git issue here: \n"
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
.define("allowUnstableFeatureGeneration", false);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
+ " some features may not be thread safe, which could \n"
+ " cause instability and crashes. \n"
+ " By default (false) those features are skipped, \n"
+ " improving stability, but decreasing how many features are \n"
+ " actually generated. \n"
+ " (for example: some tree generation is unstable, \n"
+ " so some trees may not be generated.) \n"
+ " By setting this to true, all features will be generated, \n"
+ " but your game will be more unstable and crashes may occur. \n"
+ " \n"
+ " I would love to remove this option and always generate everything, \n"
+ " but I'm not sure how to do that. \n"
+ " If you are a Java wizard, check out the git issue here: \n"
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
.define("allowUnstableFeatureGeneration", false);
numberOfWorldGenerationThreads = builder
.comment("\n\n"
+ " This is how many threads are used when generating terrain. \n"
+ " If you experience stuttering when generating terrain, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase the number. \n"
+ " The max is the number of processors on your CPU. \n"
+ "\n"
+ " Requires a restart to take effect. \n"
)
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
numberOfWorldGenerationThreads = builder
.comment("\n\n"
+ " This is how many threads are used when generating terrain. \n"
+ " If you experience stuttering when generating terrain, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase the number. \n"
+ " The max is the number of processors on your CPU. \n"
+ "\n"
+ " Requires a restart to take effect. \n"
)
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
shadingMode = builder
.comment("\n\n"
+ " What kind of shading should LODs have? \n"
+ " " + ShadingMode.NONE.toString() + " \n"
+ " " + "LODs will have the same lighting on every side. \n"
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
+ " " + "Fastest"
+ "/n"
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
+ " " + "LODs will have darker sides and bottoms to simulate top down lighting."
+ " " + "Fastest /n")
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
shadingMode = builder
.comment("\n\n"
+ " What kind of shading should LODs have? \n"
+ " " + ShadingMode.NONE.toString() + " \n"
+ " " + "LODs will have the same lighting on every side. \n"
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
+ " " + "Fastest"
+ "/n"
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
+ " " + "LODs will have darker sides and bottoms to simulate top down lighting."
+ " " + "Fastest /n")
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
brightnessMultiplier = builder
.comment("\n\n"
+ " Change how bright LOD colors are. \n"
+ " 0 = black \n"
+ " 1 = normal color value \n"
+ " 2 = washed out colors \n")
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
brightnessMultiplier = builder
.comment("\n\n"
+ " Change how bright LOD colors are. \n"
+ " 0 = black \n"
+ " 1 = normal color value \n"
+ " 2 = washed out colors \n")
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
saturationMultiplier = builder
.comment("\n\n"
+ " Change how saturated LOD colors are. \n"
+ " 0 = black and white \n"
+ " 1 = normal saturation \n"
+ " 2 = very saturated \n")
.defineInRange("saturationMultiplier", 1.0, 0, 2);
saturationMultiplier = builder
.comment("\n\n"
+ " Change how saturated LOD colors are. \n"
+ " 0 = black and white \n"
+ " 1 = normal saturation \n"
+ " 2 = very saturated \n")
.defineInRange("saturationMultiplier", 1.0, 0, 2);
builder.pop();
}
}
builder.pop();
}
}
/**
* {@link Path} to the configuration file of this mod
*/
private static final Path CONFIG_PATH =
Paths.get("config", ModInfo.MODID + ".toml");
/**
* {@link Path} to the configuration file of this mod
*/
private static final Path CONFIG_PATH =
Paths.get("config", ModInfo.MODID + ".toml");
public static final ForgeConfigSpec clientSpec;
public static final Client CLIENT;
public static final ForgeConfigSpec clientSpec;
public static final Client CLIENT;
static
{
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
clientSpec = specPair.getRight();
CLIENT = specPair.getLeft();
static
{
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
clientSpec = specPair.getRight();
CLIENT = specPair.getLeft();
// setup the config file
CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH)
.writingMode(WritingMode.REPLACE)
.build();
config.load();
config.save();
clientSpec.setConfig(config);
}
// setup the config file
CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH)
.writingMode(WritingMode.REPLACE)
.build();
config.load();
config.save();
clientSpec.setConfig(config);
}
@SubscribeEvent
public static void onLoad(final ModConfig.Loading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
}
@SubscribeEvent
public static void onLoad(final ModConfig.Loading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
}
@SubscribeEvent
public static void onFileChange(final ModConfig.Reloading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!");
}
@SubscribeEvent
public static void onFileChange(final ModConfig.Reloading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!");
}
}
@@ -45,73 +45,81 @@ import com.seibel.lod.util.LodUtil;
*/
public class LodDimensionFileHandler
{
/**
* This is what separates each piece of data
*/
public static final char DATA_DELIMITER = ',';
/**
* This is what separates each piece of data
*/
public static final char DATA_DELIMITER = ',';
private LodDimension loadedDimension = null;
public long regionLastWriteTime[][];
private LodDimension loadedDimension = null;
public long regionLastWriteTime[][];
private File dimensionDataSaveFolder;
private File dimensionDataSaveFolder;
/** lod */
private static final String FILE_NAME_PREFIX = "lod";
/** .txt */
private static final String FILE_EXTENSION = ".txt";
/** lod/ */
private static final String LOD_FOLDER_NAME = "lod";
/** detail-# */
private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-";
/**
* .tmp <br>
* Added to the end of the file path when saving to prevent
* nulling a currently existing file. <br>
* After the file finishes saving it will end with
* FILE_EXTENSION.
*/
private static final String TMP_FILE_EXTENSION = ".tmp";
/**
* lod
*/
private static final String FILE_NAME_PREFIX = "lod";
/**
* .txt
*/
private static final String FILE_EXTENSION = ".txt";
/**
* lod/
*/
private static final String LOD_FOLDER_NAME = "lod";
/**
* detail-#
*/
private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-";
/**
* This is the file version currently accepted by this
* file handler, older versions (smaller numbers) will be deleted and overwritten,
* newer versions (larger numbers) will be ignored and won't be read.
*/
public static final int LOD_SAVE_FILE_VERSION = 4;
/**
* .tmp <br>
* Added to the end of the file path when saving to prevent
* nulling a currently existing file. <br>
* After the file finishes saving it will end with
* FILE_EXTENSION.
*/
private static final String TMP_FILE_EXTENSION = ".tmp";
/**
* This is the string written before the file version
*/
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
/**
* This is the file version currently accepted by this
* file handler, older versions (smaller numbers) will be deleted and overwritten,
* newer versions (larger numbers) will be ignored and won't be read.
*/
public static final int LOD_SAVE_FILE_VERSION = 4;
/**
* Allow saving asynchronously, but never try to save multiple regions
* at a time
*/
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
/**
* This is the string written before the file version
*/
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
/**
* Allow saving asynchronously, but never try to save multiple regions
* at a time
*/
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
{
if (newSaveFolder == null)
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
{
if (newSaveFolder == null)
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
dimensionDataSaveFolder = newSaveFolder;
dimensionDataSaveFolder = newSaveFolder;
loadedDimension = newLoadedDimension;
// these two variable are used in sync with the LodDimension
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
for (int i = 0; i < loadedDimension.getWidth(); i++)
for (int j = 0; j < loadedDimension.getWidth(); j++)
regionLastWriteTime[i][j] = -1;
}
loadedDimension = newLoadedDimension;
// these two variable are used in sync with the LodDimension
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
for (int i = 0; i < loadedDimension.getWidth(); i++)
for (int j = 0; j < loadedDimension.getWidth(); j++)
regionLastWriteTime[i][j] = -1;
}
//================//
// read from file //
//================//
//================//
// read from file //
//================//
/**
* Return the LodRegion region at the given coordinates.
@@ -168,8 +176,8 @@ public class LodDimensionFileHandler
bufferedReader.close();
f.delete();
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
", version requested: " + LOD_SAVE_FILE_VERSION +
" File was been deleted.");
", version requested: " + LOD_SAVE_FILE_VERSION +
" File was been deleted.");
continue;
} else if (fileVersion > LOD_SAVE_FILE_VERSION)
@@ -179,8 +187,8 @@ public class LodDimensionFileHandler
// want to accidently delete anything the user may want.
bufferedReader.close();
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
", version requested: " + LOD_SAVE_FILE_VERSION +
" this region will not be written to in order to protect the newer file.");
", version requested: " + LOD_SAVE_FILE_VERSION +
" this region will not be written to in order to protect the newer file.");
continue;
}
@@ -349,38 +357,37 @@ public class LodDimensionFileHandler
}
//================//
// helper methods //
//================//
//================//
// helper methods //
//================//
/**
* Return the name of the file that should contain the
* region at the given x and z. <br>
* Returns null if this object isn't ready to read and write. <br><br>
*
* example: "lod.0.0.txt" <br><br>
*
* Returns null if there is an IO Exception.
*/
private String getFileNameAndPathForRegion(int regionX, int regionZ, byte detailLevel)
{
try
{
// saveFolder is something like
// ".\Super Flat\DIM-1\data"
// or
// ".\Super Flat\data"
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar +
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
}
catch (IOException | SecurityException e)
{
ClientProxy.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
e.printStackTrace();
return null;
}
}
/**
* Return the name of the file that should contain the
* region at the given x and z. <br>
* Returns null if this object isn't ready to read and write. <br><br>
* <p>
* example: "lod.0.0.txt" <br><br>
* <p>
* Returns null if there is an IO Exception.
*/
private String getFileNameAndPathForRegion(int regionX, int regionZ, byte detailLevel)
{
try
{
// saveFolder is something like
// ".\Super Flat\DIM-1\data"
// or
// ".\Super Flat\data"
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar +
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
} catch (IOException | SecurityException e)
{
ClientProxy.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
e.printStackTrace();
return null;
}
}
}
@@ -27,7 +27,7 @@ import net.minecraft.client.Minecraft;
* This object is used to get variables from methods
* where they are private. Specifically the fog setting
* in Optifine.
*
*
* @author James Seibel
* @version 7-03-2021
*/
@@ -35,15 +35,13 @@ public class ReflectionHandler
{
private Minecraft mc = Minecraft.getInstance();
public Field ofFogField = null;
public ReflectionHandler()
{
setupFogField();
}
/**
* Similar to setupFovMethod.
*/
@@ -51,27 +49,24 @@ public class ReflectionHandler
{
// get every variable from the entity renderer
Field[] optionFields = mc.options.getClass().getDeclaredFields();
// try and find the ofFogType variable in gameSettings
for(Field field : optionFields)
for (Field field : optionFields)
{
if(field.getName().equals("ofFogType"))
if (field.getName().equals("ofFogType"))
{
ofFogField = field;
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.
*/
@@ -84,25 +79,24 @@ public class ReflectionHandler
// the setup method wasn't called yet.
return FogQuality.FANCY;
}
int returnNum = 0;
try
{
returnNum = (int)ofFogField.get(mc.options);
}
catch (IllegalArgumentException | IllegalAccessException e)
returnNum = (int) ofFogField.get(mc.options);
} 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;
// normal options
case 1:
return FogQuality.FAST;
@@ -110,10 +104,10 @@ public class ReflectionHandler
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
default:
return FogQuality.FAST;
}
}
}
@@ -3,38 +3,46 @@ 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[] 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 getHeight(short[] dataPoint)
{
return dataPoint[0];
}
public static short getDepth(short[] dataPoint){
return dataPoint[1];
}
public static short getDepth(short[] dataPoint)
{
return dataPoint[1];
}
public static short getRed(short[] dataPoint){
return dataPoint[2];
}
public static short getRed(short[] dataPoint)
{
return dataPoint[2];
}
public static short getGreen(short[] dataPoint){
return dataPoint[3];
}
public static short getGreen(short[] dataPoint)
{
return dataPoint[3];
}
public static short getBlue(short[] dataPoint){
return dataPoint[4];
}
public static short getBlue(short[] dataPoint)
{
return dataPoint[4];
}
public static short[] getHeightDepth(short[] dataPoint){
return new short[]{dataPoint[0], dataPoint[1]};
}
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;
}
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;
}
}
@@ -7,104 +7,104 @@ import com.seibel.lod.util.LodUtil;
public class LevelContainer implements Serializable
{
public static final char DATA_DELIMITER = ',';
public static final char DATA_DELIMITER = ',';
public final byte detailLevel;
public final byte detailLevel;
public final byte[][][] colors;
public final byte[][][] colors;
public final short[][] height;
public final short[][] height;
public final short[][] depth;
public final short[][] depth;
public final byte[][] generationType;
public final byte[][] generationType;
public final boolean[][] dataExistence;
public final boolean[][] dataExistence;
public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence)
{
this.detailLevel = detailLevel;
this.colors = colors;
this.height = height;
this.depth = depth;
this.generationType = generationType;
this.dataExistence = dataExistence;
}
public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence)
{
this.detailLevel = detailLevel;
this.colors = colors;
this.height = height;
this.depth = depth;
this.generationType = generationType;
this.dataExistence = dataExistence;
}
public LevelContainer(String data)
{
public LevelContainer(String data)
{
int index = 0;
int lastIndex = 0;
int index = 0;
int lastIndex = 0;
index = data.indexOf(DATA_DELIMITER, 0);
this.detailLevel = (byte) Integer.parseInt(data.substring(0, index));
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
index = data.indexOf(DATA_DELIMITER, 0);
this.detailLevel = (byte) Integer.parseInt(data.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.generationType = new byte[size][size];
this.dataExistence = new boolean[size][size];
int intCol;
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);
this.colors = new byte[size][size][3];
this.height = new short[size][size];
this.depth = new short[size][size];
this.generationType = new byte[size][size];
this.dataExistence = new boolean[size][size];
int intCol;
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);
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);
depth[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
generationType[x][z] = Byte.parseByte(data.substring(lastIndex + 1, index), 16);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
generationType[x][z] = Byte.parseByte(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));
}
}
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex + 1, index));
}
}
}
}
@Override
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);
for (int x = 0; x < size; x++)
{
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(Integer.toHexString(generationType[x][z] & 0xffff));
stringBuilder.append(DATA_DELIMITER);
stringBuilder.append(dataExistence[x][z]);
stringBuilder.append(DATA_DELIMITER);
}
}
return stringBuilder.toString();
}
@Override
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);
for (int x = 0; x < size; x++)
{
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(Integer.toHexString(generationType[x][z] & 0xffff));
stringBuilder.append(DATA_DELIMITER);
stringBuilder.append(dataExistence[x][z]);
stringBuilder.append(DATA_DELIMITER);
}
}
return stringBuilder.toString();
}
}
@@ -4,11 +4,11 @@ import com.seibel.lod.util.LodUtil;
public interface MutableLevelPos
{
public void convert(byte newDetailLevel);
public void convert(byte newDetailLevel);
public void performRegionModule();
public void performRegionModule();
public void applyOffset(int xOffset, int zOffset);
public void applyOffset(int xOffset, int zOffset);
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ);
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -33,127 +33,127 @@ import net.minecraft.world.DimensionType;
*/
public class LodWorld
{
private String worldName;
private String worldName;
private Map<DimensionType, LodDimension> lodDimensions;
private Map<DimensionType, LodDimension> lodDimensions;
/**
* If true then the LOD world is setup and ready to use
*/
private boolean isWorldLoaded = false;
/**
* If true then the LOD world is setup and ready to use
*/
private boolean isWorldLoaded = false;
public static final String NO_WORLD_LOADED = "No world loaded";
public static final String NO_WORLD_LOADED = "No world loaded";
public LodWorld()
{
worldName = NO_WORLD_LOADED;
}
public LodWorld()
{
worldName = NO_WORLD_LOADED;
}
/**
* Set up the LodQuadTreeWorld with the given newWorldName. <br>
* This should be done whenever loading a new world.
*
* @param newWorldName name of the world
*/
public void selectWorld(String newWorldName)
{
if (newWorldName.isEmpty())
{
deselectWorld();
return;
}
/**
* Set up the LodQuadTreeWorld with the given newWorldName. <br>
* This should be done whenever loading a new world.
*
* @param newWorldName name of the world
*/
public void selectWorld(String newWorldName)
{
if (newWorldName.isEmpty())
{
deselectWorld();
return;
}
if (worldName.equals(newWorldName))
// don't recreate everything if we
// didn't actually change worlds
return;
if (worldName.equals(newWorldName))
// don't recreate everything if we
// didn't actually change worlds
return;
worldName = newWorldName;
lodDimensions = new Hashtable<DimensionType, LodDimension>();
isWorldLoaded = true;
}
worldName = newWorldName;
lodDimensions = new Hashtable<DimensionType, LodDimension>();
isWorldLoaded = true;
}
/**
* Set the worldName to "No world loaded"
* and clear the lodDimensions Map. <br>
* This should be done whenever unloaded a world.
*/
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
lodDimensions = null;
isWorldLoaded = false;
}
/**
* Set the worldName to "No world loaded"
* and clear the lodDimensions Map. <br>
* This should be done whenever unloaded a world.
*/
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
lodDimensions = null;
isWorldLoaded = false;
}
/**
* Adds newStorage to this world, if a LodQuadTreeDimension
* already exists for the given dimension it is replaced.
*/
public void addLodDimension(LodDimension newStorage)
{
if (lodDimensions == null)
return;
/**
* Adds newStorage to this world, if a LodQuadTreeDimension
* already exists for the given dimension it is replaced.
*/
public void addLodDimension(LodDimension newStorage)
{
if (lodDimensions == null)
return;
lodDimensions.put(newStorage.dimension, newStorage);
}
lodDimensions.put(newStorage.dimension, newStorage);
}
/**
* Returns null if no LodQuadTreeDimension exists for the given dimension
*/
public LodDimension getLodDimension(DimensionType dimension)
{
if (lodDimensions == null)
return null;
/**
* Returns null if no LodQuadTreeDimension exists for the given dimension
*/
public LodDimension getLodDimension(DimensionType dimension)
{
if (lodDimensions == null)
return null;
return lodDimensions.get(dimension);
}
return lodDimensions.get(dimension);
}
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
if (lodDimensions == null)
return;
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
if (lodDimensions == null)
return;
saveAllDimensions();
saveAllDimensions();
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
/**
* Requests all dimensions save any dirty regions they may have.
*/
public void saveAllDimensions()
{
if (lodDimensions == null)
return;
/**
* Requests all dimensions save any dirty regions they may have.
*/
public void saveAllDimensions()
{
if (lodDimensions == null)
return;
ClientProxy.LOGGER.info("Saving LODs");
ClientProxy.LOGGER.info("Saving LODs");
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
}
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
}
public boolean getIsWorldLoaded()
{
return isWorldLoaded;
}
public boolean getIsWorldLoaded()
{
return isWorldLoaded;
}
public String getWorldName()
{
return worldName;
}
public String getWorldName()
{
return worldName;
}
@Override
public String toString()
{
return "World name: " + worldName;
}
@Override
public String toString()
{
return "World name: " + worldName;
}
}
@@ -24,7 +24,7 @@ import net.minecraft.util.math.ChunkPos;
/**
* This object is similar to ChunkPos or BlockPos.
*
*
* @author James Seibel
* @version 8-21-2021
*/
@@ -32,11 +32,11 @@ public class RegionPos
{
public int x;
public int z;
/**
* Default Constructor <br>
*
* <p>
* Sets x and z to 0
*/
public RegionPos()
@@ -44,38 +44,41 @@ public class RegionPos
x = 0;
z = 0;
}
public RegionPos(int newX, int newZ)
{
x = newX;
z = newZ;
}
public RegionPos(BlockPos pos)
{
this(new ChunkPos(pos));
}
public RegionPos(ChunkPos pos)
{
x = Math.floorDiv(pos.x, LodUtil.REGION_WIDTH_IN_CHUNKS);
z = Math.floorDiv(pos.z, LodUtil.REGION_WIDTH_IN_CHUNKS);
}
/** Returns the ChunkPos at the center of this region */
/**
* Returns the ChunkPos at the center of this region
*/
public ChunkPos chunkPos()
{
return new ChunkPos((x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS/2, (z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS/2);
return new ChunkPos((x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2, (z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2);
}
/** Returns the BlockPos at the center of this region */
/**
* Returns the BlockPos at the center of this region
*/
public BlockPos blockPos()
{
return chunkPos().getWorldPosition().offset(LodUtil.CHUNK_WIDTH/2, 0, LodUtil.CHUNK_WIDTH/2);
return chunkPos().getWorldPosition().offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2);
}
@Override
public String toString()
{
@@ -57,266 +57,266 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
*/
public class ClientProxy
{
public static final Logger LOGGER = LogManager.getLogger("LOD");
public static final Logger LOGGER = LogManager.getLogger("LOD");
private static LodWorld lodWorld = new LodWorld();
private static LodBuilder lodBuilder = new LodBuilder();
private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder();
private static LodRenderer renderer = new LodRenderer(lodBufferBuilder);
private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE;
private static LodWorld lodWorld = new LodWorld();
private static LodBuilder lodBuilder = new LodBuilder();
private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder();
private static LodRenderer renderer = new LodRenderer(lodBufferBuilder);
private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE;
private boolean configOverrideReminderPrinted = false;
private boolean configOverrideReminderPrinted = false;
Minecraft mc = Minecraft.getInstance();
Minecraft mc = Minecraft.getInstance();
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousChunkRenderDistance = 0;
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousLodRenderDistance = 0;
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousChunkRenderDistance = 0;
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousLodRenderDistance = 0;
/**
* can be set if we want to recalculate variables related
* to the LOD view distance
*/
private boolean recalculateWidths = false;
/**
* can be set if we want to recalculate variables related
* to the LOD view distance
*/
private boolean recalculateWidths = false;
public ClientProxy()
{
public ClientProxy()
{
}
}
//==============//
// render event //
//==============//
//==============//
// render event //
//==============//
/**
* Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw.
*/
public void renderLods(float partialTicks)
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
return;
/**
* Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw.
*/
public void renderLods(float partialTicks)
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
return;
viewDistanceChangedEvent();
viewDistanceChangedEvent();
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
if (lodDim == null)
return;
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
if (lodDim == null)
return;
playerMoveEvent(lodDim);
//System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte");
//System.out.println(lodDim);
playerMoveEvent(lodDim);
//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.player.getX(), (int) mc.player.getZ());
lodDim.treeGenerator((int) mc.player.getX(), (int) mc.player.getZ());
// comment out when creating a release
applyConfigOverrides();
// comment out when creating a release
applyConfigOverrides();
// Note to self:
// if "unspecified" shows up in the pie chart, it is
// possibly because the amount of time between sections
// is too small for the profiler to measure
IProfiler profiler = mc.getProfiler();
profiler.pop(); // get out of "terrain"
profiler.push("LOD");
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
// Note to self:
// if "unspecified" shows up in the pie chart, it is
// possibly because the amount of time between sections
// is too small for the profiler to measure
IProfiler profiler = mc.getProfiler();
profiler.pop(); // get out of "terrain"
profiler.push("LOD");
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
profiler.pop(); // end LOD
profiler.push("terrain"); // restart "terrain"
profiler.pop(); // end LOD
profiler.push("terrain"); // restart "terrain"
// 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;
previousLodRenderDistance = LodConfig.CLIENT.lodChunkRenderDistance.get();
}
// 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;
previousLodRenderDistance = LodConfig.CLIENT.lodChunkRenderDistance.get();
}
private void applyConfigOverrides()
{
// remind the developer(s). that config override is active
if (!configOverrideReminderPrinted)
{
mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID());
configOverrideReminderPrinted = true;
}
private void applyConfigOverrides()
{
// remind the developer(s). that config override is active
if (!configOverrideReminderPrinted)
{
mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID());
configOverrideReminderPrinted = true;
}
// LodConfig.CLIENT.drawLODs.set(true);
LodConfig.CLIENT.debugMode.set(false);
// LodConfig.CLIENT.drawLODs.set(true);
LodConfig.CLIENT.debugMode.set(false);
LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL);
LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL);
LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL);
LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL);
LodConfig.CLIENT.fogDistance.set(FogDistance.FAR);
LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES);
LodConfig.CLIENT.brightnessMultiplier.set(1.0);
LodConfig.CLIENT.saturationMultiplier.set(1.0);
LodConfig.CLIENT.fogDistance.set(FogDistance.FAR);
LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES);
LodConfig.CLIENT.brightnessMultiplier.set(1.0);
LodConfig.CLIENT.saturationMultiplier.set(1.0);
LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
LodConfig.CLIENT.lodChunkRenderDistance.set(128);
LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
LodConfig.CLIENT.lodQuality.set(2);
LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors());
LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
LodConfig.CLIENT.lodChunkRenderDistance.set(128);
LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
LodConfig.CLIENT.lodQuality.set(2);
LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors());
// has to be set in the config file
// LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16);
}
// has to be set in the config file
// LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16);
}
//==============//
// forge events //
//==============//
//==============//
// forge events //
//==============//
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
return;
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
return;
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
if (lodDim == null)
return;
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
if (lodDim == null)
return;
lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder);
}
lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder);
}
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
lodBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER);
}
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
lodBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER);
}
@SubscribeEvent
public void worldSaveEvent(WorldEvent.Save event)
{
if (lodWorld != null)
lodWorld.saveAllDimensions();
}
@SubscribeEvent
public void worldSaveEvent(WorldEvent.Save event)
{
if (lodWorld != null)
lodWorld.saveAllDimensions();
}
@SubscribeEvent
public void worldLoadEvent(WorldEvent.Load event)
{
// the player just loaded a new world/dimension
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
// make sure the correct LODs are being rendered
// (if this isn't done the previous world's LODs may be drawn)
renderer.regenerateLODsNextFrame();
}
@SubscribeEvent
public void worldLoadEvent(WorldEvent.Load event)
{
// the player just loaded a new world/dimension
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
// make sure the correct LODs are being rendered
// (if this isn't done the previous world's LODs may be drawn)
renderer.regenerateLODsNextFrame();
}
@SubscribeEvent
public void worldUnloadEvent(WorldEvent.Unload event)
{
// the player just unloaded a world/dimension
@SubscribeEvent
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
// preventing new LodChunks form being generated
LodNodeGenWorker.restartExecuterService();
if (mc.getConnection().getLevel() == null)
{
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
LodNodeGenWorker.restartExecuterService();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
// the player has disconnected from a server
lodWorld.deselectWorld();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
// the player has disconnected from a server
lodWorld.deselectWorld();
// hopefully this should reduce issues related to the buffer builder
// breaking when changing worlds.
renderer.destroyBuffers();
recalculateWidths = true;
}
}
// hopefully this should reduce issues related to the buffer builder
// breaking when changing worlds.
renderer.destroyBuffers();
recalculateWidths = true;
}
}
@SubscribeEvent
public void blockChangeEvent(BlockEvent event)
{
if (event.getClass() == BlockEvent.BreakEvent.class ||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
// recreate the LOD where the blocks were changed
lodBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
}
}
@SubscribeEvent
public void blockChangeEvent(BlockEvent event)
{
if (event.getClass() == BlockEvent.BreakEvent.class ||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
// recreate the LOD where the blocks were changed
lodBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
}
}
//==================//
// frame LOD events //
//==================//
//==================//
// frame LOD events //
//==================//
/**
* Re-centers the given LodDimension if it needs to be.
*/
private void playerMoveEvent(LodDimension lodDim)
{
// make sure the dimension is centered
RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition());
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
lodWorld.saveAllDimensions();
lodDim.move(worldRegionOffset);
LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
}
}
/**
* Re-centers the given LodDimension if it needs to be.
*/
private void playerMoveEvent(LodDimension lodDim)
{
// make sure the dimension is centered
RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition());
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
lodWorld.saveAllDimensions();
lodDim.move(worldRegionOffset);
LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
}
}
/**
* Re-sizes all LodDimensions if they needs to be.
*/
private void viewDistanceChangedEvent()
{
// calculate how wide the dimension(s) should be in regions
int chunksWide = LodConfig.CLIENT.lodChunkRenderDistance.get() * 2 + 1;
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions
/**
* Re-sizes all LodDimensions if they needs to be.
*/
private void viewDistanceChangedEvent()
{
// calculate how wide the dimension(s) should be in regions
int chunksWide = LodConfig.CLIENT.lodChunkRenderDistance.get() * 2 + 1;
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions
// do the dimensions need to change in size?
if (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
{
// update the dimensions to fit the new width
lodWorld.resizeDimensionRegionWidth(newWidth);
lodBuilder.defaultDimensionWidthInRegions = newWidth;
renderer.setupBuffers(newWidth);
// do the dimensions need to change in size?
if (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
{
// update the dimensions to fit the new width
lodWorld.resizeDimensionRegionWidth(newWidth);
lodBuilder.defaultDimensionWidthInRegions = newWidth;
renderer.setupBuffers(newWidth);
recalculateWidths = false;
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
}
}
recalculateWidths = false;
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
}
}
//================//
// public getters //
//================//
//================//
// public getters //
//================//
public static LodWorld getLodWorld()
{
return lodWorld;
}
public static LodWorld getLodWorld()
{
return lodWorld;
}
public static LodBuilder getLodBuilder()
{
return lodBuilder;
}
public static LodBuilder getLodBuilder()
{
return lodBuilder;
}
public static LodRenderer getRenderer()
{
return renderer;
}
public static LodRenderer getRenderer()
{
return renderer;
}
}
@@ -4,57 +4,67 @@ import java.awt.*;
public class ColorUtil
{
public static int rgbToInt(int red, int green, int blue){
return (0xFF << 24) | (red << 16) | (green << 8) | blue;
}
public static int rgbToInt(int red, int green, int blue)
{
return (0xFF << 24) | (red << 16) | (green << 8) | blue;
}
public static int rgbToInt( int alpha, int red, int green, int blue){
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
public static int rgbToInt(int alpha, int red, int green, int blue)
{
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
public static int getAlpha(int color){
return (color>>24)&0xFF;
}
public static int getAlpha(int color)
{
return (color >> 24) & 0xFF;
}
public static int getRed(int color){
return (color>>16)&0xFF;
}
public static int getRed(int color)
{
return (color >> 16) & 0xFF;
}
public static int getGreen(int color){
return (color>>8)&0xFF;
}
public static int getGreen(int color)
{
return (color >> 8) & 0xFF;
}
public static int getBlue(int color){
return color &0xFF;
}
public static int getBlue(int color)
{
return color & 0xFF;
}
public static int applyShade(int color, int shade){
if(shade<0)
{
return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0);
}else{
return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255);
}
}
public static int applyShade(int color, int shade)
{
if (shade < 0)
{
return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0);
} else
{
return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255);
}
}
public static int applyShade(int color, float shade){
if(shade<1)
{
return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0);
}else{
return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) |(int) Math.min(getBlue(color) * shade, 255);
}
}
public static int applyShade(int color, float shade)
{
if (shade < 1)
{
return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0);
} else
{
return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) | (int) Math.min(getBlue(color) * shade, 255);
}
}
/**
* Edit the given color as a HSV (Hue Saturation Value) color.
*/
public static int applySaturationAndBrightnessMultipliers(int color, float saturationMultiplier, float brightnessMultiplier)
{
float[] hsv = Color.RGBtoHSB( getRed(color), getGreen(color), getBlue(color), null);
return Color.getHSBColor(
hsv[0], // hue
LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f),
LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB();
}
/**
* Edit the given color as a HSV (Hue Saturation Value) color.
*/
public static int applySaturationAndBrightnessMultipliers(int color, float saturationMultiplier, float brightnessMultiplier)
{
float[] hsv = Color.RGBtoHSB(getRed(color), getGreen(color), getBlue(color), null);
return Color.getHSBColor(
hsv[0], // hue
LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f),
LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB();
}
}
@@ -7,26 +7,26 @@ import net.minecraft.client.Minecraft;
public class DetailDistanceUtil
{
private static double genMultiplier = 1.25;
private static double treeGenMultiplier = 1.5;
private static double treeCutMultiplier = 1.25;
private static int minDetail = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel;
private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
private static int minDistance = 0;
private static int maxDistance = LodConfig.CLIENT.lodChunkRenderDistance.get() * 16;
private static double genMultiplier = 1.25;
private static double treeGenMultiplier = 1.5;
private static double treeCutMultiplier = 1.25;
private static int minDetail = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel;
private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
private static int minDistance = 0;
private static int maxDistance = LodConfig.CLIENT.lodChunkRenderDistance.get() * 16;
private static DistanceGenerationMode[] distancesGenerators = {
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE};
private static DistanceGenerationMode[] distancesGenerators = {
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE};
/*private static DistanceGenerationMode[] distancesGenerators = {
DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT,
@@ -40,113 +40,114 @@ public class DetailDistanceUtil
DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT,
DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT};*/
private static LodDetail[] lodDetails = {
LodDetail.FULL,
LodDetail.HALF,
LodDetail.QUAD,
LodDetail.DOUBLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE};
private static LodDetail[] lodDetails = {
LodDetail.FULL,
LodDetail.HALF,
LodDetail.QUAD,
LodDetail.DOUBLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE};
private static LodDetail[] lodDetailsCut = {
LodDetail.FULL,
LodDetail.HALF,
LodDetail.QUAD,
LodDetail.DOUBLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE};
private static LodDetail[] lodDetailsCut = {
LodDetail.FULL,
LodDetail.HALF,
LodDetail.QUAD,
LodDetail.DOUBLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE};
public static int getDistanceRendering(int detail)
{
int initial;
int distance = 0;
if(detail <= minDetail)
return minDistance;
if(detail == maxDetail)
return maxDistance*2;
if(detail == maxDetail+1)
return maxDistance*3;
switch (LodConfig.CLIENT.lodDistanceCalculatorType.get())
{
case LINEAR:
initial = LodConfig.CLIENT.lodQuality.get() * 128;
return (detail * initial);
case QUADRATIC:
initial = LodConfig.CLIENT.lodQuality.get() * 128;
return (int) (Math.pow(2, detail) * initial);
case RENDER_DEPENDANT:
int realRenderDistance = Minecraft.getInstance().options.renderDistance * 16;
int border = 64;
byte detailAtBorder = (byte) 4;
if(detail > detailAtBorder){
return (detail * (border-realRenderDistance)/detailAtBorder + realRenderDistance);
}else{
return ((maxDetail - detail) * (maxDistance-border)/(maxDetail - detailAtBorder) + border);
}
}
return distance;
}
public static int getDistanceRendering(int detail)
{
int initial;
int distance = 0;
if (detail <= minDetail)
return minDistance;
if (detail == maxDetail)
return maxDistance * 2;
if (detail == maxDetail + 1)
return maxDistance * 3;
switch (LodConfig.CLIENT.lodDistanceCalculatorType.get())
{
case LINEAR:
initial = LodConfig.CLIENT.lodQuality.get() * 128;
return (detail * initial);
case QUADRATIC:
initial = LodConfig.CLIENT.lodQuality.get() * 128;
return (int) (Math.pow(2, detail) * initial);
case RENDER_DEPENDANT:
int realRenderDistance = Minecraft.getInstance().options.renderDistance * 16;
int border = 64;
byte detailAtBorder = (byte) 4;
if (detail > detailAtBorder)
{
return (detail * (border - realRenderDistance) / detailAtBorder + realRenderDistance);
} else
{
return ((maxDetail - detail) * (maxDistance - border) / (maxDetail - detailAtBorder) + border);
}
}
return distance;
}
public static int getDistanceGeneration(int detail)
{
if(detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * genMultiplier);
}
public static int getDistanceTreeCut(int detail)
{
if(detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * treeCutMultiplier);
}
public static int getDistanceTreeGen(int detail)
{
if(detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * treeGenMultiplier);
}
public static int getDistanceGeneration(int detail)
{
if (detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * genMultiplier);
}
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
{
return distancesGenerators[detail];
}
public static int getDistanceTreeCut(int detail)
{
if (detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * treeCutMultiplier);
}
public static LodDetail getLodDetail(int detail)
{
if(detail < minDetail)
{
return lodDetails[minDetail];
}
else
{
return lodDetails[detail];
}
}
public static int getDistanceTreeGen(int detail)
{
if (detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * treeGenMultiplier);
}
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
{
return distancesGenerators[detail];
}
public static LodDetail getLodDetail(int detail)
{
if (detail < minDetail)
{
return lodDetails[minDetail];
} else
{
return lodDetails[detail];
}
}
public static byte getCutLodDetail(int detail)
{
if(detail < minDetail)
{
return lodDetailsCut[minDetail].detailLevel;
}
else if(detail == maxDetail)
{
return LodUtil.REGION_DETAIL_LEVEL;
}
else
{
return lodDetailsCut[detail].detailLevel;
}
}
public static byte getCutLodDetail(int detail)
{
if (detail < minDetail)
{
return lodDetailsCut[minDetail].detailLevel;
} else if (detail == maxDetail)
{
return LodUtil.REGION_DETAIL_LEVEL;
} else
{
return lodDetailsCut[detail].detailLevel;
}
}
}