Added ShaderProgram and ChunkRenderer
This commit is contained in:
parent
dfbcf44725
commit
297fcb5a1e
|
@ -1,11 +1,7 @@
|
||||||
package nl.andrewl.aos2_client;
|
package nl.andrewl.aos2_client;
|
||||||
|
|
||||||
import nl.andrewl.aos_core.FileUtils;
|
|
||||||
import nl.andrewl.aos_core.model.Chunk;
|
import nl.andrewl.aos_core.model.Chunk;
|
||||||
import org.joml.Matrix3f;
|
import org.joml.Vector3i;
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
import org.lwjgl.BufferUtils;
|
|
||||||
import org.lwjgl.Version;
|
import org.lwjgl.Version;
|
||||||
import org.lwjgl.glfw.Callbacks;
|
import org.lwjgl.glfw.Callbacks;
|
||||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||||
|
@ -13,14 +9,59 @@ import org.lwjgl.opengl.GL;
|
||||||
import org.lwjgl.opengl.GLUtil;
|
import org.lwjgl.opengl.GLUtil;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.lwjgl.glfw.GLFW.*;
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
import static org.lwjgl.opengl.GL46.*;
|
import static org.lwjgl.opengl.GL46.*;
|
||||||
|
|
||||||
public class Aos2Client {
|
public class Aos2Client {
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
public static void main(String[] args) {
|
||||||
|
long windowHandle = initUI();
|
||||||
|
|
||||||
|
Chunk chunk = Chunk.random(new Vector3i(0, 0, 0), new Random(1));
|
||||||
|
Camera cam = new Camera();
|
||||||
|
glfwSetCursorPosCallback(windowHandle, cam);
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
chunk.setBlockAt(i, 0, 0, (byte) 8);
|
||||||
|
chunk.setBlockAt(0, i, 0, (byte) 40);
|
||||||
|
chunk.setBlockAt(0, 0, i, (byte) 120);
|
||||||
|
}
|
||||||
|
chunk.setBlockAt(0, 15, 0, (byte) 0);
|
||||||
|
chunk.setBlockAt(1, 15, 0, (byte) 0);
|
||||||
|
chunk.setBlockAt(2, 15, 0, (byte) 0);
|
||||||
|
chunk.setBlockAt(2, 15, 1, (byte) 0);
|
||||||
|
chunk.setBlockAt(0, 0, 0, (byte) 0);
|
||||||
|
|
||||||
|
ChunkRenderer chunkRenderer = new ChunkRenderer();
|
||||||
|
ChunkMesh mesh = new ChunkMesh(chunk);
|
||||||
|
chunkRenderer.addChunkMesh(mesh);
|
||||||
|
|
||||||
|
while (!glfwWindowShouldClose(windowHandle)) {
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
chunkRenderer.draw(cam);
|
||||||
|
|
||||||
|
glfwSwapBuffers(windowHandle);
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
|
if (glfwGetKey(windowHandle, GLFW_KEY_W) == GLFW_PRESS) cam.move(Camera.FORWARD);
|
||||||
|
if (glfwGetKey(windowHandle, GLFW_KEY_S) == GLFW_PRESS) cam.move(Camera.BACKWARD);
|
||||||
|
if (glfwGetKey(windowHandle, GLFW_KEY_A) == GLFW_PRESS) cam.move(Camera.LEFT);
|
||||||
|
if (glfwGetKey(windowHandle, GLFW_KEY_D) == GLFW_PRESS) cam.move(Camera.RIGHT);
|
||||||
|
if (glfwGetKey(windowHandle, GLFW_KEY_SPACE) == GLFW_PRESS) cam.move(Camera.UP);
|
||||||
|
if (glfwGetKey(windowHandle, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) cam.move(Camera.DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkRenderer.free();
|
||||||
|
|
||||||
|
Callbacks.glfwFreeCallbacks(windowHandle);
|
||||||
|
glfwDestroyWindow(windowHandle);
|
||||||
|
glfwTerminate();
|
||||||
|
glfwSetErrorCallback(null).free();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long initUI() {
|
||||||
System.out.println("LWJGL Version: " + Version.getVersion());
|
System.out.println("LWJGL Version: " + Version.getVersion());
|
||||||
GLFWErrorCallback.createPrint(System.err).set();
|
GLFWErrorCallback.createPrint(System.err).set();
|
||||||
if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW");
|
if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW");
|
||||||
|
@ -53,77 +94,6 @@ public class Aos2Client {
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glCullFace(GL_BACK);
|
glCullFace(GL_BACK);
|
||||||
|
|
||||||
Chunk chunk = Chunk.random(new Random(1));
|
return windowHandle;
|
||||||
Camera cam = new Camera();
|
|
||||||
glfwSetCursorPosCallback(windowHandle, cam);
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
chunk.setBlockAt(i, 0, 0, (byte) 8);
|
|
||||||
chunk.setBlockAt(0, i, 0, (byte) 40);
|
|
||||||
chunk.setBlockAt(0, 0, i, (byte) 120);
|
|
||||||
}
|
|
||||||
chunk.setBlockAt(0, 15, 0, (byte) 0);
|
|
||||||
chunk.setBlockAt(1, 15, 0, (byte) 0);
|
|
||||||
chunk.setBlockAt(2, 15, 0, (byte) 0);
|
|
||||||
chunk.setBlockAt(2, 15, 1, (byte) 0);
|
|
||||||
chunk.setBlockAt(0, 0, 0, (byte) 0);
|
|
||||||
Matrix4f projectionTransform = new Matrix4f().perspective(70, 800 / 600.0f, 0.01f, 100.0f);
|
|
||||||
ChunkMesh mesh = new ChunkMesh(chunk);
|
|
||||||
|
|
||||||
int shaderProgram = createShaderProgram();
|
|
||||||
int projectionTransformUniform = glGetUniformLocation(shaderProgram, "projectionTransform");
|
|
||||||
int viewTransformUniform = glGetUniformLocation(shaderProgram, "viewTransform");
|
|
||||||
int normalTransformUniform = glGetUniformLocation(shaderProgram, "normalTransform");
|
|
||||||
|
|
||||||
glUniformMatrix4fv(projectionTransformUniform, false, projectionTransform.get(new float[16]));
|
|
||||||
|
|
||||||
long t = 0;
|
|
||||||
while (!glfwWindowShouldClose(windowHandle)) {
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
glUniformMatrix4fv(viewTransformUniform, false, cam.getViewTransformData());
|
|
||||||
Matrix3f normalTransform = new Matrix3f();
|
|
||||||
glUniformMatrix3fv(normalTransformUniform, false, normalTransform.get(new float[9]));
|
|
||||||
|
|
||||||
mesh.draw();
|
|
||||||
|
|
||||||
glfwSwapBuffers(windowHandle);
|
|
||||||
glfwPollEvents();
|
|
||||||
// float n = (float) (8 * Math.sin(Math.toRadians(t)) + 8);
|
|
||||||
// cam.setPosition(n, -1, n);
|
|
||||||
// cam.setOrientationDegrees(0, 0);
|
|
||||||
// Thread.sleep(20);
|
|
||||||
// t++;
|
|
||||||
// if (t >= 360) t = 0;
|
|
||||||
|
|
||||||
if (glfwGetKey(windowHandle, GLFW_KEY_W) == GLFW_PRESS) cam.move(Camera.FORWARD);
|
|
||||||
if (glfwGetKey(windowHandle, GLFW_KEY_S) == GLFW_PRESS) cam.move(Camera.BACKWARD);
|
|
||||||
if (glfwGetKey(windowHandle, GLFW_KEY_A) == GLFW_PRESS) cam.move(Camera.LEFT);
|
|
||||||
if (glfwGetKey(windowHandle, GLFW_KEY_D) == GLFW_PRESS) cam.move(Camera.RIGHT);
|
|
||||||
if (glfwGetKey(windowHandle, GLFW_KEY_SPACE) == GLFW_PRESS) cam.move(Camera.UP);
|
|
||||||
if (glfwGetKey(windowHandle, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) cam.move(Camera.DOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
Callbacks.glfwFreeCallbacks(windowHandle);
|
|
||||||
glfwDestroyWindow(windowHandle);
|
|
||||||
glfwTerminate();
|
|
||||||
glfwSetErrorCallback(null).free();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int createShaderProgram() throws IOException {
|
|
||||||
int prog = glCreateProgram();
|
|
||||||
int fragShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
glShaderSource(fragShader, FileUtils.readClasspathFile("shader/fragment.glsl"));
|
|
||||||
glCompileShader(fragShader);
|
|
||||||
glAttachShader(prog, fragShader);
|
|
||||||
int vertShader = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
glShaderSource(vertShader, FileUtils.readClasspathFile("shader/vertex.glsl"));
|
|
||||||
glCompileShader(vertShader);
|
|
||||||
glAttachShader(prog, vertShader);
|
|
||||||
|
|
||||||
glValidateProgram(prog);
|
|
||||||
glLinkProgram(prog);
|
|
||||||
glUseProgram(prog);
|
|
||||||
return prog;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,4 +156,10 @@ public class ChunkMesh {
|
||||||
|
|
||||||
glDrawElements(GL_TRIANGLES, indiciesCount, GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, indiciesCount, GL_UNSIGNED_INT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void free() {
|
||||||
|
glDeleteBuffers(vboId);
|
||||||
|
glDeleteBuffers(eboId);
|
||||||
|
glDeleteVertexArrays(vaoId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package nl.andrewl.aos2_client;
|
||||||
|
|
||||||
|
import org.joml.Matrix3f;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL46.*;
|
||||||
|
|
||||||
|
public class ChunkRenderer {
|
||||||
|
private final ShaderProgram shaderProgram;
|
||||||
|
private final int projectionTransformUniform;
|
||||||
|
private final int viewTransformUniform;
|
||||||
|
private final int normalTransformUniform;
|
||||||
|
|
||||||
|
private final Matrix4f projectionTransform = new Matrix4f().perspective(70, 800 / 600.0f, 0.01f, 100.0f);
|
||||||
|
|
||||||
|
private final List<ChunkMesh> chunkMeshes = new ArrayList<>();
|
||||||
|
|
||||||
|
public ChunkRenderer() {
|
||||||
|
this.shaderProgram = new ShaderProgram.Builder()
|
||||||
|
.withShader("shader/chunk/vertex.glsl", GL_VERTEX_SHADER)
|
||||||
|
.withShader("shader/chunk/fragment.glsl", GL_FRAGMENT_SHADER)
|
||||||
|
.build();
|
||||||
|
shaderProgram.use();
|
||||||
|
this.projectionTransformUniform = shaderProgram.getUniform("projectionTransform");
|
||||||
|
this.viewTransformUniform = shaderProgram.getUniform("viewTransform");
|
||||||
|
this.normalTransformUniform = shaderProgram.getUniform("normalTransform");
|
||||||
|
|
||||||
|
// Preemptively load projection transform, which doesn't change much.
|
||||||
|
glUniformMatrix4fv(projectionTransformUniform, false, projectionTransform.get(new float[16]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChunkMesh(ChunkMesh mesh) {
|
||||||
|
this.chunkMeshes.add(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(Camera cam) {
|
||||||
|
glUniformMatrix4fv(viewTransformUniform, false, cam.getViewTransformData());
|
||||||
|
Matrix3f normalTransform = new Matrix3f();
|
||||||
|
glUniformMatrix3fv(normalTransformUniform, false, normalTransform.get(new float[9]));
|
||||||
|
shaderProgram.use();
|
||||||
|
|
||||||
|
for (var mesh : chunkMeshes) mesh.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void free() {
|
||||||
|
for (var mesh : chunkMeshes) mesh.free();
|
||||||
|
shaderProgram.free();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package nl.andrewl.aos2_client;
|
||||||
|
|
||||||
|
import nl.andrewl.aos_core.FileUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL46.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a shader program with one or more individual shaders.
|
||||||
|
*/
|
||||||
|
public class ShaderProgram {
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
public ShaderProgram(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void use() {
|
||||||
|
glUseProgram(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUniform(String name) {
|
||||||
|
return glGetUniformLocation(id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void free() {
|
||||||
|
glDeleteProgram(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
this.id = glCreateProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withShader(String resource, int type) {
|
||||||
|
int shaderId = glCreateShader(type);
|
||||||
|
try {
|
||||||
|
glShaderSource(shaderId, FileUtils.readClasspathFile(resource));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
glCompileShader(shaderId);
|
||||||
|
glAttachShader(id, shaderId);
|
||||||
|
glDeleteShader(shaderId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderProgram build() {
|
||||||
|
glValidateProgram(id);
|
||||||
|
glLinkProgram(id);
|
||||||
|
return new ShaderProgram(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ void main() {
|
||||||
vec3 lightColor = vec3(1.0, 1.0, 0.9); // TODO: Add this via a uniform.
|
vec3 lightColor = vec3(1.0, 1.0, 0.9); // TODO: Add this via a uniform.
|
||||||
|
|
||||||
vec3 ambientComponent = vec3(0.1, 0.1, 0.1);
|
vec3 ambientComponent = vec3(0.1, 0.1, 0.1);
|
||||||
vec3 diffuseComponent = max(dot(vertexNormal, lightDirection), 0.0) * lightColor;
|
vec3 diffuseComponent = max(dot(vertexNormal * -1, lightDirection), 0.0) * lightColor;
|
||||||
// No specular component.
|
// No specular component.
|
||||||
|
|
||||||
fragmentColor = vec4((ambientComponent + diffuseComponent) * vertexColor, 1.0);
|
fragmentColor = vec4((ambientComponent + diffuseComponent) * vertexColor, 1.0);
|
|
@ -6,6 +6,9 @@ import org.joml.Vector3i;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds information about a uniform "chunk" of the voxel world.
|
||||||
|
*/
|
||||||
public class Chunk {
|
public class Chunk {
|
||||||
/**
|
/**
|
||||||
* The size of a chunk, in terms of the number of blocks on one axis of the cube.
|
* The size of a chunk, in terms of the number of blocks on one axis of the cube.
|
||||||
|
@ -14,6 +17,20 @@ public class Chunk {
|
||||||
public static final int TOTAL_SIZE = SIZE * SIZE * SIZE;
|
public static final int TOTAL_SIZE = SIZE * SIZE * SIZE;
|
||||||
|
|
||||||
private final byte[] blocks = new byte[TOTAL_SIZE];
|
private final byte[] blocks = new byte[TOTAL_SIZE];
|
||||||
|
private final Vector3i position;
|
||||||
|
|
||||||
|
public Chunk(int cx, int cy, int cz) {
|
||||||
|
this.position = new Vector3i(cx, cy, cz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk(Vector3i position) {
|
||||||
|
this.position = new Vector3i(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk(Chunk other) {
|
||||||
|
this(other.position);
|
||||||
|
System.arraycopy(other.blocks, 0, this.blocks, 0, TOTAL_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
public byte getBlockAt(int x, int y, int z) {
|
public byte getBlockAt(int x, int y, int z) {
|
||||||
if (x < 0 || x >= SIZE || y < 0 || y >= SIZE || z < 0 || z >= SIZE) return 0;
|
if (x < 0 || x >= SIZE || y < 0 || y >= SIZE || z < 0 || z >= SIZE) return 0;
|
||||||
|
@ -50,14 +67,8 @@ public class Chunk {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Chunk of(byte value) {
|
public static Chunk random(Vector3i position, Random rand) {
|
||||||
Chunk c = new Chunk();
|
Chunk c = new Chunk(position);
|
||||||
Arrays.fill(c.blocks, value);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chunk random(Random rand) {
|
|
||||||
Chunk c = new Chunk();
|
|
||||||
for (int i = 0; i < TOTAL_SIZE; i++) {
|
for (int i = 0; i < TOTAL_SIZE; i++) {
|
||||||
c.blocks[i] = (byte) rand.nextInt(1, 128);
|
c.blocks[i] = (byte) rand.nextInt(1, 128);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue