added commented use of biomeUtils and terrainUtils

This commit is contained in:
Leonardo
2021-08-21 22:01:52 +02:00
parent a02c232f5f
commit c20ec4ef59
4 changed files with 546 additions and 435 deletions
@@ -51,114 +51,131 @@ import net.minecraftforge.common.WorldWorkerManager;
/**
* This object is used to create NearFarBuffer objects.
*
*
* @author James Seibel
* @version 8-21-2021
*/
public class LodBufferBuilder
{
private Minecraft mc;
/** This holds the thread used to generate new LODs off the main thread. */
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main"));
private LodBuilder LodQuadTreeNodeBuilder;
/** The buffers that are used to create LODs using far fog */
public volatile BufferBuilder[][] buildableBuffers;
/** Used when building new VBOs */
public volatile VertexBuffer[][] buildableVbos;
/** VBOs that are sent over to the LodNodeRenderer */
public volatile VertexBuffer[][] drawableVbos;
/** if this is true the LOD buffers are currently being
* regenerated. */
public boolean generatingBuffers = false;
private Minecraft mc;
/** if this is true the LOD buffers are currently being
* regenerated. */
public Set<ChunkPos> positionWaitingToBeGenerated = new HashSet<>();
/**
* This holds the thread used to generate new LODs off the main thread.
*/
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main"));
/** if this is true new LOD buffers have been generated
* and are waiting to be swapped with the drawable buffers*/
private boolean switchVbos = false;
/** This keeps track of how many chunk generation requests are on going.
* This is 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);
/** 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 multiply by 8 to make sure there is always a buffer of chunk requests,
* to make sure the CPU is always busy and we can generate LODs as quickly as
* possible. */
public int maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8;
public LodBufferBuilder(LodBuilder newLodBuilder)
{
mc = Minecraft.getInstance();
LodQuadTreeNodeBuilder = newLodBuilder;
}
private LodDimension previousDimension = null;
/**
* 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.");
if (previousDimension != lodDim)
{
previousDimension = lodDim;
}
generatingBuffers = true;
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
// this is where we will start drawing squares
// (exactly half the total width)
BlockPos startBlockPos = new BlockPos(-(numbChunksWide * 16 / 2) + playerBlockPosRounded.getX(), 0, -(numbChunksWide * 16 / 2) + playerBlockPosRounded.getZ());
ChunkPos startChunkPos = new ChunkPos(startBlockPos);
Thread thread = new Thread(() ->
{
try
{
long startTime = System.currentTimeMillis();
ArrayList<ChunkPos> chunksToGen = new ArrayList<>(maxChunkGenRequests);
// if we don't have a full number of chunks to generate in chunksToGen
// we can top it off from the reserve
ArrayList<ChunkPos> chunksToGenReserve = new ArrayList<>(maxChunkGenRequests);
private LodBuilder LodQuadTreeNodeBuilder;
/**
* The buffers that are used to create LODs using far fog
*/
public volatile BufferBuilder[][] buildableBuffers;
/**
* Used when building new VBOs
*/
public volatile VertexBuffer[][] buildableVbos;
/**
* VBOs that are sent over to the LodNodeRenderer
*/
public volatile VertexBuffer[][] drawableVbos;
/**
* if this is true the LOD buffers are currently being
* regenerated.
*/
public boolean generatingBuffers = false;
/**
* if this is true the LOD buffers are currently being
* regenerated.
*/
public Set<ChunkPos> positionWaitingToBeGenerated = new HashSet<>();
/**
* if this is true new LOD buffers have been generated
* and are waiting to be swapped with the drawable buffers
*/
private boolean switchVbos = false;
/**
* This keeps track of how many chunk generation requests are on going.
* This is 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);
/**
* 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 multiply by 8 to make sure there is always a buffer of chunk requests,
* to make sure the CPU is always busy and we can generate LODs as quickly as
* possible.
*/
public int maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8;
public LodBufferBuilder(LodBuilder newLodBuilder)
{
mc = Minecraft.getInstance();
LodQuadTreeNodeBuilder = newLodBuilder;
}
private LodDimension previousDimension = null;
/**
* 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.");
if (previousDimension != lodDim)
{
previousDimension = lodDim;
}
generatingBuffers = true;
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
// this is where we will start drawing squares
// (exactly half the total width)
BlockPos startBlockPos = new BlockPos(-(numbChunksWide * 16 / 2) + playerBlockPosRounded.getX(), 0, -(numbChunksWide * 16 / 2) + playerBlockPosRounded.getZ());
ChunkPos startChunkPos = new ChunkPos(startBlockPos);
Thread thread = new Thread(() ->
{
try
{
long startTime = System.currentTimeMillis();
ArrayList<ChunkPos> chunksToGen = new ArrayList<>(maxChunkGenRequests);
// if we don't have a full number of chunks to generate in chunksToGen
// we can top it off from the reserve
ArrayList<ChunkPos> chunksToGenReserve = new ArrayList<>(maxChunkGenRequests);
/*
DistanceGenerationMode[] distancesGenerators = {DistanceGenerationMode.FEATURES,
@@ -174,75 +191,77 @@ public class LodBufferBuilder
*/
DistanceGenerationMode[] distancesGenerators = {
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE};
int[] distancesLinear = {0, 200, 400, 600, 800, 1000, 1500, 2000, 3000, 4000, 8000};
int[] distancesExponential = {0, 100, 200, 400, 800, 1600, 3200, 3200, 3200, 3200, 8000};
startBuffers();
// used when determining which chunks are closer when queuing distance
// generation
int minChunkDist = Integer.MAX_VALUE;
DistanceGenerationMode[] distancesGenerators = {
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE,
DistanceGenerationMode.SURFACE};
int[] distancesLinear = {0, 200, 400, 600, 800, 1000, 1500, 2000, 3000, 4000, 8000};
int[] distancesExponential = {0, 100, 200, 400, 800, 1600, 3200, 3200, 3200, 3200, 8000};
int width;
List<LevelPos> posListToRender = new ArrayList<>();
LodDataPoint lodData;
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;
startBuffers();
/**TODO make this automatic and config dependent*/
for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
posListToRender.addAll(lodDim.getDataToRender(regionPos, playerBlockPosRounded.getX(), playerBlockPosRounded.getZ(), distancesLinear[detail], distancesLinear[detail + 1], detail));
}
// used when determining which chunks are closer when queuing distance
// generation
int minChunkDist = Integer.MAX_VALUE;
for (LevelPos pos : posListToRender)
{
LevelPos chunkPos = pos.convert((byte) 3);
int chunkX = chunkPos.posX + startChunkPos.x;
int chunkZ = chunkPos.posZ + startChunkPos.z;
// skip any chunks that Minecraft is going to render
if (!(isCoordInCenterArea(pos.convert((byte) 3).posX, pos.convert((byte) 3).posZ, (numbChunksWide / 2))
&& renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ))) && lodDim.doesDataExist(pos))
{
width = (int) Math.pow(2, pos.detailLevel);
lodData = lodDim.getData(pos);
LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, lodDim, lodData,
pos.posX * width, 0, pos.posZ * width, renderer.debugging, pos.detailLevel);
}
}
posListToRender.clear();
}
}
int width;
List<LevelPos> posListToRender = new ArrayList<>();
LodDataPoint lodData;
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);
/**TODO make this automatic and config dependent*/
List<LevelPos> posListToGenerate = new ArrayList<>();
// local position in the vbo and bufferBuilder arrays
BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion];
/**TODO this order can be inverted*/
// make sure the buffers weren't
// changed while we were running this method
if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building()))
return;
/**TODO make this automatic and config dependent*/
for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
posListToRender.addAll(lodDim.getDataToRender(regionPos, playerBlockPosRounded.getX(), playerBlockPosRounded.getZ(), distancesLinear[detail], distancesLinear[detail + 1], detail));
}
for (LevelPos pos : posListToRender)
{
LevelPos chunkPos = pos.convert((byte) 2);
int chunkX = chunkPos.posX + startChunkPos.x;
int chunkZ = chunkPos.posZ + startChunkPos.z;
// skip any chunks that Minecraft is going to render
if (isCoordInCenterArea(chunkPos.posX, chunkPos.posZ, (numbChunksWide / 2)) || renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ)))
{
continue;
}
if (lodDim.doesDataExist(pos))
{
width = (int) Math.pow(2, pos.detailLevel);
lodData = lodDim.getData(pos);
LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, lodDim, lodData,
pos.posX * width, 0, pos.posZ * width, renderer.debugging, pos.detailLevel);
}
}
posListToRender.clear();
}
}
/**TODO make this automatic and config dependent*/
List<LevelPos> posListToGenerate = new ArrayList<>();
/**TODO this order can be inverted*/
/*
for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
@@ -262,275 +281,259 @@ public class LodBufferBuilder
}
}
*/
for (byte detailGen = LodUtil.BLOCK_DETAIL_LEVEL; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++)
{
if (!posListToGenerate.isEmpty()) break;
posListToGenerate.addAll(lodDim.getDataToGenerate(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
(int) (distancesLinear[detailGen]*1.5),
(int) (distancesLinear[detailGen+1]*1.5),
(byte) distancesGenerators[detailGen].complexity,
(byte) 0,
16));
}
for (byte detailGen = LodUtil.BLOCK_DETAIL_LEVEL; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++)
{
if (!posListToGenerate.isEmpty()) break;
posListToGenerate.addAll(lodDim.getDataToGenerate(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
(int) (distancesLinear[detailGen] * 1.5),
(int) (distancesLinear[detailGen + 1] * 1.5),
(byte) distancesGenerators[detailGen].complexity,
(byte) 0,
maxChunkGenRequests));
}
for (LevelPos levelPos : posListToGenerate)
{
LevelPos chunkLevelPos = levelPos.convert((byte) 3);
int chunkX = Math.floorDiv(chunkLevelPos.posX, 2);
int chunkZ = Math.floorDiv(chunkLevelPos.posZ, 2);
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
ChunkPos pos = new ChunkPos(chunkX, chunkZ);
if (positionWaitingToBeGenerated.contains(pos))
{
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(pos);
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<ChunkPos> oldReserve = new ArrayList<>(chunksToGenReserve);
chunksToGenReserve.clear();
chunksToGenReserve.addAll(chunksToGen);
// top off reserve with whatever was in oldReerve
for (int i = 0; i < oldReserve.size(); i++)
{
if (chunksToGenReserve.size() < maxChunkGenRequests)
chunksToGenReserve.add(oldReserve.get(i));
else
break;
}
chunksToGen.clear();
chunksToGen.add(pos);
} else if (newDistance == minChunkDist)
{
// this chunk position as close as the minimum distance
if (chunksToGen.size() < maxChunkGenRequests)
{
// we are still under the number of chunks to generate
// add this position to the list
chunksToGen.add(pos);
}
} 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(pos);
}
} // lod null and can generate more chunks
} // positions to generate
// issue #19
// TODO add a way for a server side mod to generate chunks requested here
if (mc.hasSingleplayerServer())
{
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
// make sure we have as many chunks to generate as we are allowed
if (chunksToGen.size() < maxChunkGenRequests)
{
Iterator<ChunkPos> reserveIterator = chunksToGenReserve.iterator();
while (chunksToGen.size() < maxChunkGenRequests && reserveIterator.hasNext())
{
chunksToGen.add(reserveIterator.next());
}
}
// start chunk generation
for (ChunkPos chunkPos : chunksToGen)
{
// don't add null chunkPos (which shouldn't happen anyway)
// or add more to the generation queue
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
continue;
positionWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DistanceGenerationMode.SURFACE, LodDetail.FULL, renderer, LodQuadTreeNodeBuilder, this, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
}
// finish the buffer building
closeBuffers();
// upload the new buffers
uploadBuffers();
long endTime = System.currentTimeMillis();
long buildTime = endTime - startTime;
//ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " 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();
}
});
mainGenThread.execute(thread);
return;
}
//====================//
// generation helpers //
//====================//
/**
* Returns if the given coordinate is in the loaded area of the world.
*
* @param centerCoordinate the center of the loaded world
*/
private boolean isCoordInCenterArea(int i, int j, int centerCoordinate)
{
return (i >= centerCoordinate - mc.options.renderDistance
&& i <= centerCoordinate + mc.options.renderDistance)
&&
(j >= centerCoordinate - mc.options.renderDistance
&& j <= centerCoordinate + mc.options.renderDistance);
}
//===============================//
// BufferBuilder related methods //
//===============================//
/**
* Called from the LodRenderer to create the
* BufferBuilders.
*/
public void setupBuffers(int numbRegionsWide, int 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);
}
}
}
/**
* Calls begin on each of the buildable BufferBuilders.
*/
public 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.
*/
public 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.
*
* @param bufferMaxCapacity
*/
public 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()
{
VertexBuffer[][] tmp = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmp;
// the vbos have been swapped
switchVbos = false;
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;
}
for (LevelPos levelPos : posListToGenerate)
{
LevelPos chunkLevelPos = levelPos.convert((byte) 3);
int chunkX = chunkLevelPos.posX / 2;
int chunkZ = chunkLevelPos.posZ / 2;
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
ChunkPos pos = new ChunkPos(chunkX, chunkZ);
if (positionWaitingToBeGenerated.contains(pos))
{
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(pos);
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<ChunkPos> oldReserve = new ArrayList<>(chunksToGenReserve);
chunksToGenReserve.clear();
chunksToGenReserve.addAll(chunksToGen);
// top off reserve with whatever was in oldReerve
for (int i = 0; i < oldReserve.size(); i++)
{
if (chunksToGenReserve.size() < maxChunkGenRequests)
chunksToGenReserve.add(oldReserve.get(i));
else
break;
}
chunksToGen.clear();
chunksToGen.add(pos);
}
else if (newDistance == minChunkDist)
{
// this chunk position as close as the minimum distance
if (chunksToGen.size() < maxChunkGenRequests)
{
// we are still under the number of chunks to generate
// add this position to the list
chunksToGen.add(pos);
}
}
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(pos);
}
} // lod null and can generate more chunks
} // positions to generate
// issue #19
// TODO add a way for a server side mod to generate chunks requested here
if (mc.hasSingleplayerServer())
{
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
// make sure we have as many chunks to generate as we are allowed
if (chunksToGen.size() < maxChunkGenRequests)
{
Iterator<ChunkPos> reserveIterator = chunksToGenReserve.iterator();
while (chunksToGen.size() < maxChunkGenRequests && reserveIterator.hasNext())
{
chunksToGen.add(reserveIterator.next());
}
}
// start chunk generation
for (ChunkPos chunkPos : chunksToGen)
{
// don't add null chunkPos (which shouldn't happen anyway)
// or add more to the generation queue
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
continue;
positionWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DistanceGenerationMode.SURFACE, LodDetail.FULL, renderer, LodQuadTreeNodeBuilder, this, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
}
// finish the buffer building
closeBuffers();
// upload the new buffers
uploadBuffers();
long endTime = System.currentTimeMillis();
long buildTime = endTime - startTime;
//ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " 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();
}
});
mainGenThread.execute(thread);
return;
}
//====================//
// generation helpers //
//====================//
/**
* Returns if the given coordinate is in the loaded area of the world.
* @param centerCoordinate the center of the loaded world
*/
private boolean isCoordInCenterArea(int i, int j, int centerCoordinate)
{
return (i >= centerCoordinate - mc.options.renderDistance
&& i <= centerCoordinate + mc.options.renderDistance)
&&
(j >= centerCoordinate - mc.options.renderDistance
&& j <= centerCoordinate + mc.options.renderDistance);
}
//===============================//
// BufferBuilder related methods //
//===============================//
/**
* Called from the LodRenderer to create the
* BufferBuilders.
*/
public void setupBuffers(int numbRegionsWide, int 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);
}
}
}
/**
* Calls begin on each of the buildable BufferBuilders.
*/
public 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.
*/
public 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.
*
* @param bufferMaxCapacity
*/
public 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()
{
VertexBuffer[][] tmp = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmp;
// the vbos have been swapped
switchVbos = false;
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;
}
}
@@ -29,9 +29,13 @@ import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodWorld;
//import com.seibel.lod.util.BiomeColorsUtils;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
//import kaptainwutax.biomeutils.source.OverworldBiomeSource;
//import kaptainwutax.mcutils.version.MCVersion;
//import kaptainwutax.terrainutils.TerrainGenerator;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
@@ -40,6 +44,7 @@ import net.minecraft.block.GrassBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.material.MaterialColor;
//import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.Biome;
@@ -187,6 +192,53 @@ public class LodBuilder
}
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
/**TODO if we want to test biome utils and terrain utils
public void generateLodNodeFromChunk(LodDimension lodDim, ChunkPos chunkPos , LodDetail detail, long seed)
throws IllegalArgumentException
{
if (chunkPos == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk pos");
int startX;
int startZ;
int endX;
int endZ;
Color color;
short height;
short depth;
LevelPos levelPos;
LodDataPoint data;
OverworldBiomeSource biomeSource = new OverworldBiomeSource(MCVersion.v1_16_5, seed);
TerrainGenerator terrainGenerator= TerrainGenerator.of(biomeSource);
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(biomeSource, chunkPos, startX, startZ, endX, endZ);
height = determineHeightPoint(terrainGenerator, chunkPos, startX, startZ, endX, endZ);
levelPos = new LevelPos((byte) 0,
chunkPos.x * 16 + startX,
chunkPos.z * 16 + startZ);
data = new LodDataPoint(height, 0, color);
lodDim.addData(levelPos.convert((byte) detail.detailLevel),
data,
DistanceGenerationMode.SURFACE,
true,
false);
}
}*/
// =====================//
// constructor helpers //
// =====================//
@@ -312,6 +364,26 @@ public class LodBuilder
return highest;
}
/**
* Find the highest point from the Top
*/
/**TODO if we want to test biome utils and terrain utils
private short determineHeightPoint(TerrainGenerator terrainGenerator, ChunkPos chunkPos, int startX, int startZ, int endX, int endZ)
{
short newHeight = 0;
int num = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
num++;
newHeight += (short) terrainGenerator.getFirstHeightInColumn(chunkPos.x*16 + x,chunkPos.z*16 + z, TerrainGenerator.WORLD_SURFACE_WG);;
}
}
return (short) (newHeight/num);
}*/
/**
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
@@ -411,6 +483,39 @@ public class LodBuilder
return new Color(red, green, blue);
}
/**TODO if we want to test biome utils and terrain utils
private Color generateLodColorForArea(OverworldBiomeSource biomeSource, ChunkPos chunkPos, int startX, int startZ, int endX,
int endZ)
{
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++)
{
Color color = BiomeColorsUtils.getColorFromBiomeManual(biomeSource.getBiome(chunkPos.x*16 + x,0,chunkPos.z*16 + z));
red += color.getBlue();
green += color.getGreen();
blue += color.getBlue();
}
}
if (numbOfBlocks == 0)
numbOfBlocks = 1;
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new Color(red, green, blue);
}
*/
/**
* Returns a color int for a given block.
*/
@@ -542,5 +647,4 @@ public class LodBuilder
return false;
}
}
@@ -400,6 +400,9 @@ public class LodNodeGenWorker implements IWorker
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(DistanceGenerationMode.SURFACE), detailLevel);
/**TODO if we want to use Biome utils and terrain utils for overworld
* lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/
}
@@ -57,6 +57,7 @@ public class LevelPos implements Cloneable
return new LevelPos(detailLevel, posX, posZ);
}
/**TODO fix the region disappearing for a second*/
public int maxDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ)
{