From 1b091fc5fde6d20dbcb3fb669e482211445616aa Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 12 Dec 2022 01:05:10 +0000 Subject: [PATCH] Added corruption visual effect --- .../unicopia/client/UnicopiaClient.java | 5 + .../client/render/shader/ViewportShader.java | 136 ++++++++++++++++++ .../mixin/client/MixinGameRenderer.java | 17 +++ 3 files changed, 158 insertions(+) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java index 5818b187..e9818465 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java +++ b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java @@ -12,17 +12,20 @@ import com.minelittlepony.unicopia.client.gui.LanSettingsScreen; import com.minelittlepony.unicopia.client.gui.UHud; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate; +import com.minelittlepony.unicopia.client.render.shader.ViewportShader; import com.minelittlepony.unicopia.container.*; import com.minelittlepony.unicopia.entity.player.PlayerCamera; import com.minelittlepony.unicopia.entity.player.Pony; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.OpenToLanScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.HandledScreens; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.resource.ResourceType; import net.minecraft.text.Text; public class UnicopiaClient implements ClientModInitializer { @@ -67,6 +70,8 @@ public class UnicopiaClient implements ClientModInitializer { ScreenInitCallback.EVENT.register(this::onScreenInit); ItemTooltipCallback.EVENT.register(new ModifierTooltipRenderer()); + ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(ViewportShader.INSTANCE); + Unicopia.SIDE = () -> Optional.ofNullable(MinecraftClient.getInstance().player).map(Pony::of); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java b/src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java new file mode 100644 index 00000000..f79027b1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java @@ -0,0 +1,136 @@ +package com.minelittlepony.unicopia.client.render.shader; + +import java.io.IOException; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; + +import com.google.common.collect.*; +import com.google.gson.JsonSyntaxException; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.logging.LogUtils; + +import it.unimi.dsi.fastutil.objects.Object2FloatMap; +import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; +import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.*; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.SynchronousResourceReloader; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; + +public class ViewportShader implements SynchronousResourceReloader, IdentifiableResourceReloadListener { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Identifier ID = Unicopia.id("viewport_shader"); + + public static final ViewportShader INSTANCE = new ViewportShader(); + + public static final Identifier CREEPER_SHADER = new Identifier("shaders/post/invert.json"); + public static final Identifier DESATURATION_SHADER = new Identifier("shaders/post/desaturate.json"); + + private final MinecraftClient client = MinecraftClient.getInstance(); + + @Nullable + private LoadedShader shader; + + @Override + public Identifier getFabricId() { + return ID; + } + + public void loadShader(@Nullable Identifier shaderId) { + if (shader != null) { + shader.close(); + } + + if (shaderId == null) { + return; + } + + try { + shader = new LoadedShader(client, shaderId); + } catch (IOException e) { + LOGGER.warn("Failed to load shader: {}", shaderId, e); + } catch (JsonSyntaxException e) { + LOGGER.warn("Failed to parse shader: {}", shaderId, e); + } + } + + public void onResized(int width, int height) { + if (shader != null) { + shader.setupDimensions(width, height); + } + } + + public void render(float tickDelta) { + if (shader != null && client.player != null) { + RenderSystem.disableBlend(); + RenderSystem.disableDepthTest(); + RenderSystem.enableTexture(); + RenderSystem.resetTextureMatrix(); + + Pony pony = Pony.of(client.player); + + float corruption = pony.getCorruption().getScaled(0.9F); + corruption = pony.getInterpolator().interpolate("corruption", corruption, 10); + + corruption = 1 - corruption + 0.05F; + + shader.setUniformValue("color_convolve", "Saturation", corruption); + shader.render(tickDelta); + } + } + + @Override + public void reload(ResourceManager var1) { + if (shader != null) { + loadShader(shader.id); + } else { + loadShader(DESATURATION_SHADER); + } + } + + static class LoadedShader extends ShaderEffect { + private final Object2FloatMap uniformValues = new Object2FloatOpenHashMap<>(); + + private Multimap programs; + + private final Identifier id; + + public LoadedShader(MinecraftClient client, Identifier id) throws IOException, JsonSyntaxException { + super(client.getTextureManager(), client.getResourceManager(), client.getFramebuffer(), id); + this.id = id; + setupDimensions( + client.getWindow().getFramebufferWidth(), + client.getWindow().getFramebufferHeight() + ); + } + + @Override + public PostProcessShader addPass(String programName, Framebuffer source, Framebuffer dest) throws IOException { + PostProcessShader pass = super.addPass(programName, source, dest); + if (programs == null) { + programs = LinkedListMultimap.create(); + } + programs.put(pass.getProgram().getName(), pass.getProgram()); + return pass; + } + + public void setUniformValue(String programName, String uniformName, float value) { + float currentValue = uniformValues.containsKey(uniformName) ? 0F : uniformValues.getFloat(uniformName); + if (!MathHelper.approximatelyEquals(value, currentValue)) { + uniformValues.put(uniformName, value); + programs.get(programName).forEach(program -> { + GlUniform uniform = program.getUniformByName(uniformName); + if (uniform != null) { + uniform.set(value); + } + }); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinGameRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinGameRenderer.java index 16709a60..1dd59d85 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinGameRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinGameRenderer.java @@ -2,12 +2,14 @@ package com.minelittlepony.unicopia.mixin.client; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.client.BatEyesApplicator; import com.minelittlepony.unicopia.client.UnicopiaClient; +import com.minelittlepony.unicopia.client.render.shader.ViewportShader; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.Camera; @@ -60,4 +62,19 @@ abstract class MixinGameRenderer implements AutoCloseable, SynchronousResourceRe info.setReturnValue(UnicopiaClient.getWorldBrightness(info.getReturnValueF())); } } + + @Inject(method = "render", + at = @At( + value = "INVOKE", + target = "net/minecraft/client/gl/Framebuffer.beginWrite(Z)V", + shift = Shift.BEFORE) + ) + private void onBeforeFrameEnd(float tickDelta, long startTime, boolean tick, CallbackInfo info) { + ViewportShader.INSTANCE.render(tickDelta); + } + + @Inject(method = "onResized", at = @At("HEAD")) + private void onResized(int width, int height, CallbackInfo info) { + ViewportShader.INSTANCE.onResized(width, height); + } }