Compare commits

..

33 Commits

Author SHA1 Message Date
James Seibel 4ad081e0c6 Add a advanced graphics option to use a extended near clip plain
This prevents some overdraw issues but causes LODs in the ocean to render incorrectly.
2021-10-25 22:20:00 -05:00
James Seibel 936a3a7ece Fix #84 (misaligned LODs in third person) 2021-10-25 21:56:22 -05:00
Leonardo 98f36936d0 smallFix 2021-10-25 22:57:37 +02:00
Leonardo f6f012c42c Added some other classes/methods to the wrappers 2021-10-25 22:51:01 +02:00
Leonardo a3e6c09268 Added more methods to the wrapper 2021-10-25 22:31:43 +02:00
Leonardo 10cb46c9f9 Added biomeWrapper 2021-10-25 21:30:44 +02:00
Leonardo 95aa9cb9ab Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-25 20:20:47 +02:00
Leonardo 638a0ddae1 BlockPosWrapper and BlockWrapper almost completed 2021-10-25 20:20:40 +02:00
James Seibel d321833335 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-25 07:21:26 -05:00
James Seibel b8cba0dc4c increase the near clip plane to 1/5 MC's vanilla render distance 2021-10-25 07:21:17 -05:00
cola98765 bd8ccb4a05 reverted las change, fixed vanillaRenderedChunksChanged at hight altitudes 2021-10-25 13:20:13 +02:00
cola98765 895895da04 update buffers only when lightmap changes. 2021-10-25 12:43:59 +02:00
Leonardo 09d5df2856 Added the main wrapper classes (empty) 2021-10-25 11:14:17 +02:00
James Seibel 668f225528 Fix the blindness potion effect 2021-10-24 23:02:30 -05:00
James Seibel 1cd71a6b50 Re-arange Vanilla Overdraw config options 2021-10-24 22:18:09 -05:00
James Seibel f041f79ae3 Close #78 (Add a config to disable vanilla MC's fog)
And change the config to use FAR fog and disable MC's fog by default.
2021-10-24 22:17:57 -05:00
James Seibel ef3ac96b2c Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-24 21:30:35 -05:00
James Seibel 59f527e6de Update 1.5 release notes.txt 2021-10-24 15:19:59 -05:00
James Seibel 16a082b17f rename "Disable Drawing" to "Enable Rendering" in the config
The config name was incorrect.
2021-10-24 15:19:54 -05:00
jas35484 a24d28b0e2 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-24 14:57:56 -05:00
jas35484 910f11f688 Fix #63 (OpenGL errors)
NVFogDistance doesn't work on low end GPUs which causes the OpenGL error I was seeing with Optifine
2021-10-24 14:57:53 -05:00
cola98765 e00de99e31 Found place where addData was used for adding whole vertical array. We have addVerticalData for that. 2021-10-24 12:11:43 +02:00
cola98765 f80af39e0e halving every array related to worldHeight, as you can't get worldHeight lods, as half of that needs to be a gaps 2021-10-24 11:57:08 +02:00
cola98765 5bba3cb3eb minor cleanup 2021-10-24 11:35:45 +02:00
cola98765 d4261d4ccf a little improvement to 'pow' changes 2021-10-24 11:01:02 +02:00
cola98765 8b854e3abd removed debug message 2021-10-24 10:33:02 +02:00
cola98765 4064155567 changed Math.pow usages with simpler (and possibly faster) x*x and 1 << x. reduced repetitions 2021-10-24 10:28:35 +02:00
cola98765 6243201f2d removed couple Math.floorDiv and Mod as for positive inputs they are slower and give the same result as standard operands 2021-10-24 10:08:12 +02:00
James Seibel c5a2944d68 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-23 12:04:26 -05:00
James Seibel 1405b7a433 Add support for OpenGL 1.5 and 3.0 2021-10-23 12:04:16 -05:00
cola98765 be00670b7e Merge branch 'CodeF53-1.16.5-patch-77785' into '1.16.5'
Update youtube video

See merge request jeseibel/minecraft-lod-mod!5
2021-10-23 13:29:09 +00:00
CodeF53 6f2c02d283 Make link open in new tab 2021-10-23 05:11:10 +00:00
CodeF53 309526e7b9 Update youtube video 2021-10-23 05:01:37 +00:00
33 changed files with 1170 additions and 295 deletions
Binary file not shown.
+1 -1
View File
@@ -7,7 +7,7 @@ allowing for an increased view distance without harming performance.
Or in other words: this mod lets you see farther without turning your game into a slide show.\
If you want to see a quick demo, check out a video covering the mod here:
[![Minecraft Level Of Detail (LOD) mod - Alpha 1.4](https://i.ytimg.com/vi_webp/MDWcEvdUGUE/mqdefault.webp)](https://www.youtube.com/watch?v=MDWcEvdUGUE)
<a href="https://www.youtube.com/watch?v=H2tnvEVbO1c" target="_blank">![Minecraft Level Of Detail (LOD) mod - Alpha 1.4](https://i.ytimg.com/vi_webp/H2tnvEVbO1c/mqdefault.webp)</a>
Forge version: 1.16.5-36.1.0
+1 -1
View File
@@ -21,7 +21,7 @@ apply plugin: 'eclipse'
apply plugin: 'maven-publish'
apply plugin: 'com.github.johnrengelman.shadow'
version = 'a1.5.0'
version = 'a1.5.1'
group = 'com.seibel.lod'
archivesBaseName = 'Distant-Horizons_1.16.5'
+1 -1
View File
@@ -41,7 +41,7 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
* @author James Seibel
* @version 7-3-2021
*/
@Mod(ModInfo.MODID)
@Mod(ModInfo.ID)
public class LodMain
{
public static LodMain instance;
+7 -5
View File
@@ -22,12 +22,14 @@ package com.seibel.lod;
/**
* This file is similar to mcmod.info
* @author James Seibel
* @version 10-16-2021
* @version 10-23-2021
*/
public final class ModInfo
{
public static final String MODID = "lod";
public static final String MODNAME = "DistantHorizons";
public static final String MODAPI = "LodAPI";
public static final String VERSION = "a1.5.0";
public static final String ID = "lod";
public static final String NAME = "DistantHorizons";
/** Human readable version of MOD_NAME */
public static final String READABLE_NAME = "Distant Horizons";
public static final String API = "LodAPI";
public static final String VERSION = "a1.5.1";
}
@@ -32,7 +32,7 @@ import java.util.concurrent.locks.ReentrantLock;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL15C;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -40,6 +40,7 @@ import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.GlProxyContext;
import com.seibel.lod.enums.GpuUploadMethod;
import com.seibel.lod.enums.VanillaOverdraw;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
@@ -67,7 +68,7 @@ import net.minecraft.world.LightType;
/**
* This object is used to create NearFarBuffer objects.
* @author James Seibel
* @version 10-10-2021
* @version 10-23-2021
*/
public class LodBufferBuilder
{
@@ -109,14 +110,6 @@ public class LodBufferBuilder
/** The OpenGL IDs of the storage buffers used by the drawableVbos */
public int[][][] drawableStorageBufferIds;
/** used to debug how the buildableStorageBuffers are growing */
public int[][][] bufferPreviousCapacity;
/**
* This is toggled when the buffers are swapped, so we only
* display the expansion log for one set of buffers
*/
public boolean printExpansionLog = true;
/** Used when building new VBOs */
public volatile VertexBuffer[][][] buildableVbos;
/** VBOs that are sent over to the LodNodeRenderer */
@@ -188,8 +181,7 @@ public class LodBufferBuilder
generatingBuffers = true;
Thread thread = new Thread(() ->
generateLodBuffersThread(renderer, lodDim, playerBlockPos, fullRegen));
Thread thread = new Thread(() -> generateLodBuffersThread(renderer, lodDim, playerBlockPos, fullRegen));
mainGenThread.execute(thread);
}
@@ -250,8 +242,8 @@ public class LodBufferBuilder
if (lodDim.doesRegionNeedBufferRegen(xRegion, zRegion) || fullRegen)
{
RegionPos regionPos = new RegionPos(
xRegion + lodDim.getCenterRegionPosX() - Math.floorDiv(lodDim.getWidth(), 2),
zRegion + lodDim.getCenterRegionPosZ() - Math.floorDiv(lodDim.getWidth(), 2));
xRegion + lodDim.getCenterRegionPosX() - lodDim.getWidth() / 2,
zRegion + lodDim.getCenterRegionPosZ() - lodDim.getWidth() / 2);
// local position in the vbo and bufferBuilder arrays
BufferBuilder[] currentBuffers = buildableBuffers[xRegion][zRegion];
@@ -318,7 +310,7 @@ public class LodBufferBuilder
for (int index = 0; index < posToRender.getNumberOfPos(); index++)
{
bufferIndex = Math.floorMod(index, currentBuffers.length);
bufferIndex = index % currentBuffers.length;
detailLevel = posToRender.getNthDetailLevel(index);
posX = posToRender.getNthPosX(index);
posZ = posToRender.getNthPosZ(index);
@@ -542,6 +534,8 @@ public class LodBufferBuilder
*/
public void setupBuffers(LodDimension lodDimension)
{
GlProxy glProxy = GlProxy.getInstance();
bufferLock.lock();
int numbRegionsWide = lodDimension.getWidth();
long regionMemoryRequired;
@@ -554,10 +548,11 @@ public class LodBufferBuilder
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][];
drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][];
buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
bufferPreviousCapacity = new int[numbRegionsWide][numbRegionsWide][];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
}
for (int x = 0; x < numbRegionsWide; x++)
{
@@ -579,9 +574,11 @@ public class LodBufferBuilder
buildableVbos[x][z] = new VertexBuffer[numberOfBuffers];
drawableVbos[x][z] = new VertexBuffer[numberOfBuffers];
buildableStorageBufferIds[x][z] = new int[numberOfBuffers];
drawableStorageBufferIds[x][z] = new int[numberOfBuffers];
bufferPreviousCapacity[x][z] = new int[numberOfBuffers];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds[x][z] = new int[numberOfBuffers];
drawableStorageBufferIds[x][z] = new int[numberOfBuffers];
}
}
else
{
@@ -591,16 +588,16 @@ public class LodBufferBuilder
buildableVbos[x][z] = new VertexBuffer[1];
drawableVbos[x][z] = new VertexBuffer[1];
buildableStorageBufferIds[x][z] = new int[1];
drawableStorageBufferIds[x][z] = new int[1];
bufferPreviousCapacity[x][z] = new int[1];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds[x][z] = new int[1];
drawableStorageBufferIds[x][z] = new int[1];
}
}
for (int i = 0; i < numberOfBuffersPerRegion[x][z]; i++)
{
bufferPreviousCapacity[x][z][i] = (int) regionMemoryRequired;
buildableBuffers[x][z][i] = new BufferBuilder((int) regionMemoryRequired);
buildableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT);
@@ -609,24 +606,27 @@ public class LodBufferBuilder
// create the initial mapped buffers (system memory)
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL45.GL_DYNAMIC_DRAW);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.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, regionMemoryRequired, GL45.GL_DYNAMIC_DRAW);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// create the buffer storage (GPU memory)
buildableStorageBufferIds[x][z][i] = GL45.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
drawableStorageBufferIds[x][z][i] = GL45.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (glProxy.bufferStorageSupported)
{
// create the buffer storage (GPU memory)
buildableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
}
}
@@ -665,8 +665,8 @@ public class LodBufferBuilder
// was called from (if any).
RenderSystem.recordRenderCall(() ->
{
GL45.glDeleteBuffers(buildableId);
GL45.glDeleteBuffers(drawableId);
GL15.glDeleteBuffers(buildableId);
GL15.glDeleteBuffers(drawableId);
});
}
}
@@ -708,9 +708,9 @@ public class LodBufferBuilder
RenderSystem.recordRenderCall(() ->
{
if (buildableId != 0)
GL45.glDeleteBuffers(buildableId);
GL15.glDeleteBuffers(buildableId);
if (drawableId != 0)
GL45.glDeleteBuffers(drawableId);
GL15.glDeleteBuffers(drawableId);
});
}
}
@@ -773,6 +773,16 @@ public class LodBufferBuilder
// this helps prevent interference (IE stuttering) with the Minecraft context.
glProxy.setGlContext(GlProxyContext.LOD_BUILDER);
// determine the upload method
GpuUploadMethod uploadMethod = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get();
if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
{
// if buffer storage isn't supported
// default to SUB_DATA
LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.SUB_DATA);
uploadMethod = GpuUploadMethod.SUB_DATA;
}
// actually upload the buffers
for (int x = 0; x < buildableVbos.length; x++)
{
@@ -783,7 +793,7 @@ public class LodBufferBuilder
for (int i = 0; i < buildableBuffers[x][z].length; i++)
{
ByteBuffer uploadBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond();
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, x, z, i, true);
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, true, uploadMethod);
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
}
}
@@ -807,11 +817,10 @@ public class LodBufferBuilder
}
}
/** Uploads the uploadBuffer into the VBO and then into GPU memory. */
/** Uploads the uploadBuffer so the GPU can use it.
* @param uploadMethod */
private void vboUpload(VertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer,
int xVboIndex, int zVboIndex, int iVboIndex, boolean allowBufferExpansion)
// x/zVboIndex are just used for the debugging console logging
// and should be removed when the logger is removed.
boolean allowBufferExpansion, GpuUploadMethod uploadMethod)
{
// this shouldn't happen, but just to be safe
if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER)
@@ -820,63 +829,98 @@ public class LodBufferBuilder
vbo.vertexCount = (uploadBuffer.capacity() / vbo.format.getVertexSize());
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo.id);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
try
{
// 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)
// if possible use the faster buffer storage route
if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
{
int previousCapacity = uploadBuffer.capacity();
// only expand the buffers if the uploadBuffer actually
// has something in it and expansion is allowed
if (previousCapacity != 0 && allowBufferExpansion)
// get a pointer to the buffer in system memory
ByteBuffer vboBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
if (vboBuffer == null)
{
// the buffer(s) aren't big enough, expand them.
// This does cause lag/stuttering, so it should be avoided!
int previousCapacity = uploadBuffer.capacity();
// expand the buffer in system memory
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL45.GL_DYNAMIC_DRAW);
GL45.glBufferSubData(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer);
// un-bind the system memory buffer
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// expand the buffer storage
GL45.glDeleteBuffers(storageBufferId);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// recursively try to upload into the newly created buffer storage
// but don't recurse again if that fails
// (we don't want an infinitely expanding buffer!)
vboUpload(vbo, storageBufferId, uploadBuffer, xVboIndex, zVboIndex, iVboIndex, false);
/*if (printExpansionLog)
// only expand the buffers if the uploadBuffer actually
// has something in it and expansion is allowed
if (previousCapacity != 0 && allowBufferExpansion)
{
// NOTE: this will display twice because we are double buffering
// (using 1 buffer to generate into and one to draw)
ClientProxy.LOGGER.info("vbo (" + xVboIndex + "," + zVboIndex + ") expanded: " + bufferPreviousCapacity[xVboIndex][zVboIndex][iVboIndex] + " -> " + (int)(uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER));
bufferPreviousCapacity[xVboIndex][zVboIndex][iVboIndex] = (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER);
}*/
// the buffer(s) aren't big enough, expand them.
// This does cause lag/stuttering, so it should be avoided!
// expand the buffer in system memory
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
// un-bind the system memory buffer
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// expand the buffer storage
GL15.glDeleteBuffers(storageBufferId);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// recursively try to upload into the newly created buffer storage
// but don't recurse again if that fails
// (we don't want an infinitely expanding buffer!)
vboUpload(vbo, storageBufferId, uploadBuffer, false, uploadMethod);
}
}
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 directly can only be done
// through the glCopyBufferSubData/glCopyNamed... methods)
GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity());
}
}
else if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING)
{
// no stuttering but high GPU usage
// stores everything in system memory instead of GPU memory
// making rendering much slower.
// Unless the user is running integrated graphics,
// in that case this will actually work better than SUB_DATA.
ByteBuffer vboBuffer = null;
// map buffer range is better since it can be explicitly unsynchronized
if (GlProxy.getInstance().mapBufferRangeSupported)
vboBuffer = GL30.glMapBufferRange(GL30.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
else
vboBuffer = GL15.glMapBuffer(GL30.GL_ARRAY_BUFFER, uploadBuffer.capacity());
if (vboBuffer == null)
{
GL15.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
}
else
{
vboBuffer.put(uploadBuffer);
}
}
else
{
// upload the buffer into system memory...
vboBuffer.put(uploadBuffer);
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
// hybrid subData/bufferData //
// less stutter, low GPU usage
// ...then upload into GPU memory
// (uploading into GPU memory directly can only be done
// through the glCopyBufferSubData/glCopyNamed... methods)
GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity());
//long size = GL31.glGetBufferParameteri64(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); // hopefully just a int should be long enough
long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER)
{
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
}
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
}
}
catch (Exception e)
@@ -886,11 +930,14 @@ public class LodBufferBuilder
}
finally
{
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING)
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
}
}//if vbo exists and in correct GL context
}//vboUpload
/** Get the newly created VBOs */
public VertexBuffersAndOffset getVertexBuffers()
@@ -907,10 +954,6 @@ public class LodBufferBuilder
drawableStorageBufferIds = buildableStorageBufferIds;
buildableStorageBufferIds = tmpStorage;
// we only want to print the expansion log for
// one set of buffers, not both
printExpansionLog = !printExpansionLog;
drawableCenterChunkPos = buildableCenterChunkPos;
// the vbos have been swapped
@@ -34,6 +34,7 @@ import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
@@ -61,6 +62,7 @@ import net.minecraft.block.material.MaterialColor;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.data.BlockModelProvider;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
@@ -98,7 +100,6 @@ public class LodBuilder
public static final ConcurrentMap<Block, Integer> colorMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Integer> tintColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> toTint = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, VoxelShape> shapeMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> notFullBlock = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> smallBlock = new ConcurrentHashMap<>();
@@ -216,13 +217,12 @@ public class LodBuilder
return;
// determine how many LODs to generate horizontally
HorizontalResolution detail;
byte minDetailLevel = region.getMinDetailLevel();
detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
HorizontalResolution detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
// determine how many LODs to generate vertically
VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
//VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
byte detailLevel = detail.detailLevel;
@@ -236,18 +236,18 @@ public class LodBuilder
endX = detail.endX[i];
endZ = detail.endZ[i];
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().x * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().z * 16 + startZ, detail.detailLevel);
long[] data;
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ, endX, endZ);
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight, DetailDistanceUtil.getMaxVerticalData(detailLevel));
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
//lodDim.clear(detailLevel, posX, posZ);
if (data != null && data.length != 0)
lodDim.addVerticalData(detailLevel, posX, posZ, data,false);
{
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().x * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().z * 16 + startZ, detail.detailLevel);
lodDim.addVerticalData(detailLevel, posX, posZ, data, false);
}
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
}
@@ -260,7 +260,7 @@ public class LodBuilder
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
int verticalData = DataPointUtil.worldHeight;
int verticalData = DataPointUtil.worldHeight / 2 + 1;
ChunkPos chunkPos = chunk.getPos();
int height;
@@ -284,8 +284,8 @@ public class LodBuilder
for (index = 0; index < size * size; index++)
{
xRel = Math.floorMod(index, size) + startX;
zRel = Math.floorDiv(index, size) + startZ;
xRel = startX + index % size;
zRel = startZ + index / size;
xAbs = chunkPos.getMinBlockX() + xRel;
zAbs = chunkPos.getMinBlockZ() + zRel;
@@ -551,8 +551,6 @@ public class LodBuilder
if (colorMap.containsKey(block) && toTint.containsKey(block))
return colorMap.get(block);
World world = mc.getClientWorld();
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
@@ -35,6 +35,7 @@ import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.GenerationPriority;
import com.seibel.lod.enums.GpuUploadMethod;
import com.seibel.lod.enums.HorizontalQuality;
import com.seibel.lod.enums.HorizontalResolution;
import com.seibel.lod.enums.HorizontalScale;
@@ -50,8 +51,9 @@ import net.minecraftforge.fml.config.ModConfig;
/**
* This handles any configuration the user has access to.
* @author Leonardo Amato
* @author James Seibel
* @version 10-19-2021
* @version 10-25-2021
*/
@Mod.EventBusSubscriber
public class LodConfig
@@ -187,6 +189,8 @@ public class LodConfig
public final ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
public final ForgeConfigSpec.BooleanValue disableVanillaFog;
FogQualityOption(ForgeConfigSpec.Builder builder)
{
@@ -197,7 +201,7 @@ public class LodConfig
+ " At what distance should Fog be drawn on the fake chunks? \n"
+ " If the fog cuts off abruptly or you are using Optifine's \"fast\" fog option \n"
+ " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n")
.defineEnum("Fog Distance", FogDistance.NEAR_AND_FAR);
.defineEnum("Fog Distance", FogDistance.FAR);
fogDrawOverride = builder
.comment("\n\n"
@@ -207,6 +211,16 @@ public class LodConfig
+ " " + FogDrawOverride.FAST + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("Fog Draw Override", FogDrawOverride.FANCY);
disableVanillaFog = builder
.comment("\n\n"
+ " If true disable vanilla Minecraft's fog. \n\n"
+ ""
+ " Unlike Optifine or Sodium's fog disabling option this won't change \n"
+ " performance (we don't actually disable the fog, we just tell it to render a infinite distance away). \n"
+ " May or may not play nice with other mods edit fog. \n")
.define("Disable Vanilla Fog", true);
builder.pop();
}
}
@@ -222,6 +236,10 @@ public class LodConfig
public final ForgeConfigSpec.EnumValue<VanillaOverdraw> vanillaOverdraw;
public final ForgeConfigSpec.EnumValue<GpuUploadMethod> gpuUploadMethod;
public final ForgeConfigSpec.BooleanValue useExtendedNearClipPlane;
AdvancedGraphicsOption(ForgeConfigSpec.Builder builder)
{
@@ -266,13 +284,32 @@ public class LodConfig
+ " How often should LODs be drawn on top of regular chunks? \n"
+ " HALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves. \n\n"
+ " " + VanillaOverdraw.NEVER + ": LODs won't render on top of vanilla chunks. \n"
+ " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks preventing only some holes in the world. \n"
+ " " + VanillaOverdraw.DYNAMIC + ": LODs will render on top of distant vanilla chunks to hide delayed loading. \n"
+ " " + " More effective on higher render distances. \n"
+ " " + " For vanilla render distances less than or equal to " + LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW + " \n"
+ " " + " " + VanillaOverdraw.NEVER + " or " + VanillaOverdraw.ALWAYS + " may be used depending on the dimension. \n"
+ " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n"
+ " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks preventing only some holes in the world. \n")
+ " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n")
.defineEnum("Vanilla Overdraw", VanillaOverdraw.DYNAMIC);
gpuUploadMethod = builder
.comment("\n\n"
+ " What method should be used to upload geometry to the GPU? \n\\n"
+ ""
+ " " + GpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. \n"
+ " " + GpuUploadMethod.SUB_DATA + ": Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. \n"
+ " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly better than " + GpuUploadMethod.SUB_DATA + " if using a integrated GPU. \n")
.defineEnum("GPU Upload Method", GpuUploadMethod.BUFFER_STORAGE);
// This is a temporary fix (like vanilla overdraw)
// hopefully we can remove both once we get individual chunk rendering figured out
useExtendedNearClipPlane = builder
.comment("\n\n"
+ " Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly \n"
+ " especially when in/near an ocean. \n")
.define("Use Extended Near Clip Plane", false);
builder.pop();
}
}
@@ -477,7 +514,7 @@ public class LodConfig
+ " If true, the mod is enabled and fake chunks will be drawn. \n"
+ " If false, the mod will still generate fake chunks, \n"
+ " but they won't be rendered. \n")
.define("Disable Drawing", true);
.define("Enable Rendering", true);
debugMode = builder
.comment("\n\n"
@@ -519,7 +556,7 @@ public class LodConfig
/** {@link Path} to the configuration file of this mod */
private static final Path CONFIG_PATH = Paths.get("config", ModInfo.MODNAME + ".toml");
private static final Path CONFIG_PATH = Paths.get("config", ModInfo.NAME + ".toml");
public static final ForgeConfigSpec CLIENT_SPEC;
public static final Client CLIENT;
@@ -540,13 +577,13 @@ public class LodConfig
@SubscribeEvent
public static void onLoad(final ModConfig.Loading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
LogManager.getLogger().debug(ModInfo.NAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
}
@SubscribeEvent
public static void onFileChange(final ModConfig.Reloading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!");
LogManager.getLogger().debug(ModInfo.NAME, "Forge config just got changed on the file system!");
}
}
@@ -0,0 +1,38 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* Buffer_Storage, Sub_Data, Buffer_Mapping
*
* @author James Seibel
* @version 10-23-2021
*/
public enum GpuUploadMethod
{
/** Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. */
BUFFER_STORAGE,
/** Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. */
SUB_DATA,
/** May end up storing buffers in System memory. Slower rendering but won't stutter when uploading. */
BUFFER_MAPPING,
}
@@ -437,6 +437,7 @@ public class LodDimension
}
/**
* Use addVerticalData when possible.
* Add the given LOD to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
@@ -866,7 +867,7 @@ public class LodDimension
public void setRegionWidth(int newWidth)
{
width = newWidth;
halfWidth = Math.floorDiv(width, 2);
halfWidth = width/ 2;
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
@@ -36,7 +36,7 @@ public class NearFarFogSettings
/**
* If true that means Minecraft is
* rendering fog alongside us
* rendering fog
*/
public boolean vanillaIsRenderingFog = true;
@@ -56,7 +56,6 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public void clear(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
@@ -68,7 +67,6 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data;
@@ -78,7 +76,6 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public boolean addVerticalData(long[] data, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
@@ -134,7 +131,7 @@ public class VerticalLevelContainer implements LevelContainer
index++;
maxVerticalData = inputData[index];
index++;
size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int x = size * size * maxVerticalData;
this.dataContainer = new long[x];
for (int i = 0; i < x; i++)
@@ -177,13 +174,7 @@ public class VerticalLevelContainer implements LevelContainer
}
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData());
for (int verticalIndex = 0; (verticalIndex < data.length) && (verticalIndex < maxVerticalData); verticalIndex++)
{
addData(data[verticalIndex],
posX,
posZ,
verticalIndex);
}
addVerticalData(data, posX, posZ);
}
@Override
@@ -217,7 +208,7 @@ public class VerticalLevelContainer implements LevelContainer
{
/*
StringBuilder stringBuilder = new StringBuilder();
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
stringBuilder.append(detailLevel);
stringBuilder.append(DATA_DELIMITER);
for (int x = 0; x < size; x++)
@@ -53,7 +53,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
* This handles all events sent to the client,
* and is the starting point for most of the mod.
* @author James_Seibel
* @version 10-15-2021
* @version 10-23-2021
*/
public class ClientProxy
{
@@ -173,9 +173,13 @@ public class ClientProxy
// LodConfig.CLIENT.worldGenerator.lodQualityMode.set(VerticalQuality.VOXEL);
// LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.set(FogDistance.FAR);
// LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
// LodConfig.CLIENT.graphics.fogQualityOption.fogDrawOverride.set(FogDrawOverride.FANCY);
// LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.set(true);
// LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES);
// LodConfig.CLIENT.graphics.vanillaOverdraw.set(VanillaOverdraw.HALF);
// LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(VanillaOverdraw.DYNAMIC);
// LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.BUFFER_STORAGE);
// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
// LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(128);
+45 -12
View File
@@ -24,7 +24,9 @@ import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.ModInfo;
import com.seibel.lod.enums.GlProxyContext;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
* A singleton that holds references to different openGL contexts
@@ -38,12 +40,14 @@ import com.seibel.lod.enums.GlProxyContext;
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br><br>
*
* @author James Seibel
* @version 10-2-2021
* @version 10-23-2021
*/
public class GlProxy
{
private static GlProxy instance = null;
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** Minecraft's GLFW window */
public final long minecraftGlContext;
/** Minecraft's GL context */
@@ -63,16 +67,22 @@ public class GlProxy
/** Does this computer's GPU support fancy fog? */
public final boolean fancyFogAvailable;
/** Requires OpenGL 4.5, and offers the best buffer uploading */
public final boolean bufferStorageSupported;
/** Requires OpenGL 3.0 */
public final boolean mapBufferRangeSupported;
private GlProxy()
{
ClientProxy.LOGGER.error("Creating " + GlProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
// getting Minecraft's context has to be done on the render thread,
// where the GL context is
if (!RenderSystem.isOnRenderThread())
throw new IllegalStateException(GlProxy.class.getSimpleName() + " was created outside the render thread!");
//============================//
@@ -101,6 +111,30 @@ public class GlProxy
//==============================//
// determine the OpenGL version //
//==============================//
bufferStorageSupported = minecraftGlCapabilities.OpenGL45;
mapBufferRangeSupported = minecraftGlCapabilities.OpenGL30;
if (!minecraftGlCapabilities.OpenGL15)
{
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater.";
mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater."));
}
if (!bufferStorageSupported)
{
String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5";
ClientProxy.LOGGER.error("This GPU doesn't support OpenGL 4.5, falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
}
//==================================//
// get any GPU related capabilities //
//==================================//
@@ -110,6 +144,12 @@ public class GlProxy
if (!fancyFogAvailable)
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available.");
// GlProxy creation success
ClientProxy.LOGGER.error(GlProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
}
@@ -188,15 +228,8 @@ public class GlProxy
public static GlProxy getInstance()
{
try
{
if (instance == null)
instance = new GlProxy();
}
catch (Exception e)
{
e.printStackTrace();
}
if (instance == null)
instance = new GlProxy();
return instance;
}
@@ -21,7 +21,6 @@ package com.seibel.lod.render;
import java.util.HashSet;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL15C;
import org.lwjgl.opengl.NVFogDistance;
@@ -36,6 +35,7 @@ import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.FogQuality;
import com.seibel.lod.enums.GpuUploadMethod;
import com.seibel.lod.handlers.ReflectionHandler;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.NearFarFogSettings;
@@ -48,10 +48,10 @@ import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
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.VertexBuffer;
import net.minecraft.potion.Effects;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
@@ -64,7 +64,7 @@ import net.minecraft.util.math.vector.Vector3d;
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 10-19-2021
* @version 10-25-2021
*/
public class LodRenderer
{
@@ -72,7 +72,7 @@ public class LodRenderer
* this is the light used when rendering the LODs,
* it should be something different from what is used by Minecraft
*/
private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2;
private static final int LOD_GL_LIGHT_NUMBER = GL15.GL_LIGHT2;
/**
* If true the LODs colors will be replaced with
@@ -105,6 +105,7 @@ public class LodRenderer
private int[] previousPos = new int[] { 0, 0, 0 };
public NativeImage lightMap = null;
public NativeImage lastLightMap = null;
// these variables are used to determine if the buffers should be rebuilt
private float prevSkyBrightness = 0;
@@ -131,6 +132,7 @@ public class LodRenderer
*/
public boolean[][] vanillaRenderedChunks;
public boolean vanillaRenderedChunksChanged;
public boolean vanillaRenderedChunksEmptySkip = false;
public int vanillaBlockRenderedDistance;
@@ -157,8 +159,13 @@ 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)
{
//=================================//
// determine if LODs should render //
//=================================//
if (lodDim == null)
{
// if there aren't any loaded LodChunks
@@ -166,6 +173,17 @@ public class LodRenderer
return;
}
if (mc.getPlayer().getActiveEffectsMap().get(Effects.BLINDNESS) != null)
{
// if the player is blind don't render LODs,
// and don't change minecraft's fog
// which blindness relies on.
return;
}
//===============//
// initial setup //
@@ -216,27 +234,27 @@ public class LodRenderer
// set the required open GL settings
if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() == DebugMode.SHOW_DETAIL_WIREFRAME)
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_LINE);
else
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL15.glDisable(GL15.GL_TEXTURE_2D);
GL15.glEnable(GL15.GL_CULL_FACE);
GL15.glEnable(GL15.GL_COLOR_MATERIAL);
GL15.glEnable(GL15.GL_DEPTH_TEST);
// enable transparent rendering
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glEnable(GL11.GL_BLEND);
GL15.glBlendFunc(GL15.GL_SRC_ALPHA, GL15.GL_ONE_MINUS_SRC_ALPHA);
GL15.glEnable(GL15.GL_BLEND);
// disable the lights Minecraft uses
GL11.glDisable(GL11.GL_LIGHT0);
GL11.glDisable(GL11.GL_LIGHT1);
GL15.glDisable(GL15.GL_LIGHT0);
GL15.glDisable(GL15.GL_LIGHT1);
// get the default projection matrix, so we can
// reset it after drawing the LODs
float[] mcProjMatrixRaw = new float[16];
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
@@ -250,7 +268,8 @@ public class LodRenderer
else
farPlaneBlockDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
setupProjectionMatrix(mcProjectionMatrix, partialTicks);
setupProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks);
// commented out until we can add shaders to handle lighting
//setupLighting(lodDim, partialTicks);
@@ -258,11 +277,10 @@ public class LodRenderer
// 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);
float defaultFogStartDist = GL15.glGetFloat(GL15.GL_FOG_START);
float defaultFogEndDist = GL15.glGetFloat(GL15.GL_FOG_END);
int defaultFogMode = GL15.glGetInteger(GL15.GL_FOG_MODE);
int defaultFogDistance = GlProxy.getInstance().fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1;
//===========//
// rendering //
@@ -276,6 +294,7 @@ public class LodRenderer
Vector3d cameraDir = new Vector3d(renderInfo.getLookVector());
boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && GlProxy.getInstance().bufferStorageSupported;
// used to determine what type of fog to render
int halfWidth = vbos.length / 2;
@@ -292,6 +311,7 @@ public class LodRenderer
RegionPos vboPos = new RegionPos(
x + vboCenterRegionPos.x - (lodDim.getWidth() / 2),
z + vboCenterRegionPos.z - (lodDim.getWidth() / 2));
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(renderInfo.getBlockPosition(), cameraDir, vboPos.blockPos()))
{
if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth)
@@ -301,10 +321,12 @@ public class LodRenderer
setupFog(fogSettings.far.distance, fogSettings.far.quality);
for (int i = 0; i < storageBufferIds[x][z].length; i++)
{
drawBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix);
}
if (storageBufferIds != null && renderBufferStorage)
for (int i = 0; i < storageBufferIds[x][z].length; i++)
drawStorageBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix);
else
for (int i = 0; i < vbos[x][z].length; i++)
drawVertexBuffer(vbos[x][z][i], modelViewMatrix);
}
}
}
@@ -317,13 +339,13 @@ public class LodRenderer
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);
GL11.glDisable(GL11.GL_BLEND);
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
GL15.glEnable(GL15.GL_TEXTURE_2D);
GL15.glDisable(LOD_GL_LIGHT_NUMBER);
GL15.glDisable(GL15.GL_BLEND);
// re-enable the lights Minecraft uses
GL11.glEnable(GL11.GL_LIGHT0);
GL11.glEnable(GL11.GL_LIGHT1);
GL15.glEnable(GL15.GL_LIGHT0);
GL15.glEnable(GL15.GL_LIGHT1);
RenderSystem.disableLighting();
// reset the fog settings so the normal chunks
@@ -336,7 +358,7 @@ public class LodRenderer
// clear the depth buffer so anything drawn is drawn
// over the LODs
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
GL15.glClear(GL15.GL_DEPTH_BUFFER_BIT);
// end of internal LOD profiling
@@ -345,16 +367,33 @@ public class LodRenderer
/** This is where the actual drawing happens. */
private void drawBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix)
private void drawStorageBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix)
{
if (vbo == null)
return;
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId);
// 0L is the starting pointer
LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L);
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
vbo.draw(modelViewMatrix, GL15.GL_QUADS);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LodUtil.LOD_VERTEX_FORMAT.clearBufferState();
}
/** This is where the actual drawing happens. */
private void drawVertexBuffer(VertexBuffer vbo, Matrix4f modelViewMatrix)
{
if (vbo == null)
return;
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
// 0L is the starting pointer
LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L);
vbo.draw(modelViewMatrix, GL15.GL_QUADS);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LodUtil.LOD_VERTEX_FORMAT.clearBufferState();
@@ -372,8 +411,7 @@ public class LodRenderer
{
if (fogQuality == FogQuality.OFF)
{
FogRenderer.setupNoFog();
RenderSystem.disableFog();
GL15.glDisable(GL15.GL_FOG);
return;
}
@@ -436,16 +474,20 @@ public class LodRenderer
}
}
GL11.glEnable(GL11.GL_FOG);
GL15.glEnable(GL15.GL_FOG);
RenderSystem.enableFog();
RenderSystem.setupNvFogDistance();
RenderSystem.fogMode(GlStateManager.FogMode.LINEAR);
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode);
if (GlProxy.getInstance().fancyFogAvailable)
GL15.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode);
}
/**
* Revert any changes that were made to the fog.
/**
* Revert any changes that were made to the fog
* and sets up the fog for Minecraft.
*/
@SuppressWarnings("deprecation")
private void cleanupFog(NearFarFogSettings fogSettings,
float defaultFogStartDist, float defaultFogEndDist,
int defaultFogMode, int defaultFogDistance)
@@ -453,15 +495,27 @@ public class LodRenderer
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))
// this setting is only valid if the GPU supports fancy fog
if (GlProxy.getInstance().fancyFogAvailable)
GL15.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, defaultFogDistance);
// disable fog if Minecraft wasn't rendering fog
// or we want it disabled
if (!fogSettings.vanillaIsRenderingFog
|| LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.get())
{
GL11.glDisable(GL11.GL_FOG);
// Make fog render a infinite distance away.
// This doesn't technically disable Minecraft's fog
// so performance will probably be the same regardless, unlike
// Optifine's no fog setting.
// we can't disable minecraft's fog outright because by default
// minecraft will re-enable the fog after our code
RenderSystem.fogStart(0.0F);
RenderSystem.fogEnd(Float.MAX_VALUE);
RenderSystem.fogDensity(0.0F);
}
}
@@ -487,12 +541,12 @@ public class LodRenderer
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
// accuracy vs the model view matrix, which only uses floats)
BlockPos bufferPos = vbosCenter.getWorldPosition();
Vector3d eyePos = mc.getPlayer().getEyePosition(partialTicks);
double xDiff = eyePos.x - bufferPos.getX();
double zDiff = eyePos.z - bufferPos.getZ();
double xDiff = projectedView.x - bufferPos.getX();
double zDiff = projectedView.z - bufferPos.getZ();
mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff);
// get the modified model view matrix
Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose();
// remove the lod ModelViewMatrix
@@ -500,44 +554,21 @@ public class LodRenderer
return lodModelViewMatrix;
}
/**
* James added this to test if Vivecraft is not using
* the MC FOV setting or if the problem is deeper
*/
public enum FovTest
{
LOD_USE_FOV(true, false),
MC_USE_FOV(false, true),
NEITHER(false, false),
BOTH(true, true);
final boolean lodProjUseFov;
final boolean defaultMcProjUseFov;
FovTest(boolean newLodProjUseFov, boolean newDefaultMcProjUseFov)
{
lodProjUseFov = newLodProjUseFov;
defaultMcProjUseFov = newDefaultMcProjUseFov;
}
}
/**
* create a new projection matrix and send it over to the GPU
* @param currentProjectionMatrix this is Minecraft's current projection matrix
* @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance
* @param partialTicks how many ticks into the frame we are
*/
private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float partialTicks)
private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks)
{
//Minimum radious of view in 2 render distance
int minDistance = 1;
// create the new projection matrix
Matrix4f lodPoj =
Matrix4f.perspective(
getFov(partialTicks, true),
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
minDistance,
LodConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get() ? vanillaBlockRenderedDistance / 5 : 1,
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2);
// get Minecraft's un-edited projection matrix
@@ -601,8 +632,8 @@ public class LodRenderer
ByteBuffer temp = ByteBuffer.allocateDirect(16);
temp.order(ByteOrder.nativeOrder());
GL11.glLightfv(LOD_GL_LIGHT_NUMBER, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
GL11.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
GL15.glLightfv(LOD_GL_LIGHT_NUMBER, GL15.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
GL15.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
RenderSystem.enableLighting();
}*/
@@ -645,9 +676,7 @@ public class LodRenderer
vbosCenter = result.drawableCenterChunkPos;
}
/**
* Calls the BufferBuilder's destroyBuffers method.
*/
/** Calls the BufferBuilder's destroyBuffers method. */
public void destroyBuffers()
{
lodBufferBuilder.destroyBuffers();
@@ -660,9 +689,7 @@ public class LodRenderer
}
/**
* Return what fog settings should be used when rendering.
*/
/** Return what fog settings should be used when rendering. */
private NearFarFogSettings determineFogSettings()
{
NearFarFogSettings fogSettings = new NearFarFogSettings();
@@ -847,6 +874,11 @@ public class LodRenderer
prevSkyBrightness = skyBrightness;
}
/*if (lightMap != lastLightMap)
{
fullRegen = true;
lastLightMap = lightMap;
}*/
//================//
// partial regens //
@@ -860,7 +892,6 @@ public class LodRenderer
{
partialRegen = true;
vanillaRenderedChunksChanged = false;
}
prevVanillaChunkTime = newTime;
}
@@ -889,6 +920,8 @@ public class LodRenderer
int zIndex;
for (ChunkPos pos : chunkPosToSkip)
{
vanillaRenderedChunksEmptySkip = false;
xIndex = (pos.x - mc.getPlayer().xChunk) + (chunkRenderDistance + 1);
zIndex = (pos.z - mc.getPlayer().zChunk) + (chunkRenderDistance + 1);
@@ -909,10 +942,11 @@ public class LodRenderer
// if the player is high enough, draw all LODs
if (chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256)
if (chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256 && !vanillaRenderedChunksEmptySkip)
{
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
vanillaRenderedChunksChanged = true;
vanillaRenderedChunksEmptySkip = true;
}
}
@@ -282,7 +282,7 @@ public class DataPointUtil
int size = dataToMerge.length / inputVerticalData;
// We initialize the arrays that are going to be used
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight + 1) * 2);
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight / 2 + 1) * 2);
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
@@ -97,12 +97,13 @@ public class DetailDistanceUtil
return (byte) minDetail;
int distanceUnit = LodConfig.CLIENT.graphics.qualityOption.horizontalScale.get().distanceUnit;
if (LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get() == HorizontalQuality.LOWEST)
detail = (byte) Math.floorDiv(distance, distanceUnit);
detail = (byte) distance / distanceUnit;
else
{
double base = LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get().quadraticBase;
double logBase = Math.log(base);
detail = (byte) (Math.log(Math.floorDiv(distance, distanceUnit)) / logBase);
//noinspection IntegerDivisionInFloatingPointContext
detail = (byte) (Math.log(distance / distanceUnit) / logBase);
}
return (byte) LodUtil.clamp(minDetail, detail, maxDetail - 1);
}
@@ -119,14 +120,11 @@ public class DetailDistanceUtil
public static byte getTreeCutDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail, true);
}
public static byte getTreeGenDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail, true);
}
@@ -156,30 +154,20 @@ public class DetailDistanceUtil
public static HorizontalResolution getLodGenDetail(int detail)
{
if (detail < minGenDetail)
{
return lodGenDetails[minGenDetail];
}
else
{
return lodGenDetails[detail];
}
}
public static byte getCutLodDetail(int detail)
{
if (detail < minGenDetail)
{
return lodGenDetails[minGenDetail].detailLevel;
}
else if (detail == maxDetail)
{
return LodUtil.REGION_DETAIL_LEVEL;
}
else
{
return lodGenDetails[detail].detailLevel;
}
}
public static int getMaxVerticalData(int detail)
@@ -149,6 +149,10 @@ public class LevelPosUtil
return convert(detailLevel, pos, LodUtil.CHUNK_DETAIL_LEVEL);
}
public static int myPow2(int x)
{
return x*x;
}
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
@@ -156,13 +160,15 @@ public class LevelPosUtil
int startPosX = posX * width;
int startPosZ = posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
int endPosX = myPow2(playerPosX - startPosX - width);
int endPosZ = myPow2(playerPosZ - startPosZ - width);
startPosX = myPow2(playerPosX - startPosX);
startPosZ = myPow2(playerPosZ - startPosZ);
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
int maxDistance = (int) Math.sqrt(startPosX + startPosZ);
maxDistance = Math.max(maxDistance, (int) Math.sqrt(startPosX + endPosZ));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(endPosX + startPosZ));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(endPosX + endPosZ));
return maxDistance;
}
@@ -205,10 +211,15 @@ public class LevelPosUtil
}
else
{
int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
startPosX = myPow2(playerPosX - startPosX);
startPosZ = myPow2(playerPosZ - startPosZ);
endPosX = myPow2(playerPosX - endPosX);
endPosZ = myPow2(playerPosZ - endPosZ);
int minDistance = (int) Math.sqrt(startPosX + startPosZ);
minDistance = Math.min(minDistance, (int) Math.sqrt(startPosX + endPosZ));
minDistance = Math.min(minDistance, (int) Math.sqrt(endPosX + startPosZ));
minDistance = Math.min(minDistance, (int) Math.sqrt(endPosX + endPosZ));
return minDistance;
}
}
+6 -19
View File
@@ -201,8 +201,8 @@ public class LodUtil
/** Convert a 2D absolute position into a quad tree relative position. */
public static RegionPos convertGenericPosToRegionPos(int x, int z, int detailLevel)
{
int relativePosX = Math.floorDiv(x, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel));
int relativePosZ = Math.floorDiv(z, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel));
int relativePosX = Math.floorDiv(x, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
int relativePosZ = Math.floorDiv(z, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
return new RegionPos(relativePosX, relativePosZ);
}
@@ -210,7 +210,7 @@ public class LodUtil
/** Convert a 2D absolute position into a quad tree relative position. */
public static int convertLevelPos(int pos, int currentDetailLevel, int targetDetailLevel)
{
return Math.floorDiv(pos, (int) Math.pow(2, targetDetailLevel - currentDetailLevel));
return pos / (1 << (targetDetailLevel - currentDetailLevel));
}
/**
@@ -224,9 +224,7 @@ public class LodUtil
for (ChunkSection section : blockStorage)
{
if (section != null && !section.isEmpty())
{
return true;
}
}
return false;
@@ -492,20 +490,9 @@ public class LodUtil
{
tempX = x + Box.DIRECTION_NORMAL_MAP.get(direction).getX();
tempZ = z + Box.DIRECTION_NORMAL_MAP.get(direction).getZ();
if (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length))
{
if (!vanillaRenderedChunks[tempX][tempZ])
{
return true;
}
}
else
{
if (vanillaRenderedChunks[x][z])
{
return true;
}
}
if (vanillaRenderedChunks[x][z] || (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length)
&& !vanillaRenderedChunks[tempX][tempZ]))
return true;
}
return false;
}
@@ -128,7 +128,7 @@ public class ThreadMapUtil
int size = 1;
for (int i = 0; i < 5; i++)
{
array[i] = new long[size * size * DataPointUtil.worldHeight + 1];
array[i] = new long[size * size * DataPointUtil.worldHeight / 2 + 1];
size = size << 1;
}
threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array);
@@ -0,0 +1,64 @@
package com.seibel.lod.wrappers.Block;
import com.seibel.lod.util.ColorUtil;
import net.minecraft.block.*;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.client.model.data.ModelDataMap;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
//This class wraps the minecraft BlockPos.Mutable (and BlockPos) class
public class BlockPosWrapper
{
private BlockPos.Mutable blockPos;
public BlockPosWrapper()
{
this.blockPos = new BlockPos.Mutable();
}
public void set(int x, int y, int z)
{
blockPos.set(x, y, z);
}
public int getX()
{
return blockPos.getX();
}
public int getY()
{
return blockPos.getY();
}
public int getZ()
{
return blockPos.getZ();
}
public BlockPos.Mutable getBlockPos()
{
return blockPos;
}
@Override public boolean equals(Object o)
{
return blockPos.equals(o);
}
@Override public int hashCode()
{
return Objects.hash(blockPos);
}
}
@@ -0,0 +1,276 @@
package com.seibel.lod.wrappers.Block;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.block.*;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraftforge.client.model.data.ModelDataMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
//This class wraps the minecraft Block class
public class BlockWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockWrapper> blockWrapperMap = new ConcurrentHashMap<>();
public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static Random random = new Random(0);
private Block block;
private boolean nonFull;
private boolean noCollision;
private int color;
private boolean isColored;
private boolean toTint;
private boolean leavesTint;
private boolean grassTint;
private boolean waterTint;
/**Constructor only require for the block instance we are wrapping**/
public BlockWrapper(Block block)
{
this.nonFull = true;
this.noCollision = true;
this.color = 0;
this.isColored = true;
this.toTint = false;
this.block = block;
setupColorAndTint();
setupShapes();
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
*/
static public BlockWrapper getBlockWrapper(Block block)
{
//first we check if the block has already been wrapped
if(blockWrapperMap.containsKey(block) && blockWrapperMap.get(block) != null)
return blockWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockWrapper blockWrapper = new BlockWrapper(block);
blockWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint()
{
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
boolean isTinted = false;
int listSize = 0;
// first step is to check if this block has a tinted face
for (Direction direction : Direction.values())
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(block.defaultBlockState(), direction, random, dataMap);
listSize = Math.max(listSize, quads.size());
for (BakedQuad bakedQuad : quads)
{
isTinted |= bakedQuad.isTinted();
}
}
//if it contains a tinted face then we store this block in the toTint set
if(isTinted)
this.toTint = true;
//now we get the first non empty face
for (Direction direction : Direction.values())
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(block.defaultBlockState(), direction, random, dataMap);
if (!quads.isEmpty())
break;
}
//the quads list is not empty we extract the first one
if (!quads.isEmpty())
texture = quads.get(0).getSprite();
else
{
return;
}
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int tempColor;
int colorMultiplier;
// generate the block's color
for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getHeight(); u++)
{
for (int v = 0; v < texture.getWidth(); v++)
{
if (texture.isTransparent(frameIndex, u, v))
continue;
tempColor = texture.getPixelRGBA(frameIndex, u, v);
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(tempColor) > (ColorUtil.getBlue(tempColor) + 30)) || !(ColorUtil.getGreen(tempColor) > (ColorUtil.getRed(tempColor) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(tempColor) * colorMultiplier;
red += ColorUtil.getBlue(tempColor) * colorMultiplier;
green += ColorUtil.getGreen(tempColor) * colorMultiplier;
blue += ColorUtil.getRed(tempColor) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = 0;
else
{
// determine the average color
alpha /= count;
red /= count;
green /= count;
blue /= count;
tempColor = ColorUtil.rgbToInt(alpha, red, green, blue);
}
// determine if this block should use the biome color tint
if ((grassInstance() || leavesInstance() || waterIstance()) && (float) numberOfGreyPixel / count > 0.75f)
this.toTint = true;
// we check which kind of tint we need to apply
if (grassInstance() && this.toTint)
this.grassTint = true;
if (leavesInstance() && this.toTint)
this.leavesTint = true;
if (waterIstance() && this.toTint)
this.waterTint = true;
color = tempColor;
}
/** determine if the given block should use the biome's grass color */
private boolean grassInstance()
{
return block instanceof GrassBlock
|| block instanceof BushBlock
|| block instanceof IGrowable
|| block instanceof AbstractPlantBlock
|| block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean leavesInstance()
{
return block instanceof LeavesBlock
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's foliage color */
private boolean waterIstance()
{
return block == Blocks.WATER;
}
private void setupShapes(){
}
//--------------//
//Colors getters//
//--------------//
public boolean hasColor()
{
return isColored;
}
public int getColor()
{
return color;
}
//------------//
//Tint getters//
//------------//
public boolean hasTint()
{
return toTint;
}
public boolean hasGrassTint()
{
return grassTint;
}
public boolean hasLeavesTint()
{
return leavesTint;
}
public boolean hasWaterTint()
{
return waterTint;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockWrapper))
return false;
BlockWrapper that = (BlockWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,7 @@
package com.seibel.lod.wrappers.Chunk;
//This class will contain all methods usefull to generate the fake ChunkWrapper
public class ChunkGenerator
{
}
@@ -0,0 +1,44 @@
package com.seibel.lod.wrappers.Chunk;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import java.util.Objects;
//This class wraps the minecraft ChunkPos class
public class ChunkPosWrapper
{
private ChunkPos chunkPos;
public ChunkPosWrapper(ChunkPos chunkPos)
{
this.chunkPos = chunkPos;
}
public int getX()
{
return chunkPos.x;
}
public int getZ()
{
return chunkPos.z;
}
public ChunkPos getChunkPos()
{
return chunkPos;
}
@Override public boolean equals(Object o)
{
return chunkPos.equals(o);
}
@Override public int hashCode()
{
return Objects.hash(chunkPos);
}
}
@@ -0,0 +1,31 @@
package com.seibel.lod.wrappers.Chunk;
import com.seibel.lod.wrappers.Block.BlockWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.world.chunk.IChunk;
public class ChunkWrapper
{
private IChunk chunk;
private ChunkPosWrapper chunkPos;
public ChunkWrapper(IChunk chunk)
{
this.chunk = chunk;
this.chunkPos = new ChunkPosWrapper(chunk.getPos());
}
public BlockWrapper getBlock(BlockPosWrapper blockPos)
{
return BlockWrapper.getBlockWrapper(chunk.getBlockState(blockPos.getBlockPos()).getBlock());
}
public ChunkPosWrapper getChunkPos(){
return chunkPos;
}
public int getEmittedBrightness(BlockPosWrapper blockPos)
{
return chunk.getLightEmission(blockPos.getBlockPos());
}
}
@@ -0,0 +1,19 @@
package com.seibel.lod.wrappers;
import net.minecraft.client.renderer.texture.NativeImage;
public class LigthMapWrapper
{
static NativeImage lightMap = null;
public static void setLightMap(NativeImage lightMap)
{
lightMap = null;
}
public static int getLightValue(int skyLight, int blockLight)
{
return lightMap.getPixelRGBA(skyLight, blockLight);
}
}
@@ -22,6 +22,8 @@ package com.seibel.lod.wrappers;
import java.awt.Color;
import java.io.File;
import com.seibel.lod.ModInfo;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.GameSettings;
@@ -36,6 +38,7 @@ import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.model.ModelManager;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.crash.CrashReport;
import net.minecraft.entity.Entity;
import net.minecraft.profiler.IProfiler;
import net.minecraft.server.integrated.IntegratedServer;
@@ -235,4 +238,20 @@ public class MinecraftWrapper
{
return mc.levelRenderer;
}
/**
* Crashes Minecraft, displaying the given errorMessage <br> <br>
* In the following format: <br>
*
* The game crashed whilst <strong>errorMessage</strong> <br>
* Error: <strong>java.lang.ExceptionClass: exceptionErrorMessage</strong> <br>
* Exit Code: -1 <br>
*/
public void crashMinecraft(String errorMessage, Throwable exception)
{
ClientProxy.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report);
}
}
@@ -0,0 +1,5 @@
package com.seibel.lod.wrappers.Vertex;
public class BufferBuilderWrapper
{
}
@@ -0,0 +1,5 @@
package com.seibel.lod.wrappers.Vertex;
public class VertexBufferWrapper
{
}
@@ -0,0 +1,133 @@
package com.seibel.lod.wrappers.World;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.wrappers.Block.BlockWrapper;
import net.minecraft.block.Blocks;
import net.minecraft.world.biome.Biome;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
//This class wraps the minecraft BlockPos.Mutable (and BlockPos) class
public class BiomeWrapper
{
public static final ConcurrentMap<Biome, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
private Biome biome;
public BiomeWrapper(Biome biome)
{
this.biome = biome;
}
static public BiomeWrapper getBiomeWrapper(Biome biome)
{
//first we check if the biome has already been wrapped
if(biomeWrapperMap.containsKey(biome) && biomeWrapperMap.get(biome) != null)
return biomeWrapperMap.get(biome);
//if it hasn't been created yet, we create it and save it in the map
BiomeWrapper biomeWrapper = new BiomeWrapper(biome);
biomeWrapperMap.put(biome, biomeWrapper);
//we return the newly created wrapper
return biomeWrapper;
}
/** Returns a color int for the given biome. */
public int getColorForBiome(int x, int z)
{
int colorInt;
int color;
int tint;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = BlockWrapper.getBlockWrapper(Blocks.NETHERRACK).getColor();
break;
case THEEND:
colorInt = BlockWrapper.getBlockWrapper(Blocks.END_STONE).getColor();
break;
case BEACH:
case DESERT:
colorInt = BlockWrapper.getBlockWrapper(Blocks.SAND).getColor();
break;
case EXTREME_HILLS:
colorInt = BlockWrapper.getBlockWrapper(Blocks.STONE).getColor();
break;
case MUSHROOM:
colorInt = BlockWrapper.getBlockWrapper(Blocks.MYCELIUM).getColor();
break;
case ICY:
colorInt = BlockWrapper.getBlockWrapper(Blocks.SNOW).getColor();
break;
case MESA:
colorInt = BlockWrapper.getBlockWrapper(Blocks.RED_SAND).getColor();
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case SWAMP:
case FOREST:
color = BlockWrapper.getBlockWrapper(Blocks.OAK_LEAVES).getColor();
tint = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
break;
case TAIGA:
color = BlockWrapper.getBlockWrapper(Blocks.SPRUCE_LEAVES).getColor();
tint = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
break;
case JUNGLE:
color = BlockWrapper.getBlockWrapper(Blocks.JUNGLE_LEAVES).getColor();
tint = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
break;
default:
case NONE:
case PLAINS:
case SAVANNA:
color = BlockWrapper.getBlockWrapper(Blocks.GRASS_BLOCK).getColor();
tint = biome.getGrassColor(x,z);
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
break;
}
return colorInt;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BiomeWrapper))
return false;
BiomeWrapper that = (BiomeWrapper) o;
return Objects.equals(biome, that.biome);
}
@Override public int hashCode()
{
return Objects.hash(biome);
}
}
@@ -0,0 +1,37 @@
package com.seibel.lod.wrappers.World;
import net.minecraft.world.DimensionType;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class DimensionTypeWrapper
{
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>();
private DimensionType dimensionType;
public DimensionTypeWrapper(DimensionType dimensionType)
{
this.dimensionType = dimensionType;
}
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
{
//first we check if the biome has already been wrapped
if(dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
return dimensionTypeWrapperMap.get(dimensionType);
//if it hasn't been created yet, we create it and save it in the map
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper;
}
public static void clearMap()
{
dimensionTypeWrapperMap.clear();
}
}
@@ -0,0 +1,68 @@
package com.seibel.lod.wrappers.World;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.world.IWorld;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class WorldWrapper
{
private static final ConcurrentMap<IWorld, WorldWrapper> worldWrapperMap = new ConcurrentHashMap<>();
private IWorld world;
public WorldWrapper(IWorld world)
{
this.world = world;
}
public WorldWrapper getWorldWrapper(IWorld world)
{
//first we check if the biome has already been wrapped
if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null)
return worldWrapperMap.get(world);
//if it hasn't been created yet, we create it and save it in the map
WorldWrapper worldWrapper = new WorldWrapper(world);
worldWrapperMap.put(world, worldWrapper);
//we return the newly created wrapper
return worldWrapper;
}
public static void clearMap()
{
worldWrapperMap.clear();
}
public DimensionTypeWrapper getDimensionType()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType());
}
public int getBlockLight(BlockPosWrapper blockPos)
{
return world.getLightEngine().skyEngine.getLightValue(blockPos.getBlockPos());
}
public int getSkyLight(BlockPosWrapper blockPos)
{
return world.getLightEngine().blockEngine.getLightValue(blockPos.getBlockPos());
}
public BiomeWrapper getBiome(BlockPosWrapper blockPos)
{
return BiomeWrapper.getBiomeWrapper(world.getBiome(blockPos.getBlockPos()));
}
public boolean hasCeiling()
{
return world.dimensionType().hasCeiling();
}
public boolean hasSkyLight()
{
return world.dimensionType().hasSkyLight();
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ modId="lod" #mandatory
#// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
#//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
#// see the associated build.gradle script for how to populate this completely automatically during a build
version="a1.5.0" #mandatory
version="a1.5.1" #mandatory
#// A display name for the mod
displayName="Distant Horizons" #mandatory