|
| 1 | +package igentuman.nc.client.renderer; |
| 2 | + |
| 3 | +import com.mojang.blaze3d.platform.GlStateManager; |
| 4 | +import com.mojang.blaze3d.systems.RenderSystem; |
| 5 | +import com.mojang.blaze3d.vertex.BufferBuilder; |
| 6 | +import com.mojang.blaze3d.vertex.DefaultVertexFormat; |
| 7 | +import com.mojang.blaze3d.vertex.Tesselator; |
| 8 | +import com.mojang.blaze3d.vertex.VertexFormat; |
| 9 | +import igentuman.nc.block.entity.kugelblitz.BlackHoleBE; |
| 10 | +import net.minecraft.client.Minecraft; |
| 11 | +import net.minecraft.client.renderer.EffectInstance; |
| 12 | +import net.minecraft.core.BlockPos; |
| 13 | +import net.minecraft.world.level.block.entity.BlockEntity; |
| 14 | +import net.minecraftforge.api.distmarker.Dist; |
| 15 | +import net.minecraftforge.client.event.RenderLevelStageEvent; |
| 16 | +import net.minecraftforge.common.MinecraftForge; |
| 17 | +import net.minecraftforge.eventbus.api.SubscribeEvent; |
| 18 | +import net.minecraftforge.fml.common.Mod; |
| 19 | +import org.joml.Matrix4f; |
| 20 | + |
| 21 | +import java.util.Collections; |
| 22 | +import java.util.HashSet; |
| 23 | +import java.util.Set; |
| 24 | + |
| 25 | +import static igentuman.nc.NuclearCraft.MODID; |
| 26 | +import static igentuman.nc.client.renderer.NCShaders.blackholePostEffect; |
| 27 | +import static igentuman.nc.multiblock.kugelblitz.KugelblitzRegistration.KUGELBLITZ_BLOCKS; |
| 28 | + |
| 29 | +@Mod.EventBusSubscriber(modid = MODID, value = Dist.CLIENT) |
| 30 | +public class DistortShader { |
| 31 | + |
| 32 | + private static int currentSize = 0; |
| 33 | + public static void register() { |
| 34 | + MinecraftForge.EVENT_BUS.register(DistortShader.class); |
| 35 | + } |
| 36 | + |
| 37 | + public static final BlackholeRegistry blackhole = new BlackholeRegistry(); |
| 38 | + |
| 39 | + public static class BlackholeRegistry { |
| 40 | + private final Set<BlockPos> positions = new HashSet<>(); |
| 41 | + |
| 42 | + public boolean contains(BlockPos pos) { return positions.contains(pos); } |
| 43 | + public void add(BlockPos pos) { positions.add(pos); } |
| 44 | + public void remove(BlockPos pos) { positions.remove(pos); } |
| 45 | + public Set<BlockPos> getPositions() { return Collections.unmodifiableSet(positions); } |
| 46 | + } |
| 47 | + |
| 48 | + private static boolean isBlackHoleBlock(net.minecraft.world.level.Level level, net.minecraft.core.BlockPos pos) { |
| 49 | + return level.getBlockState(pos).is(KUGELBLITZ_BLOCKS.get("black_hole").get()); |
| 50 | + } |
| 51 | + |
| 52 | + private static boolean processBlackHole(Minecraft mc, RenderLevelStageEvent event, EffectInstance effect, BlockPos pos) { |
| 53 | + BlockEntity be = mc.level.getExistingBlockEntity(pos); |
| 54 | + float scaleMult = 1; |
| 55 | + if(be instanceof BlackHoleBE blackHoleBE) { |
| 56 | + scaleMult = 0.3f / blackHoleBE.scale; |
| 57 | + } else { |
| 58 | + return false; |
| 59 | + } |
| 60 | + if(scaleMult != 1) { |
| 61 | + scaleMult = (float) Math.pow(scaleMult+0.375f, 5); |
| 62 | + } |
| 63 | + // Default values |
| 64 | + float blurX = 0.5f; |
| 65 | + float blurY = 0.5f; |
| 66 | + boolean blackholeVisible = false; |
| 67 | + float distanceFactor = 0.0f; |
| 68 | + |
| 69 | + if (mc.level == null && mc.player == null) { |
| 70 | + return false; |
| 71 | + } |
| 72 | + // Get matrices |
| 73 | + Matrix4f viewMatrix = event.getPoseStack().last().pose(); |
| 74 | + Matrix4f projectionMatrix = RenderSystem.getProjectionMatrix(); |
| 75 | + |
| 76 | + // Calculate distance to blackhole |
| 77 | + double distanceSq = mc.player.position().distanceToSqr( |
| 78 | + pos.getX() + 0.5, |
| 79 | + pos.getY() + 0.5, |
| 80 | + pos.getZ() + 0.5 |
| 81 | + ); |
| 82 | + double distance = Math.sqrt(distanceSq); |
| 83 | + |
| 84 | + // Get camera position |
| 85 | + net.minecraft.client.Camera camera = mc.gameRenderer.getMainCamera(); |
| 86 | + net.minecraft.world.phys.Vec3 cameraPos = camera.getPosition(); |
| 87 | + |
| 88 | + // World to screen projection |
| 89 | + float posX = (float)(pos.getX() + 0.5 - cameraPos.x()); |
| 90 | + float posY = (float)(pos.getY() + 0.5 - cameraPos.y()); |
| 91 | + float posZ = (float)(pos.getZ() + 0.5 - cameraPos.z()); |
| 92 | + |
| 93 | + // Create position vector |
| 94 | + org.joml.Vector4f pos1 = new org.joml.Vector4f(posX, posY, posZ, 1.0f); |
| 95 | + pos1.mul(viewMatrix); |
| 96 | + pos1.mul(projectionMatrix); |
| 97 | + |
| 98 | + // Perspective division |
| 99 | + if (pos1.w != 0.0f) { |
| 100 | + pos1.x /= pos1.w; |
| 101 | + pos1.y /= pos1.w; |
| 102 | + pos1.z /= pos1.w; |
| 103 | + } |
| 104 | + |
| 105 | + // Check if in front of camera |
| 106 | + if (pos1.z > -1.0f && pos1.z < 1.0f) { |
| 107 | + // Calculate screen coordinates |
| 108 | + blurX = (pos1.x * 0.5f + 0.5f); |
| 109 | + blurY = (pos1.y * 0.5f + 0.5f); |
| 110 | + |
| 111 | + // Check if on screen (with margin) |
| 112 | + float margin = 0.1f; |
| 113 | + if (blurX >= -margin && blurX <= 1.0f + margin && |
| 114 | + blurY >= -margin && blurY <= 1.0f + margin) { |
| 115 | + blackholeVisible = true; |
| 116 | + distanceFactor = (float) (7f/distance); |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + // Scale radius and magnification based on distance |
| 121 | + float baseRadius = blackholeVisible ? 150.0f : 0.0f; |
| 122 | + float radius = baseRadius * distanceFactor * scaleMult; |
| 123 | + float baseMagnification = blackholeVisible ? 5.8f : 0.1f; |
| 124 | + |
| 125 | + effect.getUniform("BlurPos").set(blurX, blurY); |
| 126 | + effect.getUniform("Radius").set(radius, baseMagnification/scaleMult); |
| 127 | + return true; |
| 128 | + } |
| 129 | + |
| 130 | + @SubscribeEvent |
| 131 | + public static void onRenderTick(RenderLevelStageEvent event) { |
| 132 | + if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_CUTOUT_BLOCKS) return; |
| 133 | + Minecraft mc = Minecraft.getInstance(); |
| 134 | + |
| 135 | + if (blackholePostEffect != null) { |
| 136 | + EffectInstance effect = blackholePostEffect.passes.get(0).getEffect(); |
| 137 | + |
| 138 | + if(currentSize != mc.getWindow().getWidth() + mc.getWindow().getHeight()) { |
| 139 | + currentSize = mc.getWindow().getWidth() + mc.getWindow().getHeight(); |
| 140 | + blackholePostEffect.resize(mc.getWindow().getWidth(), mc.getWindow().getHeight()); |
| 141 | + effect.getUniform("BlurDir").set(0.2f, 0.0f); |
| 142 | + } |
| 143 | + |
| 144 | + RenderSystem.depthMask(false); |
| 145 | + for (BlockPos pos : blackhole.getPositions()) { |
| 146 | + if(processBlackHole(mc, event, effect, pos)) { |
| 147 | + blackholePostEffect.process(mc.getFrameTime()); |
| 148 | + } |
| 149 | + } |
| 150 | + mc.getMainRenderTarget().bindWrite(false); |
| 151 | + blackholePostEffect.passes.get(blackholePostEffect.passes.size()-1).outTarget.bindRead(); |
| 152 | + RenderSystem.depthFunc(515); |
| 153 | + // Set up blending to preserve what's already in the framebuffer |
| 154 | + RenderSystem.enableBlend(); |
| 155 | + RenderSystem.blendFuncSeparate( |
| 156 | + GlStateManager.SourceFactor.SRC_ALPHA, |
| 157 | + GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, |
| 158 | + GlStateManager.SourceFactor.ONE, |
| 159 | + GlStateManager.DestFactor.ZERO |
| 160 | + ); |
| 161 | + |
| 162 | + // Draw a fullscreen quad with the processed shader result |
| 163 | + Tesselator tesselator = Tesselator.getInstance(); |
| 164 | + BufferBuilder bufferbuilder = tesselator.getBuilder(); |
| 165 | + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); |
| 166 | + bufferbuilder.vertex(0, mc.getMainRenderTarget().height, 0).uv(0, 0).endVertex(); |
| 167 | + bufferbuilder.vertex(mc.getMainRenderTarget().width, mc.getMainRenderTarget().height, 0).uv(1, 0).endVertex(); |
| 168 | + bufferbuilder.vertex(mc.getMainRenderTarget().width, 0, 0).uv(1, 1).endVertex(); |
| 169 | + bufferbuilder.vertex(0, 0, 0).uv(0, 1).endVertex(); |
| 170 | + tesselator.end(); |
| 171 | + |
| 172 | + RenderSystem.depthFunc(515); |
| 173 | + RenderSystem.depthMask(true); |
| 174 | + RenderSystem.disableBlend(); |
| 175 | + } |
| 176 | + } |
| 177 | +} |
0 commit comments