diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e673575
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.idea/
+target/
\ No newline at end of file
diff --git a/client/pom.xml b/client/pom.xml
new file mode 100644
index 0000000..9201018
--- /dev/null
+++ b/client/pom.xml
@@ -0,0 +1,215 @@
+
+
+
+ ace-of-shades-2
+ nl.andrewl
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ aos2-client
+
+
+ 17
+ 17
+ 3.3.1
+
+
+
+
+ lwjgl-natives-linux-amd64
+
+
+ unix
+ amd64
+
+
+
+ natives-linux
+
+
+
+ lwjgl-natives-linux-aarch64
+
+
+ unix
+ aarch64
+
+
+
+ natives-linux-arm64
+
+
+
+ lwjgl-natives-linux-arm
+
+
+ unix
+ arm
+
+
+
+ natives-linux-arm32
+
+
+
+ lwjgl-natives-linux-arm32
+
+
+ unix
+ arm32
+
+
+
+ natives-linux-arm32
+
+
+
+ lwjgl-natives-macos-x86_64
+
+
+ mac
+ x86_64
+
+
+
+ natives-macos
+
+
+
+ lwjgl-natives-macos-aarch64
+
+
+ mac
+ aarch64
+
+
+
+ natives-macos-arm64
+
+
+
+ lwjgl-natives-windows-amd64
+
+
+ windows
+ amd64
+
+
+
+ natives-windows
+
+
+
+ lwjgl-natives-windows-x86
+
+
+ windows
+ x86
+
+
+
+ natives-windows-x86
+
+
+
+ lwjgl-natives-windows-aarch64
+
+
+ windows
+ aarch64
+
+
+
+ natives-windows-arm64
+
+
+
+
+
+
+
+ org.lwjgl
+ lwjgl-bom
+ ${lwjgl.version}
+ import
+ pom
+
+
+
+
+
+
+ nl.andrewl
+ aos2-core
+ ${parent.version}
+
+
+
+ org.lwjgl
+ lwjgl
+
+
+ org.lwjgl
+ lwjgl-assimp
+
+
+ org.lwjgl
+ lwjgl-glfw
+
+
+ org.lwjgl
+ lwjgl-meshoptimizer
+
+
+ org.lwjgl
+ lwjgl-openal
+
+
+ org.lwjgl
+ lwjgl-opengl
+
+
+ org.lwjgl
+ lwjgl-stb
+
+
+ org.lwjgl
+ lwjgl
+ ${lwjgl.natives}
+
+
+ org.lwjgl
+ lwjgl-assimp
+ ${lwjgl.natives}
+
+
+ org.lwjgl
+ lwjgl-glfw
+ ${lwjgl.natives}
+
+
+ org.lwjgl
+ lwjgl-meshoptimizer
+ ${lwjgl.natives}
+
+
+ org.lwjgl
+ lwjgl-openal
+ ${lwjgl.natives}
+
+
+ org.lwjgl
+ lwjgl-opengl
+ ${lwjgl.natives}
+
+
+ org.lwjgl
+ lwjgl-stb
+ ${lwjgl.natives}
+
+
+
+
\ No newline at end of file
diff --git a/client/src/main/java/nl/andrewl/aos2_client/Aos2Client.java b/client/src/main/java/nl/andrewl/aos2_client/Aos2Client.java
new file mode 100644
index 0000000..73726c1
--- /dev/null
+++ b/client/src/main/java/nl/andrewl/aos2_client/Aos2Client.java
@@ -0,0 +1,99 @@
+package nl.andrewl.aos2_client;
+
+import nl.andrewl.aos_core.FileUtils;
+import nl.andrewl.aos_core.model.Chunk;
+import org.joml.Matrix4f;
+import org.joml.Vector3f;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.Version;
+import org.lwjgl.glfw.Callbacks;
+import org.lwjgl.glfw.GLFWErrorCallback;
+import org.lwjgl.opengl.GL;
+import org.lwjgl.opengl.GLUtil;
+import org.lwjgl.system.MemoryUtil;
+
+import java.io.IOException;
+
+import static org.lwjgl.glfw.GLFW.*;
+import static org.lwjgl.opengl.GL46.*;
+
+public class Aos2Client {
+ public static void main(String[] args) throws IOException {
+ System.out.println("LWJGL Version: " + Version.getVersion());
+ GLFWErrorCallback.createPrint(System.err).set();
+ if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW");
+ glfwDefaultWindowHints();
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+
+ long windowHandle = glfwCreateWindow(800, 600, "Ace of Shades 2", MemoryUtil.NULL, MemoryUtil.NULL);
+ if (windowHandle == MemoryUtil.NULL) throw new RuntimeException("Failed to create GLFW window.");
+ glfwSetKeyCallback(windowHandle, (window, key, scancode, action, mods) -> {
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
+ glfwSetWindowShouldClose(windowHandle, true);
+ }
+ });
+
+ glfwSetInputMode(windowHandle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ glfwSetInputMode(windowHandle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
+
+ glfwSetWindowPos(windowHandle, 50, 50);
+ glfwSetCursorPos(windowHandle, 0, 0);
+
+ glfwMakeContextCurrent(windowHandle);
+ glfwSwapInterval(1);
+ glfwShowWindow(windowHandle);
+
+ GL.createCapabilities();
+ GLUtil.setupDebugMessageCallback(System.out);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+ glCullFace(GL_BACK);
+
+ Chunk chunk = Chunk.of((byte) 64);
+ Matrix4f projectionTransform = new Matrix4f().perspective(70, 800 / 600.0f, 0.01f, 100.0f);
+ Matrix4f viewTransform = new Matrix4f()
+ .lookAt(new Vector3f(-5, 50, -10), new Vector3f(8, 0, 8), new Vector3f(0, 1, 0));
+ ChunkMesh mesh = new ChunkMesh(chunk);
+
+ int shaderProgram = createShaderProgram();
+ int projectionTransformUniform = glGetUniformLocation(shaderProgram, "projectionTransform");
+ int viewTransformUniform = glGetUniformLocation(shaderProgram, "viewTransform");
+
+ glUniformMatrix4fv(projectionTransformUniform, false, projectionTransform.get(new float[16]));
+
+ while (!glfwWindowShouldClose(windowHandle)) {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glUniformMatrix4fv(viewTransformUniform, false, viewTransform.get(new float[16]));
+
+ mesh.draw();
+
+ glfwSwapBuffers(windowHandle);
+ glfwPollEvents();
+ }
+
+ 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;
+ }
+}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/ChunkMesh.java b/client/src/main/java/nl/andrewl/aos2_client/ChunkMesh.java
new file mode 100644
index 0000000..3b089df
--- /dev/null
+++ b/client/src/main/java/nl/andrewl/aos2_client/ChunkMesh.java
@@ -0,0 +1,58 @@
+package nl.andrewl.aos2_client;
+
+import nl.andrewl.aos_core.model.Chunk;
+import org.lwjgl.BufferUtils;
+
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+import static org.lwjgl.opengl.GL46.*;
+
+public class ChunkMesh {
+ private final int vboId;
+ private final int vaoId;
+ private final int eboId;
+
+ private final int indiciesCount;
+
+ public ChunkMesh(Chunk chunk) {
+ this.vboId = glGenBuffers();
+ this.vaoId = glGenVertexArrays();
+ this.eboId = glGenBuffers();
+
+ var meshData = chunk.generateMesh();
+ System.out.println(meshData.first().size());
+ FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(3 * meshData.first().size());
+ for (var vertex : meshData.first()) {
+ vertexBuffer.put(vertex.x);
+ vertexBuffer.put(vertex.y);
+ vertexBuffer.put(vertex.z);
+ }
+ vertexBuffer.flip();
+ IntBuffer indexBuffer = BufferUtils.createIntBuffer(meshData.second().size());
+ for (var index : meshData.second()) {
+ indexBuffer.put(index);
+ }
+ indexBuffer.flip();
+ this.indiciesCount = meshData.second().size();
+
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+ glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBuffer, GL_STATIC_DRAW);
+
+ glBindVertexArray(vaoId);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, 3 * Float.BYTES, 0);
+ }
+
+ public void draw() {
+ // Bind elements.
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
+ glBindVertexArray(vaoId);
+
+ glDrawElements(GL_TRIANGLES, indiciesCount, GL_UNSIGNED_INT, 0);
+ }
+}
diff --git a/client/src/main/resources/shader/fragment.glsl b/client/src/main/resources/shader/fragment.glsl
new file mode 100644
index 0000000..c4577df
--- /dev/null
+++ b/client/src/main/resources/shader/fragment.glsl
@@ -0,0 +1,10 @@
+#version 460 core
+
+in vec3 vertexPosition;
+in vec3 vertexColor;
+
+out vec4 fragmentColor;
+
+void main() {
+ fragmentColor = vec4(vertexColor, 1.0);
+}
\ No newline at end of file
diff --git a/client/src/main/resources/shader/vertex.glsl b/client/src/main/resources/shader/vertex.glsl
new file mode 100644
index 0000000..735f874
--- /dev/null
+++ b/client/src/main/resources/shader/vertex.glsl
@@ -0,0 +1,15 @@
+#version 460 core
+
+layout (location = 0) in vec3 vertexPositionIn;
+
+uniform mat4 projectionTransform;
+uniform mat4 viewTransform;
+
+out vec3 vertexPosition;
+out vec3 vertexColor;
+
+void main() {
+ gl_Position = projectionTransform * viewTransform * vec4(vertexPositionIn, 1.0);
+ vertexPosition = vertexPositionIn;
+ vertexColor = vec3(1.0, 0.5, 0.5);
+}
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..9d8f294
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ ace-of-shades-2
+ nl.andrewl
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ aos2-core
+
+
+ 17
+ 17
+
+
+
+
+
+ org.joml
+ joml
+ 1.10.4
+
+
+
\ No newline at end of file
diff --git a/core/src/main/java/nl/andrewl/aos_core/FileUtils.java b/core/src/main/java/nl/andrewl/aos_core/FileUtils.java
new file mode 100644
index 0000000..3dac464
--- /dev/null
+++ b/core/src/main/java/nl/andrewl/aos_core/FileUtils.java
@@ -0,0 +1,15 @@
+package nl.andrewl.aos_core;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public final class FileUtils {
+ private FileUtils() {}
+
+ public static String readClasspathFile(String resource) throws IOException {
+ try (InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resource)) {
+ if (in == null) throw new IOException("Could not load classpath resource: " + resource);
+ return new String(in.readAllBytes());
+ }
+ }
+}
diff --git a/core/src/main/java/nl/andrewl/aos_core/Pair.java b/core/src/main/java/nl/andrewl/aos_core/Pair.java
new file mode 100644
index 0000000..64ad365
--- /dev/null
+++ b/core/src/main/java/nl/andrewl/aos_core/Pair.java
@@ -0,0 +1,3 @@
+package nl.andrewl.aos_core;
+
+public record Pair(A first, B second) {}
diff --git a/core/src/main/java/nl/andrewl/aos_core/Triple.java b/core/src/main/java/nl/andrewl/aos_core/Triple.java
new file mode 100644
index 0000000..5863c25
--- /dev/null
+++ b/core/src/main/java/nl/andrewl/aos_core/Triple.java
@@ -0,0 +1,3 @@
+package nl.andrewl.aos_core;
+
+public record Triple(A first, B second, C third) {}
diff --git a/core/src/main/java/nl/andrewl/aos_core/model/Chunk.java b/core/src/main/java/nl/andrewl/aos_core/model/Chunk.java
new file mode 100644
index 0000000..b670fb4
--- /dev/null
+++ b/core/src/main/java/nl/andrewl/aos_core/model/Chunk.java
@@ -0,0 +1,97 @@
+package nl.andrewl.aos_core.model;
+
+import nl.andrewl.aos_core.Pair;
+import org.joml.Vector3f;
+import org.joml.Vector3i;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Chunk {
+ /**
+ * The size of a chunk, in terms of the number of blocks on one axis of the cube.
+ */
+ public static final byte SIZE = 16;
+
+ private final byte[] blocks = new byte[SIZE * SIZE * SIZE];
+
+ public byte getBlockAt(int x, int y, int z) {
+ int idx = x * SIZE * SIZE + y * SIZE + z;
+ if (idx < 0 || idx >= SIZE * SIZE * SIZE) return 0;
+ return blocks[x * SIZE * SIZE + y * SIZE + z];
+ }
+
+ public byte getBlockAt(Vector3i localPosition) {
+ return getBlockAt(localPosition.x, localPosition.y, localPosition.z);
+ }
+
+ public byte[] getBlocks() {
+ return blocks;
+ }
+
+ public Pair, List> generateMesh() {
+ List vertexList = new ArrayList<>();
+ List indexList = new ArrayList<>();
+ int elementIdx = 0;
+ for (int x = 0; x < SIZE; x++) {
+ for (int y = 0; y < SIZE; y++) {
+ for (int z = 0; z < SIZE; z++) {
+ byte block = getBlockAt(x, y, z);
+ if (block == 0) continue;
+ // Top
+ if (getBlockAt(x, y + 1, z) == 0) {
+ vertexList.add(new Vector3f(x + 1, y, z)); // 0
+ vertexList.add(new Vector3f(x, y, z)); // 1
+ vertexList.add(new Vector3f(x, y, z + 1)); // 2
+ vertexList.add(new Vector3f(x + 1, y, z + 1));// 3
+
+ indexList.add(elementIdx);
+ indexList.add(elementIdx + 1);
+ indexList.add(elementIdx + 2);
+
+ indexList.add(elementIdx + 2);
+ indexList.add(elementIdx + 3);
+ indexList.add(elementIdx);
+
+ elementIdx += 4;
+ }
+ // Bottom
+ if (getBlockAt(x, y - 1, z) == 0) {
+ vertexList.add(new Vector3f(x + 1, y - 1, z)); // 0
+ vertexList.add(new Vector3f(x, y - 1, z)); // 1
+ vertexList.add(new Vector3f(x, y - 1, z + 1)); // 2
+ vertexList.add(new Vector3f(x + 1, y - 1, z + 1));// 3
+
+ indexList.add(elementIdx);
+ indexList.add(elementIdx + 2);
+ indexList.add(elementIdx + 1);
+
+ indexList.add(elementIdx);
+ indexList.add(elementIdx + 3);
+ indexList.add(elementIdx + 2);
+
+ elementIdx += 4;
+ }
+ // Positive z
+ // Negative z
+ // Positive x
+ // Negative x
+ }
+ }
+ }
+
+ return new Pair<>(vertexList, indexList);
+ }
+
+ public static Chunk of(byte value) {
+ Chunk c = new Chunk();
+ Arrays.fill(c.blocks, value);
+ return c;
+ }
+
+ public static Vector3f getColor(byte blockValue) {
+ float v = blockValue / 128.0f;
+ return new Vector3f(v);
+ }
+}
diff --git a/core/src/main/java/nl/andrewl/aos_core/model/World.java b/core/src/main/java/nl/andrewl/aos_core/model/World.java
new file mode 100644
index 0000000..632a2a0
--- /dev/null
+++ b/core/src/main/java/nl/andrewl/aos_core/model/World.java
@@ -0,0 +1,27 @@
+package nl.andrewl.aos_core.model;
+
+import org.joml.Vector3i;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class World {
+ Map chunkMap = new HashMap<>();
+
+ public byte getBlockAt(int x, int y, int z) {
+ int chunkX = x / Chunk.SIZE;
+ int localX = x % Chunk.SIZE;
+ int chunkY = y / Chunk.SIZE;
+ int localY = y % Chunk.SIZE;
+ int chunkZ = z / Chunk.SIZE;
+ int localZ = z % Chunk.SIZE;
+ Vector3i chunkPos = new Vector3i(chunkX, chunkY, chunkZ);
+ Chunk chunk = chunkMap.get(chunkPos);
+ if (chunk == null) return 0;
+ return chunk.getBlockAt(localX, localY, localZ);
+ }
+
+ public Chunk getChunkAt(Vector3i chunkPos) {
+ return chunkMap.get(chunkPos);
+ }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..324b911
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ nl.andrewl
+ ace-of-shades-2
+ pom
+ 1.0-SNAPSHOT
+
+ core
+ server
+ client
+
+
+
+ 17
+ 17
+
+
+
\ No newline at end of file
diff --git a/server/pom.xml b/server/pom.xml
new file mode 100644
index 0000000..288e8a8
--- /dev/null
+++ b/server/pom.xml
@@ -0,0 +1,26 @@
+
+
+
+ ace-of-shades-2
+ nl.andrewl
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ aos2-server
+
+
+ 17
+ 17
+
+
+
+
+ nl.andrewl
+ aos2-core
+ ${parent.version}
+
+
+
\ No newline at end of file