mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-16 19:05:02 -04:00
multi texture rendering, use vbos for rendering
This commit is contained in:
parent
d06bba2eb6
commit
98beee726d
@ -82,4 +82,8 @@ public class Chunk {
|
||||
public void setBlockEntityData(HashMap<InChunkLocation, BlockEntityMetaData> blockEntities) {
|
||||
blockEntities.forEach(this::setBlockEntityData);
|
||||
}
|
||||
|
||||
public HashMap<Byte, ChunkSection> getSections() {
|
||||
return this.sections;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
113
src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java
Normal file
113
src/main/java/de/bixilon/minosoft/gui/rendering/Mesh.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
202
src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt
Normal file
202
src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt
Normal file
@ -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<Mesh>()
|
||||
|
||||
|
||||
|
||||
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() {
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user