diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiColorDepthTextureCreatedEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiColorDepthTextureCreatedEvent.java index c362fdc54..7d353be1e 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiColorDepthTextureCreatedEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiColorDepthTextureCreatedEvent.java @@ -33,7 +33,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp * @since API 2.0.0 * @deprecated Replaced by {@link DhApiBeforeColorDepthTextureCreatedEvent} since this event's name isn't obvious when it fires. */ -@Deprecated +@Deprecated // internal notes: this method must be kept around due to Iris using it and we don't want to break old Iris support public abstract class DhApiColorDepthTextureCreatedEvent implements IDhApiEvent { /** Fired before Distant Horizons creates. */ diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java index de33d3613..2519db802 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java @@ -38,7 +38,7 @@ public class DhApiRenderParam implements IDhApiEventParam /** Indicates how far into this tick the frame is. */ public final float partialTicks; - /** + /** * Indicates DH's near clip plane, measured in blocks. * Note: this may change based on time, player speed, and other factors. */ @@ -67,7 +67,6 @@ public class DhApiRenderParam implements IDhApiEventParam // constructors // //==============// - public DhApiRenderParam(DhApiRenderParam parent) { this( @@ -86,7 +85,7 @@ public class DhApiRenderParam implements IDhApiEventParam DhApiMat4f newMcProjectionMatrix, DhApiMat4f newMcModelViewMatrix, DhApiMat4f newDhProjectionMatrix, DhApiMat4f newDhModelViewMatrix, int worldYOffset - ) + ) { this.renderPass = renderPass; @@ -111,10 +110,9 @@ public class DhApiRenderParam implements IDhApiEventParam // base overrides // //================// - @Override - public DhApiRenderParam copy() - { - return new DhApiRenderParam(this); - } + @Override + public DhApiRenderParam copy() { return new DhApiRenderParam(this); } + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 3459eb023..a8867e7cb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -23,8 +23,7 @@ import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass; import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.core.api.internal.rendering.RenderState; +import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.MessageRegistry; @@ -32,11 +31,11 @@ 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.render.renderer.RenderParams; import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage; import com.seibel.distanthorizons.core.network.session.NetworkSession; @@ -45,10 +44,8 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; -import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.renderer.TestRenderer; -import com.seibel.distanthorizons.core.util.RenderUtil; import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.core.world.DhClientServerWorld; import com.seibel.distanthorizons.core.world.DhClientWorld; @@ -57,15 +54,14 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; 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 org.apache.logging.log4j.LogManager; import com.seibel.distanthorizons.core.logging.DhLogger; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import java.io.File; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; /** * This holds the methods that should be called @@ -95,14 +91,13 @@ public class ClientApi * * Only downside is making sure each variable is populated before rendering. */ - public static final RenderState RENDER_STATE = new RenderState(); + public static final DhRenderState RENDER_STATE = new DhRenderState(); private boolean isDevBuildMessagePrinted = false; private boolean lowMemoryWarningPrinted = false; private boolean highVanillaRenderDistanceWarningPrinted = false; - /** when the last static */ private long lastStaticWarningMessageSentMsTime = 0L; private final Queue chatMessageQueueForNextFrame = new LinkedBlockingQueue<>(); @@ -110,8 +105,6 @@ public class ClientApi public boolean rendererDisabledBecauseOfExceptions = false; - private long lastFlushNanoTime = 0; - private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); /** Delay loading the first level to give the server some time to respond with level to actually load */ @@ -123,8 +116,8 @@ public class ClientApi /** Holds any chunks that were loaded before the {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} was fired. */ public final HashMap, IChunkWrapper> waitingChunkByClientLevelAndPos = new HashMap<>(); - /** re-set every frame during the opaque rendering stage */ - private boolean renderingCancelledForThisFrame; + @Nullable + public String lastRenderParamValidationMessage = null; @@ -333,6 +326,7 @@ public class ClientApi // clint tick // //============// + @Deprecated public void clientTickEvent() { IProfilerWrapper profiler = MC_CLIENT.getProfiler(); @@ -340,7 +334,7 @@ public class ClientApi try { - IDhClientWorld clientWorld = SharedApi.getIDhClientWorld(); + IDhClientWorld clientWorld = SharedApi.tryGetDhClientWorld(); if (clientWorld != null) { clientWorld.clientTick(); @@ -383,9 +377,9 @@ public class ClientApi - //===========// - // rendering // - //===========// + //===============// + // LOD rendering // + //===============// /** Should be called before {@link ClientApi#renderDeferredLodsForShaders} */ public void renderLods() { this.renderLodLayer(false); } @@ -398,18 +392,9 @@ public class ClientApi private void renderLodLayer(boolean renderingDeferredLayer) { - // A global render state variable is used since MC has split up their - // render prep and actual rendering into different threads/methods - // this is annoying since it's possible to start a render with only - // partially complete info, but there isn't a better option at the moment - IClientLevelWrapper levelWrapper = RENDER_STATE.clientLevelWrapper; - Mat4f mcModelViewMatrix = RENDER_STATE.mcModelViewMatrix; - Mat4f mcProjectionMatrix = RENDER_STATE.mcProjectionMatrix; - float partialTicks = RENDER_STATE.frameTime; - - - + //=========// // logging // + //=========// this.sendQueuedChatMessages(); @@ -419,7 +404,33 @@ public class ClientApi - // render parameter setup // + //=====================// + // render thread tasks // + //=====================// + + // only run these tasks once per frame + if (!renderingDeferredLayer) + { + profiler.push("DH render thread tasks"); + + try + { + // these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks + GLProxy.getInstance().runRenderThreadTasks(); + } + catch (Exception e) + { + LOGGER.error("Unexpected issue running render thread tasks, error: [" + e.getMessage() + "].", e); + } + + profiler.pop(); + } + + + + //=================// + // parameter setup // + //=================// EDhApiRenderPass renderPass; if (DhApiRenderProxy.INSTANCE.getDeferTransparentRendering()) @@ -438,86 +449,67 @@ public class ClientApi renderPass = EDhApiRenderPass.OPAQUE_AND_TRANSPARENT; } - DhApiRenderParam renderEventParam = - new DhApiRenderParam( - renderPass, - partialTicks, - RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks), RenderUtil.getFarClipPlaneDistanceInBlocks(), - mcProjectionMatrix, mcModelViewMatrix, - RenderUtil.createLodProjectionMatrix(mcProjectionMatrix, partialTicks), RenderUtil.createLodModelViewMatrix(mcModelViewMatrix), - levelWrapper.getMinHeight() - ); + // A global render state variable is used since MC has split up their + // render prep and actual rendering into different threads/methods + // this is annoying since it's possible to start a render with only + // partially complete info, but there isn't a better option at the moment + RenderParams renderParams = + new RenderParams( + renderPass, + RENDER_STATE.frameTime, + RENDER_STATE.mcProjectionMatrix, RENDER_STATE.mcModelViewMatrix, + RENDER_STATE.clientLevelWrapper + ); - //Mat4f mcCombined = mcModelViewMatrix.copy(); - //mcCombined.multiply(mcProjectionMatrix); - // - //com.seibel.distanthorizons.api.objects.math.DhApiMat4f dhCombined = renderEventParam.dhModelViewMatrix.copy(); - //dhCombined.multiply(renderEventParam.dhProjectionMatrix); - // - //LOGGER.info("\n\n" + - // "API\n" + - // "Mc MVM: \n" + mcModelViewMatrix.toString() + "\n" + - // "Mc Proj: \n" + mcProjectionMatrix + "\n" + - // "Mc Combined:\n" + mcCombined.toString() + "\n" + - // "\n" + - // "DH MVM: \n" + renderEventParam.dhModelViewMatrix.toString() + "\n" + - // "DH Proj: \n" + renderEventParam.dhProjectionMatrix + "\n" + - // "DH Combined:\n" + mcCombined.toString() - //); + //============// + // validation // + //============// + + // TODO write this message to the F3 menu so people can see when a different mod screws with the lightmap + String validationMessage = renderParams.getValidationErrorMessage(); + if (validationMessage != null) + { + this.lastRenderParamValidationMessage = validationMessage; + return; + } + else + { + this.lastRenderParamValidationMessage = null; + } + + if (this.rendererDisabledBecauseOfExceptions) + { + // re-enable rendering if the user toggles DH rendering + if (!Config.Client.quickEnableRendering.get()) + { + LOGGER.info("DH Renderer re-enabled after exception. Some rendering issues may occur. Please reboot Minecraft if you see any rendering issues."); + this.rendererDisabledBecauseOfExceptions = false; + Config.Client.quickEnableRendering.set(true); + } + + return; + } - // render validation // + //===========// + // rendering // + //===========// try { - // TODO write this message to the F3 menu so people can see when a different mod screws with the lightmap - String reasonLodsCannotRender = RenderUtil.shouldLodsRender(levelWrapper, renderEventParam); - if (reasonLodsCannotRender != null) - { - return; - } - - IDhClientWorld dhClientWorld = SharedApi.getIDhClientWorld(); - if (dhClientWorld == null) - { - return; - } - - IDhClientLevel level = (IDhClientLevel) dhClientWorld.getLevel(levelWrapper); - if (level == null) - { - return; - } - - - if (this.rendererDisabledBecauseOfExceptions) - { - // re-enable rendering if the user toggles DH rendering - if (!Config.Client.quickEnableRendering.get()) - { - LOGGER.info("DH Renderer re-enabled after exception. Some rendering issues may occur. Please reboot Minecraft if you see any rendering issues."); - this.rendererDisabledBecauseOfExceptions = false; - Config.Client.quickEnableRendering.set(true); - } - - return; - } - - - // render pass // if (!renderingDeferredLayer) { if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT) { - this.renderingCancelledForThisFrame = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, renderEventParam); - if (!this.renderingCancelledForThisFrame) + boolean renderingCancelledForThisFrame = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, renderParams); + if (!renderingCancelledForThisFrame) { - LodRenderer.INSTANCE.render(level, levelWrapper, renderEventParam, profiler); + LodRenderer.INSTANCE.render(renderParams, profiler); } if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering()) @@ -534,10 +526,10 @@ public class ClientApi } else { - boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDeferredRenderEvent.class, renderEventParam); + boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDeferredRenderEvent.class, renderParams); if (!renderingCancelled) { - LodRenderer.INSTANCE.renderDeferred(level, levelWrapper, renderEventParam, profiler); + LodRenderer.INSTANCE.renderDeferred(renderParams, profiler); } @@ -557,24 +549,19 @@ public class ClientApi MC_CLIENT.sendChatMessage("\u00A74Toggle DH rendering via the config UI to re-activate DH rendering."); MC_CLIENT.sendChatMessage("\u00A74Error: " + e); } - finally - { - try - { - // these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks - GLProxy.getInstance().runRenderThreadTasks(); - } - catch (Exception e) - { - LOGGER.error("Unexpected issue running render thread tasks.", e); - } - - - profiler.pop(); // end LOD - profiler.push("terrain"); // go back into "terrain" - } + + + + profiler.pop(); // end LOD + profiler.push("terrain"); // go back into "terrain" } + + + //================// + // fade rendering // + //================// + /** * The first fade pass. * Called after MC finishes rendering the opaque passes. @@ -619,7 +606,6 @@ public class ClientApi - //=================// // DEBUG USE // //=================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 309352e09..41967ef6e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -61,7 +61,7 @@ public class ServerApi { try { - IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld(); if (serverWorld != null) { serverWorld.serverTick(); @@ -152,7 +152,7 @@ public class ServerApi return; } - IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld(); LOGGER.info("Player ["+player.getName()+"] joined."); if (serverWorld != null) { @@ -166,7 +166,7 @@ public class ServerApi return; } - IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld(); LOGGER.info("Player ["+player.getName()+"] disconnected."); if (serverWorld != null) { @@ -180,7 +180,7 @@ public class ServerApi return; } - IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld(); LOGGER.info("Player ["+player.getName()+"] changed level: ["+originLevel.getKeyedLevelDimensionName()+"] -> ["+destinationLevel.getKeyedLevelDimensionName()+"]."); if (serverWorld != null) { @@ -200,7 +200,7 @@ public class ServerApi return; } - IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld(); if (serverWorld != null) { serverWorld.getServerPlayerStateManager().handlePluginMessage(player, message); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index a7c3943dc..25da9cfab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -153,12 +153,14 @@ public class SharedApi @Nullable public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; } - /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */ - public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld instanceof DhClientServerWorld) ? (DhClientServerWorld) currentWorld : null; } + /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientWorld} or {@link DhClientServerWorld} */ - public static IDhClientWorld getIDhClientWorld() { return (currentWorld instanceof IDhClientWorld) ? (IDhClientWorld) currentWorld : null; } + @Nullable + public static IDhClientWorld tryGetDhClientWorld() { return (currentWorld instanceof IDhClientWorld) ? (IDhClientWorld) currentWorld : null; } + /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhServerWorld} or {@link DhClientServerWorld} */ - public static IDhServerWorld getIDhServerWorld() { return (currentWorld instanceof IDhServerWorld) ? (IDhServerWorld) currentWorld : null; } + @Nullable + public static IDhServerWorld tryGetDhServerWorld() { return (currentWorld instanceof IDhServerWorld) ? (IDhServerWorld) currentWorld : null; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/rendering/RenderState.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/rendering/DhRenderState.java similarity index 98% rename from core/src/main/java/com/seibel/distanthorizons/core/api/internal/rendering/RenderState.java rename to core/src/main/java/com/seibel/distanthorizons/core/api/internal/rendering/DhRenderState.java index 2fcf3612e..fc1b6d691 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/rendering/RenderState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/rendering/DhRenderState.java @@ -9,7 +9,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp * * @see ClientApi */ -public class RenderState +public class DhRenderState { public Mat4f mcModelViewMatrix = null; public Mat4f mcProjectionMatrix = null; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 2ec91fdc9..e8849a472 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -1061,14 +1061,6 @@ public class Config + "") .build(); - public static ConfigEntry validateBufferIdsBeforeRendering = new ConfigEntry.Builder() - .set(false) - .comment("" - + "Massively reduces FPS. \n" - + "Should only be used if mysterious EXCEPTION_ACCESS_VIOLATION crashes are happening in DH's rendering code for troubleshooting. \n" - + "") - .build(); - } public static class ColumnBuilderDebugging diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 5439d2946..fe7f256ea 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -55,14 +55,15 @@ public class ColumnRenderBufferBuilder //==============// /** @link adjData should be null for adjacent sections that cross detail level boundaries */ - public static CompletableFuture uploadBuffersAsync( + public static CompletableFuture uploadBuffersAsync( IDhClientLevel clientLevel, long pos, LodQuadBuilder quadBuilder ) { - ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos))); - CompletableFuture uploadFuture = buffer.makeAndUploadBuffersAsync(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); + DhBlockPos minBlockPos = new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos)); + LodBufferContainer bufferContainer = new LodBufferContainer(pos, minBlockPos); + CompletableFuture uploadFuture = bufferContainer.makeAndUploadBuffersAsync(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); uploadFuture.whenComplete((uploadedBuffer, exception) -> { // clean up if not uploaded diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodBufferContainer.java similarity index 80% rename from core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java rename to core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodBufferContainer.java index 8bc21b436..4b5c92c89 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodBufferContainer.java @@ -19,13 +19,11 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; -import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.StatsMap; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; @@ -40,7 +38,7 @@ import java.util.concurrent.*; * * @see ColumnRenderBufferBuilder */ -public class ColumnRenderBuffer implements AutoCloseable +public class LodBufferContainer implements AutoCloseable { private static final DhLogger LOGGER = new DhLoggerBuilder().build(); @@ -54,13 +52,14 @@ public class ColumnRenderBuffer implements AutoCloseable /** the position closest to minimum X/Z infinity and the level's lowest Y */ public final DhBlockPos minCornerBlockPos; + public final long pos; public boolean buffersUploaded = false; - private GLVertexBuffer[] vbos; - private GLVertexBuffer[] vbosTransparent; + public GLVertexBuffer[] vbos; + public GLVertexBuffer[] vbosTransparent; - private CompletableFuture uploadFuture = null; + private CompletableFuture uploadFuture = null; @@ -68,8 +67,9 @@ public class ColumnRenderBuffer implements AutoCloseable // constructors // //==============// - public ColumnRenderBuffer(DhBlockPos minCornerBlockPos) + public LodBufferContainer(long pos, DhBlockPos minCornerBlockPos) { + this.pos = pos; this.minCornerBlockPos = minCornerBlockPos; this.vbos = new GLVertexBuffer[0]; this.vbosTransparent = new GLVertexBuffer[0]; @@ -82,10 +82,10 @@ public class ColumnRenderBuffer implements AutoCloseable //==================// /** Should be run on a DH thread. */ - public synchronized CompletableFuture makeAndUploadBuffersAsync(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) + public synchronized CompletableFuture makeAndUploadBuffersAsync(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) { // separate variable to prevent race condition when checking null - CompletableFuture future = this.uploadFuture; + CompletableFuture future = this.uploadFuture; if (future != null) { // upload already in process @@ -222,69 +222,6 @@ public class ColumnRenderBuffer implements AutoCloseable - //========// - // render // - //========// - - /** @return true if something was rendered, false otherwise */ - public boolean renderOpaque(LodRenderer renderContext, DhApiRenderParam renderEventParam) - { - boolean hasRendered = false; - renderContext.setModelViewMatrixOffset(this.minCornerBlockPos, renderEventParam); - for (GLVertexBuffer vbo : this.vbos) - { - if (vbo == null) - { - continue; - } - - if (vbo.getVertexCount() == 0) - { - continue; - } - - hasRendered = true; - renderContext.drawVbo(vbo, this); - } - return hasRendered; - } - - /** @return true if something was rendered, false otherwise */ - public boolean renderTransparent(LodRenderer renderContext, DhApiRenderParam renderEventParam) - { - boolean hasRendered = false; - - try - { - // can throw an IllegalStateException if the GL program was freed before it should've been - renderContext.setModelViewMatrixOffset(this.minCornerBlockPos, renderEventParam); - - for (GLVertexBuffer vbo : this.vbosTransparent) - { - if (vbo == null) - { - continue; - } - - if (vbo.getVertexCount() == 0) - { - continue; - } - - hasRendered = true; - renderContext.drawVbo(vbo, this); - } - } - catch (IllegalStateException e) - { - LOGGER.error("renderContext program doesn't exist for pos: "+this.minCornerBlockPos, e); - } - - return hasRendered; - } - - - //================// // helper methods // //================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java index c6b058706..b55392db6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java @@ -280,7 +280,7 @@ public class LodQuadBuilder // create a new buffer if (buffer == null || !buffer.hasRemaining()) { - buffer = MemoryUtil.memAlloc(ColumnRenderBuffer.FULL_SIZED_BUFFER); + buffer = MemoryUtil.memAlloc(LodBufferContainer.FULL_SIZED_BUFFER); byteBufferList.add(buffer); } @@ -451,7 +451,7 @@ public class LodQuadBuilder } /** Returns how many GpuBuffers will be needed to render opaque quads in this builder. */ - public int getCurrentNeededOpaqueVertexBufferCount() { return MathUtil.ceilDiv(this.getCurrentOpaqueQuadsCount(), ColumnRenderBuffer.MAX_QUADS_PER_BUFFER); } + public int getCurrentNeededOpaqueVertexBufferCount() { return MathUtil.ceilDiv(this.getCurrentOpaqueQuadsCount(), LodBufferContainer.MAX_QUADS_PER_BUFFER); } /** Returns how many GpuBuffers will be needed to render transparent quads in this builder. */ public int getCurrentNeededTransparentVertexBufferCount() { @@ -460,7 +460,7 @@ public class LodQuadBuilder return 0; } - return MathUtil.ceilDiv(this.getCurrentTransparentQuadsCount(), ColumnRenderBuffer.MAX_QUADS_PER_BUFFER); + return MathUtil.ceilDiv(this.getCurrentTransparentQuadsCount(), LodBufferContainer.MAX_QUADS_PER_BUFFER); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/PregenManager.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/PregenManager.java index d1bd752a9..c8f368419 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/PregenManager.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/PregenManager.java @@ -38,7 +38,7 @@ public class PregenManager ) { PregenState pregenState = new PregenState( - (GeneratedFullDataSourceProvider) SharedApi.getIDhServerWorld().getLevel(levelWrapper).getFullDataProvider(), + (GeneratedFullDataSourceProvider) SharedApi.tryGetDhServerWorld().getLevel(levelWrapper).getFullDataProvider(), DhSectionPos.convertToDetailLevel( DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, origin.x, origin.z), DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index 97cc9a0e5..7c49c6efe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.logging.f3; +import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -110,6 +111,13 @@ public class F3Screen messageList.add("Build: " + StringUtil.shortenString(ModJarInfo.Git_Commit, 8) + " (" + ModJarInfo.Git_Branch + ")"); } + // render validation error + if (ClientApi.INSTANCE.lastRenderParamValidationMessage != null) + { + messageList.add("Render Validation Err: " + ClientApi.INSTANCE.lastRenderParamValidationMessage); + } + + // player pos if (Config.Client.Advanced.Debugging.F3Screen.showPlayerPos.get()) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index b481f638a..f9bd34f86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -24,7 +24,7 @@ import com.google.common.cache.CacheBuilder; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.render.CachedColumnRenderSource; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; -import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.level.IDhClientLevel; @@ -44,7 +44,6 @@ import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.coreapi.util.MathUtil; import it.unimi.dsi.fastutil.longs.LongIterator; -import com.seibel.distanthorizons.core.logging.DhLogger; import org.jetbrains.annotations.Nullable; import javax.annotation.WillNotClose; @@ -353,11 +352,11 @@ public class LodQuadTree extends QuadTree implements IDebugRen // onRenderDisabled doesn't need to be // called since these sections shouldn't be loaded parentRenderSection.setRenderingEnabled(false); - ColumnRenderBuffer buffer = parentRenderSection.renderBuffer; + LodBufferContainer buffer = parentRenderSection.bufferContainer; if (buffer != null) { buffer.close(); - parentRenderSection.renderBuffer = null; + parentRenderSection.bufferContainer = null; } } } @@ -398,7 +397,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen // prepare this section for rendering if (!renderSection.gpuUploadInProgress() - && renderSection.renderBuffer == null + && renderSection.bufferContainer == null // TODO this is commented out since some users reported LODs refusing to // load at their expected higher-detail levels // this check is specifically for N-sized world generators where the higher quality @@ -518,7 +517,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen for (int i = 0; i < loadSectionList.size(); i++) { LodRenderSection renderSection = loadSectionList.get(i); - if (!renderSection.gpuUploadInProgress() && renderSection.renderBuffer == null) + if (!renderSection.gpuUploadInProgress() && renderSection.bufferContainer == null) { renderSection.uploadRenderDataToGpuAsync(); } @@ -768,14 +767,14 @@ public class LodQuadTree extends QuadTree implements IDebugRen { color = Color.ORANGE; } - else if (renderSection.renderBuffer == null) + else if (renderSection.bufferContainer == null) { // uploaded but the buffer is missing color = Color.PINK; } - else if (renderSection.renderBuffer.hasNonNullVbos()) + else if (renderSection.bufferContainer.hasNonNullVbos()) { - if (renderSection.renderBuffer.vboBufferCount() != 0) + if (renderSection.bufferContainer.vboBufferCount() != 0) { color = Color.GREEN; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index ed4914d59..494cb7c66 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -39,7 +39,7 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; -import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler; import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; @@ -50,7 +50,6 @@ import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import it.unimi.dsi.fastutil.longs.LongArrayList; -import com.seibel.distanthorizons.core.logging.DhLogger; import org.jetbrains.annotations.Nullable; import javax.annotation.WillNotClose; @@ -97,7 +96,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable private boolean renderingEnabled = false; /** this reference is necessary so we can determine what VBO to render */ - public ColumnRenderBuffer renderBuffer; + public LodBufferContainer bufferContainer; /** @@ -119,7 +118,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable * Separate from {@link LodRenderSection#getAndBuildRenderDataFuture} because they run on * different threads (buffer uploading is on the MC render thread) and need to be canceled separately. */ - private CompletableFuture bufferUploadFuture = null; + private CompletableFuture bufferUploadFuture = null; /** * should be an empty array if no positions need to be generated @@ -400,15 +399,15 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable this.bufferUploadFuture.thenAccept((buffer) -> { // needed to clean up the old data - ColumnRenderBuffer previousBuffer = this.renderBuffer; + LodBufferContainer previousContainer = this.bufferContainer; // upload complete - this.renderBuffer = buffer.buffersUploaded ? buffer : null; + this.bufferContainer = buffer.buffersUploaded ? buffer : null; this.getAndBuildRenderDataFuture = null; - if (previousBuffer != null) + if (previousContainer != null) { - previousBuffer.close(); + previousContainer.close(); } }); } @@ -419,7 +418,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable // getters and properties // //========================// - public boolean canRender() { return this.renderBuffer != null; } + public boolean canRender() { return this.bufferContainer != null; } public boolean getRenderingEnabled() { return this.renderingEnabled; } /** @@ -683,9 +682,9 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable this.stopRenderingBeacons(); - if (this.renderBuffer != null) + if (this.bufferContainer != null) { - this.renderBuffer.close(); + this.bufferContainer.close(); } // removes any in-progress futures since they aren't needed any more @@ -705,7 +704,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } } - CompletableFuture uploadFuture = this.bufferUploadFuture; + CompletableFuture uploadFuture = this.bufferUploadFuture; if (uploadFuture != null) { uploadFuture.cancel(true); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java index 2ec76d017..d058652b3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java @@ -24,10 +24,9 @@ import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullin import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShadowCullingFrustum; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; @@ -35,8 +34,7 @@ import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.Pos2D; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.objects.RollingAverage; +import com.seibel.distanthorizons.core.render.renderer.RenderParams; import com.seibel.distanthorizons.core.util.objects.SortedArraySet; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; @@ -46,20 +44,15 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector; import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3f; -import com.seibel.distanthorizons.core.logging.DhLogger; +import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.joml.Matrix4fc; -import org.lwjgl.opengl.GL32; import java.util.Comparator; import java.util.Iterator; -import java.util.ListIterator; -import java.util.concurrent.atomic.AtomicBoolean; /** * This object tells the {@link LodRenderer} what buffers to render - * TODO rename this class, maybe RenderBufferOrganizer or something more specific? */ public class RenderBufferHandler implements AutoCloseable { @@ -74,7 +67,8 @@ public class RenderBufferHandler implements AutoCloseable public final LodQuadTree lodQuadTree; // 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 loadedNearToFarBuffers = null; + @Nullable + private SortedArraySet loadedNearToFarBuffers = null; private int visibleBufferCount; private int culledBufferCount; @@ -113,71 +107,14 @@ public class RenderBufferHandler implements AutoCloseable /** * The following buildRenderList sorting method is based on the following reddit post:
- * correct_depth_ordering_for_translucent_discrete

- * - * TODO: This might get locked by update() causing move() call. Is there a way to avoid this? - * Maybe dupe the base list and use atomic swap on render? Or is this not worth it? + * correct_depth_ordering_for_translucent_discrete */ - public void buildRenderListAndUpdateSections(IClientLevelWrapper clientLevelWrapper, DhApiRenderParam renderEventParam, Vec3f lookForwardVector) + public void buildRenderList(RenderParams renderParams) { - EDhDirection[] axisDirections = new EDhDirection[3]; - - // Do the axis that are the longest first (i.e. the largest absolute value of the lookForwardVector), - // with the sign being the opposite of the respective lookForwardVector component's sign - float absX = Math.abs(lookForwardVector.x); - float absY = Math.abs(lookForwardVector.y); - float absZ = Math.abs(lookForwardVector.z); - EDhDirection xDir = lookForwardVector.x < 0 ? EDhDirection.EAST : EDhDirection.WEST; - EDhDirection yDir = lookForwardVector.y < 0 ? EDhDirection.UP : EDhDirection.DOWN; - EDhDirection zDir = lookForwardVector.z < 0 ? EDhDirection.SOUTH : EDhDirection.NORTH; - - if (absX >= absY && absX >= absZ) - { - axisDirections[0] = xDir; - if (absY >= absZ) - { - axisDirections[1] = yDir; - axisDirections[2] = zDir; - } - else - { - axisDirections[1] = zDir; - axisDirections[2] = yDir; - } - } - else if (absY >= absX && absY >= absZ) - { - axisDirections[0] = yDir; - if (absX >= absZ) - { - axisDirections[1] = xDir; - axisDirections[2] = zDir; - } - else - { - axisDirections[1] = zDir; - axisDirections[2] = xDir; - } - } - else - { - axisDirections[0] = zDir; - if (absX >= absY) - { - axisDirections[1] = xDir; - axisDirections[2] = yDir; - } - else - { - axisDirections[1] = yDir; - axisDirections[2] = xDir; - } - } - Pos2D centerPos = this.lodQuadTree.getCenterBlockPos().toPos2D(); // Now that we have the axis directions, we can sort the render list - Comparator farToNearComparator = (loadedBufferA, loadedBufferB) -> + Comparator farToNearComparator = (loadedBufferA, loadedBufferB) -> { Pos2D aPos = DhSectionPos.getCenterBlockPos(loadedBufferA.pos).toPos2D(); Pos2D bPos = DhSectionPos.getCenterBlockPos(loadedBufferB.pos).toPos2D(); @@ -186,7 +123,9 @@ public class RenderBufferHandler implements AutoCloseable int bManhattanDistance = bPos.manhattanDist(centerPos); return bManhattanDistance - aManhattanDistance; }; - this.loadedNearToFarBuffers = new SortedArraySet<>((a, b) -> -farToNearComparator.compare(a, b)); // TODO is the comparator named wrong? + + // TODO is the comparator named wrong? + this.loadedNearToFarBuffers = new SortedArraySet<>((a, b) -> -farToNearComparator.compare(a, b)); @@ -213,17 +152,20 @@ public class RenderBufferHandler implements AutoCloseable // update the frustum if necessary if (enableFrustumCulling) { - int worldMinY = clientLevelWrapper.getMinHeight(); - int worldHeight = clientLevelWrapper.getMaxHeight(); + int worldMinY = renderParams.clientLevelWrapper.getMinHeight(); + int worldHeight = renderParams.clientLevelWrapper.getMaxHeight(); Vec3d cameraPos = MC_RENDER.getCameraExactPosition(); Matrix4fc matWorldView = new Matrix4f() - .setTransposed(renderEventParam.mcModelViewMatrix.getValuesAsArray()) - .translate(-(float) cameraPos.x, -(float) cameraPos.y, -(float) cameraPos.z); + .setTransposed(renderParams.mcModelViewMatrix.getValuesAsArray()) + .translate( + -(float) cameraPos.x, + -(float) cameraPos.y, + -(float) cameraPos.z); Matrix4fc matWorldViewProjection = new Matrix4f() - .setTransposed(renderEventParam.dhProjectionMatrix.getValuesAsArray()) + .setTransposed(renderParams.dhProjectionMatrix.getValuesAsArray()) .mul(matWorldView); frustum.update(worldMinY, worldMinY + worldHeight, new Mat4f(matWorldViewProjection)); @@ -308,14 +250,14 @@ public class RenderBufferHandler implements AutoCloseable try { - ColumnRenderBuffer buffer = renderSection.renderBuffer; - if (buffer == null + LodBufferContainer bufferContainer = renderSection.bufferContainer; + if (bufferContainer == null || !renderSection.getRenderingEnabled()) { continue; } - this.loadedNearToFarBuffers.add(new LoadedRenderBuffer(buffer, sectionPos)); + this.loadedNearToFarBuffers.add(bufferContainer); } catch (Exception e) { @@ -339,71 +281,7 @@ public class RenderBufferHandler implements AutoCloseable // render methods // //================// - public void renderOpaque(LodRenderer renderContext,DhApiRenderParam renderEventParam) - { this.renderPass(renderContext, renderEventParam, true); } - public void renderTransparent(LodRenderer renderContext, DhApiRenderParam renderEventParam) - { this.renderPass(renderContext, renderEventParam, false); } - - private void renderPass(LodRenderer renderContext, DhApiRenderParam renderEventParam, boolean opaquePass) - { - //=======================// - // debug wireframe setup // - //=======================// - - // TODO move this logic into LodRenderer so all the GL states can be handled there - boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); - if (renderWireframe) - { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); - GLMC.disableFaceCulling(); - } - else - { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); - GLMC.enableFaceCulling(); - } - - - //===========// - // rendering // - //===========// - - if (opaquePass) - { - // TODO why can these sometimes be null when teleporting between multiverses - if (this.loadedNearToFarBuffers != null) - { - this.loadedNearToFarBuffers.forEach(loadedBuffer -> loadedBuffer.buffer.renderOpaque(renderContext, renderEventParam)); - } - } - else - { - // TODO why can these sometimes be null when teleporting between multiverses - if (this.loadedNearToFarBuffers != null) - { - ListIterator iter = this.loadedNearToFarBuffers.listIterator(this.loadedNearToFarBuffers.size()); - while (iter.hasPrevious()) - { - LoadedRenderBuffer loadedBuffer = iter.previous(); - loadedBuffer.buffer.renderTransparent(renderContext, renderEventParam); - } - } - } - - - - //=========================// - // debug wireframe cleanup // - //=========================// - - if (renderWireframe) - { - // default back to GL_FILL since all other rendering uses it - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); - GLMC.enableFaceCulling(); - } - - } + public SortedArraySet getColumnRenderBuffers() { return this.loadedNearToFarBuffers; } @@ -447,21 +325,4 @@ public class RenderBufferHandler implements AutoCloseable - //================// - // helper classes // - //================// - - private static class LoadedRenderBuffer - { - public final ColumnRenderBuffer buffer; - public final long pos; - - LoadedRenderBuffer(ColumnRenderBuffer buffer, long pos) - { - this.buffer = buffer; - this.pos = pos; - } - - } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 6c4e72212..3fee10b5c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -19,16 +19,14 @@ 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.*; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; 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; @@ -41,21 +39,19 @@ import com.seibel.distanthorizons.core.render.glObject.texture.*; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.render.renderer.shaders.*; import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.objects.SortedArraySet; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; -import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3f; +import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL32; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; - /** * This is where all the magic happens.
* This is where LODs are draw to the world. @@ -82,20 +78,18 @@ public class LodRenderer private int activeFramebufferId = -1; private int activeColorTextureId = -1; private int activeDepthTextureId = -1; - private int cachedWidth; - private int cachedHeight; - - private final ReentrantLock setupLock = new ReentrantLock(); + private int textureWidth; + private int textureHeight; - // The shader program private IDhApiShaderProgram lodRenderProgram = null; public QuadElementBuffer quadIBO = null; - private boolean isSetupComplete = false; + private boolean renderObjectsCreated = false; // frameBuffer and texture ID's for this renderer private IDhApiFramebuffer framebuffer; /** will be null if MC's framebuffer is being used since MC already has a color texture */ + @Nullable private DhColorTexture nullableColorTexture; private DHDepthTexture depthTexture; /** @@ -110,10 +104,7 @@ public class LodRenderer // constructor // //=============// - private LodRenderer() - { - - } + private LodRenderer() { } @@ -126,16 +117,8 @@ public class LodRenderer * {@link DhApiRenderProxy#getDeferTransparentRendering()} is false, * otherwise it will only render opaque LODs. */ - public void render( - IDhClientLevel clientLevel, IClientLevelWrapper clientLevelWrapper, - DhApiRenderParam renderEventParam, IProfilerWrapper profiler) - { - this.renderLodPass( - clientLevel, clientLevelWrapper, - renderEventParam, - profiler, - false); - } + public void render(RenderParams renderParams, IProfilerWrapper profiler) + { this.renderLodPass(renderParams, profiler, false); } /** * This method is designed for Iris to be able @@ -143,59 +126,32 @@ public class LodRenderer * It needs to be updated with any major changes, * but shouldn't be activated as per deferWaterRendering. */ - public void renderDeferred( - IDhClientLevel clientLevel, IClientLevelWrapper clientLevelWrapper, - DhApiRenderParam renderEventParam, IProfilerWrapper profiler) - { - this.renderLodPass( - clientLevel, clientLevelWrapper, - renderEventParam, - profiler, - true); - } + public void renderDeferred(RenderParams renderParams, IProfilerWrapper profiler) + { this.renderLodPass(renderParams, profiler, true); } - private void renderLodPass( - IDhClientLevel clientLevel, IClientLevelWrapper clientLevelWrapper, - DhApiRenderParam renderEventParam, - IProfilerWrapper profiler, - boolean runningDeferredPass) + private void renderLodPass(RenderParams renderParams, IProfilerWrapper profiler, boolean runningDeferredPass) { //====================// // validate rendering // //====================// boolean deferTransparentRendering = DhApiRenderProxy.INSTANCE.getDeferTransparentRendering(); - if (runningDeferredPass && !deferTransparentRendering) + if (runningDeferredPass + && !deferTransparentRendering) { return; } boolean firstPass = !runningDeferredPass; - if (AbstractOptifineAccessor.optifinePresent() - && MC_RENDER.getTargetFrameBuffer() == -1) + // RenderParams parameter validation should be done before this + if (!renderParams.validationRun) { - // wait for MC to finish setting up their renderer - return; + throw new IllegalArgumentException("Render parameters validation"); } - 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) - { - return; - } + RenderBufferHandler renderBufferHandler = renderParams.renderBufferHandler; + GenericObjectRenderer genericRenderer = renderParams.genericRenderer; + ILightMapWrapper lightmap = renderParams.lightmap; @@ -203,22 +159,39 @@ public class LodRenderer // rendering setup // //=================// - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderSetupEvent.class, renderEventParam); - this.setupGLStateAndRenderObjects(profiler, renderEventParam, firstPass); - if (!this.isSetupComplete) + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderSetupEvent.class, renderParams); + profiler.push("LOD GL setup"); + + if (!this.renderObjectsCreated) { - // this shouldn't normally happen, but just in case - return; + boolean setupSuccess = this.createRenderObjects(); + if (!setupSuccess) + { + // shouldn't normally happen, but just in case + return; + } + + this.renderObjectsCreated = true; } + this.setGLState(renderParams, firstPass); + lightmap.bind(); this.quadIBO.bind(); if (firstPass) { - renderBufferHandler.buildRenderListAndUpdateSections(clientLevelWrapper, renderEventParam, MC_RENDER.getLookAtVector()); + // we only need to sort/cull the LODs during the first frame + profiler.popPush("LOD build render list"); + renderBufferHandler.buildRenderList(renderParams); } + IDhApiShaderProgram lodShaderProgram = this.lodRenderProgram; + IDhApiShaderProgram lodShaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); + if (lodShaderProgramOverride != null && lodShaderProgram.overrideThisFrame()) + { + lodShaderProgram = lodShaderProgramOverride; + } @@ -228,68 +201,57 @@ public class LodRenderer if (!runningDeferredPass) { - //=================================// - // opaque (non-deferred) rendering // - //=================================// + //=========================// + // opaque and non-deferred // + // transparent rendering // + //=========================// - - // Disable blending for opaque rendering - GLMC.disableBlend(); - - - // terrain + // opaque LODs profiler.popPush("LOD Opaque"); - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); - - - renderBufferHandler.renderOpaque(this, renderEventParam); - - + this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ true); // custom objects with SSAO if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get()) { profiler.popPush("Custom Objects"); - genericRenderer.render(renderEventParam, profiler, true); + genericRenderer.render(renderParams, profiler, true); } - // SSAO if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get()) { profiler.popPush("LOD SSAO"); - SSAORenderer.INSTANCE.render(new Mat4f(renderEventParam.dhProjectionMatrix), renderEventParam.partialTicks); + SSAORenderer.INSTANCE.render(new Mat4f(renderParams.dhProjectionMatrix), renderParams.partialTicks); } - // custom objects without SSAO if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get()) { profiler.popPush("Custom Objects"); - genericRenderer.render(renderEventParam, profiler, false); + genericRenderer.render(renderParams, profiler, false); } - + // combined pass transparent rendering if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) { - this.renderTransparentBuffersAndFireApiEvent(profiler, renderEventParam, renderBufferHandler); + profiler.popPush("LOD Transparent"); + this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ false); } - + // fog if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()) { profiler.popPush("LOD Fog"); - Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); - combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); + Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix); + combinedMatrix.multiply(renderParams.dhModelViewMatrix); - FogRenderer.INSTANCE.render(combinedMatrix, renderEventParam.partialTicks); + FogRenderer.INSTANCE.render(combinedMatrix, renderParams.partialTicks); } - //=================// // debug rendering // //=================// @@ -298,16 +260,18 @@ public class LodRenderer { profiler.popPush("Debug wireframes"); - Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); - combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); + Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix); + combinedMatrix.multiply(renderParams.dhModelViewMatrix); // Note: this can be very slow if a lot of boxes are being rendered DebugRenderer.INSTANCE.render(combinedMatrix); } - profiler.popPush("LOD cleanup"); + //===================// + // optifine clean up // + //===================// if (this.usingMcFrameBuffer) { @@ -322,13 +286,13 @@ public class LodRenderer // Apply to the MC FrameBuffer // //=============================// - boolean cancelApplyShader = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeApplyShaderRenderEvent.class, renderEventParam); + boolean cancelApplyShader = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeApplyShaderRenderEvent.class, renderParams); if (!cancelApplyShader) { profiler.popPush("LOD Apply"); // Copy the LOD framebuffer to Minecraft's framebuffer - DhApplyShader.INSTANCE.render(renderEventParam.partialTicks); + DhApplyShader.INSTANCE.render(renderParams.partialTicks); } } else @@ -340,18 +304,17 @@ public class LodRenderer if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) { profiler.popPush("LOD Transparent"); - - this.renderTransparentBuffersAndFireApiEvent(profiler, renderEventParam, renderBufferHandler); + this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ false); if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()) { profiler.popPush("LOD Fog"); - Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); - combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); + Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix); + combinedMatrix.multiply(renderParams.dhModelViewMatrix); - FogRenderer.INSTANCE.render(combinedMatrix, renderEventParam.partialTicks); + FogRenderer.INSTANCE.render(combinedMatrix, renderParams.partialTicks); } } } @@ -363,154 +326,47 @@ public class LodRenderer //================// profiler.popPush("LOD cleanup"); - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderCleanupEvent.class, renderEventParam); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderCleanupEvent.class, renderParams); 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(); + lodShaderProgram.unbind(); // end of internal LOD profiling profiler.pop(); } - private void renderTransparentBuffersAndFireApiEvent( - IProfilerWrapper profiler, - DhApiRenderParam renderEventParam, - RenderBufferHandler renderBufferHandler) - { - profiler.popPush("LOD Transparent"); - - GLMC.enableBlend(); - GLMC.enableDepthTest(); - GL32.glBlendEquation(GL32.GL_FUNC_ADD); - GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); - - renderBufferHandler.renderTransparent(this, renderEventParam); - } - - /** called by each {@link ColumnRenderBuffer} before rendering */ - public void setModelViewMatrixOffset(DhBlockPos pos, DhApiRenderParam renderEventParam) throws IllegalStateException - { - Vec3d cam = MC_RENDER.getCameraExactPosition(); - Vec3f modelPos = new Vec3f((float) (pos.getX() - cam.x), (float) (pos.getY() - cam.y), (float) (pos.getZ() - cam.z)); - - - IDhApiShaderProgram shaderProgram = this.lodRenderProgram; - IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) - { - shaderProgram = shaderProgramOverride; - } - - shaderProgram.bind(); - shaderProgram.setModelOffsetPos(modelPos); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); - } - - public void drawVbo(GLVertexBuffer vbo, ColumnRenderBuffer parentBufferContainer) - { - // this should only be enabled for debugging - if (Config.Client.Advanced.Debugging.OpenGl.validateBufferIdsBeforeRendering.get()) - { - // this is a fairly slow call and enabling it will reduce FPS significantly - if (!GL32.glIsBuffer(vbo.getId())) - { - if (RATE_LIMITED_LOGGER.canLog()) // can log check to prevent creating a bunch of strings unnecessarily - { - RATE_LIMITED_LOGGER.warn("Attempted to draw invalid buffer: [" + vbo.getId() + "], expected size: ["+vbo.getSize()+"], upload complete: [" + parentBufferContainer.buffersUploaded + "], upload in progress: [" + parentBufferContainer.uploadInProgress() + "], buffer blockPos: ["+parentBufferContainer.minCornerBlockPos +"]."); - } - return; - } - } - - - IDhApiShaderProgram shaderProgram = this.lodRenderProgram; - IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) - { - shaderProgram = shaderProgramOverride; - } - - - vbo.bind(); - shaderProgram.bindVertexBuffer(vbo.getId()); - GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? - this.quadIBO.getType(), 0); - vbo.unbind(); - } - - //=================// // Setup Functions // //=================// - private void setupGLStateAndRenderObjects( - IProfilerWrapper profiler, + private void setGLState( DhApiRenderParam renderEventParam, boolean firstPass) { //===================// - // draw params setup // + // framebuffer setup // //===================// - profiler.push("LOD draw setup"); - - if (!this.isSetupComplete) - { - this.setupRenderObjects(); - - // shouldn't normally happen, but just in case - if (!this.isSetupComplete) - { - return; - } - } - - 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 - this.createColorAndDepthTextures(); - } - - - IDhApiFramebuffer activeFrameBuffer = this.framebuffer; + // get the active framebuffer + IDhApiFramebuffer frameBuffer = this.framebuffer; IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class); if (framebufferOverride != null && framebufferOverride.overrideThisFrame()) { - activeFrameBuffer = framebufferOverride; + frameBuffer = framebufferOverride; } + this.activeFramebufferId = frameBuffer.getId(); + frameBuffer.bind(); - this.activeFramebufferId = activeFrameBuffer.getId(); - this.activeDepthTextureId = this.depthTexture.getTextureId(); - if (this.nullableColorTexture != null) - { - 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); - this.activeColorTextureId = mcColorTextureId; - } - // Bind LOD frame buffer - activeFrameBuffer.bind(); + //==========// + // bindings // + //==========// + // by default draw everything as triangles GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); GLMC.enableFaceCulling(); @@ -527,21 +383,16 @@ public class LodRenderer // This is required for MC versions 1.21.5+ // due to MC updating the lightmap by changing the viewport size - GL32.glViewport(0, 0, this.cachedWidth, this.cachedHeight); + GL32.glViewport(0, 0, this.textureWidth, this.textureHeight); - /*---------Bind required objects--------*/ - // Setup LodRenderProgram and the LightmapTexture if it has not yet been done - // also binds LightmapTexture, VAO, and ShaderProgram - if (!this.isSetupComplete) - { - this.setupRenderObjects(); - } - else - { - this.lodRenderProgram.bind(); - } + this.lodRenderProgram.bind(); + + //==========// + // uniforms // + //==========// + IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); if (shaderProgramOverride != null) { @@ -551,6 +402,37 @@ public class LodRenderer this.lodRenderProgram.fillUniformData(renderEventParam); + + + //===============// + // texture setup // + //===============// + + // resize the textures if needed + if (MC_RENDER.getTargetFrameBufferViewportWidth() != this.textureWidth + || MC_RENDER.getTargetFrameBufferViewportHeight() != this.textureHeight) + { + // just resizing the textures doesn't work when Optifine is present, + // so recreate the textures with the new size instead + this.createAndBindTextures(); + } + + + // set the active textures + this.activeDepthTextureId = this.depthTexture.getTextureId(); + + if (this.nullableColorTexture != null) + { + 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); + this.activeColorTextureId = mcColorTextureId; + } + + // needs to be fired after all the textures have been created/bound boolean clearTextures = !ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeTextureClearEvent.class, renderEventParam); if (clearTextures) @@ -565,7 +447,7 @@ public class LodRenderer { // 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 - activeFrameBuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); + frameBuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); // don't clear the color texture, that removes the sky @@ -576,92 +458,88 @@ public class LodRenderer GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); } } + + } - /** Setup all render objects - MUST be called on the render thread */ - private void setupRenderObjects() + private boolean createRenderObjects() { - if (this.isSetupComplete) + if (this.renderObjectsCreated) { LOGGER.warn("Renderer setup called but it has already completed setup!"); - return; + 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; + return false; } - try + + + LOGGER.info("Setting up renderer"); + this.lodRenderProgram = new DhTerrainShaderProgram(); + + this.quadIBO = new QuadElementBuffer(); + this.quadIBO.reserve(LodBufferContainer.MAX_QUADS_PER_BUFFER); + + + // create or get the frame buffer + if (AbstractOptifineAccessor.optifinePresent()) { - this.setupLock.lock(); - - - LOGGER.info("Setting up renderer"); - this.lodRenderProgram = new DhTerrainShaderProgram(); - - this.quadIBO = new QuadElementBuffer(); - this.quadIBO.reserve(ColumnRenderBuffer.MAX_QUADS_PER_BUFFER); - - - // create or get the frame buffer - if (AbstractOptifineAccessor.optifinePresent()) - { - // use MC/Optifine's default FrameBuffer so shaders won't remove the LODs - int currentFrameBufferId = MC_RENDER.getTargetFrameBuffer(); - this.framebuffer = new DhFramebuffer(currentFrameBufferId); - this.usingMcFrameBuffer = true; - } - else - { - // normal use case - this.framebuffer = new DhFramebuffer(); - this.usingMcFrameBuffer = false; - } - - // create and bind the necessary textures - this.createColorAndDepthTextures(); - - if(this.framebuffer.getStatus() != GL32.GL_FRAMEBUFFER_COMPLETE) - { - // This generally means something wasn't bound, IE missing either the color or depth texture - LOGGER.warn("FrameBuffer ["+this.framebuffer.getId()+"] isn't complete."); - return; - } - - - this.isSetupComplete = true; - LOGGER.info("Renderer setup complete"); + // use MC/Optifine's default FrameBuffer so shaders won't remove the LODs + int currentFrameBufferId = MC_RENDER.getTargetFrameBuffer(); + this.framebuffer = new DhFramebuffer(currentFrameBufferId); + this.usingMcFrameBuffer = true; } - finally + else { - this.setupLock.unlock(); + // normal use case + this.framebuffer = new DhFramebuffer(); + this.usingMcFrameBuffer = false; } + + // create and bind the necessary textures + this.createAndBindTextures(); + + if(this.framebuffer.getStatus() != GL32.GL_FRAMEBUFFER_COMPLETE) + { + // This generally means something wasn't bound, IE missing either the color or depth texture + LOGGER.warn("FrameBuffer ["+this.framebuffer.getId()+"] isn't complete."); + return false; + } + + + + LOGGER.info("Renderer setup complete"); + return true; } - /** also binds the new textures to the {@link LodRenderer#framebuffer} */ - private void createColorAndDepthTextures() + + private void createAndBindTextures() { - int oldWidth = this.cachedWidth; - int oldHeight = this.cachedHeight; - this.cachedWidth = MC_RENDER.getTargetFrameBufferViewportWidth(); - this.cachedHeight = MC_RENDER.getTargetFrameBufferViewportHeight(); + int oldWidth = this.textureWidth; + int oldHeight = this.textureHeight; + this.textureWidth = MC_RENDER.getTargetFrameBufferViewportWidth(); + this.textureHeight = MC_RENDER.getTargetFrameBufferViewportHeight(); DhApiTextureCreatedParam textureCreatedParam = new DhApiTextureCreatedParam( oldWidth, oldHeight, - this.cachedWidth, this.cachedHeight + this.textureWidth, this.textureHeight ); - + // DhApiColorDepthTextureCreatedEvent needs to be kept around since old versions of Iris need it ApiEventInjector.INSTANCE.fireAllEvents(DhApiColorDepthTextureCreatedEvent.class, new DhApiColorDepthTextureCreatedEvent.EventParam(textureCreatedParam)); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeColorDepthTextureCreatedEvent.class, textureCreatedParam); - - // also update the override if present + + // also update the framebuffer override if present IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class); - this.depthTexture = new DHDepthTexture(this.cachedWidth, this.cachedHeight, EDhDepthBufferFormat.DEPTH32F); + + this.depthTexture = new DHDepthTexture(this.textureWidth, this.textureHeight, EDhDepthBufferFormat.DEPTH32F); this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); if (framebufferOverride != null) { @@ -671,7 +549,7 @@ public class LodRenderer // if we are using MC's frame buffer, a color texture is already present and shouldn't need to be bound if (!this.usingMcFrameBuffer) { - this.nullableColorTexture = DhColorTexture.builder().setDimensions(this.cachedWidth, this.cachedHeight) + this.nullableColorTexture = DhColorTexture.builder().setDimensions(this.textureWidth, this.textureHeight) .setInternalFormat(EDhInternalTextureFormat.RGBA8) .setPixelType(EDhPixelType.UNSIGNED_BYTE) .setPixelFormat(EDhPixelFormat.RGBA) @@ -694,20 +572,129 @@ public class LodRenderer + //===============// + // LOD rendering // + //===============// + + private void renderLodPass(IDhApiShaderProgram shaderProgram, RenderBufferHandler lodBufferHandler, RenderParams renderEventParam, boolean opaquePass) + { + //=======================// + // debug wireframe setup // + //=======================// + + // TODO move this logic into LodRenderer so all the GL states can be handled there + boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); + if (renderWireframe) + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); + GLMC.disableFaceCulling(); + } + else + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + } + + if (!opaquePass) + { + GLMC.enableBlend(); + GLMC.enableDepthTest(); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + } + else + { + GLMC.disableBlend(); + } + + + + + //===========// + // rendering // + //===========// + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); + + SortedArraySet lodBufferContainer = lodBufferHandler.getColumnRenderBuffers(); + if (lodBufferContainer != null) + { + for (int lodIndex = 0; lodIndex < lodBufferContainer.size(); lodIndex++) + { + LodBufferContainer bufferContainer = lodBufferContainer.get(lodIndex); + this.setShaderProgramMvmOffset(bufferContainer.minCornerBlockPos, shaderProgram, renderEventParam); + + GLVertexBuffer[] vbos = opaquePass ? bufferContainer.vbos : bufferContainer.vbosTransparent; + for (int vboIndex = 0; vboIndex < vbos.length; vboIndex++) + { + GLVertexBuffer vbo = vbos[vboIndex]; + if (vbo == null) + { + continue; + } + + if (vbo.getVertexCount() == 0) + { + continue; + } + + vbo.bind(); + shaderProgram.bindVertexBuffer(vbo.getId()); + GL32.glDrawElements( + GL32.GL_TRIANGLES, + (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? + this.quadIBO.getType(), 0); + vbo.unbind(); + } + } + } + + + + //=========================// + // debug wireframe cleanup // + //=========================// + + if (renderWireframe) + { + // default back to GL_FILL since all other rendering uses it + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + } + + } + + /** + * the MVM offset is needed so LODs can be rendered anywhere in the MC world + * without running into floating point percision loss. + */ + private void setShaderProgramMvmOffset(DhBlockPos pos, IDhApiShaderProgram shaderProgram, RenderParams renderEventParam) throws IllegalStateException + { + Vec3d camPos = renderEventParam.exactCameraPosition; + Vec3f modelPos = new Vec3f( + (float) (pos.getX() - camPos.x), + (float) (pos.getY() - camPos.y), + (float) (pos.getZ() - camPos.z)); + + shaderProgram.bind(); + shaderProgram.setModelOffsetPos(modelPos); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); + } + + + //===============// // API functions // //===============// - /** Returns -1 if no frame buffer has been bound yet */ + /** @return -1 if no frame buffer has been bound yet */ public int getActiveFramebufferId() { return this.activeFramebufferId; } - /** Returns -1 if no texture has been bound yet */ + /** @return -1 if no texture has been bound yet */ 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 - */ + /** @return -1 if no texture has been bound yet */ public int getActiveDepthTextureId() { return this.activeDepthTextureId; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/RenderParams.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/RenderParams.java new file mode 100644 index 000000000..2706fbf3b --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/RenderParams.java @@ -0,0 +1,157 @@ +package com.seibel.distanthorizons.core.render.renderer; + +import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.core.api.internal.SharedApi; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.level.IDhClientLevel; +import com.seibel.distanthorizons.core.render.RenderBufferHandler; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.world.IDhClientWorld; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; + +/** + * An extension of {@link DhApiRenderParam} + * that allows additional validation and putting all + * rendering variables in a single place. + */ +public class RenderParams extends DhApiRenderParam +{ + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + + public IDhClientWorld dhClientWorld; + public IDhClientLevel dhClientLevel; + public IClientLevelWrapper clientLevelWrapper; + public ILightMapWrapper lightmap; + public RenderBufferHandler renderBufferHandler; + public GenericObjectRenderer genericRenderer; + public Vec3d exactCameraPosition; + + public boolean validationRun = false; + + + + //=============// + // constructor // + //=============// + + public RenderParams( + EDhApiRenderPass renderPass, + float newPartialTicks, + Mat4f newMcProjectionMatrix, Mat4f newMcModelViewMatrix, + IClientLevelWrapper clientLevelWrapper + ) + { + super(renderPass, + newPartialTicks, + RenderUtil.getNearClipPlaneDistanceInBlocks(newPartialTicks), RenderUtil.getFarClipPlaneDistanceInBlocks(), + newMcProjectionMatrix, newMcModelViewMatrix, + RenderUtil.createLodProjectionMatrix(newMcProjectionMatrix, newPartialTicks), RenderUtil.createLodModelViewMatrix(newMcModelViewMatrix), + clientLevelWrapper.getMinHeight()); + + + this.dhClientWorld = SharedApi.tryGetDhClientWorld(); + if (this.dhClientWorld != null) + { + // TODO changing to getOrLoadClientLevel() fixes Immersive Portals only rendering the level the user starts in + // however this may break how other level handling is done so James doesn't want to change it. + // Special handling may be necessary when Immersive Portals is present, although additional testing is needed. + this.dhClientLevel = (IDhClientLevel) this.dhClientWorld.getLevel(clientLevelWrapper); + if (this.dhClientLevel != null) + { + this.renderBufferHandler = this.dhClientLevel.getRenderBufferHandler(); + this.genericRenderer = this.dhClientLevel.getGenericRenderer(); + } + } + + this.clientLevelWrapper = clientLevelWrapper; + this.lightmap = MC_RENDER.getLightmapWrapper(this.clientLevelWrapper); + + if (MC_CLIENT.playerExists()) + { + this.exactCameraPosition = MC_RENDER.getCameraExactPosition(); + } + + } + + + + //======================// + // parameter validation // + //======================// + + /** + * Should be called before rendering is done. + * @return a message if LODs shouldn't be rendered, null if the LODs can render + */ + public String getValidationErrorMessage() + { + // Note: all strings here should be constants to prevent String allocations + + this.validationRun = true; + + + if (!MC_CLIENT.playerExists()) + { + return "No Player Exists"; + } + + if (this.dhClientWorld == null) + { + return "No DH Client World Loaded"; + } + + if (this.dhClientLevel == null) + { + return "No DH Client Level Loaded"; + } + + if (this.clientLevelWrapper == null) + { + return "No Client Level Wrapper Loaded"; + } + + if (this.lightmap == null) + { + return "No Lightmap Loaded"; + } + + if (this.renderBufferHandler == null) + { + return "No RenderBufferHandler Present"; + } + + if (this.genericRenderer == null) + { + return "No Generic Renderer Present"; + } + + if (this.dhModelViewMatrix == null + || this.mcModelViewMatrix == null) + { + return "No MVM or Proj Matrix Given"; + } + + if (AbstractOptifineAccessor.optifinePresent() + && MC_RENDER.getTargetFrameBuffer() == -1) + { + // wait for MC to finish setting up their renderer + return "Optifine Target Frame Buffer not set"; + } + + + return null; + } + + + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java index bcb287ef3..4ad9574dd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java @@ -34,10 +34,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; /** * This holds miscellaneous helper code - * to be used in the rendering process. - * - * @author James Seibel - * @version 2022-8-21 + * used in the rendering process. */ public class RenderUtil { @@ -86,6 +83,12 @@ public class RenderUtil return mcModelViewMat.copy(); } + + + //=============// + // clip planes // + //=============// + public static float getNearClipPlaneDistanceInBlocks(float partialTicks) { // 0.2 should provide a decent distance so the clip plane isn't visible @@ -211,46 +214,6 @@ public class RenderUtil return -1.0f; } - /** @return a message if LODs shouldn't be rendered, null if the LODs can render */ - public static String shouldLodsRender(ILevelWrapper levelWrapper, DhApiRenderParam renderEventParam) - { - if (!MC.playerExists()) - { - return "No Player Exists"; - } - - if (levelWrapper == null) - { - return "No Level Given"; - } - - IDhClientWorld clientWorld = SharedApi.getIDhClientWorld(); - if (clientWorld == null) - { - return "No Client World Loaded"; - } - - // TODO changing to getOrLoadClientLevel() fixes Immersive Portals only rendering the level the user starts in - // however this may break how other level handling is done so James doesn't want to change it. - // Special handling may be necessary when Immersive Portals is present, although additional testing is needed. - IDhClientLevel level = clientWorld.getClientLevel(levelWrapper); - if (level == null) - { - return "No Client Level Loaded"; //Level is not ready yet. - } - - if (MC_RENDER.getLightmapWrapper(levelWrapper) == null) - { - return "No Lightmap loaded"; - } - - if (renderEventParam.dhModelViewMatrix == null - || renderEventParam.mcModelViewMatrix == null) - { - return "No MVM or Proj Matrix Given"; - } - - return null; - } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/SortedArraySet.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/SortedArraySet.java index 051937255..d4195cc23 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/SortedArraySet.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/SortedArraySet.java @@ -21,188 +21,70 @@ package com.seibel.distanthorizons.core.util.objects; import java.util.*; -public class SortedArraySet implements SortedSet +public class SortedArraySet { - private final ArrayList list; + private final ArrayList list = new ArrayList<>(); + private final HashSet set = new HashSet<>(); + private final Comparator comparator; - public SortedArraySet() - { - list = new ArrayList<>(); - comparator = null; - } + + + //==============// + // constructors // + //==============// public SortedArraySet(Comparator comparator) { - list = new ArrayList<>(); this.comparator = comparator; } - public SortedArraySet(Collection collection) - { - list = new ArrayList<>(collection); - comparator = null; - list.sort(null); - } - public SortedArraySet(Collection collection, Comparator comparator) { - list = new ArrayList<>(collection); this.comparator = comparator; - list.sort(comparator); - } - - @Override - public Comparator comparator() - { - return comparator; - } - - @Override - public E first() - { - return list.get(0); - } - - @Override - public E last() - { - return list.get(list.size() - 1); - } - - @Override - public int size() - { - return list.size(); - } - - @Override - public boolean isEmpty() - { - return list.isEmpty(); - } - - @Override - public boolean contains(Object o) - { - return list.contains(o); - } - - @Override - public Iterator iterator() - { - return list.iterator(); - } - - public ListIterator listIterator() - { - return list.listIterator(); - } - - public ListIterator listIterator(int index) - { - return list.listIterator(index); + this.list.addAll(collection); + this.list.sort(comparator); } - @Override - public Object[] toArray() - { - return list.toArray(); - } - @Override - public T[] toArray(T[] a) - { - return list.toArray(a); - } + //==============// + // list methods // + //==============// - @Override - public boolean add(E e) + public void add(E element) { - int index = Collections.binarySearch(list, e, comparator); - if (index < 0) + if (!this.set.contains(element)) { - index = ~index; + this.list.add(element); } - list.add(index, e); - return true; } - @Override - public boolean remove(Object o) - { - return list.remove(o); - } + public E get(int index) { return this.list.get(index); } - @Override - public boolean containsAll(Collection c) - { - return list.containsAll(c); - } + public int size() { return this.list.size(); } - @Override - public boolean addAll(Collection c) - { - boolean changed = false; - for (E e : c) - { - changed |= add(e); - } - return changed; - } - - @Override - public boolean retainAll(Collection c) - { - return list.retainAll(c); - } - - @Override - public boolean removeAll(Collection c) - { - return list.removeAll(c); - } - - @Override public void clear() { - list.clear(); + this.list.clear(); + this.set.clear(); } + + + //================// + // base overrides // + //================// + @Override public String toString() { return "SortedArraySet{" + - "list=" + list + - ", comparator=" + comparator + + "list=" + this.list + + ", comparator=" + this.comparator + '}'; } - @Override - public SortedSet subSet(E fromElement, E toElement) - { - int fromIndex = Collections.binarySearch(list, fromElement, comparator); - if (fromIndex < 0) fromIndex = ~fromIndex; - int toIndex = Collections.binarySearch(list, toElement, comparator); - if (toIndex < 0) toIndex = ~toIndex; - return new SortedArraySet<>(list.subList(fromIndex, toIndex), comparator); - } - @Override - public SortedSet headSet(E toElement) - { - int toIndex = Collections.binarySearch(list, toElement, comparator); - if (toIndex < 0) toIndex = ~toIndex; - return new SortedArraySet<>(list.subList(0, toIndex), comparator); - } - - @Override - public SortedSet tailSet(E fromElement) - { - int fromIndex = Collections.binarySearch(list, fromElement, comparator); - if (fromIndex < 0) fromIndex = ~fromIndex; - return new SortedArraySet<>(list.subList(fromIndex, list.size()), comparator); - } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java index fbc5a0b0c..1a2ff0c80 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3f; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -79,7 +80,7 @@ public interface IMinecraftRenderWrapper extends IBindable /** Can return null if the given level hasn't had a light map assigned to it */ @Nullable - ILightMapWrapper getLightmapWrapper(ILevelWrapper level); + ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level); }