From 98beee726d2d238b469d97eb42f34726709273e0 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Wed, 3 Feb 2021 16:34:34 +0100 Subject: [PATCH] multi texture rendering, use vbos for rendering --- .../de/bixilon/minosoft/data/world/Chunk.java | 4 + .../minosoft/gui/rendering/DummyData.java | 28 ++ .../bixilon/minosoft/gui/rendering/Mesh.java | 113 +++++++ .../minosoft/gui/rendering/RenderWindow.java | 305 ------------------ .../minosoft/gui/rendering/RenderWindow.kt | 202 ++++++++++++ .../minosoft/gui/rendering/Texture.java | 49 --- .../minosoft/gui/rendering/TextureArray.java | 60 ++++ .../assets/rendering/shader/fragment.glsl | 14 +- .../assets/rendering/shader/vertex.glsl | 19 +- 9 files changed, 425 insertions(+), 369 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java delete mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt delete mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/Texture.java create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/TextureArray.java diff --git a/src/main/java/de/bixilon/minosoft/data/world/Chunk.java b/src/main/java/de/bixilon/minosoft/data/world/Chunk.java index aece41d62..3810b0e9a 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/Chunk.java +++ b/src/main/java/de/bixilon/minosoft/data/world/Chunk.java @@ -82,4 +82,8 @@ public class Chunk { public void setBlockEntityData(HashMap blockEntities) { blockEntities.forEach(this::setBlockEntityData); } + + public HashMap getSections() { + return this.sections; + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java new file mode 100644 index 000000000..82c05f8a7 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyData.java @@ -0,0 +1,28 @@ +package de.bixilon.minosoft.gui.rendering; + +import de.bixilon.minosoft.data.mappings.blocks.Block; +import de.bixilon.minosoft.data.world.Chunk; +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; + +import java.util.HashMap; + +public class DummyData { + private static final Block BEDROCK = new Block("bedrock"); + private static final Block DIRT = new Block("dirt"); + + public static Chunk getDummyChunk() { + Chunk chunk = new Chunk(new HashMap<>()); + for (int y = 0; y < ProtocolDefinition.SECTION_HEIGHT_Y; y++) { + for (int x = 0; x < ProtocolDefinition.SECTION_WIDTH_X; x++) { + for (int z = 0; z < ProtocolDefinition.SECTION_WIDTH_Z; z++) { + if (y == 0) { + chunk.setBlock(x, y, z, BEDROCK); + } else if (y < 8) { + chunk.setBlock(x, y, z, DIRT); + } + } + } + } + return chunk; + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java b/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java new file mode 100644 index 000000000..5966442a0 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java @@ -0,0 +1,113 @@ +package de.bixilon.minosoft.gui.rendering; + +import glm_.mat4x4.Mat4; +import glm_.vec3.Vec3; +import glm_.vec4.Vec4; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL20; + +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.glDrawArrays; +import static org.lwjgl.opengl.GL15.glGenBuffers; +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL30.glGenVertexArrays; + +public class Mesh { + public static final float[][] VERTICIES = { + { + -0.5f, -0.5f, -0.5f, 0.0f, + 0.5f, -0.5f, -0.5f, 1.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + -0.5f, 0.5f, -0.5f, 3.0f, + -0.5f, -0.5f, -0.5f, 0.0f, + }, + { + -0.5f, -0.5f, 0.5f, 0.0f, + 0.5f, -0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, 0.5f, 2.0f, + 0.5f, 0.5f, 0.5f, 2.0f, + -0.5f, 0.5f, 0.5f, 3.0f, + -0.5f, -0.5f, 0.5f, 0.0f, + }, + { + -0.5f, 0.5f, 0.5f, 1.0f, + -0.5f, 0.5f, -0.5f, 2.0f, + -0.5f, -0.5f, -0.5f, 3.0f, + -0.5f, -0.5f, -0.5f, 3.0f, + -0.5f, -0.5f, 0.5f, 0.0f, + -0.5f, 0.5f, 0.5f, 1.0f, + }, + { + 0.5f, 0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + 0.5f, -0.5f, -0.5f, 3.0f, + 0.5f, -0.5f, -0.5f, 3.0f, + 0.5f, -0.5f, 0.5f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, + }, + { + -0.5f, -0.5f, -0.5f, 3.0f, + 0.5f, -0.5f, -0.5f, 2.0f, + 0.5f, -0.5f, 0.5f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, + -0.5f, -0.5f, -0.5f, 3.0f, + }, + { + -0.5f, 0.5f, -0.5f, 3.0f, + 0.5f, 0.5f, -0.5f, 2.0f, + 0.5f, 0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, 0.5f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, + -0.5f, 0.5f, -0.5f, 3.0f, + } + }; + int textureLayer; + int vAO; + int vBO; + + public Mesh(int textureLayer, Vec3 position) { + this.textureLayer = textureLayer; + float[] result = new float[VERTICIES.length * VERTICIES[0].length + (VERTICIES.length * VERTICIES[0].length / 2)]; + int resultIndex = 0; + + var model = new Mat4().translate(position); + + for (float[] side : VERTICIES) { + for (int vertex = 0; vertex < side.length; ) { + Vec4 input = new Vec4(side[vertex++], side[vertex++], side[vertex++], 1.0f); + var output = model.times(input); + // Log.debug("input=%s; position=%s; output=%s;", input, position, output); + result[resultIndex++] = output.x; + result[resultIndex++] = output.y; + result[resultIndex++] = output.z; + result[resultIndex++] = side[vertex++]; + result[resultIndex++] = textureLayer; + } + } + + this.vAO = glGenVertexArrays(); + this.vBO = glGenBuffers(); + + // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). + glBindVertexArray(this.vAO); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vBO); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, result, GL15.GL_STATIC_DRAW); + GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 5 * Float.BYTES, 0L); + GL20.glEnableVertexAttribArray(0); + GL20.glVertexAttribPointer(1, 1, GL11.GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES); + GL20.glEnableVertexAttribArray(1); + GL20.glVertexAttribPointer(2, 1, GL11.GL_FLOAT, false, 5 * Float.BYTES, 4 * Float.BYTES); + GL20.glEnableVertexAttribArray(2); + + // 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 + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + + public void draw() { + glBindVertexArray(this.vAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java deleted file mode 100644 index dce682b97..000000000 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java +++ /dev/null @@ -1,305 +0,0 @@ -package de.bixilon.minosoft.gui.rendering; - -import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException; -import glm_.glm; -import glm_.mat4x4.Mat4; -import glm_.vec3.Vec3; -import org.lwjgl.glfw.GLFWErrorCallback; -import org.lwjgl.glfw.GLFWVidMode; -import org.lwjgl.glfw.GLFWWindowSizeCallback; -import org.lwjgl.opengl.GL; -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.glEnableVertexAttribArray; -import static org.lwjgl.opengl.GL20.glVertexAttribPointer; -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 { - private final float[] vertices = { - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, - - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f - }; - private final Vec3[] cubePositions = { - new Vec3(0.0f, 0.0f, 0.0f), - new Vec3(2.0f, 5.0f, -15.0f), - new Vec3(-1.5f, -2.2f, -2.5f), - new Vec3(-3.8f, -2.0f, -12.3f), - new Vec3(2.4f, -0.4f, -3.5f), - new Vec3(-1.7f, 3.0f, -7.5f), - new Vec3(1.3f, -2.0f, -2.5f), - new Vec3(1.5f, 2.0f, -2.5f), - new Vec3(1.5f, 0.2f, -1.5f), - new Vec3(-1.3f, 1.0f, -1.5f) - }; - private int screenWidth = 800; - private int screenHeight = 600; - private float visibilityLevel; - private boolean polygonEnabled; - private Shader shader; - private Texture texture0; - private Texture texture1; - private long windowId; - - private double deltaTime; // time between current frame and last frame - private double lastFrame; - - private Camera camera; - - public void init() { - // Setup an error callback. The default implementation - // will print the error message in System.err. - GLFWErrorCallback.createPrint(System.err).set(); - - // Initialize GLFW. Most GLFW functions will not work before doing this. - if (!glfwInit()) { - throw new IllegalStateException("Unable to initialize GLFW"); - } - - // Configure GLFW - glfwDefaultWindowHints(); // optional, the current window hints are already the default - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable - - // Create the window - this.windowId = glfwCreateWindow(this.screenWidth, this.screenHeight, "Hello World!", NULL, NULL); - if (this.windowId == NULL) { - glfwTerminate(); - throw new RuntimeException("Failed to create the GLFW window"); - } - - this.camera = new Camera(45f, this.windowId); - - - glfwSetWindowSizeCallback(this.windowId, new GLFWWindowSizeCallback() { - @Override - public void invoke(long window, int width, int height) { - glViewport(0, 0, width, height); - RenderWindow.this.screenWidth = width; - RenderWindow.this.screenHeight = height; - RenderWindow.this.camera.calculateProjectionMatrix(RenderWindow.this.screenWidth, RenderWindow.this.screenHeight, RenderWindow.this.shader); - } - }); - - - // Setup a key callback. It will be called every time a key is pressed, repeated or released. - glfwSetKeyCallback(this.windowId, (window, key, scancode, action, mods) -> { - if (action != GLFW_RELEASE) { - return; - } - switch (key) { - case GLFW_KEY_ESCAPE -> glfwSetWindowShouldClose(this.windowId, true); - case GLFW_KEY_P -> switchPolygonMode(); - } - }); - - glfwSetInputMode(this.windowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - - - glfwSetCursorPosCallback(this.windowId, ((window, xPos, yPos) -> this.camera.mouseCallback(xPos, yPos))); - - - // Get the thread stack and push a new frame - try (MemoryStack stack = stackPush()) { - IntBuffer pWidth = stack.mallocInt(1); // int* - IntBuffer pHeight = stack.mallocInt(1); // int* - - // Get the window size passed to glfwCreateWindow - glfwGetWindowSize(this.windowId, pWidth, pHeight); - - // Get the resolution of the primary monitor - GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - - // Center the window - glfwSetWindowPos(this.windowId, (videoMode.width() - pWidth.get(0)) / 2, (videoMode.height() - pHeight.get(0)) / 2); - } // the stack frame is popped automatically - - // Make the OpenGL context current - glfwMakeContextCurrent(this.windowId); - // Enable v-sync - glfwSwapInterval(1); - - - // Make the window visible - glfwShowWindow(this.windowId); - - GL.createCapabilities(); - glClearColor(0.2f, 0.3f, 0.3f, 1.0f); - - glEnable(GL_DEPTH_TEST); - } - - public void startLoop() throws IOException, ShaderLoadingException { - int vAO = glGenVertexArrays(); - int vBO = 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); - - - glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * Float.BYTES, 0L); - glEnableVertexAttribArray(0); - - glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * Float.BYTES, (3 * Float.BYTES)); - glEnableVertexAttribArray(1); - - // 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); - - this.texture0 = new Texture("/textures/emerald_block.png"); - this.texture0.load(); - this.texture1 = new Texture("/textures/brown_wool.png"); - this.texture1.load(); - - this.shader = new Shader("vertex.glsl", "fragment.glsl"); - this.shader.load(); - this.shader.use(); - - - this.shader.setInt("texture0", 0); - this.shader.setInt("texture1", 1); - - this.shader.setFloat("visibility", this.visibilityLevel); - - this.camera.calculateProjectionMatrix(this.screenWidth, this.screenHeight, this.shader); - this.camera.calculateViewMatrix(this.shader); - - - while (!glfwWindowShouldClose(this.windowId)) { - - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer - - double currentFrame = glfwGetTime(); - - - this.deltaTime = currentFrame - this.lastFrame; - this.lastFrame = currentFrame; - - this.camera.calculateViewMatrix(this.shader); - - - this.texture0.use(GL_TEXTURE0); - this.texture1.use(GL_TEXTURE1); - - this.shader.use(); - - - 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 - - for (int i = 0; i < this.cubePositions.length; i++) { - float angle = 50.0f * (i + 1) * (float) glfwGetTime(); - Mat4 model = new Mat4().translate(this.cubePositions[i]).rotate(glm.INSTANCE.radians(angle), new Vec3(i / 0.5f + 0.1f, i / 0.3f + 0.1f, i / 0.1f + 0.1f)); - this.shader.setMat4("model", model); - - glDrawArrays(GL_TRIANGLES, 0, 36); - } - - - glfwSwapBuffers(this.windowId); // swap the color buffers - - // Poll for window events. The key callback above will only be - // invoked during this call. - glfwPollEvents(); - handleInput(); - this.camera.handleInput(this.deltaTime); - } - } - - - public void exit() { - // Free the window callbacks and destroy the window - glfwFreeCallbacks(this.windowId); - glfwDestroyWindow(this.windowId); - - // Terminate GLFW and free the error callback - glfwTerminate(); - glfwSetErrorCallback(null).free(); - } - - private void switchPolygonMode() { - glPolygonMode(GL_FRONT_AND_BACK, (this.polygonEnabled ? GL_LINE : GL_FILL)); - this.polygonEnabled = !this.polygonEnabled; - } - - private void handleInput() { - if (glfwGetKey(this.windowId, GLFW_KEY_UP) == GLFW_PRESS) { - this.shader.use(); - this.visibilityLevel += 0.1f; - if (this.visibilityLevel > 1.0f) { - this.visibilityLevel = 1.0f; - } - this.shader.setFloat("visibility", this.visibilityLevel); - } - if (glfwGetKey(this.windowId, GLFW_KEY_DOWN) == GLFW_PRESS) { - this.shader.use(); - this.visibilityLevel -= 0.1f; - if (this.visibilityLevel < 0.0f) { - this.visibilityLevel = 0.0f; - } - this.shader.setFloat("visibility", this.visibilityLevel); - } - } -} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt new file mode 100644 index 000000000..8ad7db582 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -0,0 +1,202 @@ +package de.bixilon.minosoft.gui.rendering + +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import glm_.vec3.Vec3 +import org.lwjgl.* +import org.lwjgl.glfw.Callbacks +import org.lwjgl.glfw.GLFW.* +import org.lwjgl.glfw.GLFWErrorCallback +import org.lwjgl.glfw.GLFWWindowSizeCallback +import org.lwjgl.opengl.* +import org.lwjgl.opengl.GL11.glClear +import org.lwjgl.system.MemoryStack +import org.lwjgl.system.MemoryUtil + +class RenderWindow { + private var screenWidth = 800 + private var screenHeight = 600 + private var polygonEnabled = false + private lateinit var shader: Shader + private lateinit var texture0: TextureArray + private var windowId: Long = 0 + private var deltaTime = 0.0 // time between current frame and last frame + + private var lastFrame = 0.0 + private lateinit var camera: Camera + + fun init() { + // Setup an error callback. The default implementation + // will print the error message in System.err. + GLFWErrorCallback.createPrint(System.err).set() + + // Initialize Most GLFW functions will not work before doing this. + check(glfwInit()) { "Unable to initialize GLFW" } + + // Configure GLFW + glfwDefaultWindowHints() // optional, the current window hints are already the default + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE) + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE) // the window will stay hidden after creation + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE) // the window will be resizable + + // Create the window + windowId = glfwCreateWindow(screenWidth, screenHeight, "Hello World!", MemoryUtil.NULL, MemoryUtil.NULL) + if (windowId == MemoryUtil.NULL) { + glfwTerminate() + throw RuntimeException("Failed to create the GLFW window") + } + camera = Camera(45f, windowId) + glfwSetWindowSizeCallback(windowId, object : GLFWWindowSizeCallback() { + override fun invoke(window: Long, width: Int, height: Int) { + GL11.glViewport(0, 0, width, height) + screenWidth = width + screenHeight = height + camera.calculateProjectionMatrix(screenWidth, screenHeight, shader) + } + }) + + // Setup a key callback. It will be called every time a key is pressed, repeated or released. + glfwSetKeyCallback(this.windowId) { windowId: Long, key: Int, scanCode: Int, action: Int, mods: Int -> + run { + if (action != GLFW_RELEASE) { + return@run + } + when (key) { + GLFW_KEY_ESCAPE -> { + glfwSetWindowShouldClose(this.windowId, true) + } + GLFW_KEY_P -> { + switchPolygonMode() + } + } + } + + } + + glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED) + glfwSetCursorPosCallback(windowId) { windowId: Long, xPos: Double, yPos: Double -> camera.mouseCallback(xPos, yPos) } + MemoryStack.stackPush().let { stack -> + val pWidth = stack.mallocInt(1) // int* + val pHeight = stack.mallocInt(1) // int* + + // Get the window size passed to glfwCreateWindow + glfwGetWindowSize(windowId, pWidth, pHeight) + + // Get the resolution of the primary monitor + val videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor()) + + // Center the window + glfwSetWindowPos(windowId, (videoMode!!.width() - pWidth[0]) / 2, (videoMode.height() - pHeight[0]) / 2) + } + + // Make the OpenGL context current + glfwMakeContextCurrent(windowId) + // Enable v-sync + glfwSwapInterval(1) + + + // Make the window visible + glfwShowWindow(windowId) + GL.createCapabilities() + GL11.glClearColor(0.2f, 0.3f, 0.3f, 1.0f) + GL11.glEnable(GL11.GL_DEPTH_TEST) + } + + fun startLoop() { + val chunk = DummyData.getDummyChunk() + + texture0 = TextureArray(arrayOf("/textures/emerald_block.png", "/textures/brown_wool.png")) + texture0.load() + + shader = Shader("vertex.glsl", "fragment.glsl") + shader.load() + shader.use() + shader.setInt("texture0", 0) + + camera.calculateProjectionMatrix(screenWidth, screenHeight, shader) + camera.calculateViewMatrix(shader) + + val preparedChunks = mutableListOf() + + + + for ((sectionHeight, section) in chunk.sections) { + for ((location, block) in section.blocks) { + val textureIndex = when (block.fullIdentifier) { + "minecraft:dirt" -> 1 + else -> 0 + } + preparedChunks.add(Mesh(textureIndex, Vec3(location.x, location.y + ProtocolDefinition.SECTION_HEIGHT_Y * sectionHeight, location.z))) + // break + } + // break + } + + + var framesLastSecond = 0 + var lastCalcTime = glfwGetTime() + while (!glfwWindowShouldClose(windowId)) { + glClear(GL11.GL_COLOR_BUFFER_BIT or GL11.GL_DEPTH_BUFFER_BIT) // clear the framebuffer + + val currentFrame = glfwGetTime() + + deltaTime = currentFrame - lastFrame + lastFrame = currentFrame + + texture0.use(GL13.GL_TEXTURE0) + + shader.use() + + camera.calculateViewMatrix(shader) + + for (mesh in preparedChunks) { + mesh.draw() + } + + // for ((key, value) in chunk.sections) { + // for ((key1) in value.blocks) { + // val model = Mat4().translate(Vec3(key1.x, key1.y + ProtocolDefinition.SECTION_HEIGHT_Y * key, key1.z)) + // shader.setMat4("model", model) + // glDrawArrays(GL11.GL_TRIANGLES, 0, 36) + // } + // } + + glfwSwapBuffers(windowId) // swap the color buffers + + // Poll for window events. The key callback above will only be + // invoked during this call. + glfwPollEvents() + handleInput() + camera.handleInput(deltaTime) + if (glfwGetTime() - lastCalcTime >= 1.0) { + glfwSetWindowTitle(windowId, "FPS: $framesLastSecond") + lastCalcTime = glfwGetTime() + framesLastSecond = 0 + } + framesLastSecond++ + } + } + + fun exit() { + // Free the window callbacks and destroy the window + Callbacks.glfwFreeCallbacks(windowId) + glfwDestroyWindow(windowId) + + // Terminate GLFW and free the error callback + glfwTerminate() + glfwSetErrorCallback(null)!!.free() + } + + private fun switchPolygonMode() { + GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, if (polygonEnabled) { + GL11.GL_LINE + } else { + GL11.GL_FILL + }) + polygonEnabled = !polygonEnabled + } + + private fun handleInput() { + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Texture.java b/src/main/java/de/bixilon/minosoft/gui/rendering/Texture.java deleted file mode 100644 index a003f839b..000000000 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Texture.java +++ /dev/null @@ -1,49 +0,0 @@ -package de.bixilon.minosoft.gui.rendering; - -import de.matthiasmann.twl.utils.PNGDecoder; -import org.lwjgl.BufferUtils; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import static org.lwjgl.opengl.GL20.*; -import static org.lwjgl.opengl.GL30.glGenerateMipmap; - -public class Texture { - private final String texturePath; - private int textureId; - - public Texture(String texturePath) { - this.texturePath = texturePath; - } - - - public int load() throws IOException { - this.textureId = glGenTextures(); - glBindTexture(GL_TEXTURE_2D, this.textureId); - // set the texture wrapping/filtering options (on the currently bound texture object) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - // load and generate the texture - - PNGDecoder decoder = new PNGDecoder(OpenGLUtil.class.getResourceAsStream(this.texturePath)); - ByteBuffer buffer = BufferUtils.createByteBuffer(decoder.getWidth() * decoder.getHeight() * 4); - decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA); - buffer.flip(); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - glGenerateMipmap(GL_TEXTURE_2D); - return this.textureId; - } - - public int getTextureId() { - return this.textureId; - } - - public void use(int textureMode) { - glActiveTexture(textureMode); // activate the texture unit first before binding texture - glBindTexture(GL_TEXTURE_2D, this.textureId); - } -} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/TextureArray.java b/src/main/java/de/bixilon/minosoft/gui/rendering/TextureArray.java new file mode 100644 index 000000000..06fdedbfc --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/TextureArray.java @@ -0,0 +1,60 @@ +package de.bixilon.minosoft.gui.rendering; + +import de.matthiasmann.twl.utils.PNGDecoder; +import org.lwjgl.BufferUtils; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import static org.lwjgl.opengl.GL20.*; +import static org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY; +import static org.lwjgl.opengl.GL30.glGenerateMipmap; + +public class TextureArray { + private final String[] texturePaths; + private int textureId; + + public TextureArray(String[] texturePaths) { + this.texturePaths = texturePaths; + } + + public int load() throws IOException { + this.textureId = glGenTextures(); + glBindTexture(GL_TEXTURE_2D_ARRAY, this.textureId); + // set the texture wrapping/filtering options (on the currently bound texture object) + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // load and generate the texture + + boolean sizeSet = false; + + for (int i = 0; i < this.texturePaths.length; i++) { + PNGDecoder decoder = new PNGDecoder(OpenGLUtil.class.getResourceAsStream(this.texturePaths[i])); + ByteBuffer buffer = BufferUtils.createByteBuffer(decoder.getWidth() * decoder.getHeight() * 4); + decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA); + buffer.flip(); + if (!sizeSet) { + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), this.texturePaths.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null); + + sizeSet = true; + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, decoder.getWidth(), decoder.getHeight(), 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + + } + + //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + glGenerateMipmap(GL_TEXTURE_2D); + return this.textureId; + } + + public int getTextureId() { + return this.textureId; + } + + public void use(int textureMode) { + glActiveTexture(textureMode); // activate the texture unit first before binding texture + glBindTexture(GL_TEXTURE_2D, this.textureId); + } +} diff --git a/src/main/resources/assets/rendering/shader/fragment.glsl b/src/main/resources/assets/rendering/shader/fragment.glsl index cd93807fe..7e2ba5c5b 100644 --- a/src/main/resources/assets/rendering/shader/fragment.glsl +++ b/src/main/resources/assets/rendering/shader/fragment.glsl @@ -1,16 +1,12 @@ #version 330 core -out vec4 FragColor; -in vec3 vertexColor; -in vec2 TexCoord; +out vec4 outColor; + +in vec3 passTextureCoordinates; // texture sampler -uniform sampler2D texture0; -uniform sampler2D texture1; - -uniform float visibility; +uniform sampler2DArray texureArray; void main() { - vec2 newTextCoord = vec2(TexCoord.x, -TexCoord.y); - FragColor = mix(texture(texture0, newTextCoord), texture(texture1, newTextCoord), visibility); + outColor = texture(texureArray, passTextureCoordinates); } diff --git a/src/main/resources/assets/rendering/shader/vertex.glsl b/src/main/resources/assets/rendering/shader/vertex.glsl index ffbd4cd76..3c4fe46fa 100644 --- a/src/main/resources/assets/rendering/shader/vertex.glsl +++ b/src/main/resources/assets/rendering/shader/vertex.glsl @@ -1,16 +1,23 @@ #version 330 core -layout (location = 0) in vec3 aPos; -layout (location = 1) in vec2 aTexCoord; +layout (location = 0) in vec3 inPosition; +layout (location = 1) in float textureIndex; +layout (location = 2) in float textureLayer; out vec3 vertexColor; -out vec2 TexCoord; +out vec3 passTextureCoordinates; -uniform mat4 model; uniform mat4 view; uniform mat4 projection; +vec2 textureIndexCoordinates[4] = vec2[4]( +vec2(0.0f, 0.0f), +vec2(1.0f, 0.0f), +vec2(1.0f, 1.0f), +vec2(0.0f, 1.0f) +); + void main() { - gl_Position = projection * view * model * vec4(aPos, 1.0); - TexCoord = vec2(aTexCoord.x, aTexCoord.y); + gl_Position = projection * view * vec4(inPosition, 1.0f); + passTextureCoordinates = vec3(textureIndexCoordinates[int(textureIndex)], textureLayer); }