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