diff --git a/build.gradle.kts b/build.gradle.kts index c29c57e..b2e41c6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -67,7 +67,7 @@ dependencies { implementation("org.joml:joml:1.10.5") implementation("it.unimi.dsi:fastutil:8.5.13") - compileOnly("makamys:neodymium-mc1.7.10:0.4.0-unofficial:dev") + compileOnly("makamys:neodymium-mc1.7.10:0.4.2-unofficial:dev") compileOnly("com.github.GTNewHorizons:lwjgl3ify:2.1.5:dev") diff --git a/src/main/java/com/falsepattern/falsetweaks/FalseTweaks.java b/src/main/java/com/falsepattern/falsetweaks/FalseTweaks.java index 093de11..f53ea9b 100644 --- a/src/main/java/com/falsepattern/falsetweaks/FalseTweaks.java +++ b/src/main/java/com/falsepattern/falsetweaks/FalseTweaks.java @@ -44,7 +44,7 @@ guiFactory = Tags.ROOT_PKG + ".config.FalseTweaksGuiFactory", acceptableRemoteVersions = "*", dependencies = "required-after:falsepatternlib@[1.5.5,);" + - "after:neodymium@[0.4.0,);" + + "after:neodymium@[0.4.2,);" + "after:gtnhlib@[0.5.21,);" ) public class FalseTweaks { diff --git a/src/main/java/com/falsepattern/falsetweaks/mixin/mixins/client/occlusion/neodymium/NeoRendererMixin.java b/src/main/java/com/falsepattern/falsetweaks/mixin/mixins/client/occlusion/neodymium/NeoRendererMixin.java index b1906d6..df38902 100644 --- a/src/main/java/com/falsepattern/falsetweaks/mixin/mixins/client/occlusion/neodymium/NeoRendererMixin.java +++ b/src/main/java/com/falsepattern/falsetweaks/mixin/mixins/client/occlusion/neodymium/NeoRendererMixin.java @@ -25,25 +25,32 @@ import com.falsepattern.falsetweaks.modules.occlusion.OcclusionCompat; import com.falsepattern.falsetweaks.modules.occlusion.OcclusionHelpers; import com.falsepattern.falsetweaks.modules.occlusion.WorldRendererOcclusion; -import com.llamalad7.mixinextras.sugar.Share; -import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; -import makamys.neodymium.renderer.Mesh; +import com.falsepattern.falsetweaks.modules.occlusion.shader.ShadowPassOcclusionHelper; +import lombok.val; +import makamys.neodymium.renderer.ChunkMesh; +import makamys.neodymium.renderer.GPUMemoryManager; +import makamys.neodymium.renderer.NeoRegion; import makamys.neodymium.renderer.NeoRenderer; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Dynamic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.client.renderer.WorldRenderer; +import java.util.List; + @Mixin(value = NeoRenderer.class, remap = false) public abstract class NeoRendererMixin { + @Shadow private List mems; + + @Shadow private List loadedRegionsList; + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL30;glBindVertexArray(I)V", @@ -60,12 +67,35 @@ private void postRender(int pass, double alpha, CallbackInfoReturnable * @reason Compat */ @Overwrite - @Dynamic private boolean isRendererVisible(WorldRenderer wr, boolean shadowPass) { - if (shadowPass) { - return ((WorldRendererOcclusion)wr).ft$isVisibleShadows(); - } else { + if (!shadowPass) { return wr.isVisible; } + if (!((WorldRendererOcclusion) wr).ft$isVisibleShadows()) { + return false; + } + return ShadowPassOcclusionHelper.isShadowVisible(wr); + } + + @Inject(method = "initIndexBuffers", + at = @At("HEAD"), + require = 1) + private void initIndexBuffers(boolean shadowPass, CallbackInfo ci) { + if (!shadowPass) + return; + ShadowPassOcclusionHelper.begin(); + int regionsSize = loadedRegionsList.size(); + for (val mem: mems) { + for (int regionI = 0; regionI < regionsSize; regionI++) { + val region = loadedRegionsList.get(regionI).getRenderData(mem); + for (val mesh: region.getSentMeshes()) { + val wr = ((ChunkMesh)mesh).wr(); + if (wr.isVisible && wr.isInFrustum) { + ShadowPassOcclusionHelper.addShadowReceiver(wr); + } + } + } + } + ShadowPassOcclusionHelper.end(); } } diff --git a/src/main/java/com/falsepattern/falsetweaks/mixin/mixins/client/occlusion/optifine/shaders/ShadersMixin.java b/src/main/java/com/falsepattern/falsetweaks/mixin/mixins/client/occlusion/optifine/shaders/ShadersMixin.java new file mode 100644 index 0000000..21ff2ac --- /dev/null +++ b/src/main/java/com/falsepattern/falsetweaks/mixin/mixins/client/occlusion/optifine/shaders/ShadersMixin.java @@ -0,0 +1,49 @@ +/* + * This file is part of FalseTweaks. + * + * Copyright (C) 2022-2024 FalsePattern + * All Rights Reserved + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * FalseTweaks is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FalseTweaks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FalseTweaks. If not, see . + */ + +package com.falsepattern.falsetweaks.mixin.mixins.client.occlusion.optifine.shaders; + +import com.falsepattern.falsetweaks.modules.occlusion.shader.ShadowPassOcclusionHelper; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import shadersmod.client.Shaders; + +import java.nio.FloatBuffer; + +@Mixin(value = Shaders.class, + remap = false) +public abstract class ShadersMixin { + @Shadow @Final static FloatBuffer shadowModelView; + + @Inject(method = "setCameraShadow", + at = @At("RETURN"), + require = 1) + private static void onSetCameraShadow(CallbackInfo ci) { + shadowModelView.position(0); + ShadowPassOcclusionHelper.shadowModelViewMatrix.set(shadowModelView); + } +} diff --git a/src/main/java/com/falsepattern/falsetweaks/mixin/plugin/standard/Mixin.java b/src/main/java/com/falsepattern/falsetweaks/mixin/plugin/standard/Mixin.java index 31b466e..21cc2e1 100644 --- a/src/main/java/com/falsepattern/falsetweaks/mixin/plugin/standard/Mixin.java +++ b/src/main/java/com/falsepattern/falsetweaks/mixin/plugin/standard/Mixin.java @@ -138,6 +138,9 @@ public enum Mixin implements IMixin { Occlusion_Optifine_Shaders_FrustrumMixin(Side.CLIENT, THREADING.and(REQUIRE_OPTIFINE_WITH_SHADERS), "occlusion.optifine.shaders.FrustrumMixin"), + Occlusion_Optifine_Shaders_ShadersMixin(Side.CLIENT, + THREADING.and(REQUIRE_OPTIFINE_WITH_SHADERS), + "occlusion.optifine.shaders.ShadersMixin"), //FastCraft diff --git a/src/main/java/com/falsepattern/falsetweaks/modules/occlusion/OcclusionRenderer.java b/src/main/java/com/falsepattern/falsetweaks/modules/occlusion/OcclusionRenderer.java index f37cd0b..6537c8b 100644 --- a/src/main/java/com/falsepattern/falsetweaks/modules/occlusion/OcclusionRenderer.java +++ b/src/main/java/com/falsepattern/falsetweaks/modules/occlusion/OcclusionRenderer.java @@ -27,6 +27,7 @@ import com.falsepattern.falsetweaks.config.OcclusionConfig; import com.falsepattern.falsetweaks.modules.debug.Debug; import com.falsepattern.falsetweaks.modules.occlusion.interfaces.IRenderGlobalMixin; +import com.falsepattern.falsetweaks.modules.occlusion.shader.ShadowPassOcclusionHelper; import com.falsepattern.falsetweaks.modules.threadedupdates.ThreadedChunkUpdateHelper; import com.falsepattern.falsetweaks.modules.threadexec.FTWorker; import com.falsepattern.falsetweaks.modules.threadexec.ThreadedTask; @@ -1120,6 +1121,18 @@ public int sortAndRender(int start, int end, int pass, double tick) { prof.startSection("setup_lists"); int glListsRendered = 0, allRenderListsLength = 0; + + if (shadowPass) { + val renderers = rg.worldRenderers; + ShadowPassOcclusionHelper.begin(); + for (int i = 0; i < renderers.length; i++) { + val wr = renderers[i]; + if (wr != null && wr.isVisible && wr.isInFrustum && !wr.skipAllRenderPasses()) { + ShadowPassOcclusionHelper.addShadowReceiver(wr); + } + } + ShadowPassOcclusionHelper.end(); + } WorldRenderer[] sortedWorldRenderers = shadowPass ? rg.worldRenderers : rg.sortedWorldRenderers; for (int i = loopStart; i != loopEnd; i += dir) { @@ -1131,7 +1144,7 @@ public int sortAndRender(int start, int end, int pass, double tick) { isVisible = rend.isVisible; isInFrustum = rend.isInFrustum; rend.isVisible = iwr.ft$isVisibleShadows(); - rend.isInFrustum = true; + rend.isInFrustum = ShadowPassOcclusionHelper.isShadowVisible(rend); } if ((rend.isVisible && rend.isInFrustum) && !rend.skipRenderPass[pass]) { diff --git a/src/main/java/com/falsepattern/falsetweaks/modules/occlusion/shader/ShadowPassOcclusionHelper.java b/src/main/java/com/falsepattern/falsetweaks/modules/occlusion/shader/ShadowPassOcclusionHelper.java new file mode 100644 index 0000000..d6f6666 --- /dev/null +++ b/src/main/java/com/falsepattern/falsetweaks/modules/occlusion/shader/ShadowPassOcclusionHelper.java @@ -0,0 +1,113 @@ +/* + * This file is part of FalseTweaks. + * + * Copyright (C) 2022-2024 FalsePattern + * All Rights Reserved + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * FalseTweaks is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FalseTweaks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FalseTweaks. If not, see . + */ + +package com.falsepattern.falsetweaks.modules.occlusion.shader; + +import org.joml.Matrix4f; +import org.joml.Vector3f; + +import net.minecraft.client.renderer.WorldRenderer; + +public class ShadowPassOcclusionHelper { + private static float minCamX, minCamY, minCamZ; + private static float maxCamX, maxCamY; + + private static float minX, minY, minZ; + private static float maxX, maxY, maxZ; + + private static Vector3f scratch = new Vector3f(); + public static Matrix4f shadowModelViewMatrix = new Matrix4f(); + public static void begin() { + minCamX = Float.POSITIVE_INFINITY; + minCamY = Float.POSITIVE_INFINITY; + minCamZ = Float.POSITIVE_INFINITY; + maxCamX = Float.NEGATIVE_INFINITY; + maxCamY = Float.NEGATIVE_INFINITY; + minX = Float.POSITIVE_INFINITY; + minY = Float.POSITIVE_INFINITY; + minZ = Float.POSITIVE_INFINITY; + maxX = Float.NEGATIVE_INFINITY; + maxY = Float.NEGATIVE_INFINITY; + maxZ = Float.NEGATIVE_INFINITY; + } + + public static void addShadowReceiver(WorldRenderer wr) { + minX = Math.min(minX, wr.posX); + minY = Math.min(minY, wr.posY); + minZ = Math.min(minZ, wr.posZ); + maxX = Math.max(maxX, wr.posX + 16); + maxY = Math.max(maxY, wr.posY + 16); + maxZ = Math.max(maxZ, wr.posZ + 16); + } + + public static void end() { + for (int i = 0; i < 8; i++) { + float x = (i & 1) == 0 ? minX : maxX; + float y = (i & 2) == 0 ? minY : maxY; + float z = (i & 4) == 0 ? minZ : maxZ; + shadowModelViewMatrix.transformPosition(x, y, z, scratch); + x = scratch.x; + y = scratch.y; + z = scratch.z; + minCamX = Math.min(minCamX, x); + minCamY = Math.min(minCamY, y); + minCamZ = Math.min(minCamZ, z); + maxCamX = Math.max(maxCamX, x); + maxCamY = Math.max(maxCamY, y); + } + if (Float.isNaN(minCamX)) + minCamX = Float.NEGATIVE_INFINITY; + if (Float.isNaN(minCamY)) + minCamY = Float.NEGATIVE_INFINITY; + if (Float.isNaN(minCamZ)) + minCamZ = Float.NEGATIVE_INFINITY; + if (Float.isNaN(maxCamX)) + maxCamX = Float.POSITIVE_INFINITY; + if (Float.isNaN(maxCamY)) + maxCamY = Float.POSITIVE_INFINITY; + } + + public static boolean isShadowVisible(WorldRenderer wr) { + float posX = wr.posX; + float posY = wr.posY; + float posZ = wr.posZ; + + float minX = Float.POSITIVE_INFINITY, minY = Float.POSITIVE_INFINITY; + float maxX = Float.NEGATIVE_INFINITY, maxY = Float.NEGATIVE_INFINITY, maxZ = Float.NEGATIVE_INFINITY; + for (int i = 0; i < 8; i++) { + float x = posX + ((i & 1) == 0 ? 0 : 16); + float y = posY + ((i & 2) == 0 ? 0 : 16); + float z = posZ + ((i & 4) == 0 ? 0 : 16); + shadowModelViewMatrix.transformPosition(x, y, z, scratch); + x = scratch.x; + y = scratch.y; + z = scratch.z; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + maxZ = Math.max(maxZ, z); + } + return maxX > minCamX && minX < maxCamX && maxY > minCamY && minY < maxCamY && maxZ > minCamZ; + } +}