Compare commits

...

11 Commits

Author SHA1 Message Date
James Seibel e9a044308f remove dev from version number 2025-12-18 09:35:07 -06:00
James Seibel 1aabc0c792 remove chunkWrapper.isStillValid() 2025-12-18 09:35:02 -06:00
James Seibel b1b0642fbe LodRenderSection commenting/regions 2025-12-17 09:32:12 -06:00
James Seibel eecb28d11f Fix GLProxy error in multiplayer
Make some GLProxy methods static to prevent setup order issues
2025-12-17 09:02:07 -06:00
James Seibel 90564f2537 fix javadoc in LevelWrapper 2025-12-16 16:39:03 -06:00
James Seibel ded0b979cf Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-12-16 14:45:57 -06:00
James Seibel ed9cc5485c Add SSAO fade out distance 2025-12-16 14:45:53 -06:00
s809 cbd5974657 Fix packet handle errors not showing on F3 screen 2025-12-17 00:15:55 +05:00
James Seibel 0e5fba58ab minor shader program refactor 2025-12-16 09:13:22 -06:00
James Seibel 2943e63382 slight light engine optimization 2025-12-15 14:37:15 -06:00
James Seibel 30564aade7 up version number 2.4.2 -> 2.4.3-dev 2025-12-15 10:17:28 -06:00
23 changed files with 258 additions and 151 deletions
@@ -38,7 +38,7 @@ public final class ModInfo
public static final String NAME = "DistantHorizons";
/** Human-readable version of NAME */
public static final String READABLE_NAME = "Distant Horizons";
public static final String VERSION = "2.4.2-b";
public static final String VERSION = "2.4.3-b";
/** Returns true if the current build is an unstable developer build, false otherwise. */
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
@@ -379,8 +379,11 @@ public class ClientApi
try
{
// make sure the GLProxy is created for future use
GLProxy.getInstance();
// these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks
GLProxy.getInstance().runRenderThreadTasks();
GLProxy.runRenderThreadTasks();
}
catch (Exception e)
{
@@ -90,7 +90,7 @@ public class ClientPluginChannelApi
LOGGER.info("Server level key received: [" + msg.levelKey + "].");
GLProxy.getInstance().queueRunningOnRenderThread(() ->
GLProxy.queueRunningOnRenderThread(() ->
{
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
@@ -419,6 +419,14 @@ public class Config
"")
.build();
public static ConfigEntry<Integer> fadeDistanceInBlocks = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(0, 1_600, 30_000_000)
.comment("" +
"The distance in blocks from the camera where the SSAO will fade out to. \n"+
"This is done to prevent banding and noise at extreme distances. \n"+
"")
.build();
}
public static class GenericRendering
@@ -107,7 +107,7 @@ public class LodBufferContainer implements AutoCloseable
// upload on MC's render thread
GLProxy.getInstance().queueRunningOnRenderThread(() ->
GLProxy.queueRunningOnRenderThread(() ->
{
try
{
@@ -295,7 +295,7 @@ public class LodBufferContainer implements AutoCloseable
{
this.buffersUploaded = false;
GLProxy.getInstance().queueRunningOnRenderThread(() ->
GLProxy.queueRunningOnRenderThread(() ->
{
for (GLVertexBuffer buffer : this.vbos)
{
@@ -399,31 +399,33 @@ public class LodQuadBuilder
}
private void putVertex(ByteBuffer bb, short x, short y, short z, int color, byte normalIndex, byte irisBlockMaterialId, byte skylight, byte blocklight, int mx, int my, int mz)
{
skylight %= 16;
blocklight %= 16;
bb.putShort(x);
bb.putShort(y);
bb.putShort(z);
short meta = 0;
meta |= (skylight | (blocklight << 4));
byte mirco = 0;
// mirco offset which is a xyz 2bit value
// 0b00 = no offset
// 0b01 = positive offset
// 0b11 = negative offset
// format is: 0b00zzyyxx
if (mx != 0) mirco |= mx > 0 ? 0b01 : 0b11;
if (my != 0) mirco |= my > 0 ? 0b0100 : 0b1100;
if (mz != 0) mirco |= mz > 0 ? 0b010000 : 0b110000;
meta |= mirco << 8;
{
skylight %= 16;
blocklight %= 16;
meta |= (short) (skylight | (blocklight << 4));
byte mircoOffset = 0;
// mirco offset which is a xyz 2bit value
// 0b00 = no offset
// 0b01 = positive offset
// 0b11 = negative offset
// format is: 0b00zzyyxx
if (mx != 0) { mircoOffset |= (byte) (mx > 0 ? 0b01 : 0b11); }
if (my != 0) { mircoOffset |= (byte) (my > 0 ? 0b0100 : 0b1100); }
if (mz != 0) { mircoOffset |= (byte) (mz > 0 ? 0b010000 : 0b110000); }
meta |= (short) (mircoOffset << 8);
}
bb.putShort(meta);
byte r = (byte) ColorUtil.getRed(color);
byte g = (byte) ColorUtil.getGreen(color);
byte b = (byte) ColorUtil.getBlue(color);
byte a = this.doTransparency ? (byte) ColorUtil.getAlpha(color) : (byte) 255; // TODO should this be called here or happen somewhere else?
byte a = this.doTransparency ? (byte) ColorUtil.getAlpha(color) : (byte) 255;
bb.put(r);
bb.put(g);
bb.put(b);
@@ -118,15 +118,19 @@ public class DhLightingEngine
* @param centerChunk the chunk we want to apply lighting to
* @param nearbyChunkList should also contain centerChunk
* @param maxSkyLight should be a value between 0 and 15
*
* @return the number of light positions iterated over, can be used for profiling.
*/
private void lightChunk(
private int lightChunk(
@NotNull IChunkWrapper centerChunk, @NotNull ArrayList<IChunkWrapper> nearbyChunkList,
int maxSkyLight, boolean updateBlockLight, boolean updateSkyLight)
{
DhChunkPos centerChunkPos = centerChunk.getChunkPos();
AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk);
// how many positions we've walked over, can be used for profiling/debugging
int posIterations = 0;
// try-finally to handle the stableArray resources
StableLightPosStack blockLightWorldPosQueue = null;
StableLightPosStack skyLightWorldPosQueue = null;
@@ -245,13 +249,15 @@ public class DhLightingEngine
}
}
// block light
if (updateBlockLight)
{
// done to prevent a rare issue where the light values are incorrectly set to -1
centerChunk.clearDhBlockLighting();
this.propagateChunkLightPosList(blockLightWorldPosQueue, adjacentChunkHolder,
posIterations += this.propagateChunkLightPosList(blockLightWorldPosQueue, adjacentChunkHolder,
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
true);
@@ -262,7 +268,7 @@ public class DhLightingEngine
{
centerChunk.clearDhSkyLighting();
this.propagateChunkLightPosList(skyLightWorldPosQueue, adjacentChunkHolder,
posIterations += this.propagateChunkLightPosList(skyLightWorldPosQueue, adjacentChunkHolder,
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
false);
@@ -287,10 +293,12 @@ public class DhLightingEngine
{
centerChunk.setIsDhSkyLightCorrect(true);
}
return posIterations;
}
/** Applies each {@link LightPos} from the queue to the given set of {@link IChunkWrapper}'s. */
private void propagateChunkLightPosList(
private int propagateChunkLightPosList(
StableLightPosStack lightPosQueue, AdjacentChunkHolder adjacentChunkHolder,
IGetLightFunc getLightFunc, ISetLightFunc setLightFunc,
boolean propagatingBlockLights)
@@ -320,66 +328,89 @@ public class DhLightingEngine
IBlockStateWrapper previousBlockState = null;
// update each light position
while (!lightPosQueue.isEmpty())
int iterations = 0;
// update each light level
for (int currentLightLevel = LodUtil.MAX_MC_LIGHT; currentLightLevel >= LodUtil.MIN_MC_LIGHT; currentLightLevel--)
{
// since we don't care about the order the positions are processed,
// we can grab the last position instead of the first for a slight performance increase (this way the array doesn't need to be shifted over every loop)
lightPosQueue.popMutate(lightPos);
// Walking down from the top light level to the bottom can reduce iterating over
// the same positions multiple times.
// At best this seems to behave at roughly 2x the speed of just blindly putting light pos
// in a queue and at worse slightly faster than the blind queue.
int lightValue = lightPos.lightValue;
lightPos.lightValue = currentLightLevel;
// propagate the lighting in each cardinal direction, IE: -x, +x, -y, +y, -z, +z
for (EDhDirection direction : EDhDirection.ALL) // since this is an array instead of an ArrayList this advanced for-loop shouldn't cause any GC issues
// update each light position
while (!lightPosQueue.isLightLevelEmpty(currentLightLevel))
{
lightPos.mutateOffset(direction, neighbourBlockPos);
neighbourBlockPos.mutateToChunkRelativePos(relNeighbourBlockPos);
// since we don't care about the order the positions are processed,
// we can grab the last position instead of the first for a slight performance increase (this way the array doesn't need to be shifted over every loop)
lightPosQueue.popMutate(lightPos, currentLightLevel);
iterations++;
int lightValue = lightPos.lightValue;
// only continue if the light position is inside one of our chunks
IChunkWrapper neighbourChunk = adjacentChunkHolder.getByBlockPos(neighbourBlockPos.getX(), neighbourBlockPos.getZ());
if (neighbourChunk == null)
// propagate the lighting in each cardinal direction, IE: -x, +x, -y, +y, -z, +z
for (EDhDirection direction : EDhDirection.ALL) // since this is an array instead of an ArrayList this advanced for-loop shouldn't cause any GC issues
{
// the light pos is outside our generator's range, ignore it
continue;
}
if (relNeighbourBlockPos.getY() < neighbourChunk.getMinNonEmptyHeight()
|| relNeighbourBlockPos.getY() >= neighbourChunk.getExclusiveMaxBuildHeight())
{
// the light pos is outside the chunk's min/max height,
// this can happen if given a chunk that hasn't finished generating
continue;
}
int currentBlockLight = getLightFunc.getLight(neighbourChunk, relNeighbourBlockPos);
if (currentBlockLight >= (lightValue - 1))
{
// short circuit for when the light value at this position
// is already greater-than what we could set it
continue;
}
IBlockStateWrapper neighbourBlockState = neighbourChunk.getBlockState(relNeighbourBlockPos, mcBlockPos, previousBlockState);
previousBlockState = neighbourBlockState;
// Math.max(1, ...) is used so that the propagated light level always drops by at least 1, preventing infinite cycles.
int targetLevel = lightValue - Math.max(1, neighbourBlockState.getOpacity());
if (targetLevel > currentBlockLight)
{
// this position is darker than the new light value, update/set it
setLightFunc.setLight(neighbourChunk, relNeighbourBlockPos, targetLevel);
lightPos.mutateOffset(direction, neighbourBlockPos);
neighbourBlockPos.mutateToChunkRelativePos(relNeighbourBlockPos);
// now that light has been propagated to this blockPos
// we need to queue it up so its neighbours can be propagated as well
lightPosQueue.push(neighbourBlockPos.getX(), neighbourBlockPos.getY(), neighbourBlockPos.getZ(), targetLevel);
// only continue if the light position is inside one of our chunks
IChunkWrapper neighbourChunk = adjacentChunkHolder.getByBlockPos(neighbourBlockPos.getX(), neighbourBlockPos.getZ());
if (neighbourChunk == null)
{
// the light pos is outside our generator's range, ignore it
continue;
}
if (relNeighbourBlockPos.getY() < neighbourChunk.getMinNonEmptyHeight()
|| relNeighbourBlockPos.getY() >= neighbourChunk.getExclusiveMaxBuildHeight())
{
// the light pos is outside the chunk's min/max height,
// this can happen if given a chunk that hasn't finished generating
continue;
}
int currentBlockLight = getLightFunc.getLight(neighbourChunk, relNeighbourBlockPos);
if (currentBlockLight >= (lightValue - 1))
{
// short circuit for when the light value at this position
// is already greater-than what we could set it
continue;
}
IBlockStateWrapper neighbourBlockState = neighbourChunk.getBlockState(relNeighbourBlockPos, mcBlockPos, previousBlockState);
previousBlockState = neighbourBlockState;
// Math.max(1, ...) is used so that the propagated light level always drops by at least 1, preventing infinite cycles.
int targetLightLevel = lightValue - Math.max(1, neighbourBlockState.getOpacity());
if (targetLightLevel > currentBlockLight)
{
// this position is darker than the new light value, update/set it
setLightFunc.setLight(neighbourChunk, relNeighbourBlockPos, targetLightLevel);
// now that light has been propagated to this blockPos
// we need to queue it up so its neighbours can be propagated as well
lightPosQueue.push(neighbourBlockPos.getX(), neighbourBlockPos.getY(), neighbourBlockPos.getZ(), targetLightLevel);
}
}
}
}
for (int currentLightLevel = LodUtil.MAX_MC_LIGHT; currentLightLevel >= LodUtil.MIN_MC_LIGHT; currentLightLevel--)
{
if (!lightPosQueue.isLightLevelEmpty(currentLightLevel))
{
LodUtil.assertNotReach("Non empty light pos queue for light level ["+currentLightLevel+"] after light engine running");
}
}
// can be enabled if troubleshooting lighting issues
if (RENDER_BLOCK_LIGHT_WIREFRAME
@@ -395,6 +426,7 @@ public class DhLightingEngine
// propagation complete
return iterations;
}
@@ -748,16 +780,24 @@ public class DhLightingEngine
private static final Queue<StableLightPosStack> lightArrayCache = new ArrayDeque<>();
/** the index of the last item in the array, -1 if empty */
private int index = -1;
private int[] indexByLightLevel = new int[LodUtil.MAX_MC_LIGHT + 1];
/** x, y, z, and lightValue. */
public static final int INTS_PER_LIGHT_POS = 4;
/** x, y, z */
public static final int INTS_PER_LIGHT_POS = 3;
/**
* When tested with a normal 1.20 world James saw a maximum of 36,709 block and 2,355 sky lights,
* so 40,000 should be a good starting point that can contain most lighting tasks.
*/
private final IntArrayList lightPositions = new IntArrayList(40_000 * INTS_PER_LIGHT_POS);
private final IntArrayList[] lightPositionsByLightLevel = new IntArrayList[LodUtil.MAX_MC_LIGHT + 1];
public StableLightPosStack()
{
for (int i = 0; i < this.lightPositionsByLightLevel.length; i++)
{
// When tested with a normal 1.20 world James saw a maximum of 36,709 block and 2,355 sky lights,
// so 40,000 should be a good starting point that can contain most lighting tasks.
this.lightPositionsByLightLevel[i] = new IntArrayList(40_000 * INTS_PER_LIGHT_POS);
this.indexByLightLevel[i] = -1;
}
}
@@ -804,45 +844,56 @@ public class DhLightingEngine
// stack methods //
//===============//
public boolean isEmpty() { return this.index == -1; }
public int size() { return this.index+1; }
public boolean isLightLevelEmpty(int lightLevel) { return this.indexByLightLevel[lightLevel] == -1; }
//public int size() { return this.index+1; }
public void push(int blockX, int blockY, int blockZ, int lightValue)
public void push(int blockX, int blockY, int blockZ, int lightLevel)
{
this.index++;
int subIndex = this.index * INTS_PER_LIGHT_POS;
if (subIndex < this.lightPositions.size())
IntArrayList lightPositions = this.lightPositionsByLightLevel[lightLevel];
this.indexByLightLevel[lightLevel]++;
int subIndex = this.indexByLightLevel[lightLevel] * INTS_PER_LIGHT_POS;
if (subIndex < lightPositions.size())
{
this.lightPositions.set(subIndex, blockX);
this.lightPositions.set(subIndex + 1, blockY);
this.lightPositions.set(subIndex + 2, blockZ);
this.lightPositions.set(subIndex + 3, lightValue);
lightPositions.set(subIndex, blockX);
lightPositions.set(subIndex + 1, blockY);
lightPositions.set(subIndex + 2, blockZ);
}
else
{
// add a new pos
this.lightPositions.add(blockX);
this.lightPositions.add(blockY);
this.lightPositions.add(blockZ);
this.lightPositions.add(lightValue);
lightPositions.add(blockX);
lightPositions.add(blockY);
lightPositions.add(blockZ);
}
}
/** mutates the given {@link LightPos} to match the next {@link LightPos} in the queue. */
public void popMutate(LightPos pos)
public void popMutate(LightPos pos, int lightLevel)
{
int subIndex = this.index * INTS_PER_LIGHT_POS;
int subIndex = this.indexByLightLevel[lightLevel] * INTS_PER_LIGHT_POS;
IntArrayList lightPositions = this.lightPositionsByLightLevel[lightLevel];
pos.setX(this.lightPositions.getInt(subIndex));
pos.setY(this.lightPositions.getInt(subIndex + 1));
pos.setZ(this.lightPositions.getInt(subIndex + 2));
pos.lightValue = this.lightPositions.getInt(subIndex + 3);
pos.setX(lightPositions.getInt(subIndex));
pos.setY(lightPositions.getInt(subIndex + 1));
pos.setZ(lightPositions.getInt(subIndex + 2));
this.index--;
this.indexByLightLevel[lightLevel]--;
}
@Override
public String toString() { return this.index + "/" + (this.lightPositions.size() / INTS_PER_LIGHT_POS); }
public String toString()
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < this.indexByLightLevel.length; i++)
{
builder.append("light: ").append(i)
.append(" size: ").append(this.indexByLightLevel[i]).append("/").append(this.lightPositionsByLightLevel[i].size() / INTS_PER_LIGHT_POS).append("\n");
}
return builder.toString();
}
}
@@ -92,7 +92,8 @@ public class NetworkSession extends AbstractNetworkEventSource
{
LOGGER.error("Failed to handle the message. New messages will be ignored.", e);
LOGGER.error("Message: ["+message+"]");
this.close();
this.close(e);
}
}
@@ -133,6 +133,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
//=============//
// constructor //
//=============//
//region constructor
public LodRenderSection(
long pos,
@@ -153,11 +154,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
}
//endregion constructor
//======================================//
// render data generation and uploading //
//======================================//
//region render data uploading
/** @return true if the upload started, false if it wasn't able to for any reason */
public synchronized boolean uploadRenderDataToGpuAsync()
@@ -314,7 +318,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
});
}
/** async is done so each thread can run without waiting on others */
/**
* async is done so each thread can run without waiting on others
* @param direction the direction to load relative to the given position, null will return the given position
*/
private CompletableFuture<ColumnRenderSource> getRenderSourceForPosAsync(long pos, @Nullable EDhDirection direction)
{
if (direction != null)
@@ -400,11 +407,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
});
}
//endregion render data uploading
//========================//
// getters and properties //
//========================//
//====================//
// enabling rendering //
//====================//
//region enabling rendering
public boolean canRender() { return this.bufferContainer != null; }
@@ -439,11 +449,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
public boolean gpuUploadInProgress() { return this.getAndBuildRenderDataFuture != null; }
//endregion enabling rendering
//=================================//
// full data retrieval (world gen) //
//=================================//
//region full data retrieval
public boolean isFullyGenerated()
{
@@ -551,11 +564,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
}
//endregion full data retrieval
//=================//
// beacon handling //
//=================//
//region beacon handling
/** gets the active beacon list and stops/starts beacon rendering as necessary */
private void getAndRefreshRenderingBeacons()
@@ -629,11 +645,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
}
//endregion beacon handling
//==============//
// base methods //
//==============//
//region base methods
@Override
public void debugRender(DebugRenderer debugRenderer)
@@ -720,6 +739,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
//endregion base methods
}
@@ -56,13 +56,13 @@ public class GLProxy
public static final Set<String> LOGGED_GL_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private static final ConcurrentLinkedQueue<Runnable> RENDER_THREAD_RUNNABLE_QUEUE = new ConcurrentLinkedQueue<>();
private static GLProxy instance = null;
private final ConcurrentLinkedQueue<Runnable> renderThreadRunnableQueue = new ConcurrentLinkedQueue<>();
/** Minecraft's GL capabilities */
public final GLCapabilities glCapabilities;
@@ -231,7 +231,7 @@ public class GLProxy
return uploadOverride;
}
public boolean runningOnRenderThread()
public static boolean runningOnRenderThread()
{
long currentContext = GLFW.glfwGetCurrentContext();
return currentContext != 0L; // if the context isn't null, it's the MC context
@@ -243,12 +243,12 @@ public class GLProxy
// Worker Thread Runnables //
//=========================//
public void queueRunningOnRenderThread(Runnable renderCall)
public static void queueRunningOnRenderThread(Runnable renderCall)
{
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
this.renderThreadRunnableQueue.add(() -> this.runOpenGlCall(renderCall, stackTrace));
RENDER_THREAD_RUNNABLE_QUEUE.add(() -> runOpenGlCall(renderCall, stackTrace));
}
private void runOpenGlCall(Runnable renderCall, StackTraceElement[] stackTrace)
private static void runOpenGlCall(Runnable renderCall, StackTraceElement[] stackTrace)
{
try
{
@@ -266,11 +266,11 @@ public class GLProxy
* Doesn't do any thread/GL Context validation.
* Running this outside of the render thread may cause crashes or other issues.
*/
public void runRenderThreadTasks()
public static void runRenderThreadTasks()
{
long startTime = System.nanoTime();
Runnable runnable = this.renderThreadRunnableQueue.poll();
Runnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
while(runnable != null)
{
runnable.run();
@@ -283,7 +283,7 @@ public class GLProxy
break;
}
runnable = this.renderThreadRunnableQueue.poll();
runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
}
}
@@ -100,7 +100,7 @@ public class GLBuffer implements AutoCloseable
protected void create(boolean asBufferStorage)
{
if (!GLProxy.getInstance().runningOnRenderThread())
if (!GLProxy.runningOnRenderThread())
{
LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread.");
}
@@ -151,7 +151,7 @@ public class GLBuffer implements AutoCloseable
BUFFER_ID_TO_PHANTOM.remove(id);
}
GLProxy.getInstance().queueRunningOnRenderThread(() ->
GLProxy.queueRunningOnRenderThread(() ->
{
// destroy the buffer if it exists,
// the buffer may not exist if the destroy method is called twice
@@ -44,7 +44,7 @@ public class QuadElementBuffer extends GLElementBuffer
public int getCapacity()
{
return super.getSize() / GLEnums.getTypeSize(getType());
return super.getSize() / GLEnums.getTypeSize(this.getType());
}
private static void buildBufferByte(int quadCount, ByteBuffer buffer)
@@ -140,7 +140,6 @@ public class QuadElementBuffer extends GLElementBuffer
return;
}
int vertexCount = quadCount * 4; // 4 vertices per quad
GLProxy gl = GLProxy.getInstance();
if (vertexCount < 255)
{ // Reserve 1 for the reset index
@@ -158,7 +157,7 @@ public class QuadElementBuffer extends GLElementBuffer
ByteBuffer buffer = MemoryUtil.memAlloc(this.indicesCount * GLEnums.getTypeSize(this.type));
buildBuffer(quadCount, buffer, this.type);
if (!gl.bufferStorageSupported)
if (!GLProxy.getInstance().bufferStorageSupported)
{
this.bind();
@@ -95,7 +95,7 @@ public class ShaderProgram
for (int i = 0; i < attributes.length; i++)
{
GL32.glBindAttribLocation(id, i, attributes[i]);
GL32.glBindAttribLocation(this.id, i, attributes[i]);
}
GL32.glLinkProgram(this.id);
@@ -59,8 +59,10 @@ public final class VertexPointer
/** Always aligned to 4 bytes */
public static VertexPointer addUnsignedBytePointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 4, useInteger); }
/** aligned to 4 bytes */
public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount), useInteger); }
public static VertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount * 2), useInteger); }
public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized, boolean useInteger)
{ return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount), useInteger); }
public static VertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized, boolean useInteger)
{ return new VertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount * 2), useInteger); }
public static VertexPointer addShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_SHORT, normalized, _align(elementCount * 2), useInteger); }
public static VertexPointer addIntPointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_INT, normalized, 4, useInteger); }
public static VertexPointer addIVec2Pointer(boolean normalized, boolean useInteger) { return new VertexPointer(2, GL32.GL_INT, normalized, 8, useInteger); }
@@ -64,7 +64,7 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
public int uNoiseDropoff = -1;
// Debug Uniform
public int uWhiteWorld = -1;
public int uIsWhiteWorld = -1;
@@ -102,7 +102,7 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff");
// Debug Uniform
this.uWhiteWorld = this.getUniformLocation("uWhiteWorld");
this.uIsWhiteWorld = this.getUniformLocation("uIsWhiteWorld");
// TODO: Add better use of the LODFormat thing
@@ -192,7 +192,7 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get());
// Debug
this.setUniform(this.uWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
// Clip Uniform
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks);
@@ -493,12 +493,8 @@ public class LodRenderer
return false;
}
if (!GLProxy.hasInstance())
{
// shouldn't normally happen, but just in case
LOGGER.warn("Renderer setup called but GLProxy has not yet been setup!");
return false;
}
// GLProxy should have already been created by this point, but just in case create it now
GLProxy.getInstance();
@@ -337,7 +337,7 @@ public class RenderableBoxGroup
@Override
public void close()
{
GLProxy.getInstance().queueRunningOnRenderThread(() ->
GLProxy.queueRunningOnRenderThread(() ->
{
if (this.instanceChunkPosVbo != 0)
{
@@ -25,8 +25,10 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.render.renderer.SSAORenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.NumberUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.lwjgl.opengl.GL32;
/**
@@ -58,6 +60,7 @@ public class SSAOShader extends AbstractShaderRenderer
public int uMinLight;
public int uBias;
public int uDepthMap;
public int uFadeDistanceInBlocks;
@@ -81,6 +84,7 @@ public class SSAOShader extends AbstractShaderRenderer
this.uMinLight = this.shader.getUniformLocation("uMinLight");
this.uBias = this.shader.getUniformLocation("uBias");
this.uDepthMap = this.shader.getUniformLocation("uDepthMap");
this.uFadeDistanceInBlocks = this.shader.getUniformLocation("uFadeDistanceInBlocks");
}
@@ -120,6 +124,10 @@ public class SSAOShader extends AbstractShaderRenderer
this.shader.setUniform(this.uBias, bias.floatValue());
GL32.glUniform1i(this.uDepthMap, 0);
float fadeDistanceInBlocks = Config.Client.Advanced.Graphics.Ssao.fadeDistanceInBlocks.get().floatValue();
fadeDistanceInBlocks = MathUtil.clamp(0.0f, fadeDistanceInBlocks, Float.MAX_VALUE); // clamp to prevent accidentally setting a negative number
this.shader.setUniform(this.uFadeDistanceInBlocks, fadeDistanceInBlocks);
}
@@ -126,8 +126,6 @@ public interface IChunkWrapper extends IBindable
IBiomeWrapper getBiome(int relX, int relY, int relZ);
boolean isStillValid();
//========================//
@@ -54,7 +54,7 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
/**
* Includes both the namespace and name. <br>
* example: "minecraft@@overworld"
* example: "minecraft:overworld"
*/
@Override
String getDimensionName();
@@ -208,6 +208,10 @@
"Blur Radius",
"distanthorizons.config.client.advanced.graphics.ssao.blurRadius.@tooltip":
"The radius, measured in pixels, that blurring is calculated for the SSAO. \nHigher numbers will reduce banding at the cost of GPU performance.",
"distanthorizons.config.client.advanced.graphics.ssao.fadeDistanceInBlocks":
"Fade Distance",
"distanthorizons.config.client.advanced.graphics.ssao.fadeDistanceInBlocks.@tooltip":
"The distance in blocks from the camera where the SSAO will fade out to. \nThis is done to prevent banding and noise at extreme distances.",
+24 -9
View File
@@ -17,6 +17,7 @@ uniform float uMinLight;
uniform float uBias;
uniform mat4 uInvProj;
uniform mat4 uProj;
uniform float uFadeDistanceInBlocks;
const float EPSILON = 1.e-6;
const float GOLDEN_ANGLE = 2.39996323;
@@ -99,16 +100,30 @@ void main()
{
vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth));
#ifdef GL_ARB_derivative_control
// Get higher precision derivatives when available
vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz));
#else
vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz));
#endif
// fading is done to prevent banding/noise
// at super far distance
float distanceFromCamera = length(viewPos);
float fadeDistance = uFadeDistanceInBlocks;
if (distanceFromCamera < fadeDistance)
{
#ifdef GL_ARB_derivative_control
// Get higher precision derivatives when available
vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz));
#else
vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz));
#endif
viewNormal = normalize(viewNormal);
occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal);
viewNormal = normalize(viewNormal);
occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal);
// linearly fade with distance
occlusion *= (fadeDistance - distanceFromCamera) / fadeDistance;
}
else
{
// we're out of range, no need to do any SSAO calculations
occlusion = 0.0;
}
}
fragColor = vec4(vec3(1.0 - occlusion), 1.0);
@@ -8,13 +8,12 @@ out vec4 vertexColor;
out vec3 vertexWorldPos;
out float vertexYPos;
uniform bool uWhiteWorld;
uniform bool uIsWhiteWorld;
uniform mat4 uCombinedMatrix;
uniform vec3 uModelOffset;
uniform float uWorldYOffset;
uniform int uWorldSkyLight;
uniform sampler2D uLightMap;
uniform float uMircoOffset;
@@ -57,7 +56,7 @@ void main()
float light = (float(lights/16u)+0.5) / 16.0;
vertexColor = vec4(texture(uLightMap, vec2(light, light2)).xyz, 1.0);
if (!uWhiteWorld)
if (!uIsWhiteWorld)
{
vertexColor *= color;
}