Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/net/neganote/monilabs/MoniLabs.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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!
Expand Down Expand Up @@ -220,6 +224,21 @@ private void registerMachines(GTCEuAPI.RegisterEvent<ResourceLocation, MachineDe
private void registerCovers(
GTCEuAPI.RegisterEvent<ResourceLocation, CoverDefinition> event) {}

private void onRegisterReloadListeners(RegisterClientReloadListenersEvent event) {
event.registerReloadListener(new SimplePreparableReloadListener<Object>() {

@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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Vector3f> 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<Void> 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();
}
}
Loading
Loading