Skip to content

Instantly share code, notes, and snippets.

@btmxh
Last active March 15, 2020 15:43
Show Gist options
  • Save btmxh/30c15c70da2141b3cf2c537c0e7f7819 to your computer and use it in GitHub Desktop.
Save btmxh/30c15c70da2141b3cf2c537c0e7f7819 to your computer and use it in GitHub Desktop.
package com.dah.scene3d.test;
import com.dah.scene3d.utils.BetterShader;
import com.dah.scene3d.utils.BetterShaderBuilder;
import com.dah.scene3d.utils.GLSLPlusProcessor;
import de.javagl.obj.Obj;
import de.javagl.obj.ObjReader;
import de.javagl.obj.ObjUtils;
import lengine.engine.Context;
import lengine.engine.mesh.Mesh;
import lengine.engine.view.Perspective;
import lengine.engine.view.cameras.FirstPersonCamera;
import lengine.entities.Transform;
import lengine.gl.GLUtils;
import lengine.glfw.Window;
import lengine.glfw.chaincb.ChainCallback;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.*;
import org.lwjgl.system.MemoryStack;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.file.Files;
import java.util.Map;
import java.util.stream.Collectors;
public class MainGameLoop {
private static FirstPersonCamera camera;
public static void main(String[] args) throws IOException {
//INITIALIZE GLFW AND CREATING WINDOWS
Window.init();
Window.useDefaultWindowHints();
Context ctx = Context.createContext(Window.createWindow());
GLFW.glfwShowWindow(ctx.windowID);
ChainCallback.FramebufferSize framebufferSize = new ChainCallback.FramebufferSize();
GLFW.glfwSetFramebufferSizeCallback(ctx.windowID, framebufferSize);
//CAMERA PERSPECTIVE (basically a wrapper for the projection matrix)
Perspective perspective = new Perspective(70.0f, 1280.0f / 720.0f, 0.1f, 1000.0f);
ChainCallback.Key keyCallback = new ChainCallback.Key();
GLFW.glfwSetKeyCallback(ctx.windowID, keyCallback);
//Two cameras - one is from the light to view the scene as the light source, one is another camera to move around the scene
FirstPersonCamera mainCamera = new FirstPersonCamera(ctx, new Vector3f(0.0f), new Vector3f(0.0f));
FirstPersonCamera lightCamera = new FirstPersonCamera(ctx, new Vector3f(0.0f), new Vector3f(0.0f));
//A callback to switch between the two cameras
camera = mainCamera;
keyCallback.add((window, key, scancode, action, mods) -> {
if(key == GLFW.GLFW_KEY_X && action == GLFW.GLFW_PRESS) {
if(camera == mainCamera) camera = lightCamera;
else if(camera == lightCamera) camera = mainCamera;
}
});
//I created a modifier to GLSL (a while ago) that let I use #include like C++ in GLSL
Map<String, String> lampShaderSource = GLSLPlusProcessor.process(Files.lines(new File("out_shaders/test/lamp_shader.glsl").toPath()).collect(Collectors.joining(System.lineSeparator())), "VERTEX", "FRAGMENT");
Map<String, String> objShaderSource = GLSLPlusProcessor.process(Files.lines(new File("out_shaders/test/obj_shader.glsl").toPath()).collect(Collectors.joining(System.lineSeparator())), "VERTEX", "FRAGMENT");
Map<String, String> planeShaderSource = GLSLPlusProcessor.process(Files.lines(new File("out_shaders/test/plane_shader.glsl").toPath()).collect(Collectors.joining(System.lineSeparator())), "VERTEX", "FRAGMENT");
Map<String, String> shadowShaderSource = GLSLPlusProcessor.process(Files.lines(new File("out_shaders/test/shadow_shader.glsl").toPath()).collect(Collectors.joining(System.lineSeparator())), "VERTEX", "GEOMETRY", "FRAGMENT");
Map<String, String> testShaderSource = GLSLPlusProcessor.process(Files.lines(new File("out_shaders/test/test_shader.glsl").toPath()).collect(Collectors.joining(System.lineSeparator())), "VERTEX", "FRAGMENT");
BetterShader lampShader = new BetterShaderBuilder(null)
.addVertexShader(lampShaderSource.get("VERTEX"))
.addFragmentShader(lampShaderSource.get("FRAGMENT"))
.build();
BetterShader objShader = new BetterShaderBuilder(null)
.addVertexShader(objShaderSource.get("VERTEX"))
.addFragmentShader(objShaderSource.get("FRAGMENT"))
.build();
BetterShader planeShader = new BetterShaderBuilder(null)
.addVertexShader(planeShaderSource.get("VERTEX"))
.addFragmentShader(planeShaderSource.get("FRAGMENT"))
.build();
BetterShader shadowShader = new BetterShaderBuilder(null)
.addVertexShader(shadowShaderSource.get("VERTEX"))
.addShader(GL32.GL_GEOMETRY_SHADER, "Geometry Shader", shadowShaderSource.get("GEOMETRY"))
.addFragmentShader(shadowShaderSource.get("FRAGMENT"))
.build();
BetterShader testShader = new BetterShaderBuilder(null)
.addVertexShader(testShaderSource.get("VERTEX"))
.addFragmentShader(testShaderSource.get("FRAGMENT"))
.build();
//test.obj is a cube
Obj obj = ObjReader.read(MainGameLoop.class.getClassLoader().getResourceAsStream("test.obj"));
obj = ObjUtils.convertToRenderable(obj);
Mesh mesh = Mesh.create(obj);
//Create a quad mesh
Mesh plane = Mesh.create(null, new float[]{
-10, -5, -10,
-10, -5, 10,
10, -5, -10,
10, -5, 10,
}, null, null);
Transform boxTransform = new Transform(new Vector3f(0, 0, -10), new Vector3f(), new Vector3f(1.0f));
Transform lampTransform = new Transform(lightCamera.position, new Vector3f(), new Vector3f(0.2f));
//Lighting stuff, doesn't matter much
PointLightImpl light = new PointLightImpl();
light.position = lampTransform.trans();
light.ambient.set(0.2f, 0.2f, 0.2f);
light.diffuse.set(0.7f, 0.7f, 0.7f);
light.specular.set(1.0f, 1.0f, 1.0f);
final int CUBEMAP_WIDTH = 1024, CUBEMAP_HEIGHT = 1024;
int depthCubemap = GL11.glGenTextures();
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, depthCubemap);
for(int i = 0; i < 6; i++) {
GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL11.GL_DEPTH_COMPONENT, CUBEMAP_WIDTH, CUBEMAP_HEIGHT, 0, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, (ByteBuffer) null);
}
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL13.GL_TEXTURE_WRAP_R, GL12.GL_CLAMP_TO_EDGE);
int depthFBO = GL30.glGenFramebuffers();
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, depthFBO);
GL32.glFramebufferTexture(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, depthCubemap, 0);
GL11.glDrawBuffer(GL11.GL_NONE);
GL11.glReadBuffer(GL11.GL_NONE);
//This just test if the framebuffer is completed
GLUtils.testFramebuffer(GL30.GL_FRAMEBUFFER);
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
final float FAR_PLANE = 25.0f;
Perspective shadowPerspective = new Perspective(90.0f, (float)CUBEMAP_WIDTH / (float)CUBEMAP_HEIGHT, 0.1f, FAR_PLANE);
//Timing (not perfect, but works)
double lastFrame = 0;
double delta = 0;
while (!GLFW.glfwWindowShouldClose(ctx.windowID)) {
GLFW.glfwPollEvents();
GLFW.glfwSwapBuffers(ctx.windowID);
double now = GLFW.glfwGetTime();
delta = now - lastFrame;
lastFrame = now;
GL11.glClearColor(0.2f, 0.2f, 0.5f, 1.0f);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GL11.glEnable(GL11.GL_DEPTH_TEST);
Matrix4f[] shadowTransforms = {
new Matrix4f().lookAt(light.position, new Vector3f(1, 0, 0).add(light.position), new Vector3f(0, -1, 0)).mulLocal(shadowPerspective.matrix()),
new Matrix4f().lookAt(light.position, new Vector3f(-1, 0, 0).add(light.position), new Vector3f(0, -1, 0)).mulLocal(shadowPerspective.matrix()),
new Matrix4f().lookAt(light.position, new Vector3f(0, 1, 0).add(light.position), new Vector3f(0, 0, 1)).mulLocal(shadowPerspective.matrix()),
new Matrix4f().lookAt(light.position, new Vector3f(0, -1, 0).add(light.position), new Vector3f(0, 0, -1)).mulLocal(shadowPerspective.matrix()),
new Matrix4f().lookAt(light.position, new Vector3f(0, 0, 1).add(light.position), new Vector3f(0, -1, 0)).mulLocal(shadowPerspective.matrix()),
new Matrix4f().lookAt(light.position, new Vector3f(0, 0, -1).add(light.position), new Vector3f(0, -1, 0)).mulLocal(shadowPerspective.matrix())
};
try(MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.mallocFloat(16);
//Setting the uniform variables
testShader.bind(); {
perspective.matrix().get(buffer);
GL20.glUniformMatrix4fv(testShader.getUniformLocation("project"), false, buffer);
camera.matrix().get(buffer);
GL20.glUniformMatrix4fv(testShader.getUniformLocation("view"), false, buffer);
new Transform(new Vector3f(0, 5, 0), new Vector3f(), new Vector3f(1)).matrix().get(buffer);
GL20.glUniformMatrix4fv(testShader.getUniformLocation("transform"), false, buffer);
}
lampShader.bind(); {
perspective.matrix().get(buffer);
GL20.glUniformMatrix4fv(lampShader.getUniformLocation("project"), false, buffer);
camera.matrix().get(buffer);
GL20.glUniformMatrix4fv(lampShader.getUniformLocation("view"), false, buffer);
lampTransform.matrix().get(buffer);
GL20.glUniformMatrix4fv(lampShader.getUniformLocation("transform"), false, buffer);
}
objShader.bind(); {
perspective.matrix().get(buffer);
GL20.glUniformMatrix4fv(objShader.getUniformLocation("project"), false, buffer);
camera.matrix().get(buffer);
GL20.glUniformMatrix4fv(objShader.getUniformLocation("view"), false, buffer);
boxTransform.matrix().get(buffer);
GL20.glUniformMatrix4fv(objShader.getUniformLocation("transform"), false, buffer);
light.load(objShader, "light");
GL20.glUniform3f(objShader.getUniformLocation("cameraPos"), camera.position.x, camera.position.y, camera.position.z);
}
planeShader.bind(); {
perspective.matrix().get(buffer);
GL20.glUniformMatrix4fv(planeShader.getUniformLocation("project"), false, buffer);
camera.matrix().get(buffer);
GL20.glUniformMatrix4fv(planeShader.getUniformLocation("view"), false, buffer);
boxTransform.matrix().get(buffer);
GL20.glUniformMatrix4fv(planeShader.getUniformLocation("transform"), false, buffer);
GL20.glUniform3f(shadowShader.getUniformLocation("lightPos"), light.position.x, light.position.y, light.position.z);
GL20.glUniform1f(planeShader.getUniformLocation("far_plane"), FAR_PLANE);
}
shadowShader.bind(); {
GL20.glUniform1f(shadowShader.getUniformLocation("far_plane"), FAR_PLANE);
for(int i = 0; i < 6; i++) {
shadowTransforms[i].get(buffer);
GL20.glUniformMatrix4fv(shadowShader.getUniformLocation("shadowMatrices[" + i + "]"), false, buffer);
}
GL20.glUniform3f(shadowShader.getUniformLocation("lightPos"), light.position.x, light.position.y, light.position.z);
}
}
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, depthFBO);
GL11.glViewport(0, 0, CUBEMAP_WIDTH, CUBEMAP_HEIGHT);
//Use the shadow shader
shadowShader.bind();
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
//More setting uniform variables
try(MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.mallocFloat(16);
boxTransform.matrix().get(buffer);
GL20.glUniformMatrix4fv(shadowShader.getUniformLocation("transform"), false, buffer);
mesh.bind();
GL11.glDrawElements(GL11.GL_TRIANGLES, mesh.vertexCount(), GL11.GL_UNSIGNED_INT, 0);
new Matrix4f().get(buffer);
GL20.glUniformMatrix4fv(shadowShader.getUniformLocation("transform"), false, buffer);
plane.bind();
GL11.glDrawElements(GL11.GL_TRIANGLES, plane.vertexCount(), GL11.GL_UNSIGNED_INT, 0);
}
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
GL11.glViewport(0, 0, 1280, 720);
//testShader render the cubemap to a debug cube
testShader.bind();
mesh.bind();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, depthCubemap);
GL20.glUniform1i(testShader.getUniformLocation("cubemap"), 0);
GL11.glDrawElements(GL11.GL_TRIANGLES, mesh.vertexCount(), GL11.GL_UNSIGNED_INT, 0);
mesh.unbind();
testShader.unbind();
//objShader render the cube like normal
objShader.bind();
mesh.bind();
GL11.glDrawElements(GL11.GL_TRIANGLES, mesh.vertexCount(), GL11.GL_UNSIGNED_INT, 0);
mesh.unbind();
objShader.unbind();
//render the plane (where the cube cast shadow to)
planeShader.bind();
plane.bind();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, depthCubemap);
GL20.glUniform1i(testShader.getUniformLocation("cubemap"), 0);
GL11.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, 4);
plane.unbind();
planeShader.unbind();
//move the camera
camera.move((float) delta);
}
//Delete GL context, etc.
Context.delete(ctx);
GLFW.glfwTerminate();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment