Last active
March 15, 2020 15:43
-
-
Save btmxh/30c15c70da2141b3cf2c537c0e7f7819 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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