Skip to content

Instantly share code, notes, and snippets.

@cplir-c
Last active November 29, 2019 23:09
Show Gist options
  • Save cplir-c/b526fb55aa97f526567d829a50ca1b0b to your computer and use it in GitHub Desktop.
Save cplir-c/b526fb55aa97f526567d829a50ca1b0b to your computer and use it in GitHub Desktop.
package therealfarfetchd.illuminate.client.render;
import com.mojang.blaze3d.platform.GLX.GL_TEXTURE0;
import com.mojang.blaze3d.platform.GLX.GL_TEXTURE2;
import static com.mojang.blaze3d.platform.GLX.glBindFramebuffer;
import com.mojang.blaze3d.platform.GlStateManager;
import static com.mojang.blaze3d.platform.GlStateManager.activeTexture;
import static com.mojang.blaze3d.platform.GlStateManager.bindTexture;
import static com.mojang.blaze3d.platform.GlStateManager.clear;
import static com.mojang.blaze3d.platform.GlStateManager.colorMask;
import static com.mojang.blaze3d.platform.GlStateManager.depthMask;
import static com.mojang.blaze3d.platform.GlStateManager.disableCull;
import static com.mojang.blaze3d.platform.GlStateManager.disableTexture;
import static com.mojang.blaze3d.platform.GlStateManager.enableTexture;
import static com.mojang.blaze3d.platform.GlStateManager.popMatrix;
import static com.mojang.blaze3d.platform.GlStateManager.pushMatrix;
import static com.mojang.blaze3d.platform.GlStateManager.translated;
import net.minecraft.client.MinecraftClient;
static import net.minecraft.client.gl.GlFramebuffer;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.util.Identifier;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_NEAREST;
import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_S;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_T;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glDrawBuffer;
import static org.lwjgl.opengl.GL11.glGetFloatv;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.opengl.GL12.GL_CLAMP_TO_EDGE;
import static org.lwjgl.opengl.GL13.GL_TEXTURE3;
import static org.lwjgl.opengl.GL13.GL_TEXTURE4;
import static org.lwjgl.opengl.GL14;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUniform3f;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL30.GL_DEPTH_ATTACHMENT;
import static org.lwjgl.opengl.GL30.GL_DRAW_FRAMEBUFFER;
import static org.lwjgl.opengl.GL30.GL_READ_FRAMEBUFFER;
import static org.lwjgl.opengl.GL30.glBlitFramebuffer;
import static therealfarfetchd.illuminate.ModID;
import therealfarfetchd.illuminate.client.api.Light;
import therealfarfetchd.illuminate.client.glwrap.WGlFramebuffer;
import therealfarfetchd.illuminate.client.glwrap.WGlShader;
import therealfarfetchd.illuminate.client.glwrap.WGlTexture2D;
import static therealfarfetchd.illuminate.client.setFramebuffer;
import static therealfarfetchd.illuminate.common.util.ext.minus;
import static therealfarfetchd.illuminate.common.util.ext.ortho;
import static therealfarfetchd.illuminate.common.util.ext.times;
import therealfarfetchd.qcommon.croco.Mat4;
import therealfarfetchd.qcommon.croco.Vec3;
import java.io.IOException;
private FloatBuffer matBuf = BufferUtils.createFloatBuffer(16)
@Suppress("PrivatePropertyName")
class PostProcess {
private MinecraftClient mc;
private GlFramebuffer = mc.framebuffer;
private int width = target.viewWidth;
private int height = target.viewHeight;
private WGlShader shader;
private int uMvp = 0;
private int uCamInv = 0;
private int uWidth = 0;
private int uHeight = 0;
private int uWorld = 0;
private int uDepth = 0;
private int[] uLightTex = new int[10];
private int[] uLightDepth = new int[10];
private int[] uLightCam = new int[10];
private int[] uLightPos = new int[10];
private int uLightCount = 0;
private GlFramebuffer offscreenFb = new GlFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC);
private LightFramebuffer lightDepthFb = new LightFramebuffer(1024, 1024, false);
private WGlFramebuffer blitFb = WGlFramebuffer.create();
WGlTexture2D playerCamDepth = WGlTexture2D.create()
Map<Light, LightContainer> lights = new HashMap<>();
Set<LightContainer> activeLights = java.util.Collections.emptySet();
/* private set */
/* What's this? */
{
playerCamDepth.texParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
playerCamDepth.texParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
playerCamDepth.texParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
playerCamDepth.texParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
try {
val vshs = mc.resourceManager.getResource(Identifier(ModID, "shaders/lighting.vert")).use(() -> { it.inputStream.bufferedReader().readText() });
val fshs = mc.resourceManager.getResource(Identifier(ModID, "shaders/lighting.frag")).use(() -> { it.inputStream.bufferedReader().readText() });
shader = mkShader(vshs, fshs);
} catch (IOException e) {
shader = WGlShader.None;
}
if (shader.isValid) {
shader.enable();
uMvp = shader.getUniformLocation("mvp");
uCamInv = shader.getUniformLocation("camInv");
uWidth = shader.getUniformLocation("width");
uHeight = shader.getUniformLocation("height");
uWorld = shader.getUniformLocation("world");
uDepth = shader.getUniformLocation("depth");
for (int i = 0; i < 10; ++i) {
uLightTex[it] = shader.getUniformLocation("lightTex[$it]");
uLightDepth[it] = shader.getUniformLocation("lightDepth[$it]");
uLightCam[it] = shader.getUniformLocation("lightCam[$it]");
uLightPos[it] = shader.getUniformLocation("lightPos[$it]");
}
uLightCount = shader.getUniformLocation("lightCount");
shader.disable();
}
blitFb.bind();
glDrawBuffer(GL11.GL_NONE);
mc.getFramebuffer.beginWrite(false);
}
/**
* Determine which lights should be rendered from the player's position
*/
void setupLights(float delta) {
if (lights.size() < 10)
activeLights = lights.values.toSet();
else {
Vec3 camPos = Vec3.from((mc.cameraEntity != null ? mc.cameraEntity : mc.player).getCameraPosVec(delta));
activeLights = lights.values.asSequence().sortedBy(it -> { (it.light.pos - camPos).lengthSq }).take(10).toSet();
}
}
/**
* Resize the framebuffers and textures to the specified size.
*/
public void resize(int width, int height) {
offscreenFb.resize(width, height, MinecraftClient.IS_SYSTEM_MAC);
}
/**
* Draws the world from each light's perspective and saves the depth buffer for later use.
*/
public void renderLightDepths(float delta, long nanoTime) {
if (activeLights.isEmpty()) return;
boolean oldHudHidden = mc.options.hudHidden;
CameraEntity oldCam = mc.cameraEntity;
mc.options.hudHidden = true;
GlFramebuffer window = mc.framebuffer;
for (int i = 0; i < activeLights.size(); ++i){
Light lc = activeLights.get(i);
lightDepthFb.beginWrite(true);
glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT);
mc.setFramebuffer(lightDepthFb);
glMatrixMode(GL11.GL_PROJECTION);
pushMatrix();
glMatrixMode(GL11.GL_MODELVIEW);
pushMatrix();
lc.getLight.prepare(delta);
setupCamera(lc);
val lightSource = new LightSource(mc.world, lc.light);
mc.cameraEntity = lightSource;
disableCull();
renderWorld(mc.gameRenderer, delta, nanoTime, i);
blitDepthToTex(lightDepthFb, lc.depth);
glMatrixMode(GL11.GL_PROJECTION);
popMatrix();
glMatrixMode(GL11.GL_MODELVIEW);
popMatrix();
}
mc.setFramebuffer(window);
window.beginWrite(true);
mc.cameraEntity = oldCam;
mc.options.hudHidden = oldHudHidden;
}
/**
* Applies the shader onto the target framebuffer
*/
public void paintSurfaces(float delta){
if (activeLights.isEmpty()) return;
if (!shader.isValid) return; // something is fucked
CameraEntity ce = mc.cameraEntity!!
Camera camera = mc.gameRenderer.camera
Vec3 cameraPosVec = camera.pos
translated(-cameraPosVec.x, -cameraPosVec.y, -cameraPosVec.z);
blitDepthToTex(target, playerCamDepth);
clear(GL_DEPTH_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC);
paintSurfaces(target, offscreenFb);
blit(offscreenFb, target);
target.beginWrite(true);
}
/**
* Apply the shader onto the source framebuffer and draw into the target framebuffer
*/
private fun paintSurfaces(GlFramebuffer from, GlFramebuffer into) {
val sourceW = from.texWidth;
val sourceH = from.texHeight;
val targetH = into.texHeight;
val targetW = into.texWidth;
from.endWrite();
shader.enable();
glUniform1i(uWidth, sourceW);
glUniform1i(uHeight, sourceH);
enableTexture();
from.beginRead();
glUniform1i(uWorld, 0);
activeTexture(GL_TEXTURE2);
enableTexture();
playerCamDepth.bind();
glUniform1i(uDepth, 2);
activeTexture(GL_TEXTURE0);
for (int i = 0; i < activeLights.size(); ++i){
Light l = activeLights.get(i);
loadLight(i, l);
}
glUniform1i(uLightCount, activeLights.size);
matBuf.clear();
ortho(0f, targetW.toFloat(), targetH.toFloat(), 0f, -1f, 1f).intoBuffer(matBuf);
matBuf.rewind();
glUniformMatrix4fv(uMvp, false, matBuf);
matBuf.clear();
glGetFloatv(GL11.GL_MODELVIEW_MATRIX, matBuf);
Mat4 mv = Mat4.fromBuffer(matBuf);
matBuf.clear();
glGetFloatv(GL11.GL_PROJECTION_MATRIX, matBuf);
Mat4 p = Mat4.fromBuffer(matBuf);
matBuf.clear();
(p.multiply(mv)).invert().intoBuffer(matBuf);
matBuf.rewind();
glUniformMatrix4fv(uCamInv, false, matBuf);
glViewport(0, 0, targetW, targetH);
into.clear(MinecraftClient.IS_SYSTEM_MAC);
into.beginWrite(false);
depthMask(true);
colorMask(true, true, true, true);
Tesselator t = Tessellator.getInstance();
BufferBuilder buf = t.bufferBuilder;
buf.begin(GL_QUADS, VertexFormats.POSITION);
buf.vertex(0.0, 0.0, 0.0).next();
buf.vertex(0.0, targetH.toDouble(), 0.0).next();
buf.vertex(targetW.toDouble(), targetH.toDouble(), 0.0).next();
buf.vertex(targetW.toDouble(), 0.0, 0.0).next();
t.draw();
into.endWrite();
shader.disable();
from.endRead();
for (i = 2; i <l 3 + activeLights.size() * 2; ++i) {
activeTexture(GL_TEXTURE0 + i);
disableTexture();
}
activeTexture(GL_TEXTURE0);
}
private void loadLight(int i, LightContainer l) {
activeTexture(GL_TEXTURE3 + 2 * i);
enableTexture();
bindTexture(l.getLight().getTex());
activeTexture(GL_TEXTURE4 + 2 * i);
enableTexture();
l.getDepth().bind();
glUniform1i(uLightTex[i], 3 + 2 * i);
glUniform1i(uLightDepth[i], 4 + 2 * i);
matBuf.clear();
l.mvp.intoBuffer(matBuf);
matBuf.rewind();
glUniformMatrix4fv(uLightCam[i], false, matBuf);
glUniform3f(uLightPos[i], l.light.pos.x, l.light.pos.y, l.light.pos.z);
}
/**
* Sets up GL's modelview and projection matrices according to the information from the passed [Light]
*/
private void setupCamera(LightContainer light) {
Buffer<Mat4> matBuf = matBuf;
matBuf.clear();
light.getP().intoBuffer(matBuf);
matBuf.rewind();
GlStateManager.matrixMode(GL11.GL_PROJECTION);
GlStateManager.loadIdentity();
GlStateManager.multMatrix(matBuf);
matBuf.clear();
light.getMv().intoBuffer(matBuf);
matBuf.rewind();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.loadIdentity();
GlStateManager.multMatrix(matBuf);
}
/**
* Copies the contents of a [GlFramebuffer] into another [GlFramebuffer].
*/
private void blit(GlFramebuffer from, GlFramebuffer into) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, from.fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, into.fbo);
glBlitFramebuffer(0, 0, from.texWidth, from.texHeight, 0, into.texHeight, into.texWidth, 0, GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
/**
* Copies depth buffer from a [GlFramebuffer] into a [WGlTexture2D].
* Needed because MC stores depth information in a renderbuffer which can't be directly accessed.
*/
private void blitDepthToTex(GlFramebuffer from, WGlTexture2D into) {
into.texImage(GL14.GL_DEPTH_COMPONENT24, from.texWidth, from.texHeight, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, 0);
blitFb.bind(GL_DRAW_FRAMEBUFFER);
into.bind();
blitFb.attachTexture(GL_DEPTH_ATTACHMENT, into, GL_DRAW_FRAMEBUFFER);
/* GL_DRAW_FRAMEBUFFER should be assigned to target in this call */
glBindFramebuffer(GL_READ_FRAMEBUFFER, from.fbo);
glBlitFramebuffer(0, 0, from.texWidth, from.texHeight, 0, 0, from.texWidth, from.texHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
from.beginWrite(true);
}
/**
* Destroy the native resources this [PostProcess] object occupies. It will be unusable after this operation
*/
public void destroy() {
shader.destroy();
playerCamDepth.destroy();
offscreenFb.delete();
lightDepthFb.delete();
blitFb.destroy();
lights.getValues().forEach(LightContainer::destroy);
lights.clear();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment