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:
James Seibel
2021-10-07 20:59:45 -05:00
parent f6ea990ab0
commit ee58f3d9dc
6 changed files with 415 additions and 509 deletions
@@ -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.