This commit is contained in:
s809
2023-12-15 20:29:15 +05:00
18 changed files with 126 additions and 82 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ This repo is for the Distant Horizons mod.
The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and potentially helping us port to different versions faster and easier.
Check out the mod's main GitLab page here:
https://gitlab.com/jeseibel/minecraft-lod-mod
https://gitlab.com/jeseibel/distant-horizons
## source code installation
@@ -26,7 +26,7 @@ import java.util.function.Consumer;
*
* @param <T> The data type of this config.
* @author James Seibel
* @version 2022-9-15
* @version 2023-12-7
* @since API 1.0.0
*/
public interface IDhApiConfigValue<T>
@@ -43,25 +43,36 @@ public interface IDhApiConfigValue<T>
* This is the value stored in the config file.
*/
T getTrueValue();
/*
/**
* Returns the value of the config if it was set by the API.
* Returns null if the config wasn't set by the API.
* Returns null if the config hasn't been set by the API.
*
* @since API 1.1.0
*/
//T getApiValue(); // not currently implemented
T getApiValue();
/**
* Sets the config's value. <br>
* If the newValue is set to null then the config
* will revert to using the True Value.<br>
* will revert to using the True Value
* (IE the value visible in the config menu).<br>
* If the config cannot be set via the API this method will return false. <br><br>
*
* To unset the config's value pass in Null. <br>
*
* @return true if the value was set, false otherwise.
*/
boolean setValue(T newValue);
/** Returns true if this config can be set via the API, false otherwise. */
/**
* Un-sets the config's API value. <br>
* After this method is called this config will
* use the value set in the config menu.
*
* @return true if the value was set, false otherwise.
* @since API 1.1.0
*/
boolean clearValue();
/** @return true if this config can be set via the API, false otherwise. */
boolean getCanBeOverrodeByApi();
/** Returns the default value for this config. */
@@ -87,6 +87,8 @@ public class DhApiConfigValue<coreType, apiType> implements IDhApiConfigValue<ap
}
}
public boolean clearValue() { return this.setValue(null); }
public boolean getCanBeOverrodeByApi() { return this.configEntry.getAllowApiOverride(); }
public apiType getDefaultValue() { return this.configConverter.convertToApiType(this.configEntry.getDefaultValue()); }
@@ -110,9 +110,11 @@ public class ClientApi
/**
* May be fired slightly before or after the associated
* {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} event
* depending on how the host mod loader functions.
* depending on how the host mod loader functions. <br><br>
*
* Synchronized shouldn't be necessary, but is present to match {@see onClientOnlyDisconnected} and prevent any unforeseen issues.
*/
public void onClientOnlyConnected()
public synchronized void onClientOnlyConnected()
{
// only continue if the client is connected to a different server
if (MC.clientConnectedToDedicatedServer())
@@ -134,7 +136,8 @@ public class ClientApi
}
}
public void onClientOnlyDisconnected()
/** Synchronized to prevent a rare issue where multiple disconnect events are triggered on top of each other. */
public synchronized void onClientOnlyDisconnected()
{
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world != null)
@@ -48,7 +48,7 @@ public class LodDataBuilder
ChunkSizedFullDataAccessor chunkData = new ChunkSizedFullDataAccessor(chunkWrapper.getChunkPos());
int minBuildHeight = chunkWrapper.getMinFilledHeight();
int minBuildHeight = chunkWrapper.getMinNonEmptyHeight();
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
@@ -54,7 +54,7 @@ public class BatchGenerator implements IDhApiWorldGenerator
* if this is too high it may cause issues when moving,
* but if it is too low the generator threads won't have enough tasks to work on
*/
private static final int MAX_QUEUED_TASKS = 3;
private static final int MAX_QUEUED_TASKS_PER_THREAD = 3;
public AbstractBatchGenerationEnvironmentWrapper generationEnvironment;
public IDhLevel targetDhLevel;
@@ -150,7 +150,9 @@ public class BatchGenerator implements IDhApiWorldGenerator
@Override
public boolean isBusy()
{
return this.generationEnvironment.getEventCount() > Math.max(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads.get().intValue(), 1) * MAX_QUEUED_TASKS;
int worldGenThreadCount = Math.max(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads.get(), 1);
int maxWorldGenTaskCount = worldGenThreadCount * MAX_QUEUED_TASKS_PER_THREAD;
return this.generationEnvironment.getEventCount() > maxWorldGenTaskCount;
}
@@ -140,13 +140,16 @@ public class DhLightingEngine
// if the dimension has skylights
if (maxSkyLight > 0)
{
int maxY = chunk.getMaxNonEmptyHeight();
int minY = chunk.getMinBuildHeight();
// get the adjacent chunk's sky lights
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++) // relative block pos
{
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
{
// set each pos' sky light all the way down until a opaque block is hit
for (int y = chunk.getMaxBuildHeight(); y >= chunk.getMinBuildHeight(); y--)
for (int y = maxY; y >= minY; y--)
{
IBlockStateWrapper block = chunk.getBlockState(relX, y, relZ);
if (block != null && block.getOpacity() != IBlockStateWrapper.FULLY_TRANSPARENT)
@@ -244,7 +247,7 @@ public class DhLightingEngine
continue;
}
if (relNeighbourBlockPos.y < neighbourChunk.getMinFilledHeight() || relNeighbourBlockPos.y > neighbourChunk.getMaxBuildHeight())
if (relNeighbourBlockPos.y < neighbourChunk.getMinNonEmptyHeight() || relNeighbourBlockPos.y > neighbourChunk.getMaxBuildHeight())
{
// the light pos is outside the chunk's min/max height,
// this can happen if given a chunk that hasn't finished generating
@@ -220,18 +220,6 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
}
});
}
private static class Mapper
{
public final WorldGenTask task;
public final int dist;
public Mapper(WorldGenTask task, int dist)
{
this.task = task;
this.dist = dist;
}
}
/**
* @param targetPos the position to center the generation around
@@ -549,6 +537,19 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
//=======//
// debug //
//=======//
@Override
public void debugRender(DebugRenderer renderer)
{
this.waitingTasks.keySet().forEach((pos) -> { renderer.renderBox(new DebugRenderer.Box(pos, -32f, 64f, 0.05f, Color.blue)); });
this.inProgressGenTasksByLodPos.forEach((pos, t) -> { renderer.renderBox(new DebugRenderer.Box(pos, -32f, 64f, 0.05f, Color.red)); });
}
//================//
// helper methods //
//================//
@@ -586,15 +587,20 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
//=======//
// debug //
//=======//
//================//
// helper classes //
//================//
@Override
public void debugRender(DebugRenderer renderer)
private static class Mapper
{
this.waitingTasks.keySet().forEach((pos) -> { renderer.renderBox(new DebugRenderer.Box(pos, -32f, 64f, 0.05f, Color.blue)); });
this.inProgressGenTasksByLodPos.forEach((pos, t) -> { renderer.renderBox(new DebugRenderer.Box(pos, -32f, 64f, 0.05f, Color.red)); });
public final WorldGenTask task;
public final int dist;
public Mapper(WorldGenTask task, int dist)
{
this.task = task;
this.dist = dist;
}
}
}
@@ -145,7 +145,7 @@ public class GitlabGetter
public static URL getLatestForVersion(String mcVer)
{
try {
return new URL("https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/main/download?job=build:%20[" + mcVer + "]");
return new URL("https://gitlab.com/jeseibel/distant-horizons/-/jobs/artifacts/main/download?job=build:%20[" + mcVer + "]");
} catch (Exception e) { e.printStackTrace(); return null; } // This should always be safe (unless you stuff up **badly** somewhere)
}
}
@@ -33,7 +33,6 @@ import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer;
import com.seibel.distanthorizons.core.render.glObject.texture.*;
import com.seibel.distanthorizons.core.render.renderer.shaders.*;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
@@ -212,12 +211,6 @@ public class LodRenderer
}
}
public void resize(int width, int height)
{
this.colorTexture.resize(width, height);
this.depthTexture.resize(width, height, EDhDepthBufferFormat.DEPTH32F);
}
//===============//
@@ -297,18 +290,34 @@ public class LodRenderer
{
this.cachedWidth = MC_RENDER.getTargetFrameBufferViewportWidth();
this.cachedHeight = MC_RENDER.getTargetFrameBufferViewportHeight();
this.resize(this.cachedWidth, this.cachedHeight);
// just resizing the textures doesn't work when Optifine is present,
// so recreate the textures with the new size instead
this.createColorAndDepthTextures();
}
this.setActiveFramebufferId(framebuffer.getId());
this.setActiveDepthTextureId(depthTexture.getTextureId());
this.setActiveColorTextureId(colorTexture.getTextureId());
this.setActiveFramebufferId(this.framebuffer.getId());
this.setActiveDepthTextureId(this.depthTexture.getTextureId());
this.setActiveColorTextureId(this.colorTexture.getTextureId());
// Bind LOD frame buffer
this.framebuffer.bind();
if (this.usingMcFrameBuffer)
{
// recreating the GL State at this point is necessary in order to get the correct depth texture
minecraftGlState = new GLState();
if (ENABLE_DUMP_GL_STATE)
{
tickLogger.debug("Re-saving GL state due to Optifine presence: " + minecraftGlState); //
}
// Due to using MC/Optifine's framebuffer we need to re-bind the depth texture,
// otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues
this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F);
// don't clear the color texture, that removes the sky
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
@@ -542,17 +551,8 @@ public class LodRenderer
this.usingMcFrameBuffer = false;
}
// color and depth texture
this.colorTexture = DhColorTexture.builder().setDimensions(MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight())
.setInternalFormat(EDhInternalTextureFormat.RGBA8)
.setPixelType(EDhPixelType.UNSIGNED_BYTE)
.setPixelFormat(EDhPixelFormat.RGBA)
.build();
this.depthTexture = new DHDepthTexture(MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight(), EDhDepthBufferFormat.DEPTH32F);
this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F);
this.framebuffer.addColorAttachment(0, this.colorTexture.getTextureId());
// create and bind the necessary textures
this.createColorAndDepthTextures();
this.cachedWidth = MC_RENDER.getTargetFrameBufferViewportWidth();
this.cachedHeight = MC_RENDER.getTargetFrameBufferViewportHeight();
@@ -570,6 +570,23 @@ public class LodRenderer
this.setupLock.unlock();
}
}
/** also binds the new textures to the {@link LodRenderer#framebuffer} */
private void createColorAndDepthTextures()
{
// don't use the cached width/height just in case they haven't been set yet
this.colorTexture = DhColorTexture.builder().setDimensions(MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight())
.setInternalFormat(EDhInternalTextureFormat.RGBA8)
.setPixelType(EDhPixelType.UNSIGNED_BYTE)
.setPixelFormat(EDhPixelFormat.RGBA)
.build();
this.depthTexture = new DHDepthTexture(MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight(), EDhDepthBufferFormat.DEPTH32F);
this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F);
this.framebuffer.addColorAttachment(0, this.colorTexture.getTextureId());
}
private Color getFogColor(float partialTicks)
{
@@ -63,7 +63,6 @@ public class SSAORenderer
this.init = true;
SSAOShader.INSTANCE.init();
SSAOApplyShader.INSTANCE.init();
}
@@ -113,13 +112,13 @@ public class SSAORenderer
this.createFramebuffer(width, height);
}
SSAOShader.INSTANCE.FrameBuffer = this.ssaoFramebuffer;
SSAOShader.INSTANCE.frameBuffer = this.ssaoFramebuffer;
SSAOShader.INSTANCE.setProjectionMatrix(projectionMatrix);
SSAOShader.INSTANCE.render(partialTicks);
primaryState.restore();
SSAOApplyShader.INSTANCE.BufferTexture = this.ssaoTexture;
SSAOApplyShader.INSTANCE.ssaoTexture = this.ssaoTexture;
SSAOApplyShader.INSTANCE.render(partialTicks);
state.restore();
@@ -61,10 +61,7 @@ public abstract class AbstractShaderRenderer
this.shader.unbind();
}
public void free()
{
this.shader.free();
}
public void free() { this.shader.free(); }
protected void onInit() {}
@@ -162,4 +162,5 @@ public class FogShader extends AbstractShaderRenderer
state.restore();
}
}
@@ -39,7 +39,7 @@ public class SSAOApplyShader extends AbstractShaderRenderer
{
public static SSAOApplyShader INSTANCE = new SSAOApplyShader();
public int BufferTexture;
public int ssaoTexture;
// uniforms
public int gSSAOMapUniform;
@@ -76,11 +76,10 @@ public class SSAOApplyShader extends AbstractShaderRenderer
GL32.glUniform1i(this.gDepthMapUniform, 0);
GL32.glActiveTexture(GL32.GL_TEXTURE1);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.BufferTexture);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.ssaoTexture);
GL32.glUniform1i(this.gSSAOMapUniform, 1);
GL32.glUniform1i(this.gBlurRadiusUniform,
Config.Client.Advanced.Graphics.Ssao.blurRadius.get());
GL32.glUniform1i(this.gBlurRadiusUniform, Config.Client.Advanced.Graphics.Ssao.blurRadius.get());
if (this.gViewSizeUniform >= 0)
{
@@ -115,7 +114,7 @@ public class SSAOApplyShader extends AbstractShaderRenderer
GL32.glBlendFuncSeparate(GL32.GL_ZERO, GL32.GL_SRC_ALPHA, GL32.GL_ZERO, GL32.GL_ONE);
// apply the rendered SSAO to the LODs
GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, SSAOShader.INSTANCE.FrameBuffer);
GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, SSAOShader.INSTANCE.frameBuffer);
GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.getActiveFramebufferId());
ScreenQuad.INSTANCE.render();
@@ -38,7 +38,7 @@ public class SSAOShader extends AbstractShaderRenderer
{
public static SSAOShader INSTANCE = new SSAOShader();
public int FrameBuffer;
public int frameBuffer;
private Mat4f projection;
private Mat4f invertedProjection;
@@ -115,7 +115,7 @@ public class SSAOShader extends AbstractShaderRenderer
@Override
protected void onRender()
{
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.FrameBuffer);
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
GL32.glDisable(GL32.GL_SCISSOR_TEST);
GL32.glDisable(GL32.GL_DEPTH_TEST);
GL32.glDisable(GL32.GL_BLEND);
@@ -68,17 +68,17 @@ public class ThreadPools
public static final DhThreadFactory LIGHT_POPULATOR_THREAD_FACTORY = new DhThreadFactory("LOD Builder - Light Populator", Thread.MIN_PRIORITY);
private static ConfigThreadPool lightPopulatorThreadPool;
@Nullable
public static ThreadPoolExecutor getLightPopulatorExecutor() { return lightPopulatorThreadPool.executor; }
public static ThreadPoolExecutor getLightPopulatorExecutor() { return (lightPopulatorThreadPool != null) ? lightPopulatorThreadPool.executor : null; }
public static final DhThreadFactory CHUNK_TO_LOD_BUILDER_THREAD_FACTORY = new DhThreadFactory("LOD Builder - Chunk to Lod Builder", Thread.MIN_PRIORITY);
private static ConfigThreadPool chunkToLodBuilderThreadPool;
@Nullable
public static ThreadPoolExecutor getChunkToLodBuilderExecutor() { return chunkToLodBuilderThreadPool.executor; }
public static ThreadPoolExecutor getChunkToLodBuilderExecutor() { return (chunkToLodBuilderThreadPool != null) ? chunkToLodBuilderThreadPool.executor : null; }
public static final DhThreadFactory BUFFER_BUILDER_THREAD_FACTORY = new DhThreadFactory("LOD Builder - Buffer Builder", Thread.MIN_PRIORITY);
private static ConfigThreadPool bufferBuilderThreadPool;
@Nullable
public static ThreadPoolExecutor getBufferBuilderExecutor() { return bufferBuilderThreadPool.executor; }
public static ThreadPoolExecutor getBufferBuilderExecutor() { return (bufferBuilderThreadPool != null) ? bufferBuilderThreadPool.executor : null; }
/** how many total worker threads can be used */
@@ -154,8 +154,9 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
@Override
public CompletableFuture<Void> saveAndFlush() { return CompletableFuture.allOf(this.dhLevels.stream().map(DhClientServerLevel::saveAsync).toArray(CompletableFuture[]::new)); }
/** synchronized to prevent a rare issue where the server tries closing the same world multiple times in rapid succession. */
@Override
public void close()
public synchronized void close()
{
// at this point the levels are probably unloaded, so this save call usually generally won't do anything
this.saveAndFlush();
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.core.wrapperInterfaces.chunk;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -29,7 +28,6 @@ import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import java.util.ArrayList;
import java.util.List;
public interface IChunkWrapper extends IBindable
{
@@ -40,10 +38,15 @@ public interface IChunkWrapper extends IBindable
int getMaxBuildHeight();
/**
* returns the Y level for the first non-empty section in this chunk,
* or {@link Integer#MAX_VALUE} if this chunk is completely empty.
* returns the Y level for the last non-empty section in this chunk,
* or {@link IChunkWrapper#getMinBuildHeight()} if this chunk is completely empty.
*/
int getMinFilledHeight();
int getMinNonEmptyHeight();
/**
* returns the Y level for the first non-empty section in this chunk,
* or {@link IChunkWrapper#getMaxBuildHeight()} if this chunk is completely empty.
*/
int getMaxNonEmptyHeight();
/** @return The highest y position of a solid block at the given relative chunk position. */
int getSolidHeightMapValue(int xRel, int zRel);