Make LodRenderer a singleton

This commit is contained in:
James Seibel
2025-10-18 11:42:36 -05:00
parent f4ab101403
commit 0e0e1e1b0f
20 changed files with 255 additions and 460 deletions
@@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.renderer.FadeRenderer;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
@@ -328,9 +329,9 @@ public class ClientApi
//===============//
// render events //
//===============//
//============//
// clint tick //
//============//
public void clientTickEvent()
{
@@ -516,7 +517,7 @@ public class ClientApi
this.renderingCancelledForThisFrame = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, renderEventParam);
if (!this.renderingCancelledForThisFrame)
{
level.render(renderEventParam, profiler);
LodRenderer.INSTANCE.render(level, levelWrapper, renderEventParam, profiler);
}
if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering())
@@ -536,7 +537,7 @@ public class ClientApi
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDeferredRenderEvent.class, renderEventParam);
if (!renderingCancelled)
{
level.renderDeferred(renderEventParam, profiler);
LodRenderer.INSTANCE.renderDeferred(level, levelWrapper, renderEventParam, profiler);
}
@@ -79,10 +79,13 @@ public class ColumnBox
byte skyLightTop = skyLight;
byte skyLightBot = RenderDataPointUtil.doesDataPointExist(bottomData) ? RenderDataPointUtil.getLightSky(bottomData) : 0;
boolean isTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
boolean transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
boolean fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled;
boolean isTransparent = ColorUtil.getAlpha(color) < 255 && transparencyEnabled;
boolean overVoid = !RenderDataPointUtil.doesDataPointExist(bottomData);
boolean isTopTransparent = RenderDataPointUtil.getAlpha(topData) < 255 && LodRenderer.transparencyEnabled;
boolean isBottomTransparent = RenderDataPointUtil.getAlpha(bottomData) < 255 && LodRenderer.transparencyEnabled;
boolean isTopTransparent = RenderDataPointUtil.getAlpha(topData) < 255 && transparencyEnabled;
boolean isBottomTransparent = RenderDataPointUtil.getAlpha(bottomData) < 255 && transparencyEnabled;
// defaulting to a value far below what we can normally render means we
// don't need to have an additional "is cave culling enabled" check
@@ -103,7 +106,7 @@ public class ColumnBox
// fake ocean transparency
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor)
if (transparencyEnabled && fakeOceanFloor)
{
if (!isTransparent && isTopTransparent && RenderDataPointUtil.doesDataPointExist(topData))
{
@@ -246,6 +249,8 @@ public class ColumnBox
// based on it's neighbors //
//===========================//
boolean transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
short yMax = (short) (yMin + ySize); // min is inclusive, max is exclusive
byte[] skyLightAtInputPos = THREAD_LOCAL_SKY_LIGHT_ARRAY.get();
@@ -283,7 +288,7 @@ public class ColumnBox
// if the adjacent data point is over the void
// don't consider it as transparent
boolean adjOverVoid = !RenderDataPointUtil.doesDataPointExist(adjBelowPoint);
boolean adjTransparent = !adjOverVoid && RenderDataPointUtil.getAlpha(adjPoint) < 255 && LodRenderer.transparencyEnabled;
boolean adjTransparent = !adjOverVoid && RenderDataPointUtil.getAlpha(adjPoint) < 255 && transparencyEnabled;
@@ -352,7 +357,7 @@ public class ColumnBox
// create vertical faces //
//=======================//
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && transparencyEnabled;
byte lastSkyLight = skyLightAtInputPos[yMin];
int quadBottomY = yMin;
int quadTopY = -1;
@@ -82,8 +82,6 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
// tick methods //
//==============//
private EDhApiDebugRendering lastDebugRendering = EDhApiDebugRendering.OFF;
public void clientTick()
{
// can be false if the level is unloading
@@ -97,6 +95,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
{
return;
}
// TODO this should probably be handled via a config change listener
// recreate the RenderState if the render distance changes
if (clientRenderState.quadtree.blockRenderDistanceDiameter != Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH * 2)
@@ -106,14 +105,8 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
return;
}
IClientLevelWrapper clientLevelWrapper = this.clientLevel.getClientLevelWrapper();
if (clientLevelWrapper == null)
{
return;
}
clientRenderState.close();
clientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer);
clientRenderState = new ClientRenderState(this.clientLevel, this.clientLevel.getFullDataProvider());
if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
{
//FIXME: How to handle this?
@@ -122,69 +115,28 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
return;
}
}
clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
boolean isBuffersDirty = false;
EDhApiDebugRendering newDebugRendering = Config.Client.Advanced.Debugging.debugRendering.get();
if (newDebugRendering != lastDebugRendering)
{
lastDebugRendering = newDebugRendering;
isBuffersDirty = true;
}
if (isBuffersDirty)
{
clientRenderState.lodRenderer.bufferHandler.MarkAllBuffersDirty();
}
clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
}
//========//
// render //
//========//
/** @return if the {@link ClientRenderState} was successfully swapped */
public boolean startRenderer(IClientLevelWrapper clientLevelWrapper)
// TODO start rendering during frame request
public void startRenderer()
{
// TODO why are we passing in a level wrapper? Our client level is already defined.
ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer);
ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, this.clientLevel.getFullDataProvider());
if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState))
{
LOGGER.warn("Failed to start renderer due to concurrency");
ClientRenderState.close();
return false;
}
else
{
return true;
}
}
public boolean isRendering()
{
return this.ClientRenderStateRef.get() != null;
}
public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState == null)
{
// either the renderer hasn't been started yet, or is being reloaded
return;
}
ClientRenderState.lodRenderer.drawLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler);
}
public void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState == null)
{
// either the renderer hasn't been started yet, or is being reloaded
return;
}
ClientRenderState.lodRenderer.drawDeferredLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler);
}
public boolean isRendering() { return this.ClientRenderStateRef.get() != null; }
public void stopRenderer()
{
@@ -290,10 +242,8 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public final IClientLevelWrapper clientLevelWrapper;
public final LodQuadTree quadtree;
public final RenderBufferHandler renderBufferHandler;
public final LodRenderer lodRenderer;
@@ -302,19 +252,17 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
//=============//
public ClientRenderState(
IDhClientLevel dhClientLevel, IClientLevelWrapper clientLevelWrapper,
FullDataSourceProviderV2 fullDataSourceProvider,
GenericObjectRenderer genericRenderer)
IDhClientLevel dhClientLevel,
FullDataSourceProviderV2 fullDataSourceProvider)
{
this.clientLevelWrapper = clientLevelWrapper;
this.quadtree = new LodQuadTree(dhClientLevel, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH * 2,
this.quadtree = new LodQuadTree(
dhClientLevel,
Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH * 2,
// initial position is (0,0) just in case the player hasn't loaded in yet, the tree will be moved once the level starts ticking
0, 0,
fullDataSourceProvider);
this.renderBufferHandler = new RenderBufferHandler(this.quadtree);
this.lodRenderer = new LodRenderer(this.renderBufferHandler, genericRenderer);
}
@@ -327,8 +275,6 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
public void close()
{
LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName());
this.lodRenderer.close();
this.quadtree.close();
}
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.core.level;
import com.google.common.cache.CacheBuilder;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -42,7 +41,6 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import org.jetbrains.annotations.NotNull;
@@ -97,8 +95,8 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
//=============//
public DhClientLevel(ISaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable ClientNetworkState networkState)
{ this(saveStructure, clientLevelWrapper, null, true, networkState); }
public DhClientLevel(ISaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable File fullDataSaveDirOverride, boolean enableRendering, @Nullable ClientNetworkState networkState)
{ this(saveStructure, clientLevelWrapper, null, networkState); }
public DhClientLevel(ISaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable File fullDataSaveDirOverride, @Nullable ClientNetworkState networkState)
{
File saveFolder = saveStructure.getSaveFolder(clientLevelWrapper);
File pre23Folder = saveStructure.getPre23SaveFolder(clientLevelWrapper);
@@ -116,7 +114,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
}
this.levelWrapper = clientLevelWrapper;
this.levelWrapper.setParentLevel(this);
this.levelWrapper.setDhLevel(this);
this.saveStructure = saveStructure;
this.networkState = networkState;
@@ -140,11 +138,8 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
this.createAndSetSupportingRepos(this.dataFileHandler.repo.databaseFile);
this.runRepoReliantSetup();
if (enableRendering)
{
this.clientside.startRenderer(clientLevelWrapper);
LOGGER.info("Started DHLevel for " + this.levelWrapper + " with saves at " + this.saveStructure);
}
this.clientside.startRenderer();
LOGGER.info("Started DHLevel for " + this.levelWrapper + " with saves at " + this.saveStructure);
}
private void registerNetworkHandlers()
{
@@ -242,30 +237,12 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
@Override
@Nullable
public DhBlockPos2D getTargetPosForGeneration()
{
return new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos());
}
public DhBlockPos2D getTargetPosForGeneration() { return new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()); }
@Override
public void worldGenTick()
{
this.worldGenModule.worldGenTick();
}
public void worldGenTick() { this.worldGenModule.worldGenTick(); }
//===========//
// rendering //
//===========//
@Override
public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{ this.clientside.render(renderEventParam, profiler); }
@Override
public void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{ this.clientside.renderDeferred(renderEventParam, profiler); }
public void startRenderer() { this.clientside.startRenderer(); }
@@ -398,7 +375,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
this.networkEventSource.close();
}
this.levelWrapper.setParentLevel(null);
this.levelWrapper.setDhLevel(null);
this.clientside.close();
super.close();
this.dataFileHandler.close();
@@ -20,26 +20,20 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/** The level used for a singleplayer world */
public class DhClientServerLevel extends AbstractDhServerLevel implements IDhClientLevel
@@ -58,7 +52,7 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
{
super(saveStructure, serverLevelWrapper, serverPlayerStateManager, false);
this.serverLevelWrapper.setParentLevel(this);
this.serverLevelWrapper.setDhLevel(this);
this.clientside = new ClientLevelModule(this);
this.runRepoReliantSetup();
}
@@ -72,19 +66,13 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
@Override
public void clientTick() { this.clientside.clientTick(); }
@Override
public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{ this.clientside.render(renderEventParam, profiler); }
@Override
public void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{ this.clientside.renderDeferred(renderEventParam, profiler); }
//========//
// render //
//========//
public void startRenderer(IClientLevelWrapper clientLevel) { this.clientside.startRenderer(clientLevel); }
public void startRenderer() { this.clientside.startRenderer(); }
public void stopRenderer() { this.clientside.stopRenderer(); }
@@ -19,11 +19,6 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import org.jetbrains.annotations.Nullable;
@@ -35,9 +30,6 @@ public interface IDhClientLevel extends IDhLevel
{
void clientTick();
void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler);
void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler);
@Nullable
IClientLevelWrapper getClientLevelWrapper();
@@ -19,8 +19,6 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
@@ -33,9 +31,7 @@ import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRend
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -86,13 +86,13 @@ public class DhApiRenderProxy implements IDhApiRenderProxy
@Override
public DhApiResult<Integer> getDhDepthTextureId()
{
int activeTexture = LodRenderer.getActiveDepthTextureId();
int activeTexture = LodRenderer.INSTANCE.getActiveDepthTextureId();
return (activeTexture == -1) ? DhApiResult.createFail("DH's depth texture hasn't been created and/or bound yet.", -1) : DhApiResult.createSuccess(activeTexture);
}
@Override
public DhApiResult<Integer> getDhColorTextureId()
{
int activeTexture = LodRenderer.getActiveColorTextureId();
int activeTexture = LodRenderer.INSTANCE.getActiveColorTextureId();
return (activeTexture == -1) ? DhApiResult.createFail("DH's color texture hasn't been created and/or bound yet.", -1) : DhApiResult.createSuccess(activeTexture);
}
@@ -76,8 +76,6 @@ public class RenderBufferHandler implements AutoCloseable
// TODO: Make sorting go into the update loop instead of the render loop as it doesn't need to be done every frame
private SortedArraySet<LoadedRenderBuffer> loadedNearToFarBuffers = null;
private final AtomicBoolean rebuildAllBuffers = new AtomicBoolean(false);
private int visibleBufferCount;
private int culledBufferCount;
private int shadowVisibleBufferCount;
@@ -335,8 +333,6 @@ public class RenderBufferHandler implements AutoCloseable
}
}
public void MarkAllBuffersDirty() { this.rebuildAllBuffers.set(true); }
//================//
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
@@ -27,6 +28,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
@@ -72,30 +74,19 @@ public class LodRenderer
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
public static final boolean ENABLE_DRAW_LAG_SPIKE_LOGGING = false;
public static final long DRAW_LAG_SPIKE_THRESHOLD_NS = TimeUnit.NANOSECONDS.convert(20, TimeUnit.MILLISECONDS);
public static final LodRenderer INSTANCE = new LodRenderer();
// TODO make these private, the LOD Builder can get these variables from the config itself
public static boolean transparencyEnabled = true;
public static boolean fakeOceanFloor = true;
/** used to prevent cleaning up render resources while they are being used */
private static final ReentrantLock renderLock = new ReentrantLock();
// these ID's either what any render is currently using (since only one renderer can be active at a time), or just used previously
private static int activeFramebufferId = -1;
private static int activeColorTextureId = -1;
private static int activeDepthTextureId = -1;
private int activeFramebufferId = -1;
private int activeColorTextureId = -1;
private int activeDepthTextureId = -1;
private int cachedWidth;
private int cachedHeight;
private final ReentrantLock setupLock = new ReentrantLock();
public final RenderBufferHandler bufferHandler;
public final GenericObjectRenderer genericObjectRenderer;
// The shader program
private IDhApiShaderProgram lodRenderProgram = null;
@@ -119,39 +110,9 @@ public class LodRenderer
// constructor //
//=============//
public LodRenderer(RenderBufferHandler bufferHandler, GenericObjectRenderer genericObjectRenderer)
private LodRenderer()
{
this.bufferHandler = bufferHandler;
this.genericObjectRenderer = genericObjectRenderer;
}
private boolean rendererClosed = false;
public void close()
{
if (this.rendererClosed)
{
LOGGER.warn("close() called twice!");
return;
}
this.rendererClosed = true;
// wait for the renderer to finish before closing (to prevent closing resources that are currently in use)
renderLock.lock();
try
{
LOGGER.info("Shutting down " + LodRenderer.class.getSimpleName() + "...");
this.cleanup();
this.bufferHandler.close();
LOGGER.info("Finished shutting down " + LodRenderer.class.getSimpleName());
}
finally
{
renderLock.unlock();
}
}
@@ -165,12 +126,12 @@ public class LodRenderer
* {@link DhApiRenderProxy#getDeferTransparentRendering()} is false,
* otherwise it will only render opaque LODs.
*/
public void drawLods(
IClientLevelWrapper clientLevelWrapper,
public void render(
IDhClientLevel clientLevel, IClientLevelWrapper clientLevelWrapper,
DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{
this.renderLodPass(
clientLevelWrapper,
clientLevel, clientLevelWrapper,
renderEventParam,
profiler,
false);
@@ -182,19 +143,19 @@ public class LodRenderer
* It needs to be updated with any major changes,
* but shouldn't be activated as per deferWaterRendering.
*/
public void drawDeferredLods(
IClientLevelWrapper clientLevelWrapper,
public void renderDeferred(
IDhClientLevel clientLevel, IClientLevelWrapper clientLevelWrapper,
DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
{
this.renderLodPass(
clientLevelWrapper,
clientLevel, clientLevelWrapper,
renderEventParam,
profiler,
true);
}
private void renderLodPass(
IClientLevelWrapper clientLevelWrapper,
IDhClientLevel clientLevel, IClientLevelWrapper clientLevelWrapper,
DhApiRenderParam renderEventParam,
IProfilerWrapper profiler,
boolean runningDeferredPass)
@@ -208,23 +169,31 @@ public class LodRenderer
{
return;
}
boolean renderingFirstPass = !runningDeferredPass;
boolean firstPass = !runningDeferredPass;
if (this.rendererClosed)
{
RATE_LIMITED_LOGGER.error("LOD rendering attempted after the renderer has been shut down!");
return;
}
if (AbstractOptifineAccessor.optifinePresent() && MC_RENDER.getTargetFrameBuffer() == -1)
if (AbstractOptifineAccessor.optifinePresent()
&& MC_RENDER.getTargetFrameBuffer() == -1)
{
// wait for MC to finish setting up their renderer
return;
}
if (!renderLock.tryLock())
ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper(clientLevelWrapper);
if (lightmap == null)
{
// this shouldn't normally happen, but just in case
return;
}
RenderBufferHandler renderBufferHandler = clientLevel.getRenderBufferHandler();
if (renderBufferHandler == null)
{
return;
}
GenericObjectRenderer genericRenderer = clientLevel.getGenericRenderer();
if (genericRenderer == null)
{
// never lock the render thread, if the lock isn't available don't wait for it
return;
}
@@ -234,90 +203,145 @@ public class LodRenderer
// rendering setup //
//=================//
try
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderSetupEvent.class, renderEventParam);
this.setupGLStateAndRenderObjects(profiler, renderEventParam, firstPass);
if (!this.isSetupComplete)
{
ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper(clientLevelWrapper);
if (lightmap == null)
// this shouldn't normally happen, but just in case
return;
}
lightmap.bind();
this.quadIBO.bind();
if (firstPass)
{
renderBufferHandler.buildRenderListAndUpdateSections(clientLevelWrapper, renderEventParam, MC_RENDER.getLookAtVector());
}
//===========//
// rendering //
//===========//
if (!runningDeferredPass)
{
//=================================//
// opaque (non-deferred) rendering //
//=================================//
// Disable blending for opaque rendering
GLMC.disableBlend();
// terrain
profiler.popPush("LOD Opaque");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
renderBufferHandler.renderOpaque(this, renderEventParam);
// custom objects with SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
// this shouldn't normally happen, but just in case
return;
profiler.popPush("Custom Objects");
genericRenderer.render(renderEventParam, profiler, true);
}
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderSetupEvent.class, renderEventParam);
this.setupGLStateAndRenderObjects(profiler, renderEventParam, renderingFirstPass);
if (!this.isSetupComplete)
// SSAO
if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get())
{
// this shouldn't normally happen, but just in case
return;
profiler.popPush("LOD SSAO");
SSAORenderer.INSTANCE.render(new Mat4f(renderEventParam.dhProjectionMatrix), renderEventParam.partialTicks);
}
lightmap.bind();
this.quadIBO.bind();
if (renderingFirstPass)
// custom objects without SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
this.bufferHandler.buildRenderListAndUpdateSections(clientLevelWrapper, renderEventParam, MC_RENDER.getLookAtVector());
profiler.popPush("Custom Objects");
genericRenderer.render(renderEventParam, profiler, false);
}
if (!deferTransparentRendering
&& Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
this.renderTransparentBuffersAndFireApiEvent(profiler, renderEventParam, renderBufferHandler);
}
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get())
{
profiler.popPush("LOD Fog");
transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled;
Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
FogRenderer.INSTANCE.render(combinedMatrix, renderEventParam.partialTicks);
}
//===========//
// rendering //
//===========//
//=================//
// debug rendering //
//=================//
if (!runningDeferredPass)
if (Config.Client.Advanced.Debugging.DebugWireframe.enableRendering.get())
{
//=================================//
// opaque (non-deferred) rendering //
//=================================//
profiler.popPush("Debug wireframes");
Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
// Disable blending for opaque rendering
GLMC.disableBlend();
// Note: this can be very slow if a lot of boxes are being rendered
DebugRenderer.INSTANCE.render(combinedMatrix);
}
profiler.popPush("LOD cleanup");
if (this.usingMcFrameBuffer)
{
// If MC's framebuffer is being used the depth needs to be cleared to prevent rendering on top of MC.
// This should only happen when Optifine shaders are being used.
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
//=============================//
// Apply to the MC FrameBuffer //
//=============================//
boolean cancelApplyShader = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeApplyShaderRenderEvent.class, renderEventParam);
if (!cancelApplyShader)
{
profiler.popPush("LOD Apply");
// Copy the LOD framebuffer to Minecraft's framebuffer
DhApplyShader.INSTANCE.render(renderEventParam.partialTicks);
}
}
else
{
//====================//
// deferred rendering //
//====================//
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
profiler.popPush("LOD Transparent");
// terrain
profiler.popPush("LOD Opaque");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
this.bufferHandler.renderOpaque(this, renderEventParam);
// custom objects with SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
profiler.popPush("Custom Objects");
this.genericObjectRenderer.render(renderEventParam, profiler, true);
}
// SSAO
if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get())
{
profiler.popPush("LOD SSAO");
SSAORenderer.INSTANCE.render(new Mat4f(renderEventParam.dhProjectionMatrix), renderEventParam.partialTicks);
}
// custom objects without SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
profiler.popPush("Custom Objects");
this.genericObjectRenderer.render(renderEventParam, profiler, false);
}
if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
this.renderTransparentBuffersAndFireApiEvent(profiler, renderEventParam);
}
this.renderTransparentBuffersAndFireApiEvent(profiler, renderEventParam, renderBufferHandler);
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get())
@@ -329,108 +353,38 @@ public class LodRenderer
FogRenderer.INSTANCE.render(combinedMatrix, renderEventParam.partialTicks);
}
//=================//
// debug rendering //
//=================//
if (Config.Client.Advanced.Debugging.DebugWireframe.enableRendering.get())
{
profiler.popPush("Debug wireframes");
Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
// Note: this can be very slow if a lot of boxes are being rendered
DebugRenderer.INSTANCE.render(combinedMatrix);
}
profiler.popPush("LOD cleanup");
if (this.usingMcFrameBuffer)
{
// If MC's framebuffer is being used the depth needs to be cleared to prevent rendering on top of MC.
// This should only happen when Optifine shaders are being used.
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
//=============================//
// Apply to the MC FrameBuffer //
//=============================//
boolean cancelApplyShader = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeApplyShaderRenderEvent.class, renderEventParam);
if (!cancelApplyShader)
{
profiler.popPush("LOD Apply");
// Copy the LOD framebuffer to Minecraft's framebuffer
DhApplyShader.INSTANCE.render(renderEventParam.partialTicks);
}
}
else
{
//====================//
// deferred rendering //
//====================//
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
profiler.popPush("LOD Transparent");
this.renderTransparentBuffersAndFireApiEvent(profiler, renderEventParam);
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get())
{
profiler.popPush("LOD Fog");
Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
FogRenderer.INSTANCE.render(combinedMatrix, renderEventParam.partialTicks);
}
}
}
//================//
// render cleanup //
//================//
profiler.popPush("LOD cleanup");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderCleanupEvent.class, renderEventParam);
lightmap.unbind();
this.quadIBO.unbind();
IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
{
shaderProgram = shaderProgramOverride;
}
shaderProgram.unbind();
// end of internal LOD profiling
profiler.pop();
}
finally
//================//
// render cleanup //
//================//
profiler.popPush("LOD cleanup");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderCleanupEvent.class, renderEventParam);
lightmap.unbind();
this.quadIBO.unbind();
IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
{
renderLock.unlock();
shaderProgram = shaderProgramOverride;
}
shaderProgram.unbind();
// end of internal LOD profiling
profiler.pop();
}
private void renderTransparentBuffersAndFireApiEvent(IProfilerWrapper profiler, DhApiRenderParam renderEventParam)
private void renderTransparentBuffersAndFireApiEvent(
IProfilerWrapper profiler,
DhApiRenderParam renderEventParam,
RenderBufferHandler renderBufferHandler)
{
profiler.popPush("LOD Transparent");
@@ -441,7 +395,7 @@ public class LodRenderer
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
this.bufferHandler.renderTransparent(this, renderEventParam);
renderBufferHandler.renderTransparent(this, renderEventParam);
}
/** called by each {@link ColumnRenderBuffer} before rendering */
@@ -525,7 +479,8 @@ public class LodRenderer
}
}
if (MC_RENDER.getTargetFrameBufferViewportWidth() != this.cachedWidth || MC_RENDER.getTargetFrameBufferViewportHeight() != this.cachedHeight)
if (MC_RENDER.getTargetFrameBufferViewportWidth() != this.cachedWidth
|| MC_RENDER.getTargetFrameBufferViewportHeight() != this.cachedHeight)
{
// just resizing the textures doesn't work when Optifine is present,
// so recreate the textures with the new size instead
@@ -540,17 +495,17 @@ public class LodRenderer
activeFrameBuffer = framebufferOverride;
}
activeFramebufferId = activeFrameBuffer.getId();
activeDepthTextureId = this.depthTexture.getTextureId();
this.activeFramebufferId = activeFrameBuffer.getId();
this.activeDepthTextureId = this.depthTexture.getTextureId();
if (this.nullableColorTexture != null)
{
activeColorTextureId = this.nullableColorTexture.getTextureId();
this.activeColorTextureId = this.nullableColorTexture.getTextureId();
}
else
{
// get MC's color texture
int mcColorTextureId = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
activeColorTextureId = mcColorTextureId;
this.activeColorTextureId = mcColorTextureId;
}
// Bind LOD frame buffer
activeFrameBuffer.bind();
@@ -744,74 +699,16 @@ public class LodRenderer
//===============//
/** Returns -1 if no frame buffer has been bound yet */
public static int getActiveFramebufferId() { return activeFramebufferId; }
public int getActiveFramebufferId() { return this.activeFramebufferId; }
/** Returns -1 if no texture has been bound yet */
public static int getActiveColorTextureId() { return activeColorTextureId; }
public int getActiveColorTextureId() { return this.activeColorTextureId; }
/**
* FIXME it's possible for this to return an invalid texture ID if the renderer is being re-built at the same time
* Returns -1 if no texture has been bound yet
*/
public static int getActiveDepthTextureId() { return activeDepthTextureId; }
//===================//
// Cleanup Functions //
//===================//
/**
* cleanup and free all render objects.
* (Many objects are Native, outside of JVM, and need manual cleanup)
*/
private void cleanup()
{
if (!GLProxy.hasInstance())
{
// shouldn't normally happen, but just in case
LOGGER.warn("Renderer Cleanup called but the GLProxy has never been initialized!");
return;
}
try
{
this.setupLock.lock();
LOGGER.info("Queuing Renderer Cleanup for main render thread");
GLProxy.getInstance().queueRunningOnRenderThread(() ->
{
LOGGER.info("Renderer Cleanup Started");
if (this.lodRenderProgram != null)
{
this.lodRenderProgram.free();
this.lodRenderProgram = null;
}
if (this.quadIBO != null)
this.quadIBO.destroyAsync();
// Delete framebuffer, color texture, and depth texture
if (this.framebuffer != null && !this.usingMcFrameBuffer)
this.framebuffer.destroy();
if (this.nullableColorTexture != null)
this.nullableColorTexture.destroy();
if (this.depthTexture != null)
this.depthTexture.destroy();
activeFramebufferId = -1;
activeColorTextureId = -1;
activeDepthTextureId = -1;
LOGGER.info("Renderer Cleanup Complete");
});
}
catch (Exception e)
{
this.setupLock.unlock();
}
}
public int getActiveDepthTextureId() { return this.activeDepthTextureId; }
@@ -111,11 +111,11 @@ public class DhApplyShader extends AbstractShaderRenderer
//GLMC.glBlendFunc(GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(LodRenderer.getActiveColorTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveColorTextureId());
GL32.glUniform1i(this.gDhColorTextureUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(LodRenderer.getActiveDepthTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.gDepthMapUniform, 1);
// Copy to MC's framebuffer
@@ -137,7 +137,7 @@ public class DhApplyShader extends AbstractShaderRenderer
return;
}
int dhFrameBufferId = LodRenderer.getActiveFramebufferId();
int dhFrameBufferId = LodRenderer.INSTANCE.getActiveFramebufferId();
if (dhFrameBufferId == -1)
{
return;
@@ -166,11 +166,11 @@ public class DhApplyShader extends AbstractShaderRenderer
//GLMC.glBlendFunc(GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(LodRenderer.getActiveColorTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveColorTextureId());
GL32.glUniform1i(this.gDhColorTextureUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(LodRenderer.getActiveDepthTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.gDepthMapUniform, 1);
@@ -87,7 +87,7 @@ public class FadeApplyShader extends AbstractShaderRenderer
GL32.glUniform1i(this.uFadeColorTextureUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(LodRenderer.getActiveDepthTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.uDhDepthTextureUniform, 1);
GLMC.glActiveTexture(GL32.GL_TEXTURE2);
@@ -158,8 +158,8 @@ public class FadeShader extends AbstractShaderRenderer
@Override
protected void onRender()
{
int depthTextureId = LodRenderer.getActiveDepthTextureId();
int colorTextureId = LodRenderer.getActiveColorTextureId();
int depthTextureId = LodRenderer.INSTANCE.getActiveDepthTextureId();
int colorTextureId = LodRenderer.INSTANCE.getActiveColorTextureId();
if (depthTextureId == -1
|| colorTextureId == -1)
@@ -82,7 +82,7 @@ public class FogApplyShader extends AbstractShaderRenderer
GL32.glUniform1i(this.colorTextureUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(LodRenderer.getActiveDepthTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.depthTextureUniform, 1);
}
@@ -108,7 +108,7 @@ public class FogApplyShader extends AbstractShaderRenderer
// apply the rendered Fog to DH's framebuffer
GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, FogShader.INSTANCE.frameBuffer);
GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.getActiveFramebufferId());
GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.INSTANCE.getActiveFramebufferId());
ScreenQuad.INSTANCE.render();
@@ -252,7 +252,7 @@ public class FogShader extends AbstractShaderRenderer
GLMC.disableBlend();
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(LodRenderer.getActiveDepthTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.uDepthMap, 0);
// this is necessary for MC 1.16 (IE Legacy OpenGL)
@@ -87,7 +87,7 @@ public class SSAOApplyShader extends AbstractShaderRenderer
protected void onApplyUniforms(float partialTicks)
{
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(LodRenderer.getActiveDepthTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.gDepthMapUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
@@ -136,7 +136,7 @@ public class SSAOApplyShader extends AbstractShaderRenderer
// apply the rendered SSAO to the LODs
GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, SSAOShader.INSTANCE.frameBuffer);
GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.getActiveFramebufferId());
GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.INSTANCE.getActiveFramebufferId());
ScreenQuad.INSTANCE.render();
@@ -137,7 +137,7 @@ public class SSAOShader extends AbstractShaderRenderer
GLMC.disableBlend();
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(LodRenderer.getActiveDepthTextureId());
GLMC.glBindTexture(LodRenderer.INSTANCE.getActiveDepthTextureId());
ScreenQuad.INSTANCE.render();
}
@@ -89,8 +89,8 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
return null;
}
level.startRenderer(clientLevelWrapper);
clientLevelWrapper.setParentLevel(level);
level.startRenderer();
clientLevelWrapper.setDhLevel(level);
return level;
});
}
@@ -23,16 +23,11 @@ import com.google.common.io.BaseEncoding;
import com.google.common.primitives.Longs;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
import org.jetbrains.annotations.Nullable;
/**
* A Level wrapper handles all MC related logic related to a given MC level.
@@ -95,7 +90,9 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
* Used so we can access DH related methods/objects
* from the {@link DhApi}.
*/
void setParentLevel(IDhLevel parentLevel);
void setDhLevel(IDhLevel parentLevel);
@Nullable
IDhLevel getDhLevel();
@@ -934,19 +934,19 @@
"Always",
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.NONE":
"Existing Only",
"1. Existing Only",
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY":
"Pre-Existing Chunks only",
"2. Pre-Existing Chunks only",
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.BIOME_ONLY":
"Biome only",
"3. Biome only",
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.BIOME_ONLY_SIMULATE_HEIGHT":
"Biome only simulate height",
"4. Biome only simulate height",
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.SURFACE":
"Surface",
"5. Surface",
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.FEATURES":
"Features",
"6. Features",
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.INTERNAL_SERVER":
"Full - Save Chunks",
"7. Full - Save Chunks",
"distanthorizons.config.enum.EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY":
"Overlay",