diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java index 553ec9da6..f8412f4ff 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java @@ -1,10 +1,12 @@ package de.bixilon.minosoft.gui.rendering; +import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException; import org.lwjgl.Version; -public class DummyRender { - public static void main(String[] args) { +import java.io.IOException; +public class DummyRender { + public static void main(String[] args) throws IOException, ShaderLoadingException { System.out.println("Hello LWJGL " + Version.getVersion() + "!"); RenderWindow renderWindow = new RenderWindow(); diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/OpenGLUtil.java b/src/main/java/de/bixilon/minosoft/gui/rendering/OpenGLUtil.java new file mode 100644 index 000000000..4cdc549a6 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/OpenGLUtil.java @@ -0,0 +1,9 @@ +package de.bixilon.minosoft.gui.rendering; + +import org.lwjgl.opengl.ARBShaderObjects; + +public final class OpenGLUtil { + public static String getLogInfo(int obj) { + return ARBShaderObjects.glGetInfoLogARB(obj, ARBShaderObjects.glGetObjectParameteriARB(obj, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB)); + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java index e3751de4e..bfa5a3518 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java @@ -1,23 +1,68 @@ package de.bixilon.minosoft.gui.rendering; +import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.glfw.GLFWWindowSizeCallback; -import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.*; import org.lwjgl.system.MemoryStack; +import java.io.IOException; import java.nio.IntBuffer; import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL20.*; +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL30.glGenVertexArrays; import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.system.MemoryUtil.NULL; public class RenderWindow { + float[] vertices = { + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f // top left + }; + int[] indices = { // note that we start from 0! + 0, 1, 3, // first Triangle + 1, 2, 3 // second Triangle + }; + boolean polygonEnabled; private long window; + private static int loadShaders() throws ShaderLoadingException, IOException { + int vertexShader = ShaderUtil.createShader("/vertex.glsl", ARBVertexShader.GL_VERTEX_SHADER_ARB); + int fragmentShader = ShaderUtil.createShader("/fragment.glsl", ARBFragmentShader.GL_FRAGMENT_SHADER_ARB); + + int shaderId = ARBShaderObjects.glCreateProgramObjectARB(); + + if (shaderId == NULL) { + throw new ShaderLoadingException(); + } + + ARBShaderObjects.glAttachObjectARB(shaderId, vertexShader); + ARBShaderObjects.glAttachObjectARB(shaderId, fragmentShader); + + ARBShaderObjects.glLinkProgramARB(shaderId); + if (ARBShaderObjects.glGetObjectParameteriARB(shaderId, ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) { + throw new ShaderLoadingException(OpenGLUtil.getLogInfo(shaderId)); + } + + ARBShaderObjects.glValidateProgramARB(shaderId); + if (ARBShaderObjects.glGetObjectParameteriARB(shaderId, ARBShaderObjects.GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE) { + throw new ShaderLoadingException(OpenGLUtil.getLogInfo(shaderId)); + } + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + return shaderId; + } + public void init() { // Setup an error callback. The default implementation // will print the error message in System.err. @@ -56,6 +101,16 @@ public class RenderWindow { glfwSetKeyCallback(this.window, (window, key, scancode, action, mods) -> { if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) { glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop + return; + } + if (key == GLFW_KEY_P && action == GLFW_RELEASE) { + if (this.polygonEnabled) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } else { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + this.polygonEnabled = !this.polygonEnabled; + return; } }); @@ -69,14 +124,10 @@ public class RenderWindow { glfwGetWindowSize(this.window, pWidth, pHeight); // Get the resolution of the primary monitor - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); // Center the window - glfwSetWindowPos( - this.window, - (vidmode.width() - pWidth.get(0)) / 2, - (vidmode.height() - pHeight.get(0)) / 2 - ); + glfwSetWindowPos(this.window, (videoMode.width() - pWidth.get(0)) / 2, (videoMode.height() - pHeight.get(0)) / 2); } // the stack frame is popped automatically // Make the OpenGL context current @@ -92,29 +143,54 @@ public class RenderWindow { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); } + public void startLoop() throws IOException, ShaderLoadingException { + + int vAO = glGenVertexArrays(); + int vBO = glGenBuffers(); + int eBO = glGenBuffers(); + + // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). + glBindVertexArray(vAO); + + glBindBuffer(GL_ARRAY_BUFFER, vBO); + glBufferData(GL_ARRAY_BUFFER, this.vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, this.indices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, false, 3 * Float.BYTES, 0L); + glEnableVertexAttribArray(0); + + // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound. + //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other + // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary. + glBindVertexArray(0); + + + int shaderId = loadShaders(); + - public void startLoop() { - // This line is critical for LWJGL's interoperation with GLFW's - // OpenGL context, or any context that is managed externally. - // LWJGL detects the context that is current in the current thread, - // creates the GLCapabilities instance and makes the OpenGL - // bindings available for use. - // Run the rendering loop until the user has attempted to close - // the window or has pressed the ESCAPE key. while (!glfwWindowShouldClose(this.window)) { - loop(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer + + glUseProgram(shaderId); + + glBindVertexArray(vAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized + glDrawElements(GL_TRIANGLES, this.indices.length, GL_UNSIGNED_INT, 0); + + glfwSwapBuffers(this.window); // swap the color buffers + + // Poll for window events. The key callback above will only be + // invoked during this call. + glfwPollEvents(); } } - private void loop() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer - - glfwSwapBuffers(this.window); // swap the color buffers - - // Poll for window events. The key callback above will only be - // invoked during this call. - glfwPollEvents(); - } public void exit() { // Free the window callbacks and destroy the window diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/ShaderUtil.java b/src/main/java/de/bixilon/minosoft/gui/rendering/ShaderUtil.java new file mode 100644 index 000000000..b2119401e --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/ShaderUtil.java @@ -0,0 +1,30 @@ +package de.bixilon.minosoft.gui.rendering; + +import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException; +import de.bixilon.minosoft.util.Util; +import org.lwjgl.opengl.ARBShaderObjects; +import org.lwjgl.opengl.GL11; + +import java.io.IOException; + +import static org.lwjgl.system.MemoryUtil.NULL; + + +public class ShaderUtil { + public static int createShader(String shaderPath, int shaderType) throws ShaderLoadingException, IOException { + int shaderId = ARBShaderObjects.glCreateShaderObjectARB(shaderType); + + if (shaderId == NULL) { + throw new ShaderLoadingException(); + } + + ARBShaderObjects.glShaderSourceARB(shaderId, Util.readAsset("/rendering/shader" + shaderPath)); + ARBShaderObjects.glCompileShaderARB(shaderId); + + if (ARBShaderObjects.glGetObjectParameteriARB(shaderId, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE) { + throw new ShaderLoadingException(OpenGLUtil.getLogInfo(shaderId)); + } + + return shaderId; + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/exceptions/ShaderLoadingException.java b/src/main/java/de/bixilon/minosoft/gui/rendering/exceptions/ShaderLoadingException.java new file mode 100644 index 000000000..318466946 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/exceptions/ShaderLoadingException.java @@ -0,0 +1,10 @@ +package de.bixilon.minosoft.gui.rendering.exceptions; + +public class ShaderLoadingException extends Exception { + public ShaderLoadingException() { + } + + public ShaderLoadingException(String message) { + super(message); + } +} diff --git a/src/main/resources/assets/rendering/shader/fragment.glsl b/src/main/resources/assets/rendering/shader/fragment.glsl new file mode 100644 index 000000000..d801e68e4 --- /dev/null +++ b/src/main/resources/assets/rendering/shader/fragment.glsl @@ -0,0 +1,6 @@ +#version 330 core +out vec4 FragColor; + +void main() { + FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); +} diff --git a/src/main/resources/assets/rendering/shader/vertex.glsl b/src/main/resources/assets/rendering/shader/vertex.glsl new file mode 100644 index 000000000..e390af09c --- /dev/null +++ b/src/main/resources/assets/rendering/shader/vertex.glsl @@ -0,0 +1,6 @@ +#version 330 core +layout (location = 0) in vec3 aPos; + +void main() { + gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); +}