diff --git a/doc/rendering/ShaderData.md b/doc/rendering/ShaderData.md
new file mode 100644
index 000000000..b72289fea
--- /dev/null
+++ b/doc/rendering/ShaderData.md
@@ -0,0 +1,4 @@
+Relative Chunk position: 3x 0-15 (3x 4bit = 12bit)
+Side: 0-6 (3 bits)
+Light level: 1x 0-15 (4bit)
+Texture index: ?
diff --git a/pom.xml b/pom.xml
index 653375807..19f57494e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -127,6 +127,13 @@
3.2.3
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
com.google.code.gson
@@ -207,6 +214,10 @@
org.lwjgl
lwjgl-opengl
+
+ org.lwjgl
+ lwjgl-stb
+
org.lwjgl
lwjgl
@@ -227,5 +238,20 @@
lwjgl-opengl
${lwjgl.natives}
+
+ org.lwjgl
+ lwjgl-stb
+ ${lwjgl.natives}
+
+
+ org.l33tlabs.twl
+ pngdecoder
+ 1.0
+
+
+ com.github.kotlin-graphics
+ glm
+ 1.0.1
+
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt
new file mode 100644
index 000000000..b5bd4c77b
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt
@@ -0,0 +1,86 @@
+package de.bixilon.minosoft.gui.rendering
+
+import glm_.glm
+import glm_.mat4x4.Mat4
+import glm_.vec3.Vec3
+import org.lwjgl.glfw.GLFW
+import kotlin.math.cos
+import kotlin.math.sin
+
+class Camera(private var fov: Float, private val windowId: Long) {
+ private var mouseSensitivity = 0.1
+ private var movementSpeed = 7
+ private var cameraFront = Vec3(0.0f, 0.0f, -1.0f)
+ private var cameraPosition = Vec3(0.0f, 0.0f, 3.0f)
+ private var lastMouseX = 0.0
+ private var lastMouseY = 0.0
+ private var yaw = 0.0
+ private var pitch = 0.0
+
+ fun mouseCallback(xPos: Double, yPos: Double) {
+ var xOffset = xPos - this.lastMouseX
+ var yOffset = this.lastMouseY - yPos // reversed since y-coordinates go from bottom to top
+ lastMouseX = xPos
+ lastMouseY = yPos
+ xOffset *= mouseSensitivity
+ yOffset *= mouseSensitivity
+ yaw += xOffset
+ pitch += yOffset
+
+ // make sure that when pitch is out of bounds, screen doesn't get flipped
+ if (this.pitch > 89.0) {
+ this.pitch = 89.0
+ } else if (this.pitch < -89.0) {
+ this.pitch = -89.0
+ }
+ cameraFront = Vec3((cos(glm.radians(yaw)) * cos(glm.radians(pitch))).toFloat(), sin(glm.radians(pitch)).toFloat(), (sin(glm.radians(yaw)) * cos(glm.radians(pitch))).toFloat()).normalize()
+ }
+
+ fun handleInput(deltaTime: Double) {
+ val cameraSpeed = movementSpeed * deltaTime
+ val currentY = cameraPosition.y
+ if (GLFW.glfwGetKey(windowId, GLFW.GLFW_KEY_W) == GLFW.GLFW_PRESS) {
+ cameraPosition = cameraPosition + cameraFront * cameraSpeed
+ }
+ if (GLFW.glfwGetKey(windowId, GLFW.GLFW_KEY_S) == GLFW.GLFW_PRESS) {
+ cameraPosition = cameraPosition - cameraFront * cameraSpeed
+ }
+ if (GLFW.glfwGetKey(windowId, GLFW.GLFW_KEY_A) == GLFW.GLFW_PRESS) {
+ cameraPosition = cameraPosition - (cameraFront.cross(CAMERA_UP_VEC3).normalize()) * cameraSpeed
+ }
+ if (GLFW.glfwGetKey(windowId, GLFW.GLFW_KEY_D) == GLFW.GLFW_PRESS) {
+ cameraPosition = cameraPosition + (cameraFront.cross(CAMERA_UP_VEC3).normalize()) * cameraSpeed
+ }
+ this.cameraPosition.y = currentY // stay on xz line when moving (aka. no clip)
+ if (GLFW.glfwGetKey(windowId, GLFW.GLFW_KEY_LEFT_SHIFT) == GLFW.GLFW_PRESS) {
+ cameraPosition = cameraPosition - CAMERA_UP_VEC3 * cameraSpeed
+ }
+ if (GLFW.glfwGetKey(windowId, GLFW.GLFW_KEY_SPACE) == GLFW.GLFW_PRESS) {
+ cameraPosition = cameraPosition + CAMERA_UP_VEC3 * cameraSpeed
+ }
+ }
+
+ fun calculateProjectionMatrix(screenWidth: Int, screenHeight: Int, shader: Shader) {
+ shader.use().setMat4("projection", calculateProjectionMatrix(screenWidth, screenHeight))
+ }
+
+ private fun calculateProjectionMatrix(screenWidth: Int, screenHeight: Int): Mat4 {
+ return glm.perspective(glm.radians(fov), screenWidth.toFloat() / screenHeight.toFloat(), 0.1f, 100f)
+ }
+
+ fun calculateViewMatrix(shader: Shader) {
+ shader.use().setMat4("view", calculateViewMatrix())
+ }
+
+ private fun calculateViewMatrix(): Mat4 {
+ return glm.lookAt(cameraPosition, cameraPosition + cameraFront, CAMERA_UP_VEC3)
+ }
+
+ fun setFOV(fov: Float) {
+ this.fov = fov
+ }
+
+ companion object {
+ private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f)
+ }
+}
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 f8412f4ff..c293ee842 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/DummyRender.java
@@ -13,6 +13,5 @@ public class DummyRender {
renderWindow.init();
renderWindow.startLoop();
renderWindow.exit();
-
}
}
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 bfa5a3518..dce682b97 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.java
@@ -1,10 +1,13 @@
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.*;
+import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryStack;
import java.io.IOException;
@@ -14,7 +17,8 @@ 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.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;
@@ -22,46 +26,74 @@ 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
+ 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
};
- int[] indices = { // note that we start from 0!
- 0, 1, 3, // first Triangle
- 1, 2, 3 // second Triangle
+ 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)
};
- boolean polygonEnabled;
- private long window;
+ 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 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);
+ private double deltaTime; // time between current frame and last frame
+ private double lastFrame;
- 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;
- }
+ private Camera camera;
public void init() {
// Setup an error callback. The default implementation
@@ -82,38 +114,42 @@ public class RenderWindow {
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
// Create the window
- this.window = glfwCreateWindow(800, 600, "Hello World!", NULL, NULL);
- if (this.window == NULL) {
+ 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.window, new GLFWWindowSizeCallback() {
+
+ 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.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
+ glfwSetKeyCallback(this.windowId, (window, key, scancode, action, mods) -> {
+ if (action != GLFW_RELEASE) {
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;
+ 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()) {
@@ -121,33 +157,33 @@ public class RenderWindow {
IntBuffer pHeight = stack.mallocInt(1); // int*
// Get the window size passed to glfwCreateWindow
- glfwGetWindowSize(this.window, pWidth, pHeight);
+ glfwGetWindowSize(this.windowId, pWidth, pHeight);
// Get the resolution of the primary monitor
GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center the window
- glfwSetWindowPos(this.window, (videoMode.width() - pWidth.get(0)) / 2, (videoMode.height() - pHeight.get(0)) / 2);
+ 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.window);
+ glfwMakeContextCurrent(this.windowId);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
- glfwShowWindow(this.window);
+ 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();
- int eBO = glGenBuffers();
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(vAO);
@@ -155,12 +191,13 @@ public class RenderWindow {
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);
+ 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);
@@ -171,34 +208,98 @@ public class RenderWindow {
// 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();
- int shaderId = loadShaders();
+ 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)) {
- while (!glfwWindowShouldClose(this.window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
- glUseProgram(shaderId);
+ 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
- glDrawElements(GL_TRIANGLES, this.indices.length, GL_UNSIGNED_INT, 0);
- glfwSwapBuffers(this.window); // swap the color buffers
+ 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.window);
- glfwDestroyWindow(this.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/Shader.java b/src/main/java/de/bixilon/minosoft/gui/rendering/Shader.java
new file mode 100644
index 000000000..f650372a4
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Shader.java
@@ -0,0 +1,86 @@
+package de.bixilon.minosoft.gui.rendering;
+
+import de.bixilon.minosoft.gui.rendering.exceptions.ShaderLoadingException;
+import glm_.mat4x4.Mat4;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.ARBFragmentShader;
+import org.lwjgl.opengl.ARBShaderObjects;
+import org.lwjgl.opengl.ARBVertexShader;
+import org.lwjgl.opengl.GL11;
+
+import java.io.IOException;
+
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.system.MemoryUtil.NULL;
+
+public class Shader {
+ private static Shader usedShader;
+ private final String vertex;
+ private final String fragment;
+ private int programId;
+
+ public Shader(String vertex, String fragment) {
+ this.vertex = vertex;
+ this.fragment = fragment;
+ }
+
+ public int load() throws ShaderLoadingException, IOException {
+ int vertexShader = ShaderUtil.createShader(this.vertex, ARBVertexShader.GL_VERTEX_SHADER_ARB);
+ int fragmentShader = ShaderUtil.createShader(this.fragment, ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
+
+ this.programId = ARBShaderObjects.glCreateProgramObjectARB();
+
+ if (this.programId == NULL) {
+ throw new ShaderLoadingException();
+ }
+
+ ARBShaderObjects.glAttachObjectARB(this.programId, vertexShader);
+ ARBShaderObjects.glAttachObjectARB(this.programId, fragmentShader);
+
+ ARBShaderObjects.glLinkProgramARB(this.programId);
+ if (ARBShaderObjects.glGetObjectParameteriARB(this.programId, ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) {
+ throw new ShaderLoadingException(OpenGLUtil.getLogInfo(this.programId));
+ }
+
+ ARBShaderObjects.glValidateProgramARB(this.programId);
+ if (ARBShaderObjects.glGetObjectParameteriARB(this.programId, ARBShaderObjects.GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE) {
+ throw new ShaderLoadingException(OpenGLUtil.getLogInfo(this.programId));
+ }
+
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ return this.programId;
+ }
+
+ public int getProgramId() {
+ return this.programId;
+ }
+
+ public Shader use() {
+ if (usedShader != this) {
+ glUseProgram(this.programId);
+ usedShader = this;
+ }
+ return this;
+ }
+
+ public int getUniformLocation(String variableName) {
+ return glGetUniformLocation(this.programId, variableName);
+ }
+
+ public void setFloat(String name, float value) {
+ glUniform1f(getUniformLocation(name), value);
+ }
+
+ public void setInt(String name, int value) {
+ glUniform1i(getUniformLocation(name), value);
+ }
+
+ public void set4f(String variableName, float[] floats) {
+ glUniformMatrix4fv(glGetUniformLocation(this.programId, variableName), false, floats);
+ }
+
+ public void setMat4(String variableName, Mat4 mat4) {
+ glUniformMatrix4fv(glGetUniformLocation(this.programId, variableName), false, mat4.to(BufferUtils.createFloatBuffer(16)));
+ }
+}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/ShaderUtil.java b/src/main/java/de/bixilon/minosoft/gui/rendering/ShaderUtil.java
index b2119401e..9a9d2d21a 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/ShaderUtil.java
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/ShaderUtil.java
@@ -18,7 +18,7 @@ public class ShaderUtil {
throw new ShaderLoadingException();
}
- ARBShaderObjects.glShaderSourceARB(shaderId, Util.readAsset("/rendering/shader" + shaderPath));
+ ARBShaderObjects.glShaderSourceARB(shaderId, Util.readAsset("/rendering/shader/" + shaderPath));
ARBShaderObjects.glCompileShaderARB(shaderId);
if (ARBShaderObjects.glGetObjectParameteriARB(shaderId, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE) {
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Texture.java b/src/main/java/de/bixilon/minosoft/gui/rendering/Texture.java
new file mode 100644
index 000000000..a003f839b
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Texture.java
@@ -0,0 +1,49 @@
+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/resources/assets/rendering/shader/fragment.glsl b/src/main/resources/assets/rendering/shader/fragment.glsl
index d801e68e4..cd93807fe 100644
--- a/src/main/resources/assets/rendering/shader/fragment.glsl
+++ b/src/main/resources/assets/rendering/shader/fragment.glsl
@@ -1,6 +1,16 @@
#version 330 core
out vec4 FragColor;
+in vec3 vertexColor;
+in vec2 TexCoord;
+
+// texture sampler
+uniform sampler2D texture0;
+uniform sampler2D texture1;
+
+uniform float visibility;
+
void main() {
- FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
+ vec2 newTextCoord = vec2(TexCoord.x, -TexCoord.y);
+ FragColor = mix(texture(texture0, newTextCoord), texture(texture1, newTextCoord), visibility);
}
diff --git a/src/main/resources/assets/rendering/shader/vertex.glsl b/src/main/resources/assets/rendering/shader/vertex.glsl
index e390af09c..ffbd4cd76 100644
--- a/src/main/resources/assets/rendering/shader/vertex.glsl
+++ b/src/main/resources/assets/rendering/shader/vertex.glsl
@@ -1,6 +1,16 @@
#version 330 core
layout (location = 0) in vec3 aPos;
+layout (location = 1) in vec2 aTexCoord;
+
+out vec3 vertexColor;
+out vec2 TexCoord;
+
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
void main() {
- gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
+ gl_Position = projection * view * model * vec4(aPos, 1.0);
+ TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}