From 16ef943b3c2ce8db2331332261143a12bdba61cf Mon Sep 17 00:00:00 2001 From: Vixid <52578495+VixidDev@users.noreply.github.com> Date: Sat, 14 Oct 2023 10:05:48 +0100 Subject: Feature: SBA Chroma but in SH (#487) Porting SBA's chroma into SkyHanni with many more options and chroma everything. #487 --- src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt | 2 +- .../at/hannibal2/skyhanni/config/Features.java | 5 + .../skyhanni/config/features/ChromaConfig.java | 62 ++++++++++ .../at/hannibal2/skyhanni/data/MinecraftData.kt | 8 +- .../skyhanni/features/chroma/ChromaFontRenderer.kt | 66 ++++++++++ .../skyhanni/features/chroma/ChromaShader.kt | 47 +++++++ .../features/chroma/ChromaShaderManager.kt | 38 ++++++ .../skyhanni/mixins/hooks/FontRendererHook.kt | 135 +++++++++++++++++++++ .../mixins/transformers/AccessorFontRenderer.java | 12 ++ .../mixins/transformers/AccessorMinecraft.java | 13 ++ .../mixins/transformers/MixinFontRenderer.java | 75 ++++++++++++ .../transformers/MixinPatcherFontRendererHook.java | 21 ++++ .../java/at/hannibal2/skyhanni/utils/ColorUtils.kt | 11 ++ .../at/hannibal2/skyhanni/utils/shader/Shader.kt | 55 +++++++++ .../skyhanni/utils/shader/ShaderHelper.kt | 124 +++++++++++++++++++ .../skyhanni/utils/shader/ShaderManager.kt | 87 +++++++++++++ .../at/hannibal2/skyhanni/utils/shader/Uniform.kt | 45 +++++++ .../resources/assets/skyhanni/shaders/chroma.fsh | 43 +++++++ .../resources/assets/skyhanni/shaders/chroma.vsh | 15 +++ src/main/resources/mixins.skyhanni.json | 7 ++ 20 files changed, 866 insertions(+), 5 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/ChromaConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaFontRenderer.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShader.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/hooks/FontRendererHook.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorFontRenderer.java create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorMinecraft.java create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinPatcherFontRendererHook.java create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt create mode 100644 src/main/resources/assets/skyhanni/shaders/chroma.fsh create mode 100644 src/main/resources/assets/skyhanni/shaders/chroma.vsh (limited to 'src') diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index edd5d62b5..4e4cbc68d 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -331,7 +331,7 @@ class SkyHanniMod { loadModule(TestExportTools) loadModule(ItemClickData()) // loadModule(Year300RaffleEvent) - loadModule(MinecraftData()) + loadModule(MinecraftData) loadModule(TitleManager()) loadModule(ItemTipHelper()) loadModule(RenderLivingEntityHelper()) diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java index 95ffa7f6c..f5916cda8 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Features.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java @@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.SkyHanniMod; import at.hannibal2.skyhanni.config.features.About; import at.hannibal2.skyhanni.config.features.BazaarConfig; import at.hannibal2.skyhanni.config.features.ChatConfig; +import at.hannibal2.skyhanni.config.features.ChromaConfig; import at.hannibal2.skyhanni.config.features.CombatConfig; import at.hannibal2.skyhanni.config.features.CommandsConfig; import at.hannibal2.skyhanni.config.features.CrimsonIsleConfig; @@ -67,6 +68,10 @@ public class Features extends Config { @Category(name = "GUI", desc = "Change the locations of GUI elements. (§e/sh gui§7)") public GUIConfig gui = new GUIConfig(); + @Expose + @Category(name = "Chroma", desc = "Settings for Chroma text. (Credit to SBA)") + public ChromaConfig chroma = new ChromaConfig(); + @Expose @Category(name = "Chat", desc = "Change how the chat looks.") public ChatConfig chat = new ChatConfig(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/ChromaConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/ChromaConfig.java new file mode 100644 index 000000000..80c40cd98 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/ChromaConfig.java @@ -0,0 +1,62 @@ +package at.hannibal2.skyhanni.config.features; + +import at.hannibal2.skyhanni.SkyHanniMod; +import at.hannibal2.skyhanni.config.FeatureToggle; +import com.google.gson.annotations.Expose; +import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean; +import io.github.moulberry.moulconfig.annotations.ConfigEditorButton; +import io.github.moulberry.moulconfig.annotations.ConfigEditorDropdown; +import io.github.moulberry.moulconfig.annotations.ConfigEditorInfoText; +import io.github.moulberry.moulconfig.annotations.ConfigEditorSlider; +import io.github.moulberry.moulconfig.annotations.ConfigOption; + +public class ChromaConfig { + + @Expose + @ConfigOption(name = "Chroma Preview", desc = "§fPlease star the mod on GitHub!") + @ConfigEditorInfoText(infoTitle = "Only In SkyBlock") + public boolean chromaPreview = false; + + @Expose + @ConfigOption(name = "Enabled", desc = "Toggle for SkyHanni's chroma. (Disables Patcher's Optimized Font Renderer while enabled)") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption(name = "Chroma Size", desc = "Change the size of each color in the chroma.") + @ConfigEditorSlider(minValue = 1f, maxValue = 100f, minStep = 1f) + public float chromaSize = 30f; + + @Expose + @ConfigOption(name = "Chroma Speed", desc = "Change how fast the chroma animation moves.") + @ConfigEditorSlider(minValue = 0.5f, maxValue = 20f, minStep = 0.5f) + public float chromaSpeed = 6f; + + @Expose + @ConfigOption(name = "Chroma Saturation", desc = "Change the saturation of the chroma.") + @ConfigEditorSlider(minValue = 0f, maxValue = 1f, minStep = 0.01f) + public float chromaSaturation = 0.75f; + + @Expose + @ConfigOption(name = "Chroma Direction", desc = "Change the slant and direction of the chroma.") + @ConfigEditorDropdown(values = {"Forward + Right", "Forward + Left", "Backward + Right", "Backward + Left"}) + public int chromaDirection = 0; + + @ConfigOption(name = "Reset to Default", desc = "Resets all chroma settings to the default.") + @ConfigEditorButton(buttonText = "Reset") + public Runnable resetSettings = this::resetChromaSettings; + + @Expose + @ConfigOption(name = "Everything Chroma", desc = "Renders §4§l§oALL §r§7text in chroma. (Some enchants may appear white with SBA enchant parsing)") + @ConfigEditorBoolean + public boolean allChroma = false; + + private void resetChromaSettings() { + SkyHanniMod.getFeature().chroma.chromaSize = 30f; + SkyHanniMod.getFeature().chroma.chromaSpeed = 6f; + SkyHanniMod.getFeature().chroma.chromaSaturation = 0.75f; + SkyHanniMod.getFeature().chroma.allChroma = false; + SkyHanniMod.getFeature().chroma.chromaDirection = 0; + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt b/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt index a09380357..da9497d08 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MinecraftData.kt @@ -18,7 +18,7 @@ import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent -class MinecraftData { +object MinecraftData { @SubscribeEvent(receiveCanceled = true) fun onSoundPacket(event: PacketEvent.ReceiveEvent) { @@ -64,13 +64,13 @@ class MinecraftData { } } - private var tick = 0 + var totalTicks = 0 @SubscribeEvent fun onTick(event: TickEvent.ClientTickEvent) { Minecraft.getMinecraft().thePlayer ?: return - tick++ - LorenzTickEvent(tick).postAndCatch() + totalTicks++ + LorenzTickEvent(totalTicks).postAndCatch() } @SubscribeEvent diff --git a/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaFontRenderer.kt b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaFontRenderer.kt new file mode 100644 index 000000000..981b7b33a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaFontRenderer.kt @@ -0,0 +1,66 @@ +package at.hannibal2.skyhanni.features.chroma + +import at.hannibal2.skyhanni.utils.ColorUtils +import at.hannibal2.skyhanni.utils.shader.ShaderHelper +import net.minecraft.client.renderer.GlStateManager +import org.lwjgl.opengl.GL11 + +/** + * Class to handle chroma font rendering + * + * Modified class from SkyblockAddons + * + * Credit: [DrawStateFontRenderer.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/utils/draw/DrawStateFontRenderer.java) + */ +class ChromaFontRenderer(private val baseColor: Int) { + + private var chromaOn = false + + fun startChroma() { + chromaOn = true + } + + fun endChroma() { + chromaOn = false + } + + fun loadChromaEnv() { + if (chromaOn) { + newChromaEnv() + } + } + + fun restoreChromaEnv() { + if (ShaderHelper.areShadersSupported()) { + if (!chromaOn) ChromaShaderManager.end() + } + } + + fun newChromaEnv() : ChromaFontRenderer { + if (ShaderHelper.areShadersSupported()) { + ChromaShaderManager.begin() + GlStateManager.shadeModel(GL11.GL_SMOOTH) + } + return this + } + + fun bindActualColor() : ChromaFontRenderer { + GlStateManager.color( + ColorUtils.getRed(baseColor).toFloat() / 255f, + ColorUtils.getGreen(baseColor).toFloat() / 255f, + ColorUtils.getBlue(baseColor).toFloat() / 255f, + ColorUtils.getAlpha(baseColor).toFloat() / 255f + ) + return this + } + + fun endChromaEnv() : ChromaFontRenderer { + if (ShaderHelper.areShadersSupported()) { + ChromaShaderManager.end() + GlStateManager.shadeModel(GL11.GL_FLAT) + } + return this + } + + fun getChromaState() = chromaOn +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShader.kt b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShader.kt new file mode 100644 index 000000000..00215b032 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShader.kt @@ -0,0 +1,47 @@ +package at.hannibal2.skyhanni.features.chroma + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.MinecraftData +import at.hannibal2.skyhanni.mixins.transformers.AccessorMinecraft +import at.hannibal2.skyhanni.utils.shader.Shader +import at.hannibal2.skyhanni.utils.shader.Uniform +import net.minecraft.client.Minecraft + +/** + * Modified from SkyblockAddons + * + * Credit: [ChromaShader.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/shader/chroma/ChromaShader.java) + */ +object ChromaShader : Shader("chroma", "chroma") { + + val INSTANCE: ChromaShader + get() = this + + override fun registerUniforms() { + registerUniform(Uniform.UniformType.FLOAT, "chromaSize") { + SkyHanniMod.feature.chroma.chromaSize * (Minecraft.getMinecraft().displayWidth / 100f) + } + registerUniform(Uniform.UniformType.FLOAT, "timeOffset") { + var ticks = (MinecraftData.totalTicks / 2) + (Minecraft.getMinecraft() as AccessorMinecraft).timer.renderPartialTicks + + ticks = when (SkyHanniMod.feature.chroma.chromaDirection) { + 0, 2 -> ticks + 1, 3 -> -ticks + else -> ticks + } + + val chromaSpeed = SkyHanniMod.feature.chroma.chromaSpeed / 360f + ticks * chromaSpeed + } + registerUniform(Uniform.UniformType.FLOAT, "saturation") { + SkyHanniMod.feature.chroma.chromaSaturation + } + registerUniform(Uniform.UniformType.BOOL, "forwardDirection") { + when (SkyHanniMod.feature.chroma.chromaDirection) { + 0, 1 -> true + 2, 3 -> false + else -> true + } + } + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt new file mode 100644 index 000000000..ccf0ec21e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt @@ -0,0 +1,38 @@ +package at.hannibal2.skyhanni.features.chroma + +import at.hannibal2.skyhanni.utils.shader.ShaderManager + +/** + * Object to handle enabling / disabling the chroma shader when rendering text + * + * Modified from SkyblockAddons + * + * Credit: [MulticolorShaderManager.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/core/chroma/MulticolorShaderManager.java) + */ +object ChromaShaderManager { + + private var chromaEnabled = false + + fun begin() { + disable() + enable() + } + + fun end() { + disable() + } + + private fun enable() { + if (!chromaEnabled) { + chromaEnabled = true + ShaderManager.enableShader("chroma") + } + } + + private fun disable() { + if (chromaEnabled) { + chromaEnabled = false + ShaderManager.disableShader() + } + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/FontRendererHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/FontRendererHook.kt new file mode 100644 index 000000000..808406946 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/FontRendererHook.kt @@ -0,0 +1,135 @@ +package at.hannibal2.skyhanni.mixins.hooks + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.features.chroma.ChromaFontRenderer +import at.hannibal2.skyhanni.mixins.transformers.AccessorFontRenderer +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager + +/** + * Object to handle chroma font states from handler methods from MixinFontRenderer + * + * Modified from SkyblockAddons + * + * Credit: [FontRendererHook.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/asm/hooks/FontRendererHook.java) + */ +object FontRendererHook { + private var CHROMA_COLOR: Int = -0x1 + private val DRAW_CHROMA = ChromaFontRenderer(CHROMA_COLOR) + private var CHROMA_COLOR_SHADOW: Int = -0xAAAAAB + private val DRAW_CHROMA_SHADOW = ChromaFontRenderer(CHROMA_COLOR_SHADOW) + + private var currentDrawState: ChromaFontRenderer? = null + private var previewChroma = false + + /** + * Setups the [ChromaFontRenderer][at.hannibal2.skyhanni.features.chroma.ChromaFontRenderer] for rendering text + * in chroma. This should only be used when you don't have control over the color code a string uses, or it + * doesn't make sense to add §Z color code to a string. + * + * If you do have control over the color code, you can prepend the string with §Z instead. + * + * **Usage:** + * + * Surround string render call with this method and [endChromaFont]. + * ``` + * FontRendererHook.setupChromaFont() + * // render string call(s) here... + * FontRendererHook.endChromaFont() + * ``` + * + * Note: This only works if the string render call ends up using + * [FontRenderer#drawString()][net.minecraft.client.gui.FontRenderer.drawString] rather than a custom font renderer + * + */ + fun setupChromaFont() { + DRAW_CHROMA.startChroma() + DRAW_CHROMA_SHADOW.startChroma() + } + + /** + * See [setupChromaFont] + */ + fun endChromaFont() { + DRAW_CHROMA.endChroma() + DRAW_CHROMA_SHADOW.endChroma() + } + + @JvmStatic + fun beginChromaRendering(text: String, shadow: Boolean) { + if (!LorenzUtils.inSkyBlock) return + if (!SkyHanniMod.feature.chroma.enabled) return + + if (text == "§fPlease star the mod on GitHub!") { + previewChroma = true + setupChromaFont() + } + + val alpha = (Minecraft.getMinecraft().fontRendererObj as AccessorFontRenderer).alpha + if (shadow) { + currentDrawState = DRAW_CHROMA_SHADOW + CHROMA_COLOR_SHADOW = ((255 * alpha).toInt() shl 24 or 0x555555) + } else { + currentDrawState = DRAW_CHROMA + CHROMA_COLOR = ((255 * alpha).toInt() shl 24 or 0xFFFFFF) + } + + // Best feature ngl + if (SkyHanniMod.feature.chroma.allChroma) { + // Handles setting the base color of text when they don't use color codes i.e. MoulConfig + if (shadow) { + GlStateManager.color(0.33f, 0.33f, 0.33f, 1f) + } else { + GlStateManager.color(1f, 1f, 1f, 1f) + } + setupChromaFont() + } + + currentDrawState?.loadChromaEnv() + } + + @JvmStatic + fun toggleChromaOn() { + if (!LorenzUtils.inSkyBlock) return + + currentDrawState?.newChromaEnv()?.bindActualColor() + } + + @JvmStatic + fun forceWhiteColorCode(i1: Int) : Int { + if (!SkyHanniMod.feature.chroma.enabled) return i1 + + val drawState = currentDrawState ?: return i1 + if (drawState.getChromaState()) { + if (i1 < 16) { + return 15 + } + } + + return i1 + } + + @JvmStatic + fun restoreChromaState() { + if (!SkyHanniMod.feature.chroma.enabled) return + if (!LorenzUtils.inSkyBlock) return + + currentDrawState?.restoreChromaEnv() + } + + @JvmStatic + fun endChromaRendering() { + if (!SkyHanniMod.feature.chroma.enabled) return + if (!LorenzUtils.inSkyBlock) return + + if (previewChroma) { + previewChroma = false + endChromaFont() + } + + if (SkyHanniMod.feature.chroma.allChroma) endChromaFont() + + currentDrawState?.endChromaEnv() + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorFontRenderer.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorFontRenderer.java new file mode 100644 index 000000000..e20462d1a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorFontRenderer.java @@ -0,0 +1,12 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import net.minecraft.client.gui.FontRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(FontRenderer.class) +public interface AccessorFontRenderer { + + @Accessor("alpha") + float getAlpha(); +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorMinecraft.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorMinecraft.java new file mode 100644 index 000000000..24074f296 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/AccessorMinecraft.java @@ -0,0 +1,13 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.Timer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Minecraft.class) +public interface AccessorMinecraft { + + @Accessor("timer") + Timer getTimer(); +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java new file mode 100644 index 000000000..cf357eb74 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java @@ -0,0 +1,75 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import at.hannibal2.skyhanni.SkyHanniMod; +import at.hannibal2.skyhanni.mixins.hooks.FontRendererHook; +import net.minecraft.client.gui.FontRenderer; +import org.spongepowered.asm.lib.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(FontRenderer.class) +public abstract class MixinFontRenderer { + + /** + * Inject call to {@link FontRendererHook#beginChromaRendering(String, boolean)} as first call + */ + @Inject(method = "renderStringAtPos", at = @At("HEAD")) + public void beginRenderString(String text, boolean shadow, CallbackInfo ci) { + FontRendererHook.beginChromaRendering(text, shadow); + } + + /** + * Modify color code constant to add Z color code + */ + @ModifyConstant(method = "renderStringAtPos", constant = @Constant(stringValue = "0123456789abcdefklmnor")) + public String insertZColorCode(String constant) { + if (!SkyHanniMod.getFeature().chroma.enabled) return constant; + return "0123456789abcdefklmnorz"; + } + + /** + * Inject call to {@link FontRendererHook#restoreChromaState()} after 1st and 3rd fontrenderer.italicStyle = ___ call + */ + @Inject(method = "renderStringAtPos", at = { + @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/gui/FontRenderer;italicStyle:Z", ordinal = 0, shift = At.Shift.AFTER), + @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/gui/FontRenderer;italicStyle:Z", ordinal = 2, shift = At.Shift.AFTER)}) + public void insertRestoreChromaState(CallbackInfo ci) { + FontRendererHook.restoreChromaState(); + } + + @Shadow + protected abstract void resetStyles(); + + /** + * Inject call to {@link FontRendererHook#toggleChromaOn()} to check for Z color code index and if so, + * reset styles and toggle chroma on + */ + @Inject(method = "renderStringAtPos", at = @At(value = "INVOKE", target = "Ljava/lang/String;indexOf(I)I", ordinal = 0, shift = At.Shift.BY, by = 2), locals = LocalCapture.CAPTURE_FAILHARD) + public void toggleChromaCondition(String text, boolean shadow, CallbackInfo ci, int i, char c0, int i1) { + if (!SkyHanniMod.getFeature().chroma.enabled) return; + if (i1 == 22) { + this.resetStyles(); + FontRendererHook.toggleChromaOn(); + } + } + + /** + * Replace all color codes (when chroma is enabled) to white so chroma renders uniformly and at best brightness + */ + @ModifyVariable(method = "renderStringAtPos", at = @At(value = "INVOKE", target = "Ljava/lang/String;indexOf(I)I", ordinal = 0, shift = At.Shift.BY, by = 2), ordinal = 1) + public int forceWhiteColorCode(int i1) { + return FontRendererHook.forceWhiteColorCode(i1); + } + + /** + * Inject call to {@link FontRendererHook#endChromaRendering()} to turn off chroma rendering after entire + * string has been rendered + */ + @Inject(method = "renderStringAtPos", at = @At("RETURN")) + public void insertEndOfString(String text, boolean shadow, CallbackInfo ci) { + FontRendererHook.endChromaRendering(); + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinPatcherFontRendererHook.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinPatcherFontRendererHook.java new file mode 100644 index 000000000..7c585b725 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinPatcherFontRendererHook.java @@ -0,0 +1,21 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import at.hannibal2.skyhanni.SkyHanniMod; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Pseudo +@Mixin(targets = "club.sk1er.patcher.hooks.FontRendererHook") +public class MixinPatcherFontRendererHook { + + @Inject(method = "renderStringAtPos(Ljava/lang/String;Z)Z", at = @At("HEAD"), cancellable = true) + public void overridePatcherFontRenderer(String string, boolean shadow, CallbackInfoReturnable cir) { + if (SkyHanniMod.getFeature().chroma.enabled) { + cir.cancel(); + cir.setReturnValue(false); + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt new file mode 100644 index 000000000..076afc0c4 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt @@ -0,0 +1,11 @@ +package at.hannibal2.skyhanni.utils + +object ColorUtils { + fun getRed(colour: Int) = colour shr 16 and 0xFF + + fun getGreen(colour: Int) = colour shr 8 and 0xFF + + fun getBlue(colour: Int) = colour and 0xFF + + fun getAlpha(colour: Int) = colour shr 24 and 0xFF +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt new file mode 100644 index 000000000..f198f7e7a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt @@ -0,0 +1,55 @@ +package at.hannibal2.skyhanni.utils.shader + +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.client.shader.ShaderLinkHelper +import org.apache.commons.lang3.StringUtils +import org.lwjgl.opengl.GL11 +import java.util.function.Supplier + +/** + * Superclass for shader objects to compile and attach vertex and fragment shaders to the shader program + * + * Modified class from SkyblockAddons + * + * Credit: [Shader.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/shader/Shader.java) + */ +abstract class Shader(vertex: String, fragment: String) { + + var shaderProgram: Int = ShaderLinkHelper.getStaticShaderLinkHelper().createProgram() + private val uniforms: MutableList> = mutableListOf() + + init { + val vertexShaderID = ShaderManager.loadShader(ShaderType.VERTEX, vertex) + ShaderManager.attachShader(shaderProgram, vertexShaderID) + val fragmentShaderID = ShaderManager.loadShader(ShaderType.FRAGMENT, fragment) + ShaderManager.attachShader(shaderProgram, fragmentShaderID) + + ShaderHelper.glLinkProgram(shaderProgram) + + val linkStatus = ShaderHelper.glGetProgrami(shaderProgram, ShaderHelper.GL_LINK_STATUS) + if (linkStatus == GL11.GL_FALSE) { + LorenzUtils.consoleLog( + "Error occurred when linking program with Vertex Shader: $vertex and Fragment Shader: $fragment : " + + StringUtils.trim(ShaderHelper.glGetProgramInfoLog(shaderProgram, 1024)) + ) + } + + this.registerUniforms() + } + + abstract fun registerUniforms() + + fun updateUniforms() { + for (uniform in uniforms) { + uniform.update() + } + } + + fun enable() = ShaderHelper.glUseProgram(shaderProgram) + + fun disable() = ShaderHelper.glUseProgram(0) + + fun registerUniform(uniformType: Uniform.UniformType, name: String, uniformValuesSupplier: Supplier) { + uniforms.add(Uniform(this, uniformType, name, uniformValuesSupplier)) + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt new file mode 100644 index 000000000..e554a4098 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt @@ -0,0 +1,124 @@ +package at.hannibal2.skyhanni.utils.shader + +import at.hannibal2.skyhanni.utils.LorenzUtils +import org.lwjgl.opengl.* + +/** + * Class to check shaders support, OpenGL capabilities, and shader helper functions + * + * Modified class from SkyblockAddons + * + * Credit: [ShaderHelper.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/shader/ShaderHelper.java) + */ +class ShaderHelper { + companion object { + private var SHADERS_SUPPORTED: Boolean + + private var USING_ARB_SHADERS: Boolean + + var GL_LINK_STATUS: Int + var GL_COMPILE_STATUS: Int + var GL_VERTEX_SHADER: Int + var GL_FRAGMENT_SHADER: Int + + init { + val capabilities: ContextCapabilities = GLContext.getCapabilities() + + // Check OpenGL 2.0 Capabilities + val openGL20supported = capabilities.OpenGL20 + SHADERS_SUPPORTED = openGL20supported || + capabilities.GL_ARB_vertex_shader && + capabilities.GL_ARB_fragment_shader && + capabilities.GL_ARB_shader_objects + + var log = "Shaders are" + if (!SHADERS_SUPPORTED) log += " not" + log += " available. " + + if (SHADERS_SUPPORTED) { + if (capabilities.OpenGL20) { + log += "OpenGL 2.0 is supported. " + USING_ARB_SHADERS = false + GL_LINK_STATUS = GL20.GL_LINK_STATUS + GL_COMPILE_STATUS = GL20.GL_COMPILE_STATUS + GL_VERTEX_SHADER = GL20.GL_VERTEX_SHADER + GL_FRAGMENT_SHADER = GL20.GL_FRAGMENT_SHADER + } else { + log += "ARB_shader_objects, ARB_vertex_shader, and ARB_fragment_shader are supported. " + USING_ARB_SHADERS = true + GL_LINK_STATUS = ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB + GL_COMPILE_STATUS = ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB + GL_VERTEX_SHADER = ARBVertexShader.GL_VERTEX_SHADER_ARB + GL_FRAGMENT_SHADER = ARBFragmentShader.GL_FRAGMENT_SHADER_ARB + } + } else { + log += "OpenGL 2.0 is not supported and ARB_shader_objects, ARB_vertex_shader, and ARB_fragment_shader are not supported." + USING_ARB_SHADERS = false + GL_LINK_STATUS = GL11.GL_FALSE + GL_COMPILE_STATUS = GL11.GL_FALSE + GL_VERTEX_SHADER = GL11.GL_FALSE + GL_FRAGMENT_SHADER = GL11.GL_FALSE + } + + LorenzUtils.consoleLog(log) + } + + fun glLinkProgram(program: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glLinkProgramARB(program) else GL20.glLinkProgram(program) + } + + fun glGetProgramInfoLog(program: Int, maxLength: Int) : String { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetInfoLogARB(program, maxLength) else GL20.glGetProgramInfoLog(program, maxLength) + } + + fun glGetProgrami(program: Int, pname: Int) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetObjectParameteriARB(program, pname) else GL20.glGetProgrami(program, pname) + } + + fun glUseProgram(program: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glUseProgramObjectARB(program) else GL20.glUseProgram(program) + } + + fun glAttachShader(program: Int, shaderIn: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glAttachObjectARB(program, shaderIn) else GL20.glAttachShader(program, shaderIn) + } + + fun glCreateShader(type: Int) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glCreateShaderObjectARB(type) else GL20.glCreateShader(type) + } + + fun glShaderSource(shader: Int, source: CharSequence) { + if (USING_ARB_SHADERS) ARBShaderObjects.glShaderSourceARB(shader, source) else GL20.glShaderSource(shader, source) + } + + fun glCompileShader(shader: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glCompileShaderARB(shader) else GL20.glCompileShader(shader) + } + + fun glGetShaderi(shader: Int, pname: Int) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetObjectParameteriARB(shader, pname) else GL20.glGetShaderi(shader, pname) + } + + fun glGetShaderInfoLog(shader: Int, maxLength: Int) : String { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetInfoLogARB(shader, maxLength) else GL20.glGetShaderInfoLog(shader, maxLength) + } + + fun glDeleteShader(shader: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glDeleteObjectARB(shader) else GL20.glDeleteShader(shader) + } + + fun glUniform1f(location: Int, v0: Float) { + if (USING_ARB_SHADERS) ARBShaderObjects.glUniform1fARB(location, v0) else GL20.glUniform1f(location, v0) + } + + fun glUniform3f(location: Int, v0: Float, v1: Float, v2: Float) { + if (USING_ARB_SHADERS) ARBShaderObjects.glUniform3fARB(location, v0, v1, v2) else GL20.glUniform3f(location, v0, v1, v2) + } + + fun glGetUniformLocation(program: Int, name: CharSequence) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetUniformLocationARB(program, name) else GL20.glGetUniformLocation(program, name) + } + + fun areShadersSupported() = SHADERS_SUPPORTED + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt new file mode 100644 index 000000000..e7eb48f11 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt @@ -0,0 +1,87 @@ +package at.hannibal2.skyhanni.utils.shader + +import at.hannibal2.skyhanni.features.chroma.ChromaShader +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.client.Minecraft +import net.minecraft.util.ResourceLocation +import org.apache.commons.lang3.StringUtils +import java.io.BufferedReader +import java.io.InputStreamReader + +/** + * Object to handle shaders for SkyHanni + */ +object ShaderManager { + + /** + * For any future shaders add the object instance in this enum and + * in the when expression + */ + enum class Shaders(val shader: Shader) { + CHROMA(ChromaShader.INSTANCE); + + companion object { + fun getShaderInstance(shaderName: String) : Shader? = when (shaderName) { + "chroma" -> CHROMA.shader + else -> { + null + } + } + } + } + + private val shaders: MutableMap = mutableMapOf() + private var activeShader: Shader? = null + + fun enableShader(shaderName: String) { + var shader = shaders[shaderName] + + if (shader == null) { + shader = Shaders.getShaderInstance(shaderName) + if (shader == null) return + shaders[shaderName] = shader + } + + activeShader = shader + shader.enable() + shader.updateUniforms() + } + + fun attachShader(shaderProgram: Int, shaderID: Int) { + ShaderHelper.glAttachShader(shaderProgram, shaderID) + } + + fun disableShader() { + if (activeShader == null) return + + activeShader?.disable() + activeShader = null + } + + fun loadShader(type: ShaderType, fileName: String) : Int { + val resourceLocation = ResourceLocation("skyhanni:shaders/$fileName${type.extension}") + + val source = StringBuilder() + + val inputStream = Minecraft.getMinecraft().resourceManager.getResource(resourceLocation).inputStream + BufferedReader(InputStreamReader(inputStream)).forEachLine { + source.append(it).append("\n") + } + + val shaderID = ShaderHelper.glCreateShader(type.shaderType) + ShaderHelper.glShaderSource(shaderID, source.toString()) + ShaderHelper.glCompileShader(shaderID) + + if (ShaderHelper.glGetShaderi(shaderID, ShaderHelper.GL_COMPILE_STATUS) == 0) { + LorenzUtils.consoleLog("Error occurred when compiling shader $fileName${type.extension} : " + + StringUtils.trim(ShaderHelper.glGetShaderInfoLog(shaderID, 1024))) + } + + return shaderID + } +} + +enum class ShaderType(val extension: String, val shaderType: Int) { + VERTEX(".vsh", ShaderHelper.GL_VERTEX_SHADER), + FRAGMENT(".fsh", ShaderHelper.GL_FRAGMENT_SHADER) +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt new file mode 100644 index 000000000..e87ea3b22 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt @@ -0,0 +1,45 @@ +package at.hannibal2.skyhanni.utils.shader + +import java.util.* +import java.util.function.Supplier + +/** + * Class to handle shader uniform types + * + * Modified from SkyblockAddons + * + * Credit: [Uniform.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/shader/Uniform.java) + */ +class Uniform( + shader: Shader, + private val uniformType: UniformType, + val name: String, + private val uniformValuesSupplier: Supplier +) { + + class UniformType { + companion object { + val FLOAT: UniformType = UniformType() + val VEC3: UniformType = UniformType() + val BOOL: UniformType = UniformType() + } + } + + private val uniformID: Int = ShaderHelper.glGetUniformLocation(shader.shaderProgram, name) + private var previousUniformValue: T? = null + + fun update() { + val newUniformValue: T = uniformValuesSupplier.get() + if (!Objects.deepEquals(previousUniformValue, newUniformValue)) { + when (uniformType) { + UniformType.FLOAT -> ShaderHelper.glUniform1f(uniformID, (newUniformValue as Float)) + UniformType.VEC3 -> { + val values = newUniformValue as FloatArray + ShaderHelper.glUniform3f(uniformID, values[0], values[1], values[2]) + } + UniformType.BOOL -> ShaderHelper.glUniform1f(uniformID, if (newUniformValue as Boolean) 1f else 0f) + } + previousUniformValue = newUniformValue + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/skyhanni/shaders/chroma.fsh b/src/main/resources/assets/skyhanni/shaders/chroma.fsh new file mode 100644 index 000000000..7b48a62f9 --- /dev/null +++ b/src/main/resources/assets/skyhanni/shaders/chroma.fsh @@ -0,0 +1,43 @@ +// Chroma Fragment Shader +// Modified from SkyblockAddons +// Credit: https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/resources/assets/skyblockaddons/shaders/program/chroma_screen_textured.fsh + +#version 120 + +uniform float chromaSize; +uniform float timeOffset; +uniform float saturation; +uniform bool forwardDirection; + +uniform sampler2D outTexture; + +varying vec2 outTextureCoords; +varying vec4 outColor; + +float rgb2b(vec3 rgb) { + return max(max(rgb.r, rgb.g), rgb.b); +} + +vec3 hsb2rgb_smooth(vec3 c) { + vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0); + rgb = rgb * rgb * (3.0 - 2.0 * rgb); // Cubic smoothing + return c.z * mix(vec3(1.0), rgb, c.y); +} + +void main() { + vec4 originalColor = texture2D(outTexture, outTextureCoords) * outColor; + + // Determine the direction chroma moves + float fragCoord; + if (forwardDirection) { + fragCoord = gl_FragCoord.x - gl_FragCoord.y; + } else { + fragCoord = gl_FragCoord.x + gl_FragCoord.y; + } + + // The hue takes in account the position, chroma settings, and time + float hue = mod(((fragCoord) / chromaSize) - timeOffset, 1.0); + + // Set the color to use the new hue & original saturation/value/alpha values + gl_FragColor = vec4(hsb2rgb_smooth(vec3(hue, saturation, rgb2b(originalColor.rgb))), originalColor.a); +} \ No newline at end of file diff --git a/src/main/resources/assets/skyhanni/shaders/chroma.vsh b/src/main/resources/assets/skyhanni/shaders/chroma.vsh new file mode 100644 index 000000000..5f5030d07 --- /dev/null +++ b/src/main/resources/assets/skyhanni/shaders/chroma.vsh @@ -0,0 +1,15 @@ +// Chroma Vertex Shader +// Credit: https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/resources/assets/skyblockaddons/shaders/program/chroma_screen_textured.vsh + +#version 120 + +varying vec2 outTextureCoords; +varying vec4 outColor; + +void main() { + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + + // Pass the color & texture coords to the fragment shader + outColor = gl_Color; + outTextureCoords = gl_MultiTexCoord0.st; +} diff --git a/src/main/resources/mixins.skyhanni.json b/src/main/resources/mixins.skyhanni.json index 8daf1a330..aa1b61873 100644 --- a/src/main/resources/mixins.skyhanni.json +++ b/src/main/resources/mixins.skyhanni.json @@ -2,6 +2,9 @@ "package": "at.hannibal2.skyhanni.mixins.transformers", "refmap": "mixins.skyhanni.refmap.json", "compatibilityLevel": "JAVA_8", + "injectors": { + "maxShiftBy": 2 + }, "mixins": [ "AccessorChatComponentText", "AccessorGuiPlayerTabOverlay", @@ -13,6 +16,7 @@ "MixinItemStack", "MixinNetHandlerPlayClient", "MixinNetworkManager", + "MixinPatcherFontRendererHook", "MixinRendererLivingEntity", "MixinRenderGlobal", "MixinRenderItem", @@ -22,10 +26,13 @@ "renderer.MixinRendererLivingEntity" ], "client": [ + "AccessorFontRenderer", "AccessorGuiEditSign", "AccessorKeyBinding", + "AccessorMinecraft", "CustomRenderGlobal", "MixinEntityRenderer", + "MixinFontRenderer", "MixinGuiChat", "MixinGuiIngame", "MixinGuiPlayerTabOverlay", -- cgit