Added ShaderProgram and ChunkRenderer
This commit is contained in:
parent
dfbcf44725
commit
297fcb5a1e
|
@ -1,11 +1,7 @@
|
|||
package nl.andrewl.aos2_client;
|
||||
|
||||
import nl.andrewl.aos_core.FileUtils;
|
||||
import nl.andrewl.aos_core.model.Chunk;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.joml.Vector3i;
|
||||
import org.lwjgl.Version;
|
||||
import org.lwjgl.glfw.Callbacks;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
|
@ -13,14 +9,59 @@ import org.lwjgl.opengl.GL;
|
|||
import org.lwjgl.opengl.GLUtil;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL46.*;
|
||||
|
||||
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());
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW");
|
||||
|
@ -53,77 +94,6 @@ public class Aos2Client {
|
|||
glEnable(GL_DEPTH_TEST);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
Chunk chunk = Chunk.random(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);
|
||||
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;
|
||||
return windowHandle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,4 +156,10 @@ public class ChunkMesh {
|
|||
|
||||
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 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.
|
||||
|
||||
fragmentColor = vec4((ambientComponent + diffuseComponent) * vertexColor, 1.0);
|
|
@ -6,6 +6,9 @@ import org.joml.Vector3i;
|
|||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Holds information about a uniform "chunk" of the voxel world.
|
||||
*/
|
||||
public class Chunk {
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
|
||||
public static Chunk of(byte value) {
|
||||
Chunk c = new Chunk();
|
||||
Arrays.fill(c.blocks, value);
|
||||
return c;
|
||||
}
|
||||
|
||||
public static Chunk random(Random rand) {
|
||||
Chunk c = new Chunk();
|
||||
public static Chunk random(Vector3i position, Random rand) {
|
||||
Chunk c = new Chunk(position);
|
||||
for (int i = 0; i < TOTAL_SIZE; i++) {
|
||||
c.blocks[i] = (byte) rand.nextInt(1, 128);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue