diff --git a/build.gradle b/build.gradle index 0a5994e0..a8732e16 100644 --- a/build.gradle +++ b/build.gradle @@ -201,6 +201,10 @@ dependencies { compileOnly(annotationProcessor("com.github.bawnorton.mixinsquared:mixinsquared-common:0.3.7-beta.1")) implementation(jarJar("com.github.bawnorton.mixinsquared:mixinsquared-forge:0.3.7-beta.1")) + //mixinextras for WrapOperation + compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.5.3")) + implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.5.3")) + // JEI, EMI, Jade modCompileOnly("mezz.jei:jei-${minecraft_version}-forge-api:${jei_version}") modCompileOnly("mezz.jei:jei-${minecraft_version}-common-api:${jei_version}") diff --git a/src/main/java/net/neganote/monilabs/MoniLabs.java b/src/main/java/net/neganote/monilabs/MoniLabs.java index 5c79f07b..3bc3986a 100644 --- a/src/main/java/net/neganote/monilabs/MoniLabs.java +++ b/src/main/java/net/neganote/monilabs/MoniLabs.java @@ -15,9 +15,13 @@ import com.gregtechceu.gtceu.common.data.GTCreativeModeTabs; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimplePreparableReloadListener; +import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.item.CreativeModeTab; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.ModelEvent; +import net.minecraftforge.client.event.RegisterClientReloadListenersEvent; import net.minecraftforge.client.event.RegisterParticleProvidersEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.IEventBus; @@ -129,7 +133,7 @@ public MoniLabs(FMLJavaModLoadingContext context) { modEventBus .addGenericListener(MachineDefinition.class, this::registerMachines); modEventBus.addGenericListener(CoverDefinition.class, this::registerCovers); - + modEventBus.addListener(this::onRegisterReloadListeners); // Most other events are fired on Forge's bus. // If we want to use annotations to register event listeners, // we need to register our object like this! @@ -220,6 +224,21 @@ private void registerMachines(GTCEuAPI.RegisterEvent event) {} + private void onRegisterReloadListeners(RegisterClientReloadListenersEvent event) { + event.registerReloadListener(new SimplePreparableReloadListener() { + + @Override + protected Object prepare(ResourceManager resourceManager, ProfilerFiller profilerFiller) { + return null; + } + + @Override + protected void apply(Object o, ResourceManager resourceManager, ProfilerFiller profilerFiller) { + BlackHoleRenderer.updateTextures(); + } + }); + } + public void registerAdditionalModels(ModelEvent.RegisterAdditional event) { event.register(CreativeEnergyRender.SPHERE); } diff --git a/src/main/java/net/neganote/monilabs/client/render/BlackHoleRenderer.java b/src/main/java/net/neganote/monilabs/client/render/BlackHoleRenderer.java new file mode 100644 index 00000000..a9d4dcad --- /dev/null +++ b/src/main/java/net/neganote/monilabs/client/render/BlackHoleRenderer.java @@ -0,0 +1,307 @@ +package net.neganote.monilabs.client.render; + +import net.irisshaders.iris.Iris; +import net.irisshaders.iris.shadows.ShadowRenderer; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RenderLevelStageEvent; +import net.minecraftforge.client.event.ScreenEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.neganote.monilabs.MoniLabs; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.pipeline.TextureTarget; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; +import org.lwjgl.opengl.*; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SideOnly(Side.CLIENT) +@Mod.EventBusSubscriber(modid = MoniLabs.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT) +public class BlackHoleRenderer { + + public static RenderTarget miscTranslucentTexture = null; + private static final int uAABBSize = 14; + private static final List blackHoles = new ArrayList<>(); + private static final Matrix4f projectionMatrix = new Matrix4f(); + private static final Matrix4f viewMatrix = new Matrix4f(); + + private static RenderTarget depthTextureForTranslucency; + private static int cachedSlot = -1; + + private static RenderTarget worldTexture = null; + + private static Vec3 lastCameraPos = new Vec3(0f, 0f, 0f); + + private static int findFreeTextureSlot() { + int maxUnits = GL31.glGetInteger(GL31.GL_MAX_TEXTURE_IMAGE_UNITS) - 1; + int freeSlot = -1; + + int originalActiveUnit = GL31.glGetInteger(GL31.GL_ACTIVE_TEXTURE); + + for (int i = maxUnits - 1; i >= 0; i--) { + GL31.glActiveTexture(GL31.GL_TEXTURE0 + i); + int boundTexture = GL31.glGetInteger(GL31.GL_TEXTURE_BINDING_2D); + + if (boundTexture == 0) { + freeSlot = i; + break; + } + } + + GL31.glActiveTexture(originalActiveUnit); + + if (freeSlot == -1) { + throw new RuntimeException("Failed to find free texture slot."); + } + + return freeSlot; + } + + private static void drawBlackHoleToDepthBuffer(PoseStack poseStack, Vector3f bhPos, Camera camera) { + // Draws the black hole to a separate depth buffer for future minecraft transparent passes filtering + Vec3 camPos = camera.getPosition(); + poseStack.pushPose(); + + var viewSpaceSpherePos = poseStack.last().pose().transform( + new Vector4f(bhPos.x - (float) camPos.x, bhPos.y - (float) camPos.y, bhPos.z - (float) camPos.z, 1.0f)); + + Tesselator tessellator = Tesselator.getInstance(); + BufferBuilder builder = tessellator.getBuilder(); + + ShaderInstance shader = MoniShaders.WORMHOLE_SHADER; + RenderSystem.setShader(() -> shader); + + shader.safeGetUniform("SpherePos").set(viewSpaceSpherePos.x, viewSpaceSpherePos.y, + viewSpaceSpherePos.z); + shader.safeGetUniform("uWriteOnlyDepth").set(1); + + AABB box = BlackHoleRendererHelpers.createAABBAt(bhPos, uAABBSize); + builder.begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_COLOR); + BlackHoleRendererHelpers.addBoxTriangles(poseStack, builder, + (float) (box.minX - camPos.x), (float) (box.minY - camPos.y), (float) (box.minZ - camPos.z), + (float) (box.maxX - camPos.x), (float) (box.maxY - camPos.y), (float) (box.maxZ - camPos.z), + 1, 1, 0, 1); + + tessellator.end(); + + poseStack.popPose(); + } + + public static void updateTextures() { + cachedSlot = -1; + Window w = Minecraft.getInstance().getWindow(); + int mcWidth = w.getWidth(); + int mcHeight = w.getHeight(); + if (mcWidth == 0 || mcHeight == 0 || + (worldTexture != null && mcWidth == worldTexture.width && mcHeight == worldTexture.height)) { + return; + } + + if (depthTextureForTranslucency != null) { + depthTextureForTranslucency.resize(mcWidth, mcHeight, false); + } else { + depthTextureForTranslucency = new TextureTarget(mcWidth, mcHeight, true, Minecraft.ON_OSX); + } + + if (worldTexture != null) { + worldTexture.resize(mcWidth, mcHeight, false); + } else { + worldTexture = new TextureTarget(mcWidth, mcHeight, true, Minecraft.ON_OSX); + } + + if (miscTranslucentTexture != null) { + miscTranslucentTexture.resize(mcWidth, mcHeight, false); + } else { + miscTranslucentTexture = new TextureTarget(mcWidth, mcHeight, true, Minecraft.ON_OSX); + } + } + + public static void render(Vector3f position) { + blackHoles.add(position); + } + + public static void handleTranslucentPassBegin(int programHandle) { + if (!BlackHoleRendererHelpers.isRenderingMinecraftTranslucentLayer || Iris.getCurrentPack().isPresent()) { + return; + } + + int activeUnit = GL31.glGetInteger(GL31.GL_ACTIVE_TEXTURE); + int uDepthLocation = GL31.glGetUniformLocation(programHandle, "u_BlackHoleDepthTexture"); + + if (uDepthLocation != -1) { + if (cachedSlot == -1) { + cachedSlot = findFreeTextureSlot(); + } + int targetUnit = cachedSlot; + + GL31.glUniform1i(uDepthLocation, targetUnit); + + GL31.glActiveTexture(GL31.GL_TEXTURE0 + targetUnit); + GL31.glBindTexture(GL31.GL_TEXTURE_2D, depthTextureForTranslucency.getDepthTextureId()); + + GL31.glActiveTexture(activeUnit); + } + } + + public static void preTranslucentPass(LevelRenderer instance, + RenderType renderType, + PoseStack poseStack, + double camX, + double camY, + double camZ, + Matrix4f projectionMatrix, + Operation original, + Camera camera) { + miscTranslucentTexture.copyDepthFrom(Minecraft.getInstance().getMainRenderTarget()); + miscTranslucentTexture.bindWrite(true); + RenderSystem.clear(GL31.GL_COLOR_BUFFER_BIT, false); + + int currentFBO = GL31.glGetInteger(GL31.GL_FRAMEBUFFER_BINDING); + + depthTextureForTranslucency.bindWrite(true); + RenderSystem.enableDepthTest(); + RenderSystem.depthMask(true); + RenderSystem.depthFunc(GL31.GL_ALWAYS); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.clearDepth(1); + RenderSystem.clear(GL31.GL_DEPTH_BUFFER_BIT, false); + GL31.glCullFace(GL31.GL_FRONT); // To render only back faces + + for (Vector3f bh : blackHoles) { + drawBlackHoleToDepthBuffer(poseStack, bh, camera); + } + + GL31.glCullFace(GL31.GL_BACK); + RenderSystem.depthFunc(GL31.GL_LEQUAL); + GL31.glBindFramebuffer(GL31.GL_FRAMEBUFFER, currentFBO); + + BlackHoleRendererHelpers.isRenderingMinecraftTranslucentLayer = true; + original.call(instance, renderType, poseStack, camX, camY, camZ, projectionMatrix); + BlackHoleRendererHelpers.isRenderingMinecraftTranslucentLayer = false; + } + + private static void renderCore(PoseStack poseStack, Vec3 cameraPos, Matrix4f projectionMatrix) { + Window w = Minecraft.getInstance().getWindow(); + if (worldTexture.width != w.getWidth() || + worldTexture.height != w.getHeight()) { + updateTextures(); + } + + poseStack.pushPose(); + RenderSystem.backupProjectionMatrix(); + RenderSystem.setProjectionMatrix(projectionMatrix, null); + RenderSystem.enableDepthTest(); + GL31.glCullFace(GL31.GL_FRONT); + + var mcWorldTexture = Minecraft.getInstance().getMainRenderTarget(); + + GL31.glBindFramebuffer(GL31.GL_READ_FRAMEBUFFER, mcWorldTexture.frameBufferId); + GL31.glBindFramebuffer(GL31.GL_DRAW_FRAMEBUFFER, worldTexture.frameBufferId); + GlStateManager._glBlitFrameBuffer(0, 0, w.getWidth(), w.getHeight(), 0, 0, w.getWidth(), w.getHeight(), + GL31.GL_COLOR_BUFFER_BIT, GL31.GL_NEAREST); + GlStateManager._glBlitFrameBuffer(0, 0, w.getWidth(), w.getHeight(), 0, 0, w.getWidth(), w.getHeight(), + GL31.GL_DEPTH_BUFFER_BIT, GL31.GL_NEAREST); + + mcWorldTexture.bindWrite(true); + + ShaderInstance shader = MoniShaders.WORMHOLE_SHADER; + RenderSystem.setShader(() -> shader); + shader.setSampler("WorldColor", worldTexture); + RenderSystem.setShaderTexture(0, worldTexture.getColorTextureId()); + + shader.safeGetUniform("uWriteOnlyDepth").set(0); + + Tesselator tesselator = Tesselator.getInstance(); + + for (Vector3f blackHolePos : blackHoles) { + BufferBuilder builder = tesselator.getBuilder(); + builder.begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_COLOR); + var viewSpaceSpherePos = poseStack.last().pose().transform( + new Vector4f(blackHolePos.x - (float) cameraPos.x, blackHolePos.y - (float) cameraPos.y, + blackHolePos.z - (float) cameraPos.z, 1.0f)); + shader.safeGetUniform("SpherePos").set(viewSpaceSpherePos.x, viewSpaceSpherePos.y, + viewSpaceSpherePos.z); + AABB box = BlackHoleRendererHelpers.createAABBAt(blackHolePos, uAABBSize); + BlackHoleRendererHelpers.addBoxTriangles(poseStack, builder, + (float) (box.minX - cameraPos.x), (float) (box.minY - cameraPos.y), + (float) (box.minZ - cameraPos.z), + (float) (box.maxX - cameraPos.x), (float) (box.maxY - cameraPos.y), + (float) (box.maxZ - cameraPos.z), + 1, 1, 0, 1); + tesselator.end(); + } + + GL31.glCullFace(GL31.GL_BACK); + poseStack.popPose(); + + if (Iris.getCurrentPack().isEmpty()) { + RenderSystem.enableBlend(); + miscTranslucentTexture.blitToScreen(miscTranslucentTexture.width, miscTranslucentTexture.height, false); + RenderSystem.enableDepthTest(); + } + + blackHoles.clear(); + RenderSystem.restoreProjectionMatrix(); + } + + @SubscribeEvent + public static void onRenderLevel(RenderLevelStageEvent event) { + if (blackHoles.isEmpty() || event.getStage() != RenderLevelStageEvent.Stage.AFTER_WEATHER || + MoniShaders.WORMHOLE_SHADER == null || + ShadowRenderer.ACTIVE) { + return; + } + if (Iris.getCurrentPack().isPresent()) { + lastCameraPos = event.getCamera().getPosition(); + // copy because viewMatrix = event.getPoseStack().last().pose() doesnt work :( + viewMatrix.identity().mul(event.getPoseStack().last().pose()); + projectionMatrix.identity().mul(event.getProjectionMatrix()); + return; + } + renderCore(event.getPoseStack(), event.getCamera().getPosition(), event.getProjectionMatrix()); + } + + public static void renderWithShadersOn() { + if (blackHoles.isEmpty()) { + return; + } + Window w = Minecraft.getInstance().getWindow(); + if (worldTexture.width != w.getWidth() || + worldTexture.height != w.getHeight()) { + updateTextures(); + } + PoseStack poseStack = new PoseStack(); + poseStack.setIdentity(); + poseStack.mulPoseMatrix(viewMatrix); + renderCore(poseStack, lastCameraPos, projectionMatrix); + } + + @SubscribeEvent + public static void onResize(ScreenEvent.Init.Post event) { + updateTextures(); + } +} diff --git a/src/main/java/net/neganote/monilabs/client/render/BlackHoleRendererHelpers.java b/src/main/java/net/neganote/monilabs/client/render/BlackHoleRendererHelpers.java new file mode 100644 index 00000000..eda2d647 --- /dev/null +++ b/src/main/java/net/neganote/monilabs/client/render/BlackHoleRendererHelpers.java @@ -0,0 +1,73 @@ +package net.neganote.monilabs.client.render; + +import net.minecraft.world.phys.AABB; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import org.joml.Matrix4f; +import org.joml.Vector3f; + +public class BlackHoleRendererHelpers { + + public static boolean isRenderingMinecraftTranslucentLayer = false; + public static boolean isTranslucentShader; + + public static void addBoxTriangles(PoseStack poseStack, BufferBuilder builder, + float x0, float y0, float z0, + float x1, float y1, float z1, + float r, float g, float b, float a) { + Matrix4f mat = poseStack.last().pose(); + // Front face (z1) + builder.vertex(mat, x0, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z1).color(r, g, b, a).endVertex(); + // Back face (z0) + builder.vertex(mat, x1, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z0).color(r, g, b, a).endVertex(); + // Top face (y1) + builder.vertex(mat, x0, y1, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z0).color(r, g, b, a).endVertex(); + // Bottom face (y0) + builder.vertex(mat, x0, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y0, z1).color(r, g, b, a).endVertex(); + // Right face (x1) + builder.vertex(mat, x1, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x1, y1, z1).color(r, g, b, a).endVertex(); + // Left face (x0) + builder.vertex(mat, x0, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y0, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y0, z0).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z1).color(r, g, b, a).endVertex(); + builder.vertex(mat, x0, y1, z0).color(r, g, b, a).endVertex(); + } + + public static AABB createAABBAt(Vector3f center, float size) { + double halfX = (double) size / 2.0; + double halfY = (double) size / 2.0; + double halfZ = (double) size / 2.0; + + return new AABB( + center.x - halfX, center.y - halfY, center.z - halfZ, + center.x + halfX, center.y + halfY, center.z + halfZ); + } +} diff --git a/src/main/java/net/neganote/monilabs/client/render/CreativeEnergyRender.java b/src/main/java/net/neganote/monilabs/client/render/CreativeEnergyRender.java index 02e4a930..5db0b7ff 100644 --- a/src/main/java/net/neganote/monilabs/client/render/CreativeEnergyRender.java +++ b/src/main/java/net/neganote/monilabs/client/render/CreativeEnergyRender.java @@ -1,35 +1,30 @@ package net.neganote.monilabs.client.render; -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.pattern.util.RelativeDirection; import com.gregtechceu.gtceu.client.renderer.machine.DynamicRender; import com.gregtechceu.gtceu.client.renderer.machine.DynamicRenderType; import com.gregtechceu.gtceu.client.util.ModelUtils; -import net.irisshaders.iris.Iris; import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.client.model.data.ModelData; import net.neganote.monilabs.MoniLabs; import net.neganote.monilabs.common.machine.multiblock.CreativeEnergyMultiMachine; +import net.neganote.monilabs.utils.LaserUtil; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.serialization.Codec; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import org.joml.Vector3f; -import java.util.List; +import java.util.Objects; import javax.annotation.ParametersAreNonnullByDefault; @@ -69,9 +64,6 @@ public void render(CreativeEnergyMultiMachine machine, float partialTick, PoseSt Direction upwards = RelativeDirection.UP.getRelative(frontFacing, upwardsFacing, machine.isFlipped()); - List sphereQuads = sphereModel.getQuads(null, null, RandomSource.create(), ModelData.EMPTY, - null); - poseStack.pushPose(); float translateX = 0.5f; float translateY = 0.5f; @@ -81,23 +73,23 @@ public void render(CreativeEnergyMultiMachine machine, float partialTick, PoseSt Vec3i backVec = back.getNormal().multiply(6); Vec3i upVec = upwards.getNormal().multiply(13); + Vec3i bhPos = machine.getPos().relative(back, 6).relative(upwards, 13); translateX += backVec.getX() + upVec.getX(); translateY += backVec.getY() + upVec.getY(); translateZ += backVec.getZ() + upVec.getZ(); poseStack.translate(translateX, translateY, translateZ); - float radius = 3.25f; - poseStack.scale(radius, radius, radius); - - PoseStack.Pose pose = poseStack.last(); - VertexConsumer consumer = buffer - .getBuffer(GTCEu.isModLoaded(GTValues.MODID_OCULUS) && Iris.getCurrentPack().isPresent() ? - RenderType.solid() : MoniRenderTypes.WORMHOLE); + int gameTime = Objects.requireNonNull(Minecraft.getInstance().player).tickCount; + poseStack.pushPose(); + poseStack.scale(4, 1, 4); + LaserUtil.renderLaser(new Vector3f(0, 256, 0), poseStack, buffer, 0.6f, 1f, 1f, 1f, 0, -translateY, 0, + partialTick, + gameTime, + false); + poseStack.popPose(); - for (BakedQuad quad : sphereQuads) { - consumer.putBulkData(pose, quad, 1.0f, 1.0f, 1.0f, packedLight, packedOverlay); - } + BlackHoleRenderer.render(new Vector3f(bhPos.getX() + 0.5f, bhPos.getY() + 0.5f, bhPos.getZ() + 0.5f)); poseStack.popPose(); } diff --git a/src/main/java/net/neganote/monilabs/mixin/DefaultChunkRendererMixin.java b/src/main/java/net/neganote/monilabs/mixin/DefaultChunkRendererMixin.java new file mode 100644 index 00000000..b482080e --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/DefaultChunkRendererMixin.java @@ -0,0 +1,64 @@ +package net.neganote.monilabs.mixin; + +import net.irisshaders.iris.Iris; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.RenderType; +import net.neganote.monilabs.client.render.BlackHoleRenderer; +import net.neganote.monilabs.mixin.accessor.ShaderChunkRendererAccessor; +import net.neganote.monilabs.mixin.accessor.TerrainRenderPassAccessor; + +import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.blaze3d.systems.RenderSystem; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; +import me.jellysquid.mods.sodium.client.gl.device.MultiDrawBatch; +import me.jellysquid.mods.sodium.client.gl.tessellation.GlTessellation; +import me.jellysquid.mods.sodium.client.render.chunk.DefaultChunkRenderer; +import me.jellysquid.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; +import org.lwjgl.opengl.GL31; +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.Redirect; + +@Mixin(value = DefaultChunkRenderer.class, remap = false) +public class DefaultChunkRendererMixin { + + @Shadow + private static void executeDrawBatch(CommandList commandList, GlTessellation tessellation, MultiDrawBatch batch) {} + + @Redirect( + method = "render", + at = @At( + value = "INVOKE", + target = "Lme/jellysquid/mods/sodium/client/render/chunk/DefaultChunkRenderer;executeDrawBatch(Lme/jellysquid/mods/sodium/client/gl/device/CommandList;Lme/jellysquid/mods/sodium/client/gl/tessellation/GlTessellation;Lme/jellysquid/mods/sodium/client/gl/device/MultiDrawBatch;)V")) + private void moniLabs$redirectExecuteDrawBatch(CommandList commandList, GlTessellation tessellation, + MultiDrawBatch batch, + @Local(argsOnly = true) TerrainRenderPass renderPass) { + DefaultChunkRenderer instance = (DefaultChunkRenderer) (Object) this; + if (((TerrainRenderPassAccessor) renderPass).getLayer() != RenderType.translucent() || + Iris.getCurrentPack().isPresent()) { + executeDrawBatch(commandList, + tessellation, + batch); + return; + } + // Draw everything NOT in FRONT of black hole + Minecraft.getInstance().getMainRenderTarget().bindWrite(true); + GL31.glUniform1i(GL31.glGetUniformLocation(((ShaderChunkRendererAccessor) instance).getActiveProgram().handle(), + "uDrawInFrontOfBlackHole"), 0); + executeDrawBatch(commandList, + tessellation, + batch); + + // Somewhat unoptimized (but should be fine). Should be in the beginning of render method (the uniform and + // glBindFramebuffer part) + GL31.glUniform1i(GL31.glGetUniformLocation(((ShaderChunkRendererAccessor) instance).getActiveProgram().handle(), + "uDrawInFrontOfBlackHole"), 1); + GL31.glBindFramebuffer(GL31.GL_DRAW_FRAMEBUFFER, BlackHoleRenderer.miscTranslucentTexture.frameBufferId); + RenderSystem.disableBlend(); + executeDrawBatch(commandList, + tessellation, + batch); + RenderSystem.enableBlend(); + } +} diff --git a/src/main/java/net/neganote/monilabs/mixin/GameRendererMixin.java b/src/main/java/net/neganote/monilabs/mixin/GameRendererMixin.java new file mode 100644 index 00000000..b656c22b --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/GameRendererMixin.java @@ -0,0 +1,28 @@ +package net.neganote.monilabs.mixin; + +import net.irisshaders.iris.Iris; +import net.irisshaders.iris.shadows.ShadowRenderer; +import net.minecraft.client.renderer.GameRenderer; +import net.neganote.monilabs.client.render.BlackHoleRenderer; + +import com.mojang.blaze3d.vertex.PoseStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = GameRenderer.class, priority = 1) +public class GameRendererMixin { + + @Inject(method = "renderLevel", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/LevelRenderer;renderLevel(Lcom/mojang/blaze3d/vertex/PoseStack;FJZLnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/GameRenderer;Lnet/minecraft/client/renderer/LightTexture;Lorg/joml/Matrix4f;)V", + shift = At.Shift.AFTER)) + public void moniLabs$renderBlackHoleWithShaders(float partialTicks, long finishTimeNano, PoseStack poseStack, + CallbackInfo ci) { + if (Iris.getCurrentPack().isPresent() && !ShadowRenderer.ACTIVE) { + BlackHoleRenderer.renderWithShadersOn(); + } + } +} diff --git a/src/main/java/net/neganote/monilabs/mixin/ShaderChunkRendererMixin.java b/src/main/java/net/neganote/monilabs/mixin/ShaderChunkRendererMixin.java new file mode 100644 index 00000000..db323141 --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/ShaderChunkRendererMixin.java @@ -0,0 +1,37 @@ +package net.neganote.monilabs.mixin; + +import net.minecraft.client.renderer.RenderType; +import net.neganote.monilabs.client.render.BlackHoleRenderer; +import net.neganote.monilabs.client.render.BlackHoleRendererHelpers; +import net.neganote.monilabs.mixin.accessor.TerrainRenderPassAccessor; + +import me.jellysquid.mods.sodium.client.gl.shader.GlProgram; +import me.jellysquid.mods.sodium.client.render.chunk.ShaderChunkRenderer; +import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderInterface; +import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderOptions; +import me.jellysquid.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; +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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(value = ShaderChunkRenderer.class, remap = false) +public class ShaderChunkRendererMixin { + + @Shadow + protected GlProgram activeProgram; + + @Inject(method = "createShader", at = @At("HEAD"), remap = false) + private void moniLabs$saveShaderType(String path, ChunkShaderOptions options, + CallbackInfoReturnable> cir) { + BlackHoleRendererHelpers.isTranslucentShader = ((TerrainRenderPassAccessor) options.pass()).getLayer() == + RenderType.translucent(); + } + + @Inject(method = "begin", at = @At("TAIL"), remap = false) + private void moniLabs$handleTranslucentPassBegin(TerrainRenderPass pass, CallbackInfo ci) { + BlackHoleRenderer.handleTranslucentPassBegin(activeProgram.handle()); + } +} diff --git a/src/main/java/net/neganote/monilabs/mixin/ShaderLoaderMixin.java b/src/main/java/net/neganote/monilabs/mixin/ShaderLoaderMixin.java new file mode 100644 index 00000000..8d0a987b --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/ShaderLoaderMixin.java @@ -0,0 +1,39 @@ +package net.neganote.monilabs.mixin; + +import net.minecraft.resources.ResourceLocation; +import net.neganote.monilabs.client.render.BlackHoleRendererHelpers; + +import me.jellysquid.mods.sodium.client.gl.shader.ShaderLoader; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(value = ShaderLoader.class, remap = false) +public class ShaderLoaderMixin { + + @Inject(method = "getShaderSource", at = @At("RETURN"), cancellable = true, remap = false) + private static void monilabs$interceptGetShaderSource(ResourceLocation name, + CallbackInfoReturnable cir) { + if (BlackHoleRendererHelpers.isTranslucentShader) { + if (name.toString().endsWith(".fsh")) { + String originalSource = cir.getReturnValue(); + String myUniform = "\nuniform sampler2D u_BlackHoleDepthTexture;\nuniform int uDrawInFrontOfBlackHole;\n"; + String regex = "(?s)(.*)(uniform sampler2D\\s+\\w+;)([^\r\n]*)"; + + var replaced = originalSource.replaceFirst(regex, "$1$2$3\n" + myUniform) + .replace("void main() {", + """ + void main() { + float sphereDepth = texture(u_BlackHoleDepthTexture, gl_FragCoord.xy / vec2(textureSize(u_BlackHoleDepthTexture, 0))).r; + bool isBehindBlackHole = gl_FragCoord.z >= sphereDepth; + if (uDrawInFrontOfBlackHole == 0 && !isBehindBlackHole && sphereDepth < 1.0) + discard; + if (uDrawInFrontOfBlackHole == 1 && (sphereDepth >= 1.0 || isBehindBlackHole)) + discard; + """); + cir.setReturnValue(replaced); + } + } + } +} diff --git a/src/main/java/net/neganote/monilabs/mixin/TranslucentRenderMixin.java b/src/main/java/net/neganote/monilabs/mixin/TranslucentRenderMixin.java new file mode 100644 index 00000000..461c2795 --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/TranslucentRenderMixin.java @@ -0,0 +1,44 @@ +package net.neganote.monilabs.mixin; + +import net.irisshaders.iris.Iris; +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.RenderType; +import net.neganote.monilabs.client.render.BlackHoleRenderer; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.blaze3d.vertex.PoseStack; +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(LevelRenderer.class) +public class TranslucentRenderMixin { + + @WrapOperation( + method = "renderLevel", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/LevelRenderer;renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V")) + private void moniLabs$wrapSecondRenderChunkLayer( + LevelRenderer instance, + RenderType renderType, + PoseStack poseStack, + double camX, + double camY, + double camZ, + Matrix4f projectionMatrix, + Operation original, + @Local(argsOnly = true) Camera camera) { + if (renderType == RenderType.translucent() && Iris.getCurrentPack().isEmpty()) { + BlackHoleRenderer.preTranslucentPass(instance, renderType, poseStack, camX, camY, camZ, projectionMatrix, + original, + camera); + + } else { + original.call(instance, renderType, poseStack, camX, camY, camZ, projectionMatrix); + } + } +} diff --git a/src/main/java/net/neganote/monilabs/mixin/accessor/ShaderChunkRendererAccessor.java b/src/main/java/net/neganote/monilabs/mixin/accessor/ShaderChunkRendererAccessor.java new file mode 100644 index 00000000..8593d10f --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/accessor/ShaderChunkRendererAccessor.java @@ -0,0 +1,14 @@ +package net.neganote.monilabs.mixin.accessor; + +import me.jellysquid.mods.sodium.client.gl.shader.GlProgram; +import me.jellysquid.mods.sodium.client.render.chunk.ShaderChunkRenderer; +import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderInterface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ShaderChunkRenderer.class) +public interface ShaderChunkRendererAccessor { + + @Accessor(value = "activeProgram", remap = false) + GlProgram getActiveProgram(); +} diff --git a/src/main/java/net/neganote/monilabs/mixin/accessor/TerrainRenderPassAccessor.java b/src/main/java/net/neganote/monilabs/mixin/accessor/TerrainRenderPassAccessor.java new file mode 100644 index 00000000..07269502 --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/accessor/TerrainRenderPassAccessor.java @@ -0,0 +1,14 @@ +package net.neganote.monilabs.mixin.accessor; + +import net.minecraft.client.renderer.RenderType; + +import me.jellysquid.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(value = TerrainRenderPass.class, remap = false) +public interface TerrainRenderPassAccessor { + + @Accessor("layer") + RenderType getLayer(); +} diff --git a/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.fsh b/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.fsh index a7f2ef39..951fa1cd 100644 --- a/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.fsh +++ b/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.fsh @@ -1,9 +1,132 @@ -#version 150 +#version 330 core -#moj_import +in vec3 rayOrigin; +in vec3 rayDirection; +uniform vec3 SpherePos; +uniform mat4 ProjMat; +uniform mat4 ViewMat; +uniform sampler2D WorldColor; +uniform int uWriteOnlyDepth; out vec4 fragColor; +bool hitSphere(vec3 ro, vec3 rd, float radius, out float t, out bool inside) { + t = -1.0; + inside = false; + vec3 oc = ro - SpherePos; + float b = dot(oc, rd); + float c = dot(oc, oc) - radius * radius; + float h = b * b - c; + + if (c < 0.0) { + inside = true; + } + if (h < 0.0) return false; + + float sqrtH = sqrt(h); + float t0 = -b - sqrtH; + float t1 = -b + sqrtH; + + t = (t0 > 0.0) ? t0 : t1; + + return t > 0.0; +} + +float writeDepth(float t, vec3 ro, vec3 rd) { + vec3 hitPoint = ro + rd * t; + vec4 clipPos = ProjMat * vec4(hitPoint, 1.0); + float ndcDepth = clipPos.z / clipPos.w; + float writtenDepth = (ndcDepth + 1.0) * 0.5; + if (writtenDepth <= 0.01) { + gl_FragDepth = gl_FragCoord.z; + return writtenDepth; + } + gl_FragDepth = writtenDepth; + return writtenDepth; +} + +vec2 getViewSpaceUV(vec3 posView, mat4 projectionMatrix) { + vec4 clipSpace = projectionMatrix * vec4(posView, 1.0); + + if (clipSpace.w <= 0.0) { + return normalize(posView.xy) * 100.0; + } + + vec3 ndc = clipSpace.xyz / clipSpace.w; + return ndc.xy * 0.5 + 0.5; +} + +uniform float uMass; +uniform float uDistModifier; +uniform float uSchwarzschildRadius; +uniform float uSphereRadius; + + +vec2 getDistortedRayUV(out bool absorbed) { + absorbed = false; + vec2 resolution = vec2(textureSize(WorldColor, 0)); + + vec2 fragUV = gl_FragCoord.xy / resolution; + vec2 bhUV = getViewSpaceUV(SpherePos, ProjMat); + bhUV = clamp(bhUV, 0.0, 1.0); + + vec2 delta = fragUV - bhUV; + + float t; + bool inside; + vec3 rd = normalize(rayDirection); + vec3 ro = rayOrigin; + absorbed = hitSphere(ro, rd, uSchwarzschildRadius, t, inside); + + vec3 L = SpherePos - ro; + float t_closest = dot(L, rd); + if (t_closest < 0.0) { + t_closest = 0; + } + vec3 closestPoint = ro + rd * t_closest; + float d3D = length(SpherePos - closestPoint); + + float surfaceDist = max(d3D - uSchwarzschildRadius, 0.001); + + float base = (uMass * 0.1) / (surfaceDist + 0.05); + float decay = exp(-pow(surfaceDist * uDistModifier * 5.0, 2.0)); + float gravityStrength = base * decay; + + return bhUV + delta * (1.0 - gravityStrength); +} + +vec2 getDistortedTextureUV(out bool isBlack) { + isBlack = false; + return fract(getDistortedRayUV(isBlack)); +} + void main() { - fragColor = vec4(0.0f, 0.0f, 0.0f, 0.5f); + vec3 rayDir = normalize(rayDirection); + float t; + bool inside; + bool hit = hitSphere(rayOrigin, rayDir, uSphereRadius, t, inside); + if (uWriteOnlyDepth == 1) { + if (!hit) { + discard; + } + float writtenDepth = writeDepth(t, rayOrigin, rayDir); + if (inside || writtenDepth <= 0.01) { + gl_FragDepth = 0.0; + } + return; + } + + bool isBlack; + vec2 uvCoord = getDistortedTextureUV(isBlack); + vec4 backgroundColor = texture(WorldColor, uvCoord); + + fragColor = backgroundColor; + fragColor.a = 1.0; + if (isBlack) { + fragColor = vec4(0, 0, 0, 1); + } + float writtenDepth = writeDepth(t, rayOrigin, rayDir); + if (inside || writtenDepth <= 0.01) { + gl_FragDepth = 0.0; + } } \ No newline at end of file diff --git a/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.json b/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.json index ceb91ae8..3f4477b5 100644 --- a/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.json +++ b/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.json @@ -7,11 +7,16 @@ "vertex": "monilabs:rendertype_wormhole", "fragment": "monilabs:rendertype_wormhole", "attributes": [], - "samplers": [], + "samplers": [ + { "name": "WorldColor" } + ], "uniforms": [ - { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 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 ] }, { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 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 ] }, - { "name": "GameTime", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "ScrollSpeed", "type": "float", "count": 1, "values": [ 600.0 ] } + { "name": "SpherePos", "type": "float", "count": 3, "values": [ 0.0, 0.0, 0.0 ] }, + { "name": "uMass", "type": "float", "count": 1, "values": [ 9.05 ] }, + { "name": "uDistModifier", "type": "float", "count": 1, "values": [ 0.2 ] }, + { "name": "uSchwarzschildRadius", "type": "float", "count": 1, "values": [ 2.55 ] }, + { "name": "uSphereRadius", "type": "float", "count": 1, "values": [ 5.2 ] }, + { "name": "uWriteOnlyDepth", "type": "int", "count": 1, "values": [ 0.0 ] } ] } \ No newline at end of file diff --git a/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.vsh b/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.vsh index cf45a250..d15ba984 100644 --- a/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.vsh +++ b/src/main/resources/assets/monilabs/shaders/core/rendertype_wormhole.vsh @@ -1,12 +1,17 @@ -#version 150 - -#moj_import +#version 330 core in vec3 Position; -uniform mat4 ModelViewMat; uniform mat4 ProjMat; +out vec3 rayOrigin; +out vec3 rayDirection; + void main() { - gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); -} \ No newline at end of file + vec4 viewPos4 = vec4(Position, 1.0); + + rayOrigin = vec3(0.0); + gl_Position = ProjMat * viewPos4; + + rayDirection = viewPos4.xyz; +} diff --git a/src/main/resources/monilabs.mixins.json b/src/main/resources/monilabs.mixins.json index 7a396b8d..1287420b 100644 --- a/src/main/resources/monilabs.mixins.json +++ b/src/main/resources/monilabs.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_17", "minVersion": "0.8", "plugin": "net.neganote.monilabs.mixin.MoniMixinPlugin", + "refmap": "mixins.monilabs.refmap.json", "mixins": [ "DataAccessHatchMixin", "KubeJSPluginsMixin", @@ -17,7 +18,15 @@ "aae.MixinPatternDetailsHelperShim", "aae.MixinProcessingPatternItemShim" ], - "client": [ + "client": + [ + "TranslucentRenderMixin", + "ShaderChunkRendererMixin", + "ShaderLoaderMixin", + "DefaultChunkRendererMixin", + "GameRendererMixin", + "accessor.TerrainRenderPassAccessor", + "accessor.ShaderChunkRendererAccessor" ], "injectors": { "defaultRequire": 1