diff --git a/README.md b/README.md index d95c55c..fb9200c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # Hexaplex +![](https://github.com/LazuriteMC/Hexaplex/blob/main/src/main/resources/assets/rayon/icon.png?raw=true) + [![GitHub](https://img.shields.io/github/license/LazuriteMC/Hexaplex?color=A31F34&label=License&labelColor=8A8B8C)](https://github.com/LazuriteMC/Hexaplex/blob/main/LICENSE) [![Discord](https://img.shields.io/discord/719662192601071747?color=7289DA&label=Discord&labelColor=2C2F33&logo=Discord)](https://discord.gg/efCMR7U) -[![Website](https://img.shields.io/website?color=E34C26&label=Website&logo=HTML5&labelColor=FFFFFF&url=https%3A%2F%2Flazurite.dev)](https://lazurite.dev) --- @@ -10,14 +11,13 @@ Hexaplex is a colorblindness correction mod for Minecraft written for the Fabric Mod Loader. -Note: Hexaplex is still in development and thus may not have all expected features implemented. Keep an eye out for -a release in the near future. - ## What isn't Hexaplex? Hexaplex *isn't* a shader pack or a resource pack. This means that you can run this mod alongside your favorite shaders and resources without conflict. +Note: Optifabric is currently incompatible. This will be resolved by a 1.0.0 release. + ## How does it work? Hexaplex utilizes a process called Daltonization which has the following four steps. @@ -29,14 +29,6 @@ Hexaplex utilizes a process called Daltonization which has the following four st On the technical side, Hexaplex uses OpenGL shaders and applies Daltonization as a post-processing filter. -## Examples - -In progress. - -## How do I install it? - -In progress. - ## What does "Hexaplex" mean? [Hexaplex](https://en.wikipedia.org/wiki/Hexaplex) is a genus of sea snail that was used by the Minoans, diff --git a/build.gradle b/build.gradle index 3e068ca..b81a3c6 100644 --- a/build.gradle +++ b/build.gradle @@ -32,11 +32,10 @@ dependencies { modImplementation "me.zeroeightsix:fiber:${project.fiber_version}" include "me.zeroeightsix:fiber:${project.fiber_version}" - // what does transitive do? - modImplementation("io.github.prospector:modmenu:1.14.13+build.22") -// { -// transitive(false) -// } + modImplementation "me.shedaniel.cloth:config-2:${project.cloth_version}" + include "me.shedaniel.cloth:config-2:${project.cloth_version}" + + modImplementation "io.github.prospector:modmenu:${project.modmenu_version}" } processResources { diff --git a/gradle.properties b/gradle.properties index 683f1f9..5e9bdf1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,15 +2,18 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=1.16.4 -yarn_mappings=1.16.4+build.7 -loader_version=0.10.8 +minecraft_version=1.16.5 +yarn_mappings=1.16.5+build.1 +loader_version=0.11.1 # Mod Properties -mod_version=1.0.0 +mod_version=0.1.0 maven_group=lazurite archives_base_name=hexaplex # Dependencies # check this on https://modmuss50.me/fabric.html -fabric_version=0.29.1+1.16 +fabric_version=0.29.4+1.16 satin_version=1.5.1 fiber_version=0.23.0-2 +cloth_version=4.8.3 +fiber2cloth_version=3.0.1 +modmenu_version=1.14.13+build.19 diff --git a/src/main/java/dev/lazurite/hexaplex/Hexaplex.java b/src/main/java/dev/lazurite/hexaplex/Hexaplex.java new file mode 100644 index 0000000..75cc887 --- /dev/null +++ b/src/main/java/dev/lazurite/hexaplex/Hexaplex.java @@ -0,0 +1,32 @@ +package dev.lazurite.hexaplex; + +import blue.endless.jankson.api.SyntaxError; +import dev.lazurite.hexaplex.config.Config; +import dev.lazurite.hexaplex.rendering.ShaderManager; +import dev.lazurite.hexaplex.rendering.MatrixLoader; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; + +@Environment(EnvType.CLIENT) +public final class Hexaplex implements ClientModInitializer { + public static final String MOD_ID = "hexaplex"; + public static final Logger LOGGER = LogManager.getLogger("Hexaplex"); + + @Override + public void onInitializeClient() { + try { + MatrixLoader.loadMatrices(this.getClass().getResourceAsStream("/assets/hexaplex/shaders/uniform/matrix4x4/filter.json")); + } catch (SyntaxError | IOException e) { + Hexaplex.LOGGER.error("Error loading Hexaplex shader matrices!"); + e.printStackTrace(); + } + + ShaderManager.registerRenderer(); + Config.INSTANCE.load(); + } +} diff --git a/src/main/java/dev/lazurite/hexaplex/config/Config.java b/src/main/java/dev/lazurite/hexaplex/config/Config.java index 24a3568..4750911 100644 --- a/src/main/java/dev/lazurite/hexaplex/config/Config.java +++ b/src/main/java/dev/lazurite/hexaplex/config/Config.java @@ -1,7 +1,7 @@ package dev.lazurite.hexaplex.config; -import dev.lazurite.hexaplex.graphics.ShaderManager; -import dev.lazurite.hexaplex.init.ClientInitializer; +import dev.lazurite.hexaplex.Hexaplex; +import dev.lazurite.hexaplex.rendering.Profiles; import io.github.fablabsmc.fablabs.api.fiber.v1.annotation.AnnotatedSettings; import io.github.fablabsmc.fablabs.api.fiber.v1.annotation.Setting; import io.github.fablabsmc.fablabs.api.fiber.v1.annotation.Settings; @@ -9,90 +9,123 @@ import io.github.fablabsmc.fablabs.api.fiber.v1.serialization.FiberSerialization; import io.github.fablabsmc.fablabs.api.fiber.v1.serialization.JanksonValueSerializer; import io.github.fablabsmc.fablabs.api.fiber.v1.tree.ConfigTree; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.fabricmc.loader.api.FabricLoader; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; +@Environment(EnvType.CLIENT) @Settings(onlyAnnotated = true) -public class Config { +public final class Config { public static final Config INSTANCE = new Config(); - private boolean dirty; + private static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve(Hexaplex.MOD_ID + ".json"); - @Setting(name = "filterProfile") - private ShaderManager.Profiles profile; + @Setting(name = "profile") + private Profiles profile; - @Setting(name = "filterStrength") + @Setting(name = "strength") @Setting.Constrain.Range(min = 0.0, max = 1.0, step = 0.01) private double strength; - private Config() { - this.profile = ShaderManager.Profiles.NORMAL; - this.strength = 0.0; - this.dirty = true; - } - - private void markDirty() { - this.dirty = true; - } + @Setting(name = "skew") + @Setting.Constrain.Range(min = 0.0, max = 1.0, step = 0.01) + private double skew; - public void markClean() { - this.dirty = true; - } + private boolean dirty; - public boolean isDirty() { - return this.dirty; + private Config() { + this.setProfile(Profiles.NORMAL); + this.setStrength(0.0); + this.setSkew(0.5); + this.markDirty(); } public void save() { try { FiberSerialization.serialize( - ConfigTree.builder().applyFromPojo(INSTANCE, AnnotatedSettings.builder().collectOnlyAnnotatedMembers().build()).build(), - Files.newOutputStream(FabricLoader.getInstance().getConfigDir().resolve("hexaplex.json")), + ConfigTree.builder() + .applyFromPojo( + INSTANCE, + AnnotatedSettings.builder() + .collectOnlyAnnotatedMembers() + .build() + ) + .build(), + Files.newOutputStream(PATH), new JanksonValueSerializer(false) ); } catch (IOException e) { - ClientInitializer.LOGGER.error("Error saving Hexaplex config."); + Hexaplex.LOGGER.error("Error saving Hexaplex config!"); e.printStackTrace(); } } public void load() { - if (Files.exists(FabricLoader.getInstance().getConfigDir().resolve("hexaplex.json"))) { + if (Files.exists(PATH)) { try { FiberSerialization.deserialize( - ConfigTree.builder().applyFromPojo(INSTANCE, AnnotatedSettings.builder().collectOnlyAnnotatedMembers().build()).build(), - Files.newInputStream(FabricLoader.getInstance().getConfigDir().resolve("hexaplex.json")), + ConfigTree.builder() + .applyFromPojo( + INSTANCE, + AnnotatedSettings.builder() + .collectOnlyAnnotatedMembers() + .build() + ) + .build(), + Files.newInputStream(PATH), new JanksonValueSerializer(false) ); - } catch (IOException | FiberException e) { - ClientInitializer.LOGGER.error("Error loading Hexaplex config."); + } catch (FiberException | IOException e) { + Hexaplex.LOGGER.error("Error loading Hexaplex config!"); e.printStackTrace(); } } else { - ClientInitializer.LOGGER.info("Creating Hexaplex config."); + Hexaplex.LOGGER.info("Creating Hexaplex config."); this.save(); } } - public void setProfile(ShaderManager.Profiles profile) { + public void setProfile(Profiles profile) { this.profile = profile; this.markDirty(); - this.save(); } - public ShaderManager.Profiles getProfile() { + public Profiles getProfile() { return this.profile; } public void setStrength(double strength) { this.strength = strength; - this.save(); + this.markDirty(); } public double getStrength() { return this.strength; } + + public void setSkew(double skew) { + this.skew = skew; + this.markDirty(); + } + + public double getSkew() { + return this.skew; + } + + private void markDirty() { + this.dirty = true; + } + + public void markClean() { + this.dirty = false; + } + + public boolean isDirty() { + return this.dirty; + } } diff --git a/src/main/java/dev/lazurite/hexaplex/graphics/ShaderManager.java b/src/main/java/dev/lazurite/hexaplex/graphics/ShaderManager.java deleted file mode 100644 index 89bfb90..0000000 --- a/src/main/java/dev/lazurite/hexaplex/graphics/ShaderManager.java +++ /dev/null @@ -1,77 +0,0 @@ -package dev.lazurite.hexaplex.graphics; - -import dev.lazurite.hexaplex.config.Config; -import dev.lazurite.hexaplex.init.ClientInitializer; -import ladysnake.satin.api.managed.ManagedShaderEffect; -import ladysnake.satin.api.managed.ShaderEffectManager; -import net.minecraft.util.Identifier; - -public final class ShaderManager { - - private ShaderManager() { } - - public enum Profiles { - NORMAL("normal"), - DEUTERAN("deuteran"), - PROTAN("protan"), - TRITAN("tritan"); - - private final String name; - - Profiles(String name) { - this.name = name; - } - - public String getName() { - return this.name; - } - - public static Profiles get(int index) { - return values()[index % values().length]; - } - } - - public static final ManagedShaderEffect FILTER = ShaderEffectManager.getInstance().manage( - new Identifier(ClientInitializer.MOD_ID, "shaders/post/filter.json"), - shader -> { - ShaderManager.FILTER.setUniformValue("rgbToLms", ClientInitializer.getMatrix("rgbToLms").copyMatrix4f()); - ShaderManager.FILTER.setUniformValue("lmsToRgb", ClientInitializer.getMatrix("lmsToRgb").copyMatrix4f()); - - ShaderManager.setUniforms(); - } - ); - - private static void setUniforms() { - switch (Config.INSTANCE.getProfile()) { - case DEUTERAN: - ShaderManager.FILTER.setUniformValue("lmsToLmsc", ClientInitializer.getMatrix("lmsToLmsd").copyMatrix4f()); - ShaderManager.FILTER.setUniformValue("rgbcErr", ClientInitializer.getMatrix("rgbdErr").copyMatrix4f()); - break; - case PROTAN: - ShaderManager.FILTER.setUniformValue("lmsToLmsc", ClientInitializer.getMatrix("lmsToLmsp").copyMatrix4f()); - ShaderManager.FILTER.setUniformValue("rgbcErr", ClientInitializer.getMatrix("rgbpErr").copyMatrix4f()); - break; - case TRITAN: - ShaderManager.FILTER.setUniformValue("lmsToLmsc", ClientInitializer.getMatrix("lmsToLmst").copyMatrix4f()); - ShaderManager.FILTER.setUniformValue("rgbcErr", ClientInitializer.getMatrix("rgbtErr").copyMatrix4f()); - break; - default: - break; - } - } - - public static void registerRenderer() { - BlitRenderCallback.EVENT.register( - tickDelta -> { - if (Config.INSTANCE.isDirty()) { - ShaderManager.setUniforms(); - Config.INSTANCE.markClean(); - } - - if (!Config.INSTANCE.getProfile().equals(Profiles.NORMAL) && !(Config.INSTANCE.getStrength() == 0.0)) { - ShaderManager.FILTER.render(tickDelta); - } - } - ); - } -} diff --git a/src/main/java/dev/lazurite/hexaplex/gui/ConfigScreen.java b/src/main/java/dev/lazurite/hexaplex/gui/ConfigScreen.java new file mode 100644 index 0000000..488fd30 --- /dev/null +++ b/src/main/java/dev/lazurite/hexaplex/gui/ConfigScreen.java @@ -0,0 +1,87 @@ +package dev.lazurite.hexaplex.gui; + +import dev.lazurite.hexaplex.config.Config; +import dev.lazurite.hexaplex.rendering.Profiles; +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.gui.entries.EnumListEntry; +import me.shedaniel.clothconfig2.gui.entries.IntegerSliderEntry; +import me.shedaniel.clothconfig2.gui.entries.SelectionListEntry; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.TranslatableText; + +public final class ConfigScreen implements ConfigScreenFactory { + + @Override + public Screen create(Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(new TranslatableText("config.hexaplex.title")) + .setSavingRunnable(Config.INSTANCE::save); + + IntegerSliderEntry strengthOption = builder.entryBuilder() + .startIntSlider(new TranslatableText("config.hexaplex.strength"), (int) (Config.INSTANCE.getStrength() * 100), 0, 100) + .setDefaultValue(0) + .setSaveConsumer(strength -> Config.INSTANCE.setStrength((double) strength / 100.0)) + .build(); + + IntegerSliderEntry skewOption = builder.entryBuilder() + .startIntSlider(new TranslatableText("config.hexaplex.skew"), (int) (Config.INSTANCE.getSkew() * 100), 0, 100) + .setDefaultValue(50) + .setSaveConsumer(skew -> Config.INSTANCE.setSkew((double) skew / 100.0)) + .build(); + + EnumListEntry profileOption = builder.entryBuilder() + .startEnumSelector(new TranslatableText("config.hexaplex.profile"), Profiles.class, Config.INSTANCE.getProfile()) + .setDefaultValue(Profiles.NORMAL) + .setEnumNameProvider( + anEnum -> { + strengthOption.setValue(strengthOption.getValue()); // force update + skewOption.setValue(skewOption.getValue()); // force update + + if (anEnum.equals(Profiles.NORMAL)) { + return new TranslatableText(((SelectionListEntry.Translatable) anEnum).getKey()); + } else if ((double) strengthOption.getValue() / 100.0 != 1.0) { + return new TranslatableText(((SelectionListEntry.Translatable) anEnum).getKey() + "omaly"); + } else { + return new TranslatableText(((SelectionListEntry.Translatable) anEnum).getKey() + "opia"); + } + } + ) + .setSaveConsumer(Config.INSTANCE::setProfile) + .build(); + + strengthOption.setTextGetter( + strength -> { + if (profileOption.getValue().equals(Profiles.NORMAL)) { + return new TranslatableText("config.hexaplex.strength.normal"); + } else { + return new TranslatableText("config.hexaplex.strength." + profileOption.getValue().toString(), strength); + } + } + ); + + skewOption.setTextGetter( + skew -> { + switch (profileOption.getValue()) { + case PROTAN: + return new TranslatableText("config.hexaplex.skew.protan", skew, 100 - skew); + case DEUTERAN: + return new TranslatableText("config.hexaplex.skew.deuteran", skew, 100 - skew); + case TRITAN: + return new TranslatableText("config.hexaplex.skew.tritan", skew, 100 - skew); + case NORMAL: + default: + return new TranslatableText("config.hexaplex.skew.normal"); + } + } + ); + + builder.getOrCreateCategory(new TranslatableText("config.hexaplex.category")) + .addEntry(profileOption) + .addEntry(strengthOption) + .addEntry(skewOption); + + return builder.build(); + } +} diff --git a/src/main/java/dev/lazurite/hexaplex/gui/ModMenu.java b/src/main/java/dev/lazurite/hexaplex/gui/ModMenu.java new file mode 100644 index 0000000..2c84c9e --- /dev/null +++ b/src/main/java/dev/lazurite/hexaplex/gui/ModMenu.java @@ -0,0 +1,12 @@ +package dev.lazurite.hexaplex.gui; + +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import io.github.prospector.modmenu.api.ModMenuApi; + +public class ModMenu implements ModMenuApi { + + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return new ConfigScreen(); + } +} diff --git a/src/main/java/dev/lazurite/hexaplex/init/ClientInitializer.java b/src/main/java/dev/lazurite/hexaplex/init/ClientInitializer.java deleted file mode 100644 index 5a87f53..0000000 --- a/src/main/java/dev/lazurite/hexaplex/init/ClientInitializer.java +++ /dev/null @@ -1,82 +0,0 @@ -package dev.lazurite.hexaplex.init; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import dev.lazurite.hexaplex.config.Config; -import dev.lazurite.hexaplex.graphics.ShaderManager; -import dev.lazurite.hexaplex.util.Matrix4x4; -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.options.CyclingOption; -import net.minecraft.client.options.DoubleOption; -import net.minecraft.text.TranslatableText; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; - -@Environment(EnvType.CLIENT) -public final class ClientInitializer implements ClientModInitializer { - public static final String MOD_ID = "hexaplex"; - public static final Logger LOGGER = LogManager.getLogger(MOD_ID); - - private static final Map MATRICES = new HashMap<>(); - - public static final CyclingOption PROFILE_OPTION = new CyclingOption( - "options.hexaplex.profile.title", - (gameOptions, amount) -> Config.INSTANCE.setProfile(ShaderManager.Profiles.get(Config.INSTANCE.getProfile().ordinal() + amount)), - (gameOptions, option) -> option.getGenericLabel(new TranslatableText("options.hexaplex.profile." + Config.INSTANCE.getProfile().getName())) - ); - - public static final DoubleOption STRENGTH_OPTION = new DoubleOption( - "options.hexaplex.strength.title", - 0.0, - 1.0, - 0.01f, - (gameOptions) -> Config.INSTANCE.getStrength(), - (gameOptions, strength) -> Config.INSTANCE.setStrength(strength), - (gameOptions, option) -> { - option.setTooltip(MinecraftClient.getInstance().textRenderer.wrapLines(new TranslatableText("options.hexaplex.strength.tooltip"), 200)); - return option.getPercentLabel(option.getRatio(option.get(gameOptions))); - } - ); - - @Override - public void onInitializeClient() { - ClientInitializer.loadMatrices(); - ShaderManager.registerRenderer(); - Config.INSTANCE.load(); - } - - private static void loadMatrices() { - JsonElement json = new JsonParser().parse(new InputStreamReader(ClientInitializer.class.getResourceAsStream("/assets/hexaplex/shaders/uniform/matrix4x4/filter.json"))); - - for (Map.Entry entry : json.getAsJsonObject().entrySet()) { - MATRICES.put(entry.getKey(), entry.getValue().getAsJsonArray()); - } - } - - public static Matrix4x4 getMatrix(String name) { - JsonArray array = MATRICES.get(name); - - float[] values = new float[array.size()]; - - for (int i = 0; i < values.length; ++i) { - JsonPrimitive primitive = array.get(i).getAsJsonPrimitive(); - - if (primitive.isString()) { - values[i] = (float) Config.INSTANCE.getStrength(); - } else { - values[i] = primitive.getAsFloat(); - } - } - - return new Matrix4x4(values); - } -} diff --git a/src/main/java/dev/lazurite/hexaplex/mixin/AccessibilityOptionsScreenMixin.java b/src/main/java/dev/lazurite/hexaplex/mixin/AccessibilityOptionsScreenMixin.java deleted file mode 100644 index 6391217..0000000 --- a/src/main/java/dev/lazurite/hexaplex/mixin/AccessibilityOptionsScreenMixin.java +++ /dev/null @@ -1,29 +0,0 @@ -package dev.lazurite.hexaplex.mixin; - -import dev.lazurite.hexaplex.init.ClientInitializer; -import net.minecraft.client.gui.screen.options.AccessibilityOptionsScreen; -import net.minecraft.client.options.Option; -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; - -@Mixin(AccessibilityOptionsScreen.class) -public class AccessibilityOptionsScreenMixin { - @Shadow private static Option[] OPTIONS; - - @Inject(method = "", at = @At("TAIL")) - private static void clinit(CallbackInfo info) { - final Option[] OLD_OPTIONS = new Option[OPTIONS.length]; - - System.arraycopy(OPTIONS, 0, OLD_OPTIONS, 0, OPTIONS.length); - - OPTIONS = new Option[OLD_OPTIONS.length + 2]; - - System.arraycopy(OLD_OPTIONS, 0, OPTIONS, 0, OLD_OPTIONS.length); - - OPTIONS[OLD_OPTIONS.length] = ClientInitializer.PROFILE_OPTION; - OPTIONS[OLD_OPTIONS.length + 1] = ClientInitializer.STRENGTH_OPTION; - } -} diff --git a/src/main/java/dev/lazurite/hexaplex/mixin/MinecraftClientMixin.java b/src/main/java/dev/lazurite/hexaplex/mixins/MinecraftClientMixin.java similarity index 72% rename from src/main/java/dev/lazurite/hexaplex/mixin/MinecraftClientMixin.java rename to src/main/java/dev/lazurite/hexaplex/mixins/MinecraftClientMixin.java index c144e7d..851d528 100644 --- a/src/main/java/dev/lazurite/hexaplex/mixin/MinecraftClientMixin.java +++ b/src/main/java/dev/lazurite/hexaplex/mixins/MinecraftClientMixin.java @@ -1,6 +1,6 @@ -package dev.lazurite.hexaplex.mixin; +package dev.lazurite.hexaplex.mixins; -import dev.lazurite.hexaplex.graphics.BlitRenderCallback; +import dev.lazurite.hexaplex.rendering.BlitRenderCallback; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.RenderTickCounter; import org.spongepowered.asm.mixin.Final; @@ -17,11 +17,11 @@ public class MinecraftClientMixin { @Shadow @Final private RenderTickCounter renderTickCounter; @Inject( - method = "render", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/gl/Framebuffer;endWrite()V" - ) + method = "render", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gl/Framebuffer;endWrite()V" + ) ) private void render(boolean tick, CallbackInfo info) { BlitRenderCallback.EVENT.invoker().renderBlit(this.paused ? this.pausedTickDelta : this.renderTickCounter.tickDelta); diff --git a/src/main/java/dev/lazurite/hexaplex/graphics/BlitRenderCallback.java b/src/main/java/dev/lazurite/hexaplex/rendering/BlitRenderCallback.java similarity index 74% rename from src/main/java/dev/lazurite/hexaplex/graphics/BlitRenderCallback.java rename to src/main/java/dev/lazurite/hexaplex/rendering/BlitRenderCallback.java index 9fb0138..99c8b32 100644 --- a/src/main/java/dev/lazurite/hexaplex/graphics/BlitRenderCallback.java +++ b/src/main/java/dev/lazurite/hexaplex/rendering/BlitRenderCallback.java @@ -1,8 +1,11 @@ -package dev.lazurite.hexaplex.graphics; +package dev.lazurite.hexaplex.rendering; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.EventFactory; +@Environment(EnvType.CLIENT) public interface BlitRenderCallback { Event EVENT = EventFactory.createArrayBacked( BlitRenderCallback.class, diff --git a/src/main/java/dev/lazurite/hexaplex/rendering/MatrixLoader.java b/src/main/java/dev/lazurite/hexaplex/rendering/MatrixLoader.java new file mode 100644 index 0000000..313b196 --- /dev/null +++ b/src/main/java/dev/lazurite/hexaplex/rendering/MatrixLoader.java @@ -0,0 +1,88 @@ +package dev.lazurite.hexaplex.rendering; + +import blue.endless.jankson.*; +import blue.endless.jankson.api.SyntaxError; +import dev.lazurite.hexaplex.config.Config; +import dev.lazurite.hexaplex.utilities.Matrix4x4; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.util.math.MathHelper; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +@Environment(EnvType.CLIENT) +public final class MatrixLoader { + + private MatrixLoader() { } + + private static final Map MATRICES = new HashMap<>(); + + public static void loadMatrices(InputStream in) throws IOException, SyntaxError { + JsonObject object = Jankson.builder().build().load(in); + + for (Map.Entry entry : object.entrySet()) { + MatrixLoader.MATRICES.put(entry.getKey(), entry.getValue()); + } + } + + public static Matrix4x4 getMatrix(String name) { + JsonArray array = (JsonArray) MatrixLoader.MATRICES.get(name); + + float[] values = new float[array.size()]; + + int variableCount = 0; + + for (int i = 0; i < values.length; ++i) { + JsonPrimitive primitive = (JsonPrimitive) array.get(i); + + if (primitive.getValue().equals("")) { + if (variableCount == 0) { + values[i] = MathHelper.clamp((float) (2 * Config.INSTANCE.getStrength() * Config.INSTANCE.getSkew()), 0.0f, (float) Config.INSTANCE.getStrength()); + } else { + values[i] = MathHelper.clamp((float) (2 * Config.INSTANCE.getStrength() * (1.0 - Config.INSTANCE.getSkew())), 0.0f, (float) Config.INSTANCE.getStrength()); + } + ++variableCount; + } else { + values[i] = primitive.asFloat(0.0f); + } + } + + return new Matrix4x4(values); + } + + public enum MatrixNames { + RGB_TO_XYZ, + XYZ_TO_LMS, + RGB_TO_LMS, + LMS_TO_RGB, + LMS_TO_LMSP, + LMS_TO_LMSD, + LMS_TO_LMST, + RGBP_ERR, + RGBD_ERR, + RGBT_ERR; + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + boolean capitalizeNext = false; + + for (char c : super.toString().toCharArray()) { + if (c == '_') { + capitalizeNext = true; + } else { + builder.append(capitalizeNext ? c : Character.toLowerCase(c)); + + if (capitalizeNext) { + capitalizeNext = false; + } + } + } + + return builder.toString(); + } + } +} diff --git a/src/main/java/dev/lazurite/hexaplex/rendering/Profiles.java b/src/main/java/dev/lazurite/hexaplex/rendering/Profiles.java new file mode 100644 index 0000000..c1ab941 --- /dev/null +++ b/src/main/java/dev/lazurite/hexaplex/rendering/Profiles.java @@ -0,0 +1,25 @@ +package dev.lazurite.hexaplex.rendering; + +import me.shedaniel.clothconfig2.gui.entries.SelectionListEntry; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.jetbrains.annotations.NotNull; + +@Environment(EnvType.CLIENT) +public enum Profiles implements SelectionListEntry.Translatable { + NORMAL, + PROTAN, + DEUTERAN, + TRITAN; + + @Override + public String toString() { + return super.toString().toLowerCase(); + } + + @Override + public @NotNull String getKey() { + return "config.hexaplex.profile." + toString(); + } +} + diff --git a/src/main/java/dev/lazurite/hexaplex/rendering/ShaderManager.java b/src/main/java/dev/lazurite/hexaplex/rendering/ShaderManager.java new file mode 100644 index 0000000..c7c7304 --- /dev/null +++ b/src/main/java/dev/lazurite/hexaplex/rendering/ShaderManager.java @@ -0,0 +1,121 @@ +package dev.lazurite.hexaplex.rendering; + +import com.mojang.blaze3d.systems.RenderSystem; +import dev.lazurite.hexaplex.Hexaplex; +import dev.lazurite.hexaplex.config.Config; +import ladysnake.satin.api.managed.ManagedShaderEffect; +import ladysnake.satin.api.managed.ShaderEffectManager; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.util.Identifier; + +@Environment(EnvType.CLIENT) +public final class ShaderManager { + + private ShaderManager() { } + + private static final ManagedShaderEffect FILTER = ShaderEffectManager.getInstance().manage( + new Identifier(Hexaplex.MOD_ID, "shaders/post/filter.json"), + shader -> { + ShaderManager.FILTER.setUniformValue( + UniformNames.RGB_TO_LMS.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.RGB_TO_LMS.toString()).copyMatrix4f() + ); + + ShaderManager.FILTER.setUniformValue( + UniformNames.LMS_TO_RGB.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.LMS_TO_RGB.toString()).copyMatrix4f() + ); + + ShaderManager.setUniforms(); + } + ); + + private static void setUniforms() { + switch (Config.INSTANCE.getProfile()) { + case PROTAN: + ShaderManager.FILTER.setUniformValue( + UniformNames.LMS_TO_LMSC.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.LMS_TO_LMSP.toString()).copyMatrix4f() + ); + + ShaderManager.FILTER.setUniformValue( + UniformNames.RGBC_ERR.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.RGBP_ERR.toString()).copyMatrix4f() + ); + break; + + case DEUTERAN: + ShaderManager.FILTER.setUniformValue( + UniformNames.LMS_TO_LMSC.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.LMS_TO_LMSD.toString()).copyMatrix4f() + ); + + ShaderManager.FILTER.setUniformValue( + UniformNames.RGBC_ERR.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.RGBD_ERR.toString()).copyMatrix4f() + ); + break; + + case TRITAN: + ShaderManager.FILTER.setUniformValue( + UniformNames.LMS_TO_LMSC.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.LMS_TO_LMST.toString()).copyMatrix4f() + ); + + ShaderManager.FILTER.setUniformValue( + UniformNames.RGBC_ERR.toString(), + MatrixLoader.getMatrix(MatrixLoader.MatrixNames.RGBT_ERR.toString()).copyMatrix4f() + ); + break; + + default: + break; + } + } + + public static void registerRenderer() { + BlitRenderCallback.EVENT.register( + tickDelta -> { + if (Config.INSTANCE.isDirty()) { + ShaderManager.setUniforms(); + Config.INSTANCE.markClean(); + } + + if (!Config.INSTANCE.getProfile().equals(Profiles.NORMAL) && !(Config.INSTANCE.getStrength() == 0.0)) { + RenderSystem.disableAlphaTest(); // idek + RenderSystem.disableBlend(); // idek + + ShaderManager.FILTER.render(tickDelta); + + RenderSystem.enableAlphaTest(); + RenderSystem.enableBlend(); + } + } + ); + } + + private enum UniformNames { + RGB_TO_LMS, + LMS_TO_RGB, + LMS_TO_LMSC, + RGBC_ERR; + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + boolean capitalizeNext = false; + + for (char c : super.toString().toCharArray()) { + if (c == '_') { + capitalizeNext = true; + } else { + builder.append(capitalizeNext ? c : Character.toLowerCase(c)); + capitalizeNext = false; + } + } + + return builder.toString(); + } + } +} diff --git a/src/main/java/dev/lazurite/hexaplex/util/Matrix4x4.java b/src/main/java/dev/lazurite/hexaplex/utilities/Matrix4x4.java similarity index 96% rename from src/main/java/dev/lazurite/hexaplex/util/Matrix4x4.java rename to src/main/java/dev/lazurite/hexaplex/utilities/Matrix4x4.java index 51e24e2..b47260a 100644 --- a/src/main/java/dev/lazurite/hexaplex/util/Matrix4x4.java +++ b/src/main/java/dev/lazurite/hexaplex/utilities/Matrix4x4.java @@ -1,4 +1,4 @@ -package dev.lazurite.hexaplex.util; +package dev.lazurite.hexaplex.utilities; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; diff --git a/src/main/resources/assets/hexaplex/icon.png b/src/main/resources/assets/hexaplex/icon.png index e69de29..7bf0b0f 100644 Binary files a/src/main/resources/assets/hexaplex/icon.png and b/src/main/resources/assets/hexaplex/icon.png differ diff --git a/src/main/resources/assets/hexaplex/lang/en_us.json b/src/main/resources/assets/hexaplex/lang/en_us.json index 7a9d5c5..eea258c 100644 --- a/src/main/resources/assets/hexaplex/lang/en_us.json +++ b/src/main/resources/assets/hexaplex/lang/en_us.json @@ -1,9 +1,23 @@ { - "options.hexaplex.profile.title": "Colorblindness", - "options.hexaplex.profile.normal": "Normal", - "options.hexaplex.profile.deuteran": "Deuteran", - "options.hexaplex.profile.protan": "Protan", - "options.hexaplex.profile.tritan": "Tritan", - "options.hexaplex.strength.tooltip": "Determines the strength of the colorblindness filter.", - "options.hexaplex.strength.title": "Colorblindness Strength" + "config.hexaplex.title": "Hexaplex Settings", + "config.hexaplex.category": "", + "config.hexaplex.percent_sign": "%", + "config.hexaplex.profile": "Profile", + "config.hexaplex.strength": "Strength", + "config.hexaplex.skew": "Skew", + "config.hexaplex.profile.normal": "Normal", + "config.hexaplex.profile.protanomaly": "Protanomaly", + "config.hexaplex.profile.deuteranomaly": "Deuteranomaly", + "config.hexaplex.profile.tritanomaly": "Tritanomaly", + "config.hexaplex.profile.protanopia": "Protanopia", + "config.hexaplex.profile.deuteranopia": "Deuteranopia", + "config.hexaplex.profile.tritanopia": "Tritanopia", + "config.hexaplex.strength.normal": "N/A", + "config.hexaplex.strength.protan": "%1$d%%", + "config.hexaplex.strength.deuteran": "%1$d%%", + "config.hexaplex.strength.tritan": "%1$d%%", + "config.hexaplex.skew.normal": "N/A", + "config.hexaplex.skew.protan": "%1$d%% Green : %2$d%% Blue", + "config.hexaplex.skew.deuteran": "%1$d%% Red : %2$d%% Blue", + "config.hexaplex.skew.tritan": "%1$d%% Red : %2$d%% Green" } diff --git a/src/main/resources/assets/hexaplex/shaders/program/filter.json b/src/main/resources/assets/hexaplex/shaders/program/filter.json index b89b89c..cb1b3c5 100644 --- a/src/main/resources/assets/hexaplex/shaders/program/filter.json +++ b/src/main/resources/assets/hexaplex/shaders/program/filter.json @@ -1,4 +1,11 @@ { + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha", + "srcalpha": "one", + "dstalpha": "zero" + }, "vertex": "minecraft:blit", "fragment": "hexaplex:filter", "attributes": [ "Position" ], diff --git a/src/main/resources/assets/hexaplex/shaders/uniform/matrix4x4/filter.json b/src/main/resources/assets/hexaplex/shaders/uniform/matrix4x4/filter.json index e9a2f1b..66a9a0b 100644 --- a/src/main/resources/assets/hexaplex/shaders/uniform/matrix4x4/filter.json +++ b/src/main/resources/assets/hexaplex/shaders/uniform/matrix4x4/filter.json @@ -1,49 +1,61 @@ { + "rgbToXyz": [ + 0.41239080, 0.35758434, 0.18048079, 0.0, + 0.21263901, 0.71516868, 0.07219232, 0.0, + 0.01933082, 0.11919478, 0.95053215, 0.0, + 0.00000000, 0.00000000, 0.00000000, 1.0 + ], + "xyzToLms": [ + 0.7328, 0.4296, -0.1624, 0.0, + -0.7036, 1.6975, 0.0061, 0.0, + 0.0030, 0.0136, 0.9834, 0.0, + 0.0000, 0.0000, 0.0000, 1.0 + ], "rgbToLms": [ - 0.31399022, 0.63951294, 0.04649755, 0.0, - 0.15537241, 0.75789446, 0.08670142, 0.0, - 0.01775239, 0.10944209, 0.87256922, 0.0, - 0.0, 0.0, 0.0, 1.0 + 0.390410371768, 0.549917037008, 0.008903722424, 0.0, + 0.070914470597, 0.963129580834, 0.001358425471, 0.0, + 0.023138991324, 0.128015193720, 0.936276574232, 0.0, + 0.000000000000, 0.000000000000, 0.000000000000, 1.0 ], "lmsToRgb": [ - 5.47221206, -4.6419601, 0.16963708, 0.0, - -1.1252419, 2.29317094, -0.1678952, 0.0, - 0.02980165, -0.19318073, 1.16364789, 0.0, - 0.0, 0.0, 0.0, 1.0 - ], - "lmsToLmsd": [ - 1.0, 0.0, 0.0, 0.0, - 0.9513092, 0.0, 0.04866992, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 + 2.858765476844, -1.628966860818, -0.024822605676, 0.0, + -0.210429576918, 1.158388040552, 0.000320442411, 0.0, + -0.041879469797, -0.118126013614, 1.068630120731, 0.0, + 0.000000000000, 0.000000000000, 0.000000000000, 1.0 ], "lmsToLmsp": [ - 0.0, 1.05118294, -0.05116099, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 + 0.00000000, 0.90817131, 0.00819207, 0.0, + 0.00000000, 1.00000000, 0.00000000, 0.0, + 0.00000000, 0.00000000, 1.00000000, 0.0, + 0.00000000, 0.00000000, 0.00000000, 1.0 + ], + "lmsToLmsd": [ + 1.00000000, 0.00000000, 0.00000000, 0.0, + 1.10111384, 0.00000000, -0.00902040, 0.0, + 0.00000000, 0.00000000, 1.00000000, 0.0, + 0.00000000, 0.00000000, 0.00000000, 1.0 ], "lmsToLmst": [ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - -0.86744736, 1.86727089, 0.0, 0.0, - 0.0, 0.0, 0.0, 1.0 + 1.00000000, 0.00000000, 0.00000000, 0.0, + 0.00000000, 1.00000000, 0.00000000, 0.0, + -0.15777270, 1.19489141, 0.00000000, 0.0, + 0.00000000, 0.00000000, 0.00000000, 1.0 ], - "rgbdErr": [ - 1.0, "", 0.0, 0.0, + "rgbpErr": [ 0.0, 0.0, 0.0, 0.0, - 0.0, "", 1.0, 0.0, + "", 1.0, 0.0, 0.0, + "", 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], - "rgbpErr": [ + "rgbdErr": [ + 1.0, "", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - "", 1.0, 0.0, 0.0, - "", 0.0, 1.0, 0.0, + 0.0, "", 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], "rgbtErr": [ - 1.0, 0.0, "", 0.0, - 0.0, 1.0, "", 0.0, + 1.0, 0.0, "", 0.0, + 0.0, 1.0, "", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 8eb631c..20b22e5 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -5,26 +5,31 @@ "name": "Hexaplex", "description": "A colorblindness correction mod for Minecraft.", "authors": [ - "Lazurite Team" + "The Lazurite Team" ], "contact": { - "website": "https://lazurite.dev/", - "repo": "https://github.com/LazuriteMC/Hexaplex" + "homepage": "https://lazurite.dev/", + "sources": "https://github.com/LazuriteMC/Hexaplex", + "issues": "https://github.com/LazuriteMC/Hexaplex/issues" }, "license": "MIT", "icon": "assets/hexaplex/icon.png", "environment": "client", "entrypoints": { "client": [ - "dev.lazurite.hexaplex.init.ClientInitializer" + "dev.lazurite.hexaplex.Hexaplex" + ], + "modmenu": [ + "dev.lazurite.hexaplex.gui.ModMenu" ] }, "mixins": [ "hexaplex.mixins.json" ], + "accessWidener": "hexaplex.accesswidener", "depends": { - "fabricloader": ">=0.10.7", + "fabricloader": ">=0.7.4", "fabric": "*", - "minecraft": "1.16.4" + "minecraft": "1.16.x" } } diff --git a/src/main/resources/hexaplex.mixins.json b/src/main/resources/hexaplex.mixins.json index 7378ea6..47d6015 100644 --- a/src/main/resources/hexaplex.mixins.json +++ b/src/main/resources/hexaplex.mixins.json @@ -1,12 +1,11 @@ { "required": true, "minVersion": "0.8", - "package": "dev.lazurite.hexaplex.mixin", + "package": "dev.lazurite.hexaplex.mixins", "compatibilityLevel": "JAVA_8", "mixins": [ ], "client": [ - "AccessibilityOptionsScreenMixin", "MinecraftClientMixin" ], "injectors": {