Use OpenGL BufferStorage to improve upload performance
I need to change how the memory is determined, right now they fill to the maximum amount; which is very bad and only for testing.
This commit is contained in:
@@ -61,53 +61,35 @@ import net.minecraft.util.math.ChunkPos;
|
||||
* This object is used to create NearFarBuffer objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-4-2021
|
||||
* @version 10-7-2021
|
||||
*/
|
||||
public class LodBufferBuilder
|
||||
{
|
||||
/**
|
||||
* This holds the thread used to generate new LODs off the main thread.
|
||||
*/
|
||||
/** The thread used to generate new LODs off the main thread. */
|
||||
public static ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - main"));
|
||||
/**
|
||||
* This holds the threads used to generate buffers.
|
||||
*/
|
||||
/** The threads used to generate buffers. */
|
||||
public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfBufferBuilderThreads.get(), new ThreadFactoryBuilder().setNameFormat("Buffer-Builder-%d").build());
|
||||
|
||||
/**
|
||||
* This boolean matrix indicate the buffer builder in that position has to be regenerated
|
||||
*/
|
||||
/** This boolean matrix indicate the buffer builder in that position has to be regenerated */
|
||||
public volatile boolean[][] regenPos;
|
||||
|
||||
/**
|
||||
* This boolean indicate that all buffer need to be regenerated
|
||||
*/
|
||||
/** This boolean indicates that ever buffer need to be regenerated */
|
||||
public volatile boolean fullRegeneration = false;
|
||||
|
||||
/**
|
||||
* max capacity of buffers
|
||||
*/
|
||||
//public volatile int BUFFER_MAX_CAPACITY = 1 << (26);
|
||||
public volatile int BUFFER_MAX_CAPACITY = (int) (64 * Math.pow(10, 6));
|
||||
/** the capacity of each buffer in bytes */
|
||||
public volatile int[][] bufferCount;
|
||||
|
||||
/**
|
||||
* buffer builder for the vertex
|
||||
*/
|
||||
public volatile int[][] bufferSize;
|
||||
|
||||
/**
|
||||
* buffer builder for the vertex
|
||||
*/
|
||||
/** Used when building the vbos */
|
||||
public volatile BufferBuilder[][][] buildableBuffers;
|
||||
|
||||
/**
|
||||
* Used when building new VBOs
|
||||
*/
|
||||
public volatile VertexBuffer[][][] buildableVbos;
|
||||
/** The OpenGL IDs of the storage buffers used by the buildableVbos */
|
||||
public int[][] buildableStorageBufferIds;
|
||||
/** The OpenGL IDs of the storage buffers used by the drawableVbos */
|
||||
public int[][] drawableStorageBufferIds;
|
||||
|
||||
/**
|
||||
* VBOs that are sent over to the LodNodeRenderer
|
||||
*/
|
||||
/** Used when building new VBOs */
|
||||
public volatile VertexBuffer[][][] buildableVbos;
|
||||
/** VBOs that are sent over to the LodNodeRenderer */
|
||||
public volatile VertexBuffer[][][] drawableVbos;
|
||||
|
||||
/**
|
||||
@@ -122,19 +104,13 @@ public class LodBufferBuilder
|
||||
*/
|
||||
private boolean switchVbos = false;
|
||||
|
||||
/**
|
||||
* Size of the buffer builders in bytes last time we created them
|
||||
*/
|
||||
/** 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
|
||||
*/
|
||||
/** 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
|
||||
*/
|
||||
/** this is used to prevent multiple threads creating, destroying, or using the buffers at the same time */
|
||||
private ReentrantLock bufferLock = new ReentrantLock();
|
||||
|
||||
private volatile Box[][] boxCache;
|
||||
@@ -149,6 +125,10 @@ public class LodBufferBuilder
|
||||
private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0, 0);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public LodBufferBuilder()
|
||||
{
|
||||
|
||||
@@ -448,57 +428,71 @@ public class LodBufferBuilder
|
||||
int numberOfBuffers;
|
||||
|
||||
previousRegionWidth = numbRegionsWide;
|
||||
bufferSize = new int[numbRegionsWide][numbRegionsWide];
|
||||
bufferCount = new int[numbRegionsWide][numbRegionsWide];
|
||||
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide][];
|
||||
|
||||
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][];
|
||||
drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][];
|
||||
|
||||
buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide];
|
||||
drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide];
|
||||
|
||||
|
||||
for (int x = 0; x < numbRegionsWide; x++)
|
||||
{
|
||||
for (int z = 0; z < numbRegionsWide; z++)
|
||||
{
|
||||
memoryRequired = lodDimension.getMemoryRequired(x, z, LodConfig.CLIENT.graphics.lodTemplate.get());
|
||||
//if the memory required is less than the max buffer capacity we divide the memory across multiple buffers
|
||||
if (BUFFER_MAX_CAPACITY > memoryRequired)
|
||||
memoryRequired = lodDimension.getRegionRequiredMemory(x, z, LodConfig.CLIENT.graphics.lodTemplate.get());
|
||||
|
||||
// if the memory required is greater than the max buffer capacity divide the memory across multiple buffers
|
||||
if (memoryRequired < LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY)
|
||||
{
|
||||
bufferSize[x][z] = 1;
|
||||
bufferCount[x][z] = 1;
|
||||
buildableBuffers[x][z] = new BufferBuilder[1];
|
||||
buildableVbos[x][z] = new VertexBuffer[1];
|
||||
drawableVbos[x][z] = new VertexBuffer[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
numberOfBuffers = (int) Math.ceil(memoryRequired / BUFFER_MAX_CAPACITY) + 1;
|
||||
memoryRequired = BUFFER_MAX_CAPACITY;
|
||||
bufferSize[x][z] = numberOfBuffers;
|
||||
numberOfBuffers = (int) Math.ceil(memoryRequired / LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY) + 1;
|
||||
memoryRequired = LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY;
|
||||
bufferCount[x][z] = numberOfBuffers;
|
||||
buildableBuffers[x][z] = new BufferBuilder[numberOfBuffers];
|
||||
buildableVbos[x][z] = new VertexBuffer[numberOfBuffers];
|
||||
drawableVbos[x][z] = new VertexBuffer[numberOfBuffers];
|
||||
}
|
||||
|
||||
for (int i = 0; i < bufferSize[x][z]; i++)
|
||||
|
||||
for (int i = 0; i < bufferCount[x][z]; i++)
|
||||
{
|
||||
buildableBuffers[x][z][i] = new BufferBuilder((int) memoryRequired);
|
||||
|
||||
buildableVbos[x][z][i] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
|
||||
drawableVbos[x][z][i] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
|
||||
buildableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT);
|
||||
drawableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT);
|
||||
|
||||
// create the initial mapped buffers (system memory)
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, memoryRequired, GL45.GL_DYNAMIC_DRAW);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, memoryRequired, GL45.GL_DYNAMIC_DRAW);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
|
||||
// // buffer storage
|
||||
// GL15.glDeleteBuffers(buildableVbos[x][z][i].id);
|
||||
// GL15.glDeleteBuffers(drawableVbos[x][z][i].id);
|
||||
//
|
||||
// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id);
|
||||
// // no matter the size the buffer are created at sometimes they will still fail
|
||||
// // to map later on, James isn't sure why that is
|
||||
// GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, BUFFER_MAX_CAPACITY / 2, GL45.GL_MAP_WRITE_BIT | GL45.GL_MAP_PERSISTENT_BIT | GL45.GL_MAP_COHERENT_BIT);
|
||||
// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
//
|
||||
// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id);
|
||||
// GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, BUFFER_MAX_CAPACITY / 2, GL45.GL_MAP_WRITE_BIT | GL45.GL_MAP_PERSISTENT_BIT | GL45.GL_MAP_COHERENT_BIT);
|
||||
// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
// // buffer storage end
|
||||
|
||||
// create the buffer storage (GPU memory)
|
||||
|
||||
buildableStorageBufferIds[x][z] = GL45.glGenBuffers();
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z]);
|
||||
// TODO get a better max size, BufferStorage's can't be resized after they are created
|
||||
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY, 0); // the 0 flag means to create the storage in the GPU's memory
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
drawableStorageBufferIds[x][z] = GL45.glGenBuffers();
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z]);
|
||||
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY, 0);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -522,9 +516,7 @@ public class LodBufferBuilder
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls begin on each of the buildable BufferBuilders.
|
||||
*/
|
||||
/** Calls begin on each of the buildable BufferBuilders. */
|
||||
private void startBuffers(boolean fullRegen, LodDimension lodDim)
|
||||
{
|
||||
for (int x = 0; x < buildableBuffers.length; x++)
|
||||
@@ -541,16 +533,14 @@ public class LodBufferBuilder
|
||||
{
|
||||
buildableBuffers[x][z][i].discard();
|
||||
|
||||
buildableBuffers[x][z][i].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
buildableBuffers[x][z][i].begin(GL11.GL_QUADS, LodUtil.LOD_VERTEX_FORMAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls end on each of the buildable BufferBuilders.
|
||||
*/
|
||||
/** Calls end on each of the buildable BufferBuilders. */
|
||||
private void closeBuffers(boolean fullRegen, LodDimension lodDim)
|
||||
{
|
||||
for (int x = 0; x < buildableBuffers.length; x++)
|
||||
@@ -565,9 +555,7 @@ public class LodBufferBuilder
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Upload all buildableBuffers to the GPU.
|
||||
*/
|
||||
/** Upload all buildableBuffers to the GPU. */
|
||||
private void uploadBuffers(boolean fullRegen, LodDimension lodDim)
|
||||
{
|
||||
GlProxy glProxy = GlProxy.getInstance();
|
||||
@@ -588,15 +576,16 @@ public class LodBufferBuilder
|
||||
for (int i = 0; i < buildableBuffers[x][z].length; i++)
|
||||
{
|
||||
ByteBuffer builderBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond();
|
||||
vboUpload(buildableVbos[x][z][i], builderBuffer);
|
||||
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z], builderBuffer);
|
||||
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) // TODO this try/catch may not be needed anymore, James needs to check
|
||||
catch (Exception e)
|
||||
{
|
||||
// this doesn't appear to be necessary anymore, but just in case.
|
||||
ClientProxy.LOGGER.error(LodBufferBuilder.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -604,15 +593,13 @@ public class LodBufferBuilder
|
||||
{
|
||||
// close the context so it can be re-used later.
|
||||
// I'm guessing we can't just leave it because the executer service
|
||||
// does something that invalidates the OpenGL context
|
||||
// does something that invalidates the OpenGL context.
|
||||
glProxy.setGlContext(GlProxyContext.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the uploadBuffer into the VBO in GPU memory.
|
||||
*/
|
||||
private void vboUpload(VertexBuffer vbo, ByteBuffer uploadBuffer)
|
||||
/** Uploads the uploadBuffer into the VBO and then into GPU memory. */
|
||||
private void vboUpload(VertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer)
|
||||
{
|
||||
// this shouldn't happen, but just to be safe
|
||||
if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER)
|
||||
@@ -621,123 +608,49 @@ public class LodBufferBuilder
|
||||
vbo.vertexCount = (uploadBuffer.remaining() / vbo.format.getVertexSize());
|
||||
|
||||
|
||||
|
||||
// // buffer mapping //
|
||||
// // no stutter 50% GPU useage
|
||||
// // stores everything in system memory instead of GPU memory
|
||||
// // making rendering much slower
|
||||
//
|
||||
// GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo.id);
|
||||
//
|
||||
// ByteBuffer vboBuffer = null;
|
||||
// try
|
||||
// {
|
||||
// vboBuffer = GL45.glMapBufferRange(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL45.GL_MAP_WRITE_BIT | GL45.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
// if (vboBuffer == null)
|
||||
// {
|
||||
// GL45.glBufferData(GL45.GL_ARRAY_BUFFER, uploadBuffer.capacity() * 4, GL45.GL_STREAM_DRAW);
|
||||
// GL45.glBufferSubData(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
//
|
||||
// ClientProxy.LOGGER.info("null");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// vboBuffer.put(uploadBuffer);
|
||||
//// ClientProxy.LOGGER.info("not null");
|
||||
// }
|
||||
// }
|
||||
// catch(Exception e)
|
||||
// {
|
||||
// ClientProxy.LOGGER.info(e.getClass().getSimpleName());
|
||||
//// e.printStackTrace();
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
// GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // bufferstorage //
|
||||
// // to test: uncomment the bufferstorage lines in setupBuffers as well
|
||||
// // then reboot the game
|
||||
//
|
||||
// // moderate stutter 50% gpu
|
||||
// // stores everything in system memory instead of GPU memory
|
||||
// // making rendering much slower
|
||||
//
|
||||
// GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo.id);
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// ByteBuffer vboBuffer = GL45.glMapBufferRange(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL45.GL_MAP_WRITE_BIT | GL45.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
// if (vboBuffer == null)
|
||||
// {
|
||||
// ClientProxy.LOGGER.info("mapping fail");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// vboBuffer.put(uploadBuffer);
|
||||
// }
|
||||
// }
|
||||
// catch(Exception e)
|
||||
// {
|
||||
// ClientProxy.LOGGER.info(e.getClass().getSimpleName());
|
||||
// //e.printStackTrace();
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// GL45.glUnmapBuffer(GL45.GL_ARRAY_BUFFER);
|
||||
// GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, 0);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// hybrid subData/bufferData //
|
||||
// less stutter 12% GPU usage
|
||||
|
||||
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo.id);
|
||||
|
||||
long size = GL45.glGetBufferParameteri64(GL45.GL_ARRAY_BUFFER, GL45.GL_BUFFER_SIZE);
|
||||
if (size < uploadBuffer.capacity() * 4)
|
||||
try
|
||||
{
|
||||
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, uploadBuffer.capacity() * 5, GL45.GL_STREAM_DRAW);
|
||||
ClientProxy.LOGGER.info("expand buffer: " + size + " -> " + (uploadBuffer.capacity() * 4));
|
||||
// get a pointer to the buffer in system memory
|
||||
ByteBuffer vboBuffer = GL45.glMapBufferRange(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL45.GL_MAP_WRITE_BIT | GL45.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
if (vboBuffer == null)
|
||||
{
|
||||
// the buffer in system memory isn't big enough,
|
||||
// expand it so next time hopefully it will be big enough.
|
||||
// This does cause lag/stuttering so it should be avoided!
|
||||
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, uploadBuffer.capacity() * 4, GL45.GL_STREAM_DRAW);
|
||||
GL45.glBufferSubData(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
|
||||
ClientProxy.LOGGER.info("null");
|
||||
}
|
||||
else
|
||||
{
|
||||
// upload the buffer into system memory...
|
||||
vboBuffer.put(uploadBuffer);
|
||||
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
|
||||
// ...then upload into GPU memory
|
||||
// (uploading into GPU memory can only be done through )
|
||||
GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity());
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
ClientProxy.LOGGER.info(e.getClass().getSimpleName());
|
||||
// e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
GL45.glBufferSubData(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
|
||||
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
|
||||
|
||||
// // old method - bufferData
|
||||
// // bad stuttering 12% GPU usage
|
||||
//
|
||||
// GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
|
||||
// // subData only works if the memory is allocated beforehand.
|
||||
// GL15C.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer.remaining(), GL15C.GL_DYNAMIC_DRAW);
|
||||
//
|
||||
// // interestingly bufferSubData renders faster than glMapBuffer
|
||||
// // even though OpenGLInsights-AsynchronousBufferTransfers says glMapBuffer
|
||||
// // is faster for transferring data. They must put the data in different memory
|
||||
// // or something.
|
||||
// GL15C.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
// GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the newly created VBOs
|
||||
*/
|
||||
/** Get the newly created VBOs */
|
||||
public VertexBuffersAndOffset getVertexBuffers()
|
||||
{
|
||||
// don't wait for the lock to open
|
||||
// don't wait for the lock to open,
|
||||
// since this is called on the main render thread
|
||||
if (bufferLock.tryLock())
|
||||
{
|
||||
@@ -745,6 +658,10 @@ public class LodBufferBuilder
|
||||
drawableVbos = buildableVbos;
|
||||
buildableVbos = tmpVbo;
|
||||
|
||||
int[][] tmpStorage = drawableStorageBufferIds;
|
||||
drawableStorageBufferIds = buildableStorageBufferIds;
|
||||
buildableStorageBufferIds = tmpStorage;
|
||||
|
||||
|
||||
drawableCenterChunkPos = buildableCenterChunkPos;
|
||||
|
||||
@@ -753,20 +670,20 @@ public class LodBufferBuilder
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos);
|
||||
return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple container to pass multiple objects back in the getVertexBuffers method.
|
||||
*/
|
||||
/** A simple container to pass multiple objects back in the getVertexBuffers method. */
|
||||
public class VertexBuffersAndOffset
|
||||
{
|
||||
public VertexBuffer[][][] vbos;
|
||||
public int[][] storageBufferIds;
|
||||
public ChunkPos drawableCenterChunkPos;
|
||||
|
||||
public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, ChunkPos newDrawableCenterChunkPos)
|
||||
public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, int[][] newStorageBufferIds, ChunkPos newDrawableCenterChunkPos)
|
||||
{
|
||||
vbos = newVbos;
|
||||
storageBufferIds = newStorageBufferIds;
|
||||
drawableCenterChunkPos = newDrawableCenterChunkPos;
|
||||
}
|
||||
}
|
||||
@@ -780,19 +697,4 @@ public class LodBufferBuilder
|
||||
{
|
||||
return switchVbos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void setPartialRegen()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void setFullRegen()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,7 +870,7 @@ public class LodDimension
|
||||
}
|
||||
|
||||
/** Returns the minimum memory required by the dimension in Bytes */
|
||||
public long getMemoryRequired(int x, int z, LodTemplate template)
|
||||
public long getRegionRequiredMemory(int x, int z, LodTemplate template)
|
||||
{
|
||||
/*return regions[x][z].getMinMemoryNeeded(template);*/
|
||||
|
||||
@@ -884,18 +884,7 @@ public class LodDimension
|
||||
break;
|
||||
case BY_REGION_FAST:
|
||||
}*/
|
||||
/*return regions[x][z].getMinMemoryNeeded(template);*/
|
||||
|
||||
/*TODO add memory use calculated with the following cases
|
||||
switch (LodConfig.CLIENT.graphics.detailDropOff.get())
|
||||
{
|
||||
default:
|
||||
case BY_BLOCK:
|
||||
break;
|
||||
case BY_REGION_FANCY:
|
||||
break;
|
||||
case BY_REGION_FAST:
|
||||
}*/
|
||||
|
||||
int minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, x, z, halfWidth, halfWidth);
|
||||
int detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
|
||||
int levelToGen = DetailDistanceUtil.getLodDrawDetail(detail);
|
||||
@@ -904,6 +893,5 @@ public class LodDimension
|
||||
long memoryUse = LodUtil.regionRenderingMemoryUse(x,z,template);
|
||||
//System.out.println(detail + " " + memoryUse + " " + template.getBufferMemoryForSingleLod(maxVerticalData));
|
||||
return memoryUse;
|
||||
//return memoryUse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ public class LodRegion
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lowest detail level in this region
|
||||
* Returns the lowest (least detailed) detail level in this region
|
||||
* TODO is that right?
|
||||
*/
|
||||
public byte getMinDetailLevel()
|
||||
|
||||
@@ -50,9 +50,7 @@ import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
@@ -65,7 +63,7 @@ import net.minecraft.util.math.vector.Vector3d;
|
||||
* This is where LODs are draw to the world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-2-2021
|
||||
* @version 10-7-2021
|
||||
*/
|
||||
public class LodRenderer
|
||||
{
|
||||
@@ -74,39 +72,39 @@ public class LodRenderer
|
||||
* it should be something different than what is used by Minecraft
|
||||
*/
|
||||
private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2;
|
||||
|
||||
|
||||
/**
|
||||
* If true the LODs colors will be replaced with
|
||||
* a checkerboard, this can be used for debugging.
|
||||
*/
|
||||
public DebugMode previousDebugMode = DebugMode.OFF;
|
||||
|
||||
|
||||
private MinecraftWrapper mc;
|
||||
private GameRenderer gameRender;
|
||||
private IProfiler profiler;
|
||||
private int farPlaneBlockDistance;
|
||||
|
||||
|
||||
/**
|
||||
* This is used to generate the buildable buffers
|
||||
*/
|
||||
|
||||
|
||||
/** This is used to generate the buildable buffers */
|
||||
private LodBufferBuilder lodBufferBuilder;
|
||||
|
||||
/**
|
||||
* Each VertexBuffer represents 1 region
|
||||
*/
|
||||
|
||||
/** Each VertexBuffer represents 1 region */
|
||||
private VertexBuffer[][][] vbos;
|
||||
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
|
||||
private ChunkPos vbosCenter = new ChunkPos(0, 0);
|
||||
|
||||
|
||||
/**
|
||||
* This is used to determine if the LODs should be regenerated
|
||||
/**
|
||||
* the OpenGL IDs for the vbos of the same indices.
|
||||
* These have to be separate because we can't override the
|
||||
* buffers in the VBOs (and we don't want too)
|
||||
*/
|
||||
private int[][] storageBufferIds;
|
||||
|
||||
private ChunkPos vbosCenter = new ChunkPos(0, 0);
|
||||
|
||||
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int[] previousPos = new int[]{0, 0, 0};
|
||||
|
||||
|
||||
public NativeImage lightMap = null;
|
||||
|
||||
|
||||
// these variables are used to determine if the buffers should be rebuilt
|
||||
private long prevDayTime = 0;
|
||||
private double prevBrightness = 0;
|
||||
@@ -114,37 +112,42 @@ public class LodRenderer
|
||||
private long prevPlayerPosTime = 0;
|
||||
private long prevVanillaChunkTime = 0;
|
||||
private long prevChunkTime = 0;
|
||||
|
||||
|
||||
/**
|
||||
* This is used to determine if the LODs should be regenerated
|
||||
*/
|
||||
|
||||
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR;
|
||||
|
||||
|
||||
/**
|
||||
* if this is true the LOD buffers should be regenerated,
|
||||
* provided they aren't already being regenerated.
|
||||
*/
|
||||
private volatile boolean partialRegen = false;
|
||||
private volatile boolean fullRegen = true;
|
||||
|
||||
|
||||
/**
|
||||
* This HashSet contains every chunk that Vanilla Minecraft
|
||||
* is going to render
|
||||
*/
|
||||
public boolean[][] vanillaRenderedChunks;
|
||||
public boolean vanillaRenderedChunksChanged;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public LodRenderer(LodBufferBuilder newLodNodeBufferBuilder)
|
||||
{
|
||||
mc = MinecraftWrapper.INSTANCE;
|
||||
gameRender = mc.getGameRenderer();
|
||||
|
||||
|
||||
lodBufferBuilder = newLodNodeBufferBuilder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Besides drawing the LODs this method also starts
|
||||
* the async process of generating the Buffers that hold those LODs.
|
||||
@@ -153,6 +156,7 @@ public class LodRenderer
|
||||
* @param mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method
|
||||
* @param partialTicks how far into the current tick this method was called.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, float partialTicks, IProfiler newProfiler)
|
||||
{
|
||||
if (lodDim == null)
|
||||
@@ -161,24 +165,24 @@ public class LodRenderer
|
||||
// don't try drawing anything
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// initial setup //
|
||||
//===============//
|
||||
|
||||
|
||||
profiler = newProfiler;
|
||||
profiler.push("LOD setup");
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
|
||||
// starting here...
|
||||
determineIfLodsShouldRegenerate(lodDim);
|
||||
|
||||
|
||||
//=================//
|
||||
// create the LODs //
|
||||
//=================//
|
||||
|
||||
|
||||
// only regenerate the LODs if:
|
||||
// 1. we want to regenerate LODs
|
||||
// 2. we aren't already regenerating the LODs
|
||||
@@ -188,34 +192,34 @@ public class LodRenderer
|
||||
{
|
||||
// generate the LODs on a separate thread to prevent stuttering or freezing
|
||||
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayer().blockPosition(), true);
|
||||
|
||||
|
||||
// the regen process has been started,
|
||||
// it will be done when lodBufferBuilder.newBuffersAvaliable()
|
||||
// is true
|
||||
fullRegen = false;
|
||||
partialRegen = false;
|
||||
}
|
||||
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
|
||||
// ...ending here
|
||||
|
||||
|
||||
if (lodBufferBuilder.newBuffersAvaliable())
|
||||
{
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===========================//
|
||||
// GL settings for rendering //
|
||||
//===========================//
|
||||
|
||||
|
||||
// set the required open GL settings
|
||||
|
||||
|
||||
if (LodConfig.CLIENT.debugging.debugMode.get() == DebugMode.SHOW_DETAIL_WIREFRAME)
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
|
||||
else
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glEnable(GL11.GL_CULL_FACE);
|
||||
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
|
||||
@@ -228,7 +232,7 @@ public class LodRenderer
|
||||
// disable the lights Minecraft uses
|
||||
GL11.glDisable(GL11.GL_LIGHT0);
|
||||
GL11.glDisable(GL11.GL_LIGHT1);
|
||||
|
||||
|
||||
// get the default projection matrix so we can
|
||||
// reset it after drawing the LODs
|
||||
float[] mcProjMatrixRaw = new float[16];
|
||||
@@ -237,43 +241,43 @@ public class LodRenderer
|
||||
// OpenGl outputs their matricies in col,row form instead of row,col
|
||||
// (or maybe vice versa I have no idea :P)
|
||||
mcProjectionMatrix.transpose();
|
||||
|
||||
|
||||
Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcMatrixStack, partialTicks);
|
||||
|
||||
|
||||
// required for setupFog and setupProjectionMatrix
|
||||
farPlaneBlockDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
|
||||
|
||||
|
||||
setupProjectionMatrix(mcProjectionMatrix, partialTicks);
|
||||
// commented out until we can add shaders to handle lighting
|
||||
//setupLighting(lodDim, partialTicks);
|
||||
|
||||
|
||||
NearFarFogSettings fogSettings = determineFogSettings();
|
||||
|
||||
|
||||
// determine the current fog settings so they can be
|
||||
// reset after drawing the LODs
|
||||
float defaultFogStartDist = GL11.glGetFloat(GL11.GL_FOG_START);
|
||||
float defaultFogEndDist = GL11.glGetFloat(GL11.GL_FOG_END);
|
||||
int defaultFogMode = GL11.glGetInteger(GL11.GL_FOG_MODE);
|
||||
int defaultFogDistance = GL11.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV);
|
||||
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// rendering //
|
||||
//===========//
|
||||
|
||||
|
||||
profiler.popPush("LOD draw");
|
||||
|
||||
|
||||
if (vbos != null)
|
||||
{
|
||||
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
|
||||
Vector3d cameraDir = new Vector3d(renderInfo.getLookVector());
|
||||
|
||||
|
||||
boolean cullingDisabled = LodConfig.CLIENT.graphics.disableDirectionalCulling.get();
|
||||
|
||||
|
||||
// used to determine what type of fog to render
|
||||
int halfWidth = vbos.length / 2;
|
||||
int quarterWidth = vbos.length / 4;
|
||||
|
||||
|
||||
for (int x = 0; x < vbos.length; x++)
|
||||
{
|
||||
for (int z = 0; z < vbos.length; z++)
|
||||
@@ -285,24 +289,24 @@ public class LodRenderer
|
||||
setupFog(fogSettings.near.distance, fogSettings.near.quality);
|
||||
else
|
||||
setupFog(fogSettings.far.distance, fogSettings.far.quality);
|
||||
|
||||
|
||||
for (int i = 0; i < lodBufferBuilder.bufferSize[x][z]; i++)
|
||||
|
||||
|
||||
for (int i = 0; i < lodBufferBuilder.bufferCount[x][z]; i++)
|
||||
{
|
||||
sendLodsToGpuAndDraw(vbos[x][z][i], modelViewMatrix);
|
||||
sendLodsToGpuAndDraw(vbos[x][z][i], storageBufferIds[x][z], modelViewMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// cleanup //
|
||||
//=========//
|
||||
|
||||
|
||||
profiler.popPush("LOD cleanup");
|
||||
|
||||
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glDisable(LOD_GL_LIGHT_NUMBER);
|
||||
@@ -311,48 +315,48 @@ public class LodRenderer
|
||||
GL11.glEnable(GL11.GL_LIGHT0);
|
||||
GL11.glEnable(GL11.GL_LIGHT1);
|
||||
RenderSystem.disableLighting();
|
||||
|
||||
|
||||
// reset the fog settings so the normal chunks
|
||||
// will be drawn correctly
|
||||
cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance);
|
||||
|
||||
|
||||
// reset the projection matrix so anything drawn after
|
||||
// the LODs will use the correct projection matrix
|
||||
gameRender.resetProjectionMatrix(mcProjectionMatrix);
|
||||
|
||||
|
||||
// clear the depth buffer so anything drawn is drawn
|
||||
// over the LODs
|
||||
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
|
||||
|
||||
// end of internal LOD profiling
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is where the actual drawing happens.
|
||||
*/
|
||||
private void sendLodsToGpuAndDraw(VertexBuffer vbo, Matrix4f modelViewMatrix)
|
||||
private void sendLodsToGpuAndDraw(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix)
|
||||
{
|
||||
if (vbo == null)
|
||||
return;
|
||||
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
|
||||
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId);
|
||||
// 0L is the starting pointer
|
||||
LOD_VERTEX_FORMAT.setupBufferState(0L);
|
||||
|
||||
LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L);
|
||||
|
||||
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
|
||||
|
||||
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
LOD_VERTEX_FORMAT.clearBufferState();
|
||||
LodUtil.LOD_VERTEX_FORMAT.clearBufferState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// Setup Functions //
|
||||
//=================//
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
|
||||
{
|
||||
@@ -362,12 +366,12 @@ public class LodRenderer
|
||||
RenderSystem.disableFog();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (fogDistance == FogDistance.NEAR_AND_FAR)
|
||||
{
|
||||
throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance.");
|
||||
}
|
||||
|
||||
|
||||
// determine the fog distance mode to use
|
||||
int glFogDistanceMode;
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
@@ -379,7 +383,7 @@ public class LodRenderer
|
||||
// fast fog (frustum distance based fog)
|
||||
glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV;
|
||||
}
|
||||
|
||||
|
||||
// the multipliers are percentages
|
||||
// of the regular view distance.
|
||||
if (fogDistance == FogDistance.FAR)
|
||||
@@ -388,7 +392,7 @@ public class LodRenderer
|
||||
// is because we are using fog backwards to how
|
||||
// it is normally used, with it hiding near objects
|
||||
// instead of far objects.
|
||||
|
||||
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
{
|
||||
// for more realistic fog when using FAR
|
||||
@@ -421,37 +425,38 @@ public class LodRenderer
|
||||
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.5f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GL11.glEnable(GL11.GL_FOG);
|
||||
RenderSystem.enableFog();
|
||||
RenderSystem.setupNvFogDistance();
|
||||
RenderSystem.fogMode(GlStateManager.FogMode.LINEAR);
|
||||
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Revert any changes that were made to the fog.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void cleanupFog(NearFarFogSettings fogSettings,
|
||||
float defaultFogStartDist, float defaultFogEndDist,
|
||||
int defaultFogMode, int defaultFogDistance)
|
||||
float defaultFogStartDist, float defaultFogEndDist,
|
||||
int defaultFogMode, int defaultFogDistance)
|
||||
{
|
||||
RenderSystem.fogStart(defaultFogStartDist);
|
||||
RenderSystem.fogEnd(defaultFogEndDist);
|
||||
RenderSystem.fogMode(defaultFogMode);
|
||||
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, defaultFogDistance);
|
||||
|
||||
|
||||
// disable fog if Minecraft wasn't rendering fog
|
||||
// but we were
|
||||
if (!fogSettings.vanillaIsRenderingFog &&
|
||||
(fogSettings.near.quality != FogQuality.OFF ||
|
||||
fogSettings.far.quality != FogQuality.OFF))
|
||||
(fogSettings.near.quality != FogQuality.OFF ||
|
||||
fogSettings.far.quality != FogQuality.OFF))
|
||||
{
|
||||
GL11.glDisable(GL11.GL_FOG);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Translate the camera relative to the LodDimension's center,
|
||||
* this is done since all LOD buffers are created in world space
|
||||
@@ -463,12 +468,12 @@ public class LodRenderer
|
||||
{
|
||||
// duplicate the last matrix
|
||||
mcMatrixStack.pushPose();
|
||||
|
||||
|
||||
|
||||
|
||||
// get all relevant camera info
|
||||
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
|
||||
Vector3d projectedView = renderInfo.getPosition();
|
||||
|
||||
|
||||
// translate the camera relative to the regions' center
|
||||
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
|
||||
// accuracy vs the model view matrix, which only uses floats)
|
||||
@@ -477,17 +482,17 @@ public class LodRenderer
|
||||
double xDiff = eyePos.x - bufferPos.getX();
|
||||
double zDiff = eyePos.z - bufferPos.getZ();
|
||||
mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff);
|
||||
|
||||
|
||||
|
||||
|
||||
// get the modified model view matrix
|
||||
Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose();
|
||||
// remove the lod ModelViewMatrix
|
||||
mcMatrixStack.popPose();
|
||||
|
||||
|
||||
return lodModelViewMatrix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* James added this to test if Vivecraft is not using
|
||||
* the MC FOV setting or if the problem is deeper
|
||||
@@ -498,17 +503,17 @@ public class LodRenderer
|
||||
MC_USE_FOV(false, true),
|
||||
NEITHER(false, false),
|
||||
BOTH(true, true);
|
||||
|
||||
|
||||
boolean lodProjUseFov;
|
||||
boolean defaultMcProjUseFov;
|
||||
|
||||
|
||||
private FovTest(boolean newLodProjUseFov, boolean newDefaultMcProjUseFov)
|
||||
{
|
||||
lodProjUseFov = newLodProjUseFov;
|
||||
defaultMcProjUseFov = newDefaultMcProjUseFov;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create a new projection matrix and send it over to the GPU
|
||||
*
|
||||
@@ -524,33 +529,33 @@ public class LodRenderer
|
||||
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
|
||||
mc.getRenderDistance() / 2,
|
||||
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2 / 4);
|
||||
|
||||
|
||||
// get Minecraft's un-edited projection matrix
|
||||
// (this is before it is zoomed, distorted, etc.)
|
||||
Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, LodConfig.CLIENT.graphics.useFovSetting.get().defaultMcProjUseFov);
|
||||
// true here means use "use fov setting" (probably)
|
||||
|
||||
|
||||
|
||||
|
||||
// this logic strips away the defaultMcProj matrix so we
|
||||
// can get the distortionMatrix, which represents all
|
||||
// transformations, zooming, distortions, etc. done
|
||||
// to Minecraft's Projection matrix
|
||||
Matrix4f defaultMcProjInv = defaultMcProj.copy();
|
||||
defaultMcProjInv.invert();
|
||||
|
||||
|
||||
Matrix4f distortionMatrix = defaultMcProjInv.copy();
|
||||
distortionMatrix.multiply(currentProjectionMatrix);
|
||||
|
||||
|
||||
|
||||
|
||||
// edit the lod projection to match Minecraft's
|
||||
// (so the LODs line up with the real world)
|
||||
lodPoj.multiply(distortionMatrix);
|
||||
|
||||
|
||||
// send the projection over to the GPU
|
||||
gameRender.resetProjectionMatrix(lodPoj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* setup the lighting to be used for the LODs
|
||||
*/
|
||||
@@ -593,7 +598,7 @@ public class LodRenderer
|
||||
|
||||
RenderSystem.enableLighting();
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
* Create all buffers that will be used.
|
||||
*/
|
||||
@@ -601,12 +606,12 @@ public class LodRenderer
|
||||
{
|
||||
lodBufferBuilder.setupBuffers(lodDim);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//======================//
|
||||
// Other Misc Functions //
|
||||
//======================//
|
||||
|
||||
|
||||
/**
|
||||
* If this is called then the next time "drawLODs" is called
|
||||
* the LODs will be regenerated; the same as if the player moved.
|
||||
@@ -615,8 +620,8 @@ public class LodRenderer
|
||||
{
|
||||
fullRegen = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Replace the current Vertex Buffers with the newly
|
||||
* created buffers from the lodBufferBuilder. <br><br>
|
||||
@@ -630,9 +635,10 @@ public class LodRenderer
|
||||
// the newly created buffers from the lodBufferBuilder
|
||||
VertexBuffersAndOffset result = lodBufferBuilder.getVertexBuffers();
|
||||
vbos = result.vbos;
|
||||
storageBufferIds = result.storageBufferIds;
|
||||
vbosCenter = result.drawableCenterChunkPos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the BufferBuilder's destroyBuffers method.
|
||||
*/
|
||||
@@ -640,143 +646,143 @@ public class LodRenderer
|
||||
{
|
||||
lodBufferBuilder.destroyBuffers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private double getFov(float partialTicks, boolean useFovSetting)
|
||||
{
|
||||
return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return what fog settings should be used when rendering.
|
||||
*/
|
||||
private NearFarFogSettings determineFogSettings()
|
||||
{
|
||||
NearFarFogSettings fogSettings = new NearFarFogSettings();
|
||||
|
||||
|
||||
|
||||
|
||||
FogQuality quality = ReflectionHandler.INSTANCE.getFogQuality();
|
||||
FogDrawOverride override = LodConfig.CLIENT.graphics.fogDrawOverride.get();
|
||||
|
||||
|
||||
|
||||
|
||||
fogSettings.vanillaIsRenderingFog = quality != FogQuality.OFF;
|
||||
|
||||
|
||||
|
||||
|
||||
// use any fog overrides the user may have set
|
||||
switch (override)
|
||||
{
|
||||
case ALWAYS_DRAW_FOG_FANCY:
|
||||
quality = FogQuality.FANCY;
|
||||
break;
|
||||
|
||||
case NEVER_DRAW_FOG:
|
||||
quality = FogQuality.OFF;
|
||||
break;
|
||||
|
||||
case ALWAYS_DRAW_FOG_FAST:
|
||||
quality = FogQuality.FAST;
|
||||
break;
|
||||
|
||||
case USE_OPTIFINE_FOG_SETTING:
|
||||
// don't override anything
|
||||
break;
|
||||
case ALWAYS_DRAW_FOG_FANCY:
|
||||
quality = FogQuality.FANCY;
|
||||
break;
|
||||
|
||||
case NEVER_DRAW_FOG:
|
||||
quality = FogQuality.OFF;
|
||||
break;
|
||||
|
||||
case ALWAYS_DRAW_FOG_FAST:
|
||||
quality = FogQuality.FAST;
|
||||
break;
|
||||
|
||||
case USE_OPTIFINE_FOG_SETTING:
|
||||
// don't override anything
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// only use fancy fog if the user's GPU can deliver
|
||||
if (!GlProxy.getInstance().fancyFogAvailable && quality == FogQuality.FANCY)
|
||||
{
|
||||
quality = FogQuality.FAST;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// how different distances are drawn depends on the quality set
|
||||
switch (quality)
|
||||
{
|
||||
case FANCY:
|
||||
fogSettings.near.quality = FogQuality.FANCY;
|
||||
fogSettings.far.quality = FogQuality.FANCY;
|
||||
|
||||
switch (LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
|
||||
case NEAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case FAR:
|
||||
fogSettings.near.distance = FogDistance.FAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
}
|
||||
case FANCY:
|
||||
fogSettings.near.quality = FogQuality.FANCY;
|
||||
fogSettings.far.quality = FogQuality.FANCY;
|
||||
|
||||
switch (LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
|
||||
case FAST:
|
||||
fogSettings.near.quality = FogQuality.FAST;
|
||||
fogSettings.far.quality = FogQuality.FAST;
|
||||
|
||||
// fast fog setting should only have one type of
|
||||
// fog, since the LODs are separated into a near
|
||||
// and far portion; and fast fog is rendered from the
|
||||
// frustrum's perspective instead of the camera
|
||||
switch (LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case NEAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case FAR:
|
||||
fogSettings.near.distance = FogDistance.FAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
}
|
||||
|
||||
case NEAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case OFF:
|
||||
|
||||
fogSettings.near.quality = FogQuality.OFF;
|
||||
fogSettings.far.quality = FogQuality.OFF;
|
||||
|
||||
case FAR:
|
||||
fogSettings.near.distance = FogDistance.FAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case FAST:
|
||||
fogSettings.near.quality = FogQuality.FAST;
|
||||
fogSettings.far.quality = FogQuality.FAST;
|
||||
|
||||
// fast fog setting should only have one type of
|
||||
// fog, since the LODs are separated into a near
|
||||
// and far portion; and fast fog is rendered from the
|
||||
// frustrum's perspective instead of the camera
|
||||
switch (LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case NEAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case FAR:
|
||||
fogSettings.near.distance = FogDistance.FAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case OFF:
|
||||
|
||||
fogSettings.near.quality = FogQuality.OFF;
|
||||
fogSettings.far.quality = FogQuality.OFF;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return fogSettings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the LODs should have a fullRegen or partialRegen
|
||||
*/
|
||||
private void determineIfLodsShouldRegenerate(LodDimension lodDim)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
short chunkRenderDistance = (short) mc.getRenderDistance();
|
||||
int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2;
|
||||
|
||||
|
||||
//=============//
|
||||
// full regens //
|
||||
//=============//
|
||||
|
||||
|
||||
// check if the view distance changed
|
||||
if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.lodChunkRenderDistance.get()
|
||||
|| chunkRenderDistance != prevRenderDistance
|
||||
|| prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
|| chunkRenderDistance != prevRenderDistance
|
||||
|| prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
{
|
||||
|
||||
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
DetailDistanceUtil.updateSettings();
|
||||
fullRegen = true;
|
||||
@@ -784,25 +790,25 @@ public class LodRenderer
|
||||
prevFogDistance = LodConfig.CLIENT.graphics.fogDistance.get();
|
||||
prevRenderDistance = chunkRenderDistance;
|
||||
}
|
||||
|
||||
|
||||
// did the user change the debug setting?
|
||||
if (LodConfig.CLIENT.debugging.debugMode.get() != previousDebugMode)
|
||||
{
|
||||
previousDebugMode = LodConfig.CLIENT.debugging.debugMode.get();
|
||||
fullRegen = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
long newTime = System.currentTimeMillis();
|
||||
|
||||
|
||||
if (LodConfig.CLIENT.graphics.detailDropOff.get() == DetailDropOff.FANCY)
|
||||
{
|
||||
// check if the player has moved
|
||||
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get())
|
||||
{
|
||||
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|
||||
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
|
||||
|| mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos))
|
||||
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
|
||||
|| mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos))
|
||||
{
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
fullRegen = true;
|
||||
@@ -811,13 +817,13 @@ public class LodRenderer
|
||||
prevPlayerPosTime = newTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// partial regens //
|
||||
//================//
|
||||
|
||||
|
||||
|
||||
|
||||
// check if the vanilla rendered chunks changed
|
||||
if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.get())
|
||||
{
|
||||
@@ -825,12 +831,12 @@ public class LodRenderer
|
||||
{
|
||||
partialRegen = true;
|
||||
vanillaRenderedChunksChanged = false;
|
||||
|
||||
|
||||
}
|
||||
prevVanillaChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// check if there is any newly generated terrain to show
|
||||
if (newTime - prevChunkTime > LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.get())
|
||||
{
|
||||
@@ -841,7 +847,7 @@ public class LodRenderer
|
||||
}
|
||||
prevChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
// check if the lighting has changed
|
||||
if (mc.getClientWorld().getDayTime() - prevDayTime > 1000 || mc.getOptions().gamma != prevBrightness || lightMap == null)
|
||||
{
|
||||
@@ -850,12 +856,12 @@ public class LodRenderer
|
||||
prevBrightness = mc.getOptions().gamma;
|
||||
prevDayTime = mc.getClientWorld().getDayTime();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// LOD skipping //
|
||||
//==============//
|
||||
|
||||
|
||||
// determine which LODs should not be rendered close to the player
|
||||
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition());
|
||||
int xIndex;
|
||||
@@ -864,12 +870,12 @@ public class LodRenderer
|
||||
{
|
||||
xIndex = (pos.x - mc.getPlayer().xChunk) + (chunkRenderDistance + 1);
|
||||
zIndex = (pos.z - mc.getPlayer().zChunk) + (chunkRenderDistance + 1);
|
||||
|
||||
|
||||
// sometimes we are given chunks that are outside the render distance,
|
||||
// This prevents index out of bounds exceptions
|
||||
if (xIndex >= 0 && zIndex >= 0
|
||||
&& xIndex < vanillaRenderedChunks.length
|
||||
&& zIndex < vanillaRenderedChunks.length)
|
||||
&& xIndex < vanillaRenderedChunks.length
|
||||
&& zIndex < vanillaRenderedChunks.length)
|
||||
{
|
||||
if (!vanillaRenderedChunks[xIndex][zIndex])
|
||||
{
|
||||
@@ -879,8 +885,8 @@ public class LodRenderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// if the player is high enough, draw all LODs
|
||||
if(chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256)
|
||||
{
|
||||
@@ -888,5 +894,5 @@ public class LodRenderer
|
||||
vanillaRenderedChunksChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ public class DetailDistanceUtil
|
||||
private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
|
||||
private static int minDistance = 0;
|
||||
private static int maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 2;
|
||||
|
||||
|
||||
|
||||
private static int[] maxVerticalData = {
|
||||
4,
|
||||
4,
|
||||
@@ -28,8 +29,8 @@ public class DetailDistanceUtil
|
||||
1,
|
||||
1,
|
||||
1};
|
||||
|
||||
|
||||
|
||||
|
||||
private static HorizontalResolution[] lodGenDetails = {
|
||||
HorizontalResolution.BLOCK,
|
||||
HorizontalResolution.TWO_BLOCKS,
|
||||
@@ -42,84 +43,84 @@ public class DetailDistanceUtil
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static void updateSettings(){
|
||||
minGenDetail = LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel;
|
||||
minDrawDetail = Math.max(LodConfig.CLIENT.graphics.drawResolution.get().detailLevel,LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel);
|
||||
maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 8;
|
||||
}
|
||||
|
||||
|
||||
public static int baseDistanceFunction(int detail)
|
||||
{
|
||||
if (detail <= minGenDetail)
|
||||
return minDistance;
|
||||
if (detail >= maxDetail)
|
||||
return maxDistance;
|
||||
|
||||
|
||||
int distanceUnit = LodConfig.CLIENT.worldGenerator.horizontalScale.get().distanceUnit;
|
||||
switch (LodConfig.CLIENT.worldGenerator.horizontalQuality.get()){
|
||||
|
||||
case LINEAR:;
|
||||
return (detail * distanceUnit);
|
||||
default:
|
||||
double base = LodConfig.CLIENT.worldGenerator.horizontalQuality.get().quadraticBase;
|
||||
return (int) (Math.pow(base, detail) * distanceUnit);
|
||||
|
||||
case LINEAR:;
|
||||
return (detail * distanceUnit);
|
||||
default:
|
||||
double base = LodConfig.CLIENT.worldGenerator.horizontalQuality.get().quadraticBase;
|
||||
return (int) (Math.pow(base, detail) * distanceUnit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static int getDrawDistanceFromDetail(int detail)
|
||||
{
|
||||
return baseDistanceFunction(detail);
|
||||
}
|
||||
|
||||
|
||||
public static byte baseInverseFunction(int distance, int minDetail)
|
||||
{
|
||||
|
||||
|
||||
int detail = 0;
|
||||
if (distance == 0)
|
||||
return (byte) minDetail;
|
||||
int distanceUnit = LodConfig.CLIENT.worldGenerator.horizontalScale.get().distanceUnit;
|
||||
switch (LodConfig.CLIENT.worldGenerator.horizontalQuality.get()){
|
||||
case LINEAR:
|
||||
detail = (byte) Math.floorDiv(distance, distanceUnit);
|
||||
break;
|
||||
default:
|
||||
double base = LodConfig.CLIENT.worldGenerator.horizontalQuality.get().quadraticBase;
|
||||
double logBase = Math.log(base);
|
||||
detail = (byte) (Math.log(Math.floorDiv(distance, distanceUnit))/logBase);
|
||||
break;
|
||||
case LINEAR:
|
||||
detail = (byte) Math.floorDiv(distance, distanceUnit);
|
||||
break;
|
||||
default:
|
||||
double base = LodConfig.CLIENT.worldGenerator.horizontalQuality.get().quadraticBase;
|
||||
double logBase = Math.log(base);
|
||||
detail = (byte) (Math.log(Math.floorDiv(distance, distanceUnit))/logBase);
|
||||
break;
|
||||
}
|
||||
return (byte) LodUtil.clamp(minDetail, detail, maxDetail-1);
|
||||
}
|
||||
|
||||
|
||||
public static byte getDrawDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction(distance, minDrawDetail);
|
||||
}
|
||||
|
||||
|
||||
public static byte getGenerationDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction((int) (distance * genMultiplier), minGenDetail);
|
||||
}
|
||||
|
||||
|
||||
public static byte getTreeCutDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static byte getTreeGenDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail);
|
||||
}
|
||||
|
||||
|
||||
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
|
||||
{
|
||||
return LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
|
||||
}
|
||||
|
||||
|
||||
public static byte getLodDrawDetail(int detail)
|
||||
{
|
||||
if (detail < minDrawDetail)
|
||||
@@ -128,7 +129,8 @@ public class DetailDistanceUtil
|
||||
return getLodGenDetail(minDrawDetail).detailLevel;
|
||||
else
|
||||
return (byte) minDrawDetail;
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
if(LodConfig.CLIENT.graphics.alwaysDrawAtMaxQuality.get())
|
||||
return getLodGenDetail(detail).detailLevel;
|
||||
@@ -136,7 +138,7 @@ public class DetailDistanceUtil
|
||||
return (byte) detail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static HorizontalResolution getLodGenDetail(int detail)
|
||||
{
|
||||
if (detail < minGenDetail)
|
||||
@@ -147,8 +149,8 @@ public class DetailDistanceUtil
|
||||
return lodGenDetails[detail];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static byte getCutLodDetail(int detail)
|
||||
{
|
||||
if (detail < minGenDetail)
|
||||
@@ -162,12 +164,12 @@ public class DetailDistanceUtil
|
||||
return lodGenDetails[detail].detailLevel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static int getMaxVerticalData(int detail)
|
||||
{
|
||||
if(LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == VerticalQuality.HEIGHTMAP)
|
||||
return 1;
|
||||
return maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -46,7 +48,7 @@ import net.minecraft.world.server.ServerWorld;
|
||||
* This class holds methods and constants that may be used in multiple places.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-2-2021
|
||||
* @version 10-7-2021
|
||||
*/
|
||||
public class LodUtil
|
||||
{
|
||||
@@ -137,6 +139,12 @@ public class LodUtil
|
||||
*/
|
||||
public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
|
||||
|
||||
|
||||
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the first valid ServerWorld.
|
||||
|
||||
Reference in New Issue
Block a user