Clean up LodRendering logic
This commit is contained in:
@@ -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<String> 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<Pair<IClientLevelWrapper, DhChunkPos>, 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 //
|
||||
//=================//
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
@@ -1061,14 +1061,6 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> validateBufferIdsBeforeRendering = new ConfigEntry.Builder<Boolean>()
|
||||
.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
|
||||
|
||||
+4
-3
@@ -55,14 +55,15 @@ public class ColumnRenderBufferBuilder
|
||||
//==============//
|
||||
|
||||
/** @link adjData should be null for adjacent sections that cross detail level boundaries */
|
||||
public static CompletableFuture<ColumnRenderBuffer> uploadBuffersAsync(
|
||||
public static CompletableFuture<LodBufferContainer> uploadBuffersAsync(
|
||||
IDhClientLevel clientLevel,
|
||||
long pos,
|
||||
LodQuadBuilder quadBuilder
|
||||
)
|
||||
{
|
||||
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos)));
|
||||
CompletableFuture<ColumnRenderBuffer> 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<LodBufferContainer> uploadFuture = bufferContainer.makeAndUploadBuffersAsync(quadBuilder, GLProxy.getInstance().getGpuUploadMethod());
|
||||
uploadFuture.whenComplete((uploadedBuffer, exception) ->
|
||||
{
|
||||
// clean up if not uploaded
|
||||
|
||||
+9
-72
@@ -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<ColumnRenderBuffer> uploadFuture = null;
|
||||
private CompletableFuture<LodBufferContainer> 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<ColumnRenderBuffer> makeAndUploadBuffersAsync(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod)
|
||||
public synchronized CompletableFuture<LodBufferContainer> makeAndUploadBuffersAsync(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod)
|
||||
{
|
||||
// separate variable to prevent race condition when checking null
|
||||
CompletableFuture<ColumnRenderBuffer> future = this.uploadFuture;
|
||||
CompletableFuture<LodBufferContainer> 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 //
|
||||
//================//
|
||||
+3
-3
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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;
|
||||
}
|
||||
|
||||
@@ -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<ColumnRenderBuffer> bufferUploadFuture = null;
|
||||
private CompletableFuture<LodBufferContainer> 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<ColumnRenderBuffer> uploadFuture = this.bufferUploadFuture;
|
||||
CompletableFuture<LodBufferContainer> uploadFuture = this.bufferUploadFuture;
|
||||
if (uploadFuture != null)
|
||||
{
|
||||
uploadFuture.cancel(true);
|
||||
|
||||
+23
-162
@@ -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<LoadedRenderBuffer> loadedNearToFarBuffers = null;
|
||||
@Nullable
|
||||
private SortedArraySet<LodBufferContainer> 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: <br>
|
||||
* <a href="https://www.reddit.com/r/VoxelGameDev/comments/a0l8zc/correct_depthordering_for_translucent_discrete/">correct_depth_ordering_for_translucent_discrete</a> <br><br>
|
||||
*
|
||||
* 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?
|
||||
* <a href="https://www.reddit.com/r/VoxelGameDev/comments/a0l8zc/correct_depthordering_for_translucent_discrete/">correct_depth_ordering_for_translucent_discrete</a>
|
||||
*/
|
||||
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<LoadedRenderBuffer> farToNearComparator = (loadedBufferA, loadedBufferB) ->
|
||||
Comparator<LodBufferContainer> 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<LoadedRenderBuffer> 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<LodBufferContainer> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+293
-306
@@ -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. <br>
|
||||
* 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> 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; }
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+29
-147
@@ -21,188 +21,70 @@ package com.seibel.distanthorizons.core.util.objects;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SortedArraySet<E> implements SortedSet<E>
|
||||
public class SortedArraySet<E>
|
||||
{
|
||||
private final ArrayList<E> list;
|
||||
private final ArrayList<E> list = new ArrayList<>();
|
||||
private final HashSet<E> set = new HashSet<>();
|
||||
|
||||
private final Comparator<? super E> comparator;
|
||||
|
||||
public SortedArraySet()
|
||||
{
|
||||
list = new ArrayList<>();
|
||||
comparator = null;
|
||||
}
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public SortedArraySet(Comparator<? super E> comparator)
|
||||
{
|
||||
list = new ArrayList<>();
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
public SortedArraySet(Collection<? extends E> collection)
|
||||
{
|
||||
list = new ArrayList<>(collection);
|
||||
comparator = null;
|
||||
list.sort(null);
|
||||
}
|
||||
|
||||
public SortedArraySet(Collection<? extends E> collection, Comparator<? super E> comparator)
|
||||
{
|
||||
list = new ArrayList<>(collection);
|
||||
this.comparator = comparator;
|
||||
list.sort(comparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super E> 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<E> iterator()
|
||||
{
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
public ListIterator<E> listIterator()
|
||||
{
|
||||
return list.listIterator();
|
||||
}
|
||||
|
||||
public ListIterator<E> 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> 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<? extends E> 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<E> 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<E> 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<E> tailSet(E fromElement)
|
||||
{
|
||||
int fromIndex = Collections.binarySearch(list, fromElement, comparator);
|
||||
if (fromIndex < 0) fromIndex = ~fromIndex;
|
||||
return new SortedArraySet<>(list.subList(fromIndex, list.size()), comparator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user