diff --git a/build.gradle.kts b/build.gradle.kts index 70e5c6d..dd03657 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -85,7 +85,8 @@ subprojects { runs.configureEach { vmArg("-XX:+AllowEnhancedClassRedefinition") vmArg("-XX:+IgnoreUnrecognizedVMOptions") - vmArg("-Dmixin.debug.export=true") + // Disabled by default: exports every mixin target to disk and can make startup hang for minutes. + // Enable manually when debugging mixins: -Dmixin.debug.export=true vmArg("-Dmixin.env.remapRefMap=true") vmArg("-Dmixin.env.refMapRemappingFile=${projectDir}/build/createSrgToMcp/output.srg") } diff --git a/common/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/LevelRendererMixin.java b/common/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/LevelRendererMixin.java index dbead1f..d1cdaca 100644 --- a/common/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/LevelRendererMixin.java +++ b/common/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/LevelRendererMixin.java @@ -4,6 +4,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.pipeline.TextureTarget; +import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import io.github.slimeistdev.crystalline_sky.compat.Mods; import io.github.slimeistdev.crystalline_sky.compat.iris.IrisHelpers; @@ -15,10 +16,12 @@ import net.minecraft.client.Camera; import net.minecraft.client.CloudStatus; import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderType; +import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; @@ -48,6 +51,9 @@ public abstract class LevelRendererMixin implements LevelRenderer_Duck { @Shadow public abstract void renderClouds(PoseStack matrices, Matrix4f matrix4f, Matrix4f matrix4f2, float tickDelta, double cameraX, double cameraY, double cameraZ); + @Shadow + public abstract void renderSky(Matrix4f frustumMatrix, Matrix4f projectionMatrix, float partialTick, Camera camera, boolean isFoggy, Runnable skyFogSetup); + @Unique @Nullable private RenderTarget crystalline_sky$skyBuffer; @@ -104,9 +110,15 @@ public abstract class LevelRendererMixin implements LevelRenderer_Duck { Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo ci) { var skyBuffer = crystalline_sky$getSkyFramebuffer(); skyBuffer.clear(Minecraft.ON_OSX); - var copiedFromIris = Mods.IRIS.runIfInstalled(() -> () -> IrisHelpers.copySkyToBuffer(skyBuffer)); - if (!copiedFromIris.orElse(false)) - ((RenderTarget_Duck) skyBuffer).crystalline_sky$copyColorFrom(minecraft.getMainRenderTarget()); + + float partialTick = tickCounter.getGameTimeDeltaPartialTick(false); + if (crystalline_sky$isBelowHorizon(partialTick)) { + crystalline_sky$renderSkyBuffer(skyBuffer, tickCounter, camera, gameRenderer, matrix4f, matrix4f2); + } else { + var copiedFromIris = Mods.IRIS.runIfInstalled(() -> () -> IrisHelpers.copySkyToBuffer(skyBuffer)); + if (!copiedFromIris.orElse(false)) + ((RenderTarget_Duck) skyBuffer).crystalline_sky$copyColorFrom(minecraft.getMainRenderTarget()); + } // render clouds CloudStatus cloudRenderMode = this.minecraft.options.getCloudsType(); @@ -143,4 +155,60 @@ public abstract class LevelRendererMixin implements LevelRenderer_Duck { renderSectionLayer(CrystallineRenderTypes.SKY, x, y, z, matrix4f, positionMatrix); renderSectionLayer(CrystallineRenderTypes.SKYBOX, x, y, z, matrix4f, positionMatrix); } + + @Unique + private boolean crystalline_sky$isBelowHorizon(float partialTick) { + var level = minecraft.level; + var player = minecraft.player; + if (level == null || player == null) { + return false; + } + + double eyeY = player.getEyePosition(partialTick).y; + double horizon = level.getLevelData().getHorizonHeight(level); + return eyeY < horizon; + } + + @Unique + private void crystalline_sky$renderSkyBuffer( + RenderTarget skyBuffer, + DeltaTracker tickCounter, + Camera camera, + GameRenderer gameRenderer, + Matrix4f frustumMatrix, + Matrix4f projectionMatrix + ) { + float partialTick = tickCounter.getGameTimeDeltaPartialTick(false); + Vec3 cameraPos = camera.getPosition(); + float renderDistance = gameRenderer.getRenderDistance(); + boolean isFoggy = minecraft.level.effects().isFoggyAt(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y)) + || minecraft.gui.getBossOverlay().shouldCreateWorldFog(); + + skyBuffer.bindWrite(false); + SharedRenderVariables.pushSkipVoidSky(); + try { + FogRenderer.setupColor( + camera, + partialTick, + minecraft.level, + minecraft.options.getEffectiveRenderDistance(), + gameRenderer.getDarkenWorldAmount(partialTick) + ); + FogRenderer.levelFogColor(); + RenderSystem.clear(16640, Minecraft.ON_OSX); + FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, isFoggy, partialTick); + RenderSystem.setShader(GameRenderer::getPositionShader); + this.renderSky( + frustumMatrix, + projectionMatrix, + partialTick, + camera, + isFoggy, + () -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, isFoggy, partialTick) + ); + } finally { + SharedRenderVariables.popSkipVoidSky(); + minecraft.getMainRenderTarget().bindWrite(false); + } + } } diff --git a/common/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/LevelRendererRenderSkyMixin.java b/common/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/LevelRendererRenderSkyMixin.java new file mode 100644 index 0000000..5fdfaef --- /dev/null +++ b/common/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/LevelRendererRenderSkyMixin.java @@ -0,0 +1,32 @@ +package io.github.slimeistdev.crystalline_sky.mixin.client; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import io.github.slimeistdev.crystalline_sky.util.SharedRenderVariables; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.world.level.LevelHeightAccessor; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(value = LevelRenderer.class, remap = false) +public class LevelRendererRenderSkyMixin { + @WrapOperation( + method = "renderSky", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/multiplayer/ClientLevel$ClientLevelData;getHorizonHeight(Lnet/minecraft/world/level/LevelHeightAccessor;)D" + ) + ) + private double crystalline_sky$skipVoidSkyWhenCapturing( + ClientLevel.ClientLevelData levelData, + LevelHeightAccessor level, + Operation original + ) { + if (SharedRenderVariables.shouldSkipVoidSky()) { + return Double.NEGATIVE_INFINITY; + } + + return original.call(levelData, level); + } +} diff --git a/common/src/main/java/io/github/slimeistdev/crystalline_sky/util/SharedRenderVariables.java b/common/src/main/java/io/github/slimeistdev/crystalline_sky/util/SharedRenderVariables.java index 995eb3b..bc561f6 100644 --- a/common/src/main/java/io/github/slimeistdev/crystalline_sky/util/SharedRenderVariables.java +++ b/common/src/main/java/io/github/slimeistdev/crystalline_sky/util/SharedRenderVariables.java @@ -8,6 +8,7 @@ public class SharedRenderVariables { // because chunk meshing is multithreaded :( public static final ThreadLocal shadeFullBright = ThreadLocal.withInitial(() -> new MutableInt(0)); private static int blockIrisShaderFramebufferBind = 0; + private static int skipVoidSky = 0; public static boolean shouldShadeFullBright() { return shadeFullBright.get().intValue() > 0; @@ -32,4 +33,16 @@ public class SharedRenderVariables { public static void popBlockIrisShaderFramebufferBind() { blockIrisShaderFramebufferBind--; } + + public static boolean shouldSkipVoidSky() { + return skipVoidSky > 0; + } + + public static void pushSkipVoidSky() { + skipVoidSky++; + } + + public static void popSkipVoidSky() { + skipVoidSky--; + } } diff --git a/common/src/main/resources/crystalline_sky-common.mixins.json b/common/src/main/resources/crystalline_sky-common.mixins.json index 36c34e2..bba841e 100644 --- a/common/src/main/resources/crystalline_sky-common.mixins.json +++ b/common/src/main/resources/crystalline_sky-common.mixins.json @@ -22,6 +22,7 @@ "client.GameRendererMixin", "client.LevelRenderer_NoSodium_Mixin", "client.LevelRendererMixin", + "client.LevelRendererRenderSkyMixin", "client.ModelBlockRendererMixin", "client.RenderTargetMixin", "client.TextureAtlasAccessor", diff --git a/gradle.properties b/gradle.properties index 4e61eaa..c97fa1d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,10 @@ org.gradle.jvmargs=-Xmx2G -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 # -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006 org.gradle.caching=true -org.gradle.configureondemand=true +# Disabled: with configure-on-demand, :fabric is not configured when running :neoforge:runClient, +# but prepareArchitecturyTransformer queries all Loom projects and fails with +# "Cannot get MappingsProvider before it has been setup". +org.gradle.configureondemand=false # IntelliJ's run configs don't work with the configuration cache org.gradle.configuration-cache=false # Loom @@ -26,7 +29,11 @@ modVersion=0.2.0 branchName=1.21.1 compatibleVersions=1.21.1 -# mod compat +# mod compat (compileOnly) enable_sodium=true enable_iris=true +# modLocalRuntime for Sodium/Iris hangs NeoForge dev startup for minutes (mixin stalls on RenderTarget). +# For in-game Sodium/Iris testing, copy the jars into neoforge/run/mods/ instead. +enable_sodium_runtime=false +enable_iris_runtime=false enable_axiom=false diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts index fdf0730..cd7b85b 100644 --- a/neoforge/build.gradle.kts +++ b/neoforge/build.gradle.kts @@ -32,12 +32,12 @@ dependencies { modCompileOnly(libs.ffapi.renderer) // only for sodium compat modCompileOnly(libs.sodium.neoforge) - if (!inCI && "enable_sodium"().toBoolean()) { // this doesn't work, because of jar-in-jar. Sodium must be placed in the mods folder + if (!inCI && "enable_sodium_runtime"().toBoolean()) { // Sodium must be in run/mods for jar-in-jar; modLocalRuntime stalls dev startup modLocalRuntime(libs.sodium.neoforge) } modCompileOnly(libs.iris.neoforge) - if (!inCI && "enable_iris"().toBoolean()) { // this doesn't work, because of jar-in-jar. Iris must be placed in the mods folder + if (!inCI && "enable_iris_runtime"().toBoolean()) { // Iris must be in run/mods for jar-in-jar; modLocalRuntime stalls dev startup modLocalRuntime(libs.iris.neoforge) /*modLocalRuntime("org.antlr:antlr4-runtime:4.13.1") modLocalRuntime("io.github.douira:glsl-transformer:2.0.1")