diff --git a/src/main/java/com/seibel/lod/core/a7/render/a7LodRenderer.java b/src/main/java/com/seibel/lod/core/a7/render/a7LodRenderer.java index f32f38f46..2d949728a 100644 --- a/src/main/java/com/seibel/lod/core/a7/render/a7LodRenderer.java +++ b/src/main/java/com/seibel/lod/core/a7/render/a7LodRenderer.java @@ -34,6 +34,7 @@ import com.seibel.lod.core.objects.math.Vec3f; import com.seibel.lod.core.render.GLProxy; import com.seibel.lod.core.render.LodFogConfig; import com.seibel.lod.core.render.LodRenderProgram; +import com.seibel.lod.core.render.RenderUtil; import com.seibel.lod.core.render.objects.GLState; import com.seibel.lod.core.render.objects.GLVertexBuffer; import com.seibel.lod.core.render.objects.QuadElementBuffer; @@ -54,7 +55,7 @@ import java.util.concurrent.TimeUnit; * This is where LODs are draw to the world. * * @author James Seibel - * @version 12-12-2021 + * @version 2022-8-21 */ public class a7LodRenderer { @@ -125,18 +126,7 @@ public class a7LodRenderer EVENT_LOGGER.error("drawLODs() called after close()!"); return; } - //=================================// - // determine if LODs should render // - //=================================// - if (MC_RENDER.playerHasBlindnessEffect()) - { - // if the player is blind, don't render LODs, - // and don't change minecraft's fog - // which blindness relies on. - return; - } - if (MC_RENDER.getLightmapWrapper() == null) - return; + // get MC's shader program // Save all MC render state @@ -203,24 +193,14 @@ public class a7LodRenderer //LightmapTexture lightmapTexture = new LightmapTexture(); /*---------Get required data--------*/ - // Get the matrixs for rendering int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; - int lodChunkDist = Config.Client.Graphics.Quality.lodChunkRenderDistance.get(); - int farPlaneBlockDistance; - // required for setupFog and setupProjectionMatrix - if (MC.getWrappedClientWorld().getDimensionType().hasCeiling()) - farPlaneBlockDistance = Math.min(lodChunkDist, LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH; - else - farPlaneBlockDistance = lodChunkDist * LodUtil.CHUNK_WIDTH; - - Mat4f combinedMatrix = createCombinedMatrix(baseProjectionMatrix, baseModelViewMatrix, - vanillaBlockRenderedDistance, farPlaneBlockDistance, partialTicks); + Mat4f modelViewProjectionMatrix = RenderUtil.createCombinedModelViewProjectionMatrix(baseProjectionMatrix, baseModelViewMatrix, partialTicks); /*---------Fill uniform data--------*/ // Fill the uniform data. Note: GL33.GL_TEXTURE0 == texture bindpoint 0 - shaderProgram.fillUniformData(combinedMatrix, + shaderProgram.fillUniformData(modelViewProjectionMatrix, MC_RENDER.isFogStateSpecial() ? getSpecialFogColor(partialTicks) : getFogColor(partialTicks), - 0, MC.getWrappedClientWorld().getHeight(), MC.getWrappedClientWorld().getMinHeight(), farPlaneBlockDistance, + 0, MC.getWrappedClientWorld().getHeight(), MC.getWrappedClientWorld().getMinHeight(), RenderUtil.getFarClipPlaneDistanceInBlocks(), vanillaBlockRenderedDistance, MC_RENDER.isFogStateSpecial()); // Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one. @@ -272,6 +252,8 @@ public class a7LodRenderer tickLogger.incLogTries(); } + + //=================// // Setup Functions // //=================// @@ -313,44 +295,7 @@ public class a7LodRenderer return MC_RENDER.getSpecialFogColor(partialTicks); } - private static float calculateNearClipPlane(float distance, float partialTicks) { - double fov = MC_RENDER.getFov(partialTicks); - double aspectRatio = (double)MC_RENDER.getScreenWidth()/MC_RENDER.getScreenHeight(); - return (float) (distance - / Math.sqrt(1d + LodUtil.pow2(Math.tan(fov/180d*Math.PI/2d)) - * (LodUtil.pow2(aspectRatio) + 1d))); - } - - /** - * create and return a new projection matrix based on MC's projection matrix - * @param projMat this is Minecraft's current projection matrix - * @param modelMat this is Minecraft's current model matrix - * @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance - */ - private static Mat4f createCombinedMatrix(Mat4f projMat, Mat4f modelMat, float vanillaBlockRenderedDistance, - int farPlaneBlockDistance, float partialTicks) - { - //Create a copy of the current matrix, so the current matrix isn't modified. - Mat4f lodProj = projMat.copy(); - - float nearClipPlane; - if (Config.Client.Advanced.lodOnlyMode.get()) { - nearClipPlane = 0.1f; - } else if (Config.Client.Graphics.AdvancedGraphics.useExtendedNearClipPlane.get()) { - nearClipPlane = Math.min((vanillaBlockRenderedDistance-16f),8f*16f); - } else { - nearClipPlane = 16f; - } - - //Set new far and near clip plane values. - lodProj.setClipPlanes( - calculateNearClipPlane(nearClipPlane, partialTicks), - (float)((farPlaneBlockDistance+LodUtil.REGION_WIDTH) * Math.sqrt(2))); - - lodProj.multiply(modelMat); - - return lodProj; - } + //======================// // Cleanup Functions // diff --git a/src/main/java/com/seibel/lod/core/api/internal/a7/ClientApi.java b/src/main/java/com/seibel/lod/core/api/internal/a7/ClientApi.java index 480c22285..ca6044c56 100644 --- a/src/main/java/com/seibel/lod/core/api/internal/a7/ClientApi.java +++ b/src/main/java/com/seibel/lod/core/api/internal/a7/ClientApi.java @@ -32,6 +32,7 @@ import com.seibel.lod.core.logging.SpamReducedLogger; import com.seibel.lod.core.objects.math.Mat4f; import com.seibel.lod.core.render.GLProxy; import com.seibel.lod.core.render.RenderSystemTest; +import com.seibel.lod.core.render.RenderUtil; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; @@ -52,7 +53,7 @@ import java.util.concurrent.TimeUnit; * Specifically for the client. * * @author James Seibel - * @version 2022-8-20 + * @version 2022-8-21 */ public class ClientApi { @@ -231,19 +232,13 @@ public class ClientApi profiler.push("DH-RenderLevel"); try { - if (!MC.playerExists()) - return; - if (levelWrapper == null) - return; - DhWorld dhWorld = SharedApi.currentWorld; - if (dhWorld == null) - return; - if (!(SharedApi.currentWorld instanceof IClientWorld)) + if (!RenderUtil.shouldLodsRender(levelWrapper)) return; + //FIXME: Improve class hierarchy of DhWorld, IClientWorld, IServerWorld to fix all this hard casting + // (also in RenderUtil) + DhWorld dhWorld = SharedApi.currentWorld; IClientLevel level = (IClientLevel) dhWorld.getOrLoadLevel(levelWrapper); - if (level == null) - return; //Level is not ready yet. if (prefLoggerEnabled) { diff --git a/src/main/java/com/seibel/lod/core/render/RenderUtil.java b/src/main/java/com/seibel/lod/core/render/RenderUtil.java index 8530170fb..31abccd87 100644 --- a/src/main/java/com/seibel/lod/core/render/RenderUtil.java +++ b/src/main/java/com/seibel/lod/core/render/RenderUtil.java @@ -19,25 +19,38 @@ package com.seibel.lod.core.render; +import com.seibel.lod.core.a7.level.IClientLevel; +import com.seibel.lod.core.a7.world.DhWorld; +import com.seibel.lod.core.a7.world.IClientWorld; +import com.seibel.lod.core.api.internal.a7.SharedApi; +import com.seibel.lod.core.config.Config; import com.seibel.lod.core.handlers.dependencyInjection.SingletonInjector; import com.seibel.lod.core.objects.DHBlockPos; import com.seibel.lod.core.objects.DHChunkPos; +import com.seibel.lod.core.objects.math.Mat4f; import com.seibel.lod.core.objects.math.Vec3f; import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper; /** * This holds miscellaneous helper code * to be used in the rendering process. * * @author James Seibel - * @version 10-19-2021 + * @version 2022-8-21 */ public class RenderUtil { + private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + //=================// + // culling methods // + //=================// + /** * Returns if the given ChunkPos is in the loaded area of the world. * @param center the center of the loaded world (probably the player's ChunkPos) @@ -64,7 +77,6 @@ public class RenderUtil && z <= centerCoordinate + MC_RENDER.getRenderDistance()); } - /** * Find the coordinates that are in the center half of the given * 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius). @@ -80,7 +92,6 @@ public class RenderUtil && j <= lodRadius + halfRadius); } - /** * Returns true if one of the region's 4 corners is in front * of the camera. @@ -118,4 +129,112 @@ public class RenderUtil // flickering or odd disappearances return objectVector.dotProduct(cameraDir) > -0.1; } + + + + //=====================// + // matrix manipulation // + //=====================// + + /** + * create and return a new projection matrix based on MC's modelView and projection matrices + * @param mcProjMat Minecraft's current projection matrix + */ + public static Mat4f createLodProjectionMatrix(Mat4f mcProjMat, float partialTicks) + { + int farPlaneDistanceInBlocks = RenderUtil.getFarClipPlaneDistanceInBlocks(); + + // Create a copy of the current matrix, so it won't be modified. + Mat4f lodProj = mcProjMat.copy(); + + // Set new far and near clip plane values. + lodProj.setClipPlanes( + getNearClipPlaneDistanceInBlocks(partialTicks), + (float)((farPlaneDistanceInBlocks+LodUtil.REGION_WIDTH) * Math.sqrt(2))); + + return lodProj; + } + + /** create and return a new projection matrix based on MC's modelView and projection matrices */ + public static Mat4f createLodModelViewMatrix(Mat4f mcModelViewMat) + { + // nothing beyond copying needs to be done to MC's MVM currently, + // this method is just here in case that changes in the future + return mcModelViewMat.copy(); + } + + /** + * create and return a new combined modelView/projection matrix based on MC's modelView and projection matrices + * @param mcProjMat Minecraft's current projection matrix + * @param mcModelViewMat Minecraft's current model view matrix + */ + public static Mat4f createCombinedModelViewProjectionMatrix(Mat4f mcProjMat, Mat4f mcModelViewMat, float partialTicks) + { + Mat4f lodProj = createLodProjectionMatrix(mcProjMat, partialTicks); + lodProj.multiply(createLodModelViewMatrix(mcModelViewMat)); + return lodProj; + } + + public static float getNearClipPlaneDistanceInBlocks(float partialTicks) + { + int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; + + float nearClipPlane; + if (Config.Client.Advanced.lodOnlyMode.get()) { + nearClipPlane = 0.1f; + } else if (Config.Client.Graphics.AdvancedGraphics.useExtendedNearClipPlane.get()) { + nearClipPlane = Math.min(vanillaBlockRenderedDistance - LodUtil.CHUNK_WIDTH, (float) 8 * LodUtil.CHUNK_WIDTH); // allow a max near clip plane of 8 chunks + } else { + nearClipPlane = 16f; + } + + // modify the based on the player's FOV + double fov = MC_RENDER.getFov(partialTicks); + double aspectRatio = (double) MC_RENDER.getScreenWidth() / MC_RENDER.getScreenHeight(); + return (float) (nearClipPlane + / Math.sqrt(1d + LodUtil.pow2(Math.tan(fov / 180d * Math.PI / 2d)) + * (LodUtil.pow2(aspectRatio) + 1d))); + } + public static int getFarClipPlaneDistanceInBlocks() + { + int lodChunkDist = Config.Client.Graphics.Quality.lodChunkRenderDistance.get(); + return lodChunkDist * LodUtil.CHUNK_WIDTH; + } + + /** @return false if LODs shouldn't be rendered for any reason */ + public static boolean shouldLodsRender(ILevelWrapper levelWrapper) + { + if (!MC.playerExists()) + return false; + + if (levelWrapper == null) + return false; + + DhWorld dhWorld = SharedApi.currentWorld; + if (dhWorld == null) + return false; + + if (!(SharedApi.currentWorld instanceof IClientWorld)) + return false; // don't attempt to render server worlds + + //FIXME: Improve class hierarchy of DhWorld, IClientWorld, IServerWorld to fix all this hard casting + // (also in ClientApi) + IClientLevel level = (IClientLevel) dhWorld.getOrLoadLevel(levelWrapper); + if (level == null) + return false; //Level is not ready yet. + + if (MC_RENDER.playerHasBlindnessEffect()) + { + // if the player is blind, don't render LODs, + // and don't change minecraft's fog + // which blindness relies on. + return false; + } + + if (MC_RENDER.getLightmapWrapper() == null) + return false; + + return true; + } + }