diff --git a/build.gradle b/build.gradle index bd19fe2..f445bc8 100644 --- a/build.gradle +++ b/build.gradle @@ -71,6 +71,12 @@ dependencies { modRuntimeOnly libs.mm } + modCompileOnly libs.sodium + + if (!inCI && enable_sodium.toBoolean()) { + modRuntimeOnly libs.sodium + } + include(implementation(annotationProcessor(libs.mixinextras.get()))) } diff --git a/gradle.properties b/gradle.properties index 1253ec3..17532c1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,3 +21,6 @@ modVersion=0.1.0 branchName=fabric-1.21.8 compatibleVersions=1.21.8 compatibleLoaders=fabric + +# mod compat +enable_sodium=true diff --git a/libs.versions.toml b/libs.versions.toml index 0ba7e40..123a4db 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -9,6 +9,7 @@ fapi = "0.130.0+1.21.8" mixinextras = "0.5.0" mm = "15.0.0-beta.3" manningham = "v2.3" +sodium = "mc1.21.6-0.6.13-fabric" [plugins] loom = { id = "fabric-loom", version.ref = "loom" } @@ -22,3 +23,4 @@ fapi = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = " mixinextras = { group = "io.github.llamalad7", name = "mixinextras-fabric", version.ref = "mixinextras" } mm = { group = "com.terraformersmc", name = "modmenu", version.ref = "mm" } manningham = { group = "com.github.Chocohead", name = "Fabric-ASM", version.ref = "manningham" } +sodium = { group = "maven.modrinth", name = "sodium", version.ref = "sodium" } diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/annotation/mixin/ConditionalMixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/annotation/mixin/ConditionalMixin.java new file mode 100644 index 0000000..3f84a7d --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/annotation/mixin/ConditionalMixin.java @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2023-Present Bawnorton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.slimeistdev.crystalline_sky.annotation.mixin; + +import io.github.slimeistdev.crystalline_sky.compat.Mods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ConditionalMixin { + Mods[] mods(); + + boolean applyIfPresent() default true; +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/compat/Mods.java b/src/main/java/io/github/slimeistdev/crystalline_sky/compat/Mods.java new file mode 100644 index 0000000..f0022cb --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/compat/Mods.java @@ -0,0 +1,41 @@ +package io.github.slimeistdev.crystalline_sky.compat; + +import net.fabricmc.loader.api.FabricLoader; + +import java.util.Optional; +import java.util.function.Supplier; + +public enum Mods { + SODIUM("sodium"); + + public final String id; + public final boolean isLoaded; + + Mods(String id) { + this.id = id; + this.isLoaded = FabricLoader.getInstance().isModLoaded(id); + } + + /** + * Simple hook to run code if a mod is installed + * + * @param toRun will be run only if the mod is loaded + * @return Optional.empty() if the mod is not loaded, otherwise an Optional of the return value of the given supplier + */ + public Optional runIfInstalled(Supplier> toRun) { + if (isLoaded) + return Optional.of(toRun.get().get()); + return Optional.empty(); + } + + /** + * Simple hook to execute code if a mod is installed + * + * @param toExecute will be executed only if the mod is loaded + */ + public void executeIfInstalled(Supplier toExecute) { + if (isLoaded) { + toExecute.get().run(); + } + } +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/compat/sodium/SkyShaderInterface.java b/src/main/java/io/github/slimeistdev/crystalline_sky/compat/sodium/SkyShaderInterface.java new file mode 100644 index 0000000..028e98f --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/compat/sodium/SkyShaderInterface.java @@ -0,0 +1,103 @@ +package io.github.slimeistdev.crystalline_sky.compat.sodium; + +import com.mojang.blaze3d.opengl.GlStateManager; +import com.mojang.blaze3d.textures.GpuTextureView; +import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems; +import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat3v; +import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat4v; +import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformInt; +import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f; +import net.caffeinemc.mods.sodium.client.render.chunk.shader.*; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; +import net.caffeinemc.mods.sodium.client.util.FogParameters; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.texture.GlTexture; +import net.minecraft.util.math.MathHelper; +import org.joml.Matrix4fc; +import org.lwjgl.opengl.GL32C; + +import java.util.EnumMap; +import java.util.Map; + +@SuppressWarnings("DeprecatedIsStillUsed") +public class SkyShaderInterface implements ChunkShaderInterface { + private final Map uniformTextures; + + private final GlUniformMatrix4f uniformModelViewMatrix; + private final GlUniformMatrix4f uniformProjectionMatrix; + private final GlUniformFloat3v uniformRegionOffset; + private final GlUniformFloat4v uniformColorModulator; + + // The fog shader component used by this program in order to set up the appropriate GL state + private final ChunkShaderFogComponent fogShader; + + public SkyShaderInterface(ShaderBindingContext context, ChunkShaderOptions options) { + this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new); + this.uniformProjectionMatrix = context.bindUniform("u_ProjectionMatrix", GlUniformMatrix4f::new); + this.uniformRegionOffset = context.bindUniform("u_RegionOffset", GlUniformFloat3v::new); + this.uniformColorModulator = context.bindUniform("u_ColorModulator", GlUniformFloat4v::new); + + this.uniformTextures = new EnumMap<>(ChunkShaderTextureSlot.class); + this.uniformTextures.put(ChunkShaderTextureSlot.BLOCK, context.bindUniform("u_BlockTex", GlUniformInt::new)); + + this.fogShader = options.fog().getFactory().apply(context); + } + + @Override // the shader interface should not modify pipeline state + public void setupState(TerrainRenderPass pass, FogParameters parameters) { + this.bindTexture(ChunkShaderTextureSlot.BLOCK, pass.getAtlas()); + + MinecraftClient client = MinecraftClient.getInstance(); + if (client.world != null + && client.player != null + && (client.player.getMainHandStack().isOf(CrystallineItems.SKY) + || client.player.getOffHandStack().isOf(CrystallineItems.SKY) + || client.player.getMainHandStack().isOf(CrystallineItems.WEEPING_SKY) + || client.player.getOffHandStack().isOf(CrystallineItems.WEEPING_SKY))) { + + float f = client.world.getTime() + client.getRenderTickCounter().getTickProgress(true); + float alpha = (MathHelper.sin(f / 10.0f) + 1.0f) / 2.0f; + // remap alpha from [0, 1] to [0, 0.75] + float maxAlpha = 1.0f - 0.25f; + alpha = alpha * maxAlpha; + this.uniformColorModulator.set(1.0f, 1.0f, 1.0f, alpha); + } else { + this.uniformColorModulator.set(1.0f, 1.0f, 1.0f, 1.0f); + } + + this.fogShader.setup(parameters); + } + + @Override // the shader interface should not modify pipeline state + public void resetState() { + // This is used by alternate implementations. + } + + @Deprecated(forRemoval = true) // should be handled properly in GFX instead. + private void bindTexture(ChunkShaderTextureSlot slot, GpuTextureView textureView) { + GlTexture tex = (GlTexture) textureView.texture(); + GlStateManager._activeTexture(GL32C.GL_TEXTURE0 + slot.ordinal()); + GlStateManager._bindTexture(tex.getGlId()); + GlStateManager._texParameter(GL32C.GL_TEXTURE_2D, 33084, textureView.baseMipLevel()); + GlStateManager._texParameter(GL32C.GL_TEXTURE_2D, 33085, textureView.baseMipLevel() + textureView.mipLevels() - 1); + tex.checkDirty(GL32C.GL_TEXTURE_2D); + + var uniform = this.uniformTextures.get(slot); + uniform.setInt(slot.ordinal()); + } + + @Override + public void setProjectionMatrix(Matrix4fc matrix) { + this.uniformProjectionMatrix.set(matrix); + } + + @Override + public void setModelViewMatrix(Matrix4fc matrix) { + this.uniformModelViewMatrix.set(matrix); + } + + @Override + public void setRegionOffset(float x, float y, float z) { + this.uniformRegionOffset.set(x, y, z); + } +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/ChunkSkyLightMixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/ChunkSkyLightMixin.java index 1bd1f9e..0c74bd8 100644 --- a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/ChunkSkyLightMixin.java +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/ChunkSkyLightMixin.java @@ -143,8 +143,8 @@ public abstract class ChunkSkyLightMixin implements ChunkSkyLight_Duck { crystalline_sky$weepingStorage.insertSolid(localX, y - weepingMinY, localZ, crystalline_sky$weepingScanner); } - CrystallineSky.LOG.info("Weeping sky ranges for local column [{}, {}] in {}:", localX, localZ, blockView); - crystalline_sky$weepingStorage.debugState(localX, localZ, CrystallineSky.LOG::info); + CrystallineSky.LOG.debug("Weeping sky ranges for local column [{}, {}] in {}:", localX, localZ, blockView); + crystalline_sky$weepingStorage.debugState(localX, localZ, CrystallineSky.LOG::debug); crystalline_sky$weepingScanner.setBlockView(null); } diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/CrystallineSkyMixinPlugin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/CrystallineSkyMixinPlugin.java new file mode 100644 index 0000000..fbd5a66 --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/CrystallineSkyMixinPlugin.java @@ -0,0 +1,38 @@ +package io.github.slimeistdev.crystalline_sky.mixin; + +import io.github.slimeistdev.crystalline_sky.util.ConditionalMixinManager; +import org.objectweb.asm.tree.ClassNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class CrystallineSkyMixinPlugin implements IMixinConfigPlugin { + public static final Logger LOGGER = LoggerFactory.getLogger("CrystallineSky/MixinPlugin"); + + @Override + public void onLoad(String mixinPackage) { } // NO-OP + + @Override + public String getRefMapperConfig() { return null; } // DEFAULT + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return ConditionalMixinManager.shouldApply(mixinClassName); + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { } // NO-OP + + @Override + public List getMixins() { return null; } // DEFAULT + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } // NO-OP + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } // NO-OP +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/WorldRendererMixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/WorldRendererMixin.java index 282a45c..969ab05 100644 --- a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/WorldRendererMixin.java +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/WorldRendererMixin.java @@ -1,16 +1,11 @@ package io.github.slimeistdev.crystalline_sky.mixin.client; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.buffers.GpuBufferSlice; -import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerExt; import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerGroupExt; import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.DefaultFramebufferSet_Duck; import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.Framebuffer_Duck; -import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.DynamicUniforms; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gl.SimpleFramebufferFactory; import net.minecraft.client.option.CloudRenderMode; @@ -18,10 +13,10 @@ import net.minecraft.client.render.*; import net.minecraft.client.util.Handle; import net.minecraft.client.util.ObjectAllocator; import net.minecraft.client.world.ClientWorld; -import net.minecraft.util.math.MathHelper; import net.minecraft.util.profiler.Profiler; import org.jetbrains.annotations.Nullable; -import org.joml.*; +import org.joml.Matrix4f; +import org.joml.Vector4f; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -61,13 +56,7 @@ public abstract class WorldRendererMixin { Vector4f fogColor, boolean shouldRenderSky, CallbackInfo ci, @Local FrameGraphBuilder frameGraphBuilder, @Local SimpleFramebufferFactory factory) { - SimpleFramebufferFactory depthlessFactory = new SimpleFramebufferFactory( - factory.width(), - factory.height(), - false, - factory.clearColor() - ); - Handle framebuffer = frameGraphBuilder.createResourceHandle("crystalline_sky:sky_framebuffer", depthlessFactory); + Handle framebuffer = frameGraphBuilder.createResourceHandle("crystalline_sky:sky_framebuffer", factory); ((DefaultFramebufferSet_Duck) framebufferSet).crystalline_sky$setSkyFramebuffer(framebuffer); } @@ -149,27 +138,4 @@ public abstract class WorldRendererMixin { @Local SectionRenderState sectionRenderState) { sectionRenderState.renderSection(BlockRenderLayerGroupExt.CRYSTALLINE_SKY_SKY); } - - @WrapOperation(method = "renderBlockLayers", at = @At(value = "NEW", target = "net/minecraft/client/gl/DynamicUniforms$UniformValue")) - private DynamicUniforms.UniformValue tintSkyBlocks(Matrix4fc modelView, Vector4fc colorModulator, - Vector3fc modelOffset, Matrix4fc textureMatrix, float lineWidth, - Operation original, - @Local BlockRenderLayer blockRenderLayer) { - if (blockRenderLayer == BlockRenderLayerExt.CRYSTALLINE_SKY_SKY - && client.player != null - && (client.player.getMainHandStack().isOf(CrystallineItems.SKY) - || client.player.getOffHandStack().isOf(CrystallineItems.SKY) - || client.player.getMainHandStack().isOf(CrystallineItems.WEEPING_SKY) - || client.player.getOffHandStack().isOf(CrystallineItems.WEEPING_SKY))) { - - float f = ticks + client.getRenderTickCounter().getTickProgress(true); - float alpha = (MathHelper.sin(f / 10.0f) + 1.0f) / 2.0f; - // remap alpha from [0, 1] to [0, 0.75] - float maxAlpha = 1.0f - 0.25f; - alpha = alpha * maxAlpha; - colorModulator = new Vector4f(1.0f, 1.0f, 1.0f, alpha).mul(colorModulator); - } - - return original.call(modelView, colorModulator, modelOffset, textureMatrix, lineWidth); - } } diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/WorldRenderer_NoSodium_Mixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/WorldRenderer_NoSodium_Mixin.java new file mode 100644 index 0000000..5e9a7d3 --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/WorldRenderer_NoSodium_Mixin.java @@ -0,0 +1,56 @@ +package io.github.slimeistdev.crystalline_sky.mixin.client; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin; +import io.github.slimeistdev.crystalline_sky.compat.Mods; +import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerExt; +import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.DynamicUniforms; +import net.minecraft.client.render.BlockRenderLayer; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.util.math.MathHelper; +import org.joml.Matrix4fc; +import org.joml.Vector3fc; +import org.joml.Vector4f; +import org.joml.Vector4fc; +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; + +@ConditionalMixin(mods = Mods.SODIUM, applyIfPresent = false) +@Mixin(WorldRenderer.class) +public class WorldRenderer_NoSodium_Mixin { + @Shadow + @Final + private MinecraftClient client; + + @Shadow + private int ticks; + + @WrapOperation(method = "renderBlockLayers", at = @At(value = "NEW", target = "net/minecraft/client/gl/DynamicUniforms$UniformValue")) + private DynamicUniforms.UniformValue tintSkyBlocks(Matrix4fc modelView, Vector4fc colorModulator, + Vector3fc modelOffset, Matrix4fc textureMatrix, float lineWidth, + Operation original, + @Local BlockRenderLayer blockRenderLayer) { + if (blockRenderLayer == BlockRenderLayerExt.CRYSTALLINE_SKY_SKY + && client.player != null + && (client.player.getMainHandStack().isOf(CrystallineItems.SKY) + || client.player.getOffHandStack().isOf(CrystallineItems.SKY) + || client.player.getMainHandStack().isOf(CrystallineItems.WEEPING_SKY) + || client.player.getOffHandStack().isOf(CrystallineItems.WEEPING_SKY))) { + + float f = ticks + client.getRenderTickCounter().getTickProgress(true); + float alpha = (MathHelper.sin(f / 10.0f) + 1.0f) / 2.0f; + // remap alpha from [0, 1] to [0, 0.75] + float maxAlpha = 1.0f - 0.25f; + alpha = alpha * maxAlpha; + colorModulator = new Vector4f(1.0f, 1.0f, 1.0f, alpha).mul(colorModulator); + } + + return original.call(modelView, colorModulator, modelOffset, textureMatrix, lineWidth); + } +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/DefaultMaterialsMixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/DefaultMaterialsMixin.java new file mode 100644 index 0000000..9c8787b --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/DefaultMaterialsMixin.java @@ -0,0 +1,39 @@ +package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium; + +import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin; +import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerExt; +import io.github.slimeistdev.crystalline_sky.compat.Mods; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.Material; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.parameters.AlphaCutoffParameter; +import net.minecraft.client.render.BlockRenderLayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Arrays; + +@ConditionalMixin(mods = Mods.SODIUM) +@Mixin(DefaultMaterials.class) +public class DefaultMaterialsMixin { + @Unique + @SuppressWarnings("OptionalGetWithoutIsPresent") + private static final Material crystalline_sky$SKY = new Material( + Arrays.stream(DefaultTerrainRenderPasses.ALL) + .filter(pass -> ((TerrainRenderPassAccessor) pass).crystalline_sky$getRenderLayer() == BlockRenderLayerExt.CRYSTALLINE_SKY_SKY) + .findFirst() + .get(), + AlphaCutoffParameter.ZERO, + false + ); + + @Inject(method = "forChunkLayer", at = @At(value = "NEW", target = "(Ljava/lang/String;Ljava/lang/Throwable;)Ljava/lang/MatchException;"), cancellable = true) + private static void addCrystallineSkyMaterial(BlockRenderLayer layer, CallbackInfoReturnable cir) { + if (layer == BlockRenderLayerExt.CRYSTALLINE_SKY_SKY) { + cir.setReturnValue(crystalline_sky$SKY); + } + } +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/DefaultTerrainRenderPassesMixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/DefaultTerrainRenderPassesMixin.java new file mode 100644 index 0000000..30229f4 --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/DefaultTerrainRenderPassesMixin.java @@ -0,0 +1,31 @@ +package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium; + +import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin; +import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerExt; +import io.github.slimeistdev.crystalline_sky.compat.Mods; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +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; + +@ConditionalMixin(mods = Mods.SODIUM) +@Mixin(DefaultTerrainRenderPasses.class) +public class DefaultTerrainRenderPassesMixin { + @Mutable + @Shadow + @Final + public static TerrainRenderPass[] ALL; + + @Inject(method = "", at = @At("RETURN")) + private static void addCrystallineSkyPass(CallbackInfo ci) { + var ALL$ = new TerrainRenderPass[ALL.length + 1]; + System.arraycopy(ALL, 0, ALL$, 0, ALL.length); + ALL$[ALL.length] = new TerrainRenderPass(BlockRenderLayerExt.CRYSTALLINE_SKY_SKY, false, true); + ALL = ALL$; + } +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/LICENSE b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/LICENSE new file mode 100644 index 0000000..f80154c --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/LICENSE @@ -0,0 +1,6 @@ +The following mixins in this package are from derived from https://github.com/wisp-forest/affinity, and as such are licensed under the MIT license: + +- DefaultMaterialsMixin +- DefaultTerrainRenderPassesMixin +- SodiumWorldRendererMixin +- TerrainRenderPassAccessor diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/ShaderChunkRendererMixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/ShaderChunkRendererMixin.java new file mode 100644 index 0000000..3164046 --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/ShaderChunkRendererMixin.java @@ -0,0 +1,50 @@ +package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin; +import io.github.slimeistdev.crystalline_sky.compat.Mods; +import io.github.slimeistdev.crystalline_sky.compat.sodium.SkyShaderInterface; +import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerExt; +import net.caffeinemc.mods.sodium.client.gl.shader.GlProgram; +import net.caffeinemc.mods.sodium.client.render.chunk.ShaderChunkRenderer; +import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderInterface; +import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderOptions; +import net.caffeinemc.mods.sodium.client.render.chunk.shader.ShaderBindingContext; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.function.Function; + +@ConditionalMixin(mods = Mods.SODIUM) +@Mixin(ShaderChunkRenderer.class) +public class ShaderChunkRendererMixin { + @WrapOperation(method = "compileProgram", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer;createShader(Ljava/lang/String;Lnet/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderOptions;)Lnet/caffeinemc/mods/sodium/client/gl/shader/GlProgram;"), remap = false) + private GlProgram createSkyShader(ShaderChunkRenderer instance, String path, ChunkShaderOptions options, Operation> original) { + if (options.pass() == DefaultMaterials.forChunkLayer(BlockRenderLayerExt.CRYSTALLINE_SKY_SKY).pass) { + return original.call(instance, "sodium_compat/block_layer_sky", options); + } + + return original.call(instance, path, options); + } + + @WrapOperation(method = "createShader", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Identifier;of(Ljava/lang/String;Ljava/lang/String;)Lnet/minecraft/util/Identifier;")) + private Identifier redirectShaderNamespace(String namespace, String path, Operation original, String pathArg) { + if (pathArg.equals("sodium_compat/block_layer_sky")) { + namespace = "crystalline_sky"; + } + + return original.call(namespace, path); + } + + @WrapOperation(method = "createShader", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/gl/shader/GlProgram$Builder;link(Ljava/util/function/Function;)Lnet/caffeinemc/mods/sodium/client/gl/shader/GlProgram;"), remap = false) + private static GlProgram useSkyShaderInterface(GlProgram.Builder instance, Function factory, Operation> original, String argPath, ChunkShaderOptions argOptions) { + if (argPath.equals("sodium_compat/block_layer_sky")) { + factory = (shader) -> new SkyShaderInterface(shader, argOptions); + } + + return original.call(instance, factory); + } +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/SodiumWorldRendererMixin.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/SodiumWorldRendererMixin.java new file mode 100644 index 0000000..dc5f2ff --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/SodiumWorldRendererMixin.java @@ -0,0 +1,30 @@ +package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium; + +import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin; +import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerExt; +import io.github.slimeistdev.crystalline_sky.extenders_cove.BlockRenderLayerGroupExt; +import io.github.slimeistdev.crystalline_sky.compat.Mods; +import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer; +import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices; +import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials; +import net.minecraft.client.render.BlockRenderLayerGroup; +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; + +@ConditionalMixin(mods = Mods.SODIUM) +@Mixin(SodiumWorldRenderer.class) +public class SodiumWorldRendererMixin { + @Shadow + private RenderSectionManager renderSectionManager; + + @Inject(method = "drawChunkLayer", at = @At("RETURN")) + private void drawCrystallineLayer(BlockRenderLayerGroup group, ChunkRenderMatrices matrices, double x, double y, double z, CallbackInfo ci) { + if (group != BlockRenderLayerGroupExt.CRYSTALLINE_SKY_SKY) return; + + renderSectionManager.renderLayer(matrices, DefaultMaterials.forChunkLayer(BlockRenderLayerExt.CRYSTALLINE_SKY_SKY).pass, x, y, z); + } +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/TerrainRenderPassAccessor.java b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/TerrainRenderPassAccessor.java new file mode 100644 index 0000000..4c3f9a6 --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/mixin/client/compat/sodium/TerrainRenderPassAccessor.java @@ -0,0 +1,15 @@ +package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium; + +import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin; +import io.github.slimeistdev.crystalline_sky.compat.Mods; +import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; +import net.minecraft.client.render.BlockRenderLayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@ConditionalMixin(mods = Mods.SODIUM) +@Mixin(TerrainRenderPass.class) +public interface TerrainRenderPassAccessor { + @Accessor("renderType") + BlockRenderLayer crystalline_sky$getRenderLayer(); +} diff --git a/src/main/java/io/github/slimeistdev/crystalline_sky/util/ConditionalMixinManager.java b/src/main/java/io/github/slimeistdev/crystalline_sky/util/ConditionalMixinManager.java new file mode 100644 index 0000000..f282d6f --- /dev/null +++ b/src/main/java/io/github/slimeistdev/crystalline_sky/util/ConditionalMixinManager.java @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2023-Present Bawnorton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.slimeistdev.crystalline_sky.util; + +import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin; +import io.github.slimeistdev.crystalline_sky.compat.Mods; +import io.github.slimeistdev.crystalline_sky.mixin.CrystallineSkyMixinPlugin; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AnnotationNode; +import org.spongepowered.asm.service.MixinService; +import org.spongepowered.asm.util.Annotations; + +import java.io.IOException; +import java.util.List; + +public class ConditionalMixinManager { + public static boolean shouldApply(String className) { + try { + List annotationNodes = MixinService.getService().getBytecodeProvider().getClassNode(className).visibleAnnotations; + if (annotationNodes == null) return true; + + boolean shouldApply = true; + for (AnnotationNode node : annotationNodes) { + if (node.desc.equals(Type.getDescriptor(ConditionalMixin.class))) { + List mods = Annotations.getValue(node, "mods", true, Mods.class); + boolean applyIfPresent = Annotations.getValue(node, "applyIfPresent", Boolean.TRUE); + boolean anyModsLoaded = anyModsLoaded(mods); + shouldApply = anyModsLoaded == applyIfPresent; + CrystallineSkyMixinPlugin.LOGGER.debug("{} is{}being applied because the mod(s) {} are{}loaded", className, shouldApply ? " " : " not ", mods, anyModsLoaded ? " " : " not "); + } + } + return shouldApply; + } catch (ClassNotFoundException | IOException e) { + throw new RuntimeException(e); + } + } + + private static boolean anyModsLoaded(List mods) { + for (Mods mod : mods) { + if (mod.isLoaded) return true; + } + return false; + } +} diff --git a/src/main/resources/assets/crystalline_sky/shaders/include/projection.glsl b/src/main/resources/assets/crystalline_sky/shaders/include/projection.glsl new file mode 100644 index 0000000..c976cb8 --- /dev/null +++ b/src/main/resources/assets/crystalline_sky/shaders/include/projection.glsl @@ -0,0 +1,6 @@ +vec4 projection_from_position(vec4 position) { + vec4 projection = position * 0.5; + projection.xy = vec2(projection.x + projection.w, projection.y + projection.w); + projection.zw = position.zw; + return projection; +} diff --git a/src/main/resources/assets/crystalline_sky/shaders/sodium_compat/block_layer_sky.fsh b/src/main/resources/assets/crystalline_sky/shaders/sodium_compat/block_layer_sky.fsh new file mode 100644 index 0000000..ca4eb5a --- /dev/null +++ b/src/main/resources/assets/crystalline_sky/shaders/sodium_compat/block_layer_sky.fsh @@ -0,0 +1,50 @@ +#version 330 core + +#import + +in vec4 v_Color; // The interpolated vertex color +in vec4 v_TexProj; // The interpolated sky texture coordinates +in vec2 v_FragDistance; // The fragment's distance from the camera (cylindrical and spherical) + +in float v_MaterialMipBias; +in float v_MaterialAlphaCutoff; + +uniform sampler2D u_BlockTex; // The block (sky) texture + +uniform vec4 u_FogColor; // The color of the shader fog +uniform vec2 u_EnvironmentFog; // The start and end position for environmental fog +uniform vec2 u_RenderFog; // The start and end position for border fog + +uniform vec4 u_ColorModulator; + +out vec4 fragColor; // The output fragment for the color framebuffer + +#define SCALE 1 +#define BAYER_BIAS 0.03125 + +const mat4x4 bayerMatrix4x4 = mat4x4( + 0.0, 8.0, 2.0, 10.0, + 12.0, 4.0, 14.0, 6.0, + 3.0, 11.0, 1.0, 9.0, + 15.0, 7.0, 13.0, 5.0 +) / 16.0; + +void main() { + vec4 diffuseColor = textureProj(u_BlockTex, v_TexProj, v_MaterialMipBias);//texture(u_BlockTex, v_TexCoord, v_MaterialMipBias); + + /*// Apply per-vertex color + diffuseColor *= v_Color;*/ + diffuseColor *= vec4(u_ColorModulator.rgb, 1.0); + +#ifdef USE_FRAGMENT_DISCARD + int x = int(gl_FragCoord.x / SCALE) % 4; + int y = int(gl_FragCoord.y / SCALE) % 4; + float bayer = bayerMatrix4x4[y][x]; + + if (u_ColorModulator.a <= bayer + BAYER_BIAS) { + discard; + } +#endif + + fragColor = _linearFog(diffuseColor, v_FragDistance, u_FogColor, u_EnvironmentFog, u_RenderFog); +} diff --git a/src/main/resources/assets/crystalline_sky/shaders/sodium_compat/block_layer_sky.vsh b/src/main/resources/assets/crystalline_sky/shaders/sodium_compat/block_layer_sky.vsh new file mode 100644 index 0000000..1ce49e2 --- /dev/null +++ b/src/main/resources/assets/crystalline_sky/shaders/sodium_compat/block_layer_sky.vsh @@ -0,0 +1,54 @@ +#version 330 core + +#import +#import +#import +#import + +#import + +out vec4 v_Color; +out vec4 v_TexProj; + +out float v_MaterialMipBias; +#ifdef USE_FRAGMENT_DISCARD +out float v_MaterialAlphaCutoff; +#endif + +#ifdef USE_FOG +out vec2 v_FragDistance; +#endif + +uniform vec3 u_RegionOffset; + +uvec3 _get_relative_chunk_coord(uint pos) { + // Packing scheme is defined by LocalSectionIndex + return uvec3(pos) >> uvec3(5u, 0u, 2u) & uvec3(7u, 3u, 7u); +} + +vec3 _get_draw_translation(uint pos) { + return _get_relative_chunk_coord(pos) * vec3(16.0); +} + +void main() { + _vert_init(); + + // Transform the chunk-local vertex position into world model space + vec3 translation = u_RegionOffset + _get_draw_translation(_draw_id); + vec3 position = _vert_position + translation; + +#ifdef USE_FOG + v_FragDistance = getFragDistance(position); +#endif + + // Transform the vertex position into model-view-projection space + gl_Position = u_ProjectionMatrix * u_ModelViewMatrix * vec4(position, 1.0); + + v_Color = _vert_color; + v_TexProj = projection_from_position(gl_Position); + + v_MaterialMipBias = _material_mip_bias(_material_params); +#ifdef USE_FRAGMENT_DISCARD + v_MaterialAlphaCutoff = _material_alpha_cutoff(_material_params); +#endif +} diff --git a/src/main/resources/crystalline_sky.mixins.json b/src/main/resources/crystalline_sky.mixins.json index f90666c..a262149 100644 --- a/src/main/resources/crystalline_sky.mixins.json +++ b/src/main/resources/crystalline_sky.mixins.json @@ -5,6 +5,7 @@ "minVersion": "0.5.0" }, "package": "io.github.slimeistdev.crystalline_sky.mixin", + "plugin": "io.github.slimeistdev.crystalline_sky.mixin.CrystallineSkyMixinPlugin", "compatibilityLevel": "JAVA_21", "mixins": [ "ChunkLightProviderMixin", @@ -19,8 +20,14 @@ "client.DebugRendererMixin", "client.DefaultFramebufferSetMixin", "client.FramebufferMixin", + "client.WorldRenderer_NoSodium_Mixin", "client.WorldRendererAccessor", - "client.WorldRendererMixin" + "client.WorldRendererMixin", + "client.compat.sodium.DefaultMaterialsMixin", + "client.compat.sodium.DefaultTerrainRenderPassesMixin", + "client.compat.sodium.ShaderChunkRendererMixin", + "client.compat.sodium.SodiumWorldRendererMixin", + "client.compat.sodium.TerrainRenderPassAccessor" ], "server": [ ],