Fix #8 and #9 (inaccurate lighting and rendering behind transparent objects)

Instead of using a stencil and rendering in the forgelastdraw event, we now you a mixin to render right before the sold block layer.

The main purpose of this was to allow for LODs to be drawn behind transparent objects; however as a happy accident it seems to have also improved the lighting, I'm not sure if it is perfect, but it is much better.
This commit is contained in:
James Seibel
2021-03-28 22:39:58 -05:00
parent b71d6a5e3f
commit 18c08ccd88
4 changed files with 43 additions and 112 deletions
@@ -5,7 +5,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.backsun.lod.renderer.RenderGlobalHook; import com.backsun.lod.LodMain;
import com.backsun.lod.util.LodConfig;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
@@ -14,9 +15,22 @@ import net.minecraft.client.renderer.WorldRenderer;
@Mixin(WorldRenderer.class) @Mixin(WorldRenderer.class)
public class MixinWorldRenderer public class MixinWorldRenderer
{ {
@Inject(at = @At("HEAD"), method = "renderBlockLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/matrix/MatrixStack;DDD)V", cancellable = false) private static float previousPartialTicks = 0;
private void renderBlockLayer(RenderType blockLayerIn, MatrixStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
@Inject(at = @At("RETURN"), method = "renderSky(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V", cancellable = false)
private void renderSky(MatrixStack matrixStackIn, float partialTicks, CallbackInfo callback)
{ {
RenderGlobalHook.startRenderingStencil(blockLayerIn); // get the partial ticks since renderBlockLayer doesn't
// have access to them
previousPartialTicks = partialTicks;
}
@Inject(at = @At("HEAD"), method = "renderBlockLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/matrix/MatrixStack;DDD)V", cancellable = false)
private void renderBlockLayer(RenderType renderType, MatrixStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
{
// only render if LODs are enabled and
// only render before solid blocks
if (LodConfig.CLIENT.drawLODs.get() && renderType == RenderType.getSolid())
LodMain.client_proxy.renderLods(previousPartialTicks);
} }
} }
@@ -1,18 +1,13 @@
package com.backsun.lod.proxy; package com.backsun.lod.proxy;
import org.lwjgl.opengl.GL11;
import com.backsun.lod.builders.LodBuilder; import com.backsun.lod.builders.LodBuilder;
import com.backsun.lod.objects.LodChunk; import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension; import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.LodRegion; import com.backsun.lod.objects.LodRegion;
import com.backsun.lod.objects.LodWorld; import com.backsun.lod.objects.LodWorld;
import com.backsun.lod.renderer.LodRenderer; import com.backsun.lod.renderer.LodRenderer;
import com.backsun.lod.renderer.RenderGlobalHook;
import com.backsun.lod.util.LodConfig;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.world.WorldEvent;
@@ -49,25 +44,13 @@ public class ClientProxy
// render event // // render event //
//==============// //==============//
@SubscribeEvent
public void renderWorldLast(RenderWorldLastEvent event)
{
RenderGlobalHook.endRenderingStencil();
GL11.glStencilFunc(GL11.GL_EQUAL, 0, 0xFF);
if (LodConfig.CLIENT.drawLODs.get())
renderLods(event.getPartialTicks());
GL11.glDisable(GL11.GL_STENCIL_TEST);
}
/** /**
* Do any setup that is required to draw LODs * Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw. * and then tell the LodRenderer to draw.
*/ */
public void renderLods(float partialTicks) public void renderLods(float partialTicks)
{ {
// update the // update each regions' width to match the new render distance
int newWidth = Math.max(4, (mc.gameSettings.renderDistanceChunks * LodChunk.WIDTH * 2) / LodRegion.SIZE); int newWidth = Math.max(4, (mc.gameSettings.renderDistanceChunks * LodChunk.WIDTH * 2) / LodRegion.SIZE);
if (lodWorld != null && lodBuilder.regionWidth != newWidth) if (lodWorld != null && lodBuilder.regionWidth != newWidth)
{ {
@@ -241,18 +241,27 @@ public class LodRenderer
// set the required open GL settings // set the required open GL settings
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_TEXTURE_2D); // TODO store the default values of each of these so they can be reset correctly
GL11.glEnable(GL11.GL_CULL_FACE); GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_COLOR_MATERIAL); GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glEnable(GL11.GL_DEPTH_TEST);
// disable the lights Minecraft uses
GL11.glDisable(GL11.GL_LIGHT0);
GL11.glDisable(GL11.GL_LIGHT1);
Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks); Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks);
setupProjectionMatrix(partialTicks); setupProjectionMatrix(partialTicks);
setupLighting(partialTicks); setupLighting(partialTicks);
NearFarFogSetting fogSetting = determineFogSettings(); NearFarFogSetting fogSetting = determineFogSettings();
// determine the current fog settings so they can be
// reset after drawing the LODs
float defaultFogStartDist = GL11.glGetFloat(GL11.GL_FOG_START);
float defaultFogEndDist = GL11.glGetFloat(GL11.GL_FOG_END);
int defaultFogMode = GL11.glGetInteger(GL11.GL_FOG_MODE);
@@ -278,19 +287,26 @@ public class LodRenderer
profiler.endStartSection("LOD cleanup"); profiler.endStartSection("LOD cleanup");
// this must be done otherwise other parts of the screen may be drawn with a fog effect
// IE the GUI
FogRenderer.resetFog();
RenderSystem.disableFog();
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(LOD_GL_LIGHT_NUMBER); GL11.glDisable(LOD_GL_LIGHT_NUMBER);
// re-enable the lights Minecraft uses
GL11.glEnable(GL11.GL_LIGHT0);
GL11.glEnable(GL11.GL_LIGHT1);
// TODO record the light states before to make sure they are being reset correctly
RenderSystem.disableLighting();
// this can't be called until after the buffers are built // this can't be called until after the buffers are built
// because otherwise the buffers may be set to the wrong size // because otherwise the buffers may be set to the wrong size
previousChunkRenderDistance = mc.gameSettings.renderDistanceChunks; previousChunkRenderDistance = mc.gameSettings.renderDistanceChunks;
// reset the fog settings so the normal chunks
// will be drawn correctly
RenderSystem.fogStart(defaultFogStartDist);
RenderSystem.fogEnd(defaultFogEndDist);
RenderSystem.fogMode(defaultFogMode);
// end of profiler tracking // end of profiler tracking
profiler.endSection(); profiler.endSection();
@@ -478,7 +494,7 @@ public class LodRenderer
{ {
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.world.getSunBrightness(partialTicks) : 0.2f; float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.world.getSunBrightness(partialTicks) : 0.2f;
float gammaMultiplyer = (float)mc.gameSettings.gamma - 0.5f; float gammaMultiplyer = (float)mc.gameSettings.gamma - 0.5f;
float lightStrength = sunBrightness - 0.7f + (gammaMultiplyer * 0.2f); float lightStrength = sunBrightness - 0.4f + (gammaMultiplyer * 0.2f);
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f}; float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
@@ -1,82 +0,0 @@
package com.backsun.lod.renderer;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.renderer.RenderType;
/**
* This code is used for drawing
* to the stencil buffer.
*
* @author James Seibel
* @version 02-17-2021
*/
public class RenderGlobalHook
{
/**
* this variable should be the same as the method name below.
* It is used when transforming the RenderGlobal class'
* renderBlockLayer method.
*/
public static final String START_STENCIL_METHOD_NAME = "startRenderingStencil";
/**
* This method tells OpenGL to start drawing everything to the stencil.
* This is done to prevent LODs from being rendered on top of the world.
* <br><br>
* Called in the order (as of minecraft 1.16.4): <br>
* RenderType.getSolid() <br>
* RenderType.getCutoutMipped() <br>
* RenderType.getCutout() <br>
* RenderType.getTranslucent() <br>
* RenderType.getTripwire() <br>
*/
public static void startRenderingStencil(RenderType blockLayerIn)
{
// we only enable drawing to the stencil once since
// we want to skip the rendering of the out of world fog
// but catch everything else
if (blockLayerIn == RenderType.getSolid())
{
// solid is the first layer rendered
// clear the buffer so we can start fresh.
// if this isn't cleared first we will have overlap with the fog
// outside the world
GL11.glClearStencil(0);
GL11.glStencilMask(0x11111111);
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
GL11.glEnable(GL11.GL_STENCIL_TEST);
GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0x11111111);
GL11.glStencilMask(0b11111111);
GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used
GL11.GL_KEEP, // stencil test passes
GL11.GL_REPLACE); // stencil + depth pass
}
}
/**
* this variable should be the same as the method name below.
* It is used when transforming the RenderGlobal class'
* renderBlockLayer method.
*/
public static final String END_STENCIL_METHOD_NAME = "endRenderingStencil";
/**
* Currently this method isn't used in any transformations since we end
* the stencil drawing in the ClientProxy right before we draw the LODs.
*/
public static void endRenderingStencil(RenderType blockLayerIn)
{
GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used
GL11.GL_KEEP, // stencil test passes
GL11.GL_KEEP); // stencil + depth pass
}
public static void endRenderingStencil()
{
endRenderingStencil(null);
}
}