diff --git a/client/pom.xml b/client/pom.xml
index 6912333..6fc7531 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -146,6 +146,11 @@
aos2-core
${parent.version}
+
+ de.javagl
+ obj
+ 0.3.0
+
org.lwjgl
diff --git a/client/src/main/java/nl/andrewl/aos2_client/Client.java b/client/src/main/java/nl/andrewl/aos2_client/Client.java
index 57b06cf..3815a84 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/Client.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/Client.java
@@ -5,16 +5,11 @@ import nl.andrewl.aos2_client.control.PlayerInputKeyCallback;
import nl.andrewl.aos2_client.control.PlayerInputMouseClickCallback;
import nl.andrewl.aos2_client.control.PlayerViewCursorCallback;
import nl.andrewl.aos2_client.render.GameRenderer;
-import nl.andrewl.aos_core.model.Chunk;
import nl.andrewl.aos_core.model.ColorPalette;
-import nl.andrewl.aos_core.model.World;
-import nl.andrewl.aos_core.net.ChunkDataMessage;
-import nl.andrewl.aos_core.net.ChunkHashMessage;
-import nl.andrewl.aos_core.net.WorldInfoMessage;
+import nl.andrewl.aos_core.net.*;
import nl.andrewl.aos_core.net.udp.ChunkUpdateMessage;
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
import nl.andrewl.record_net.Message;
-import org.joml.Vector3i;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,14 +55,18 @@ public class Client implements Runnable {
gameRenderer.setupWindow(
new PlayerViewCursorCallback(gameRenderer.getCamera(), communicationHandler),
new PlayerInputKeyCallback(inputHandler),
- new PlayerInputMouseClickCallback(inputHandler)
+ new PlayerInputMouseClickCallback(inputHandler),
+ false,
+ false
);
long lastFrameAt = System.currentTimeMillis();
while (!gameRenderer.windowShouldClose()) {
long now = System.currentTimeMillis();
float dt = (now - lastFrameAt) / 1000f;
+ world.processQueuedChunkUpdates();
gameRenderer.getCamera().interpolatePosition(dt);
+ world.interpolatePlayers(dt);
gameRenderer.draw();
lastFrameAt = now;
}
@@ -75,31 +74,17 @@ public class Client implements Runnable {
communicationHandler.shutdown();
}
- public int getClientId() {
- return clientId;
- }
-
- public World getWorld() {
- return world;
- }
-
public void onMessageReceived(Message msg) {
if (msg instanceof WorldInfoMessage worldInfo) {
world.setPalette(ColorPalette.fromArray(worldInfo.palette()));
}
if (msg instanceof ChunkDataMessage chunkDataMessage) {
- Chunk chunk = chunkDataMessage.toChunk();
- world.addChunk(chunk);
- gameRenderer.getChunkRenderer().queueChunkMesh(chunk);
+ world.addChunk(chunkDataMessage);
}
if (msg instanceof ChunkUpdateMessage u) {
- Vector3i chunkPos = new Vector3i(u.cx(), u.cy(), u.cz());
- Chunk chunk = world.getChunkAt(chunkPos);
- System.out.println(u);
- if (chunk != null) {
- chunk.setBlockAt(u.lx(), u.ly(), u.lz(), u.newBlock());
- gameRenderer.getChunkRenderer().queueChunkMesh(chunk);
- } else {
+ world.updateChunk(u);
+ // If we received an update for a chunk we don't have, request it!
+ if (world.getChunkAt(u.getChunkPos()) == null) {
communicationHandler.sendMessage(new ChunkHashMessage(u.cx(), u.cy(), u.cz(), -1));
}
}
@@ -108,9 +93,16 @@ public class Client implements Runnable {
float eyeHeight = playerUpdate.crouching() ? 1.3f : 1.7f;
gameRenderer.getCamera().setPosition(playerUpdate.px(), playerUpdate.py() + eyeHeight, playerUpdate.pz());
gameRenderer.getCamera().setVelocity(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz());
- // TODO: Unload far away chunks and request close chunks we don't have.
+ } else {
+ world.playerUpdated(playerUpdate);
}
}
+ if (msg instanceof PlayerJoinMessage joinMessage) {
+ world.playerJoined(joinMessage);
+ }
+ if (msg instanceof PlayerLeaveMessage leaveMessage) {
+ world.playerLeft(leaveMessage);
+ }
}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/ClientWorld.java b/client/src/main/java/nl/andrewl/aos2_client/ClientWorld.java
index b434ada..b163577 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/ClientWorld.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/ClientWorld.java
@@ -1,7 +1,138 @@
package nl.andrewl.aos2_client;
+import nl.andrewl.aos2_client.render.chunk.ChunkMesh;
+import nl.andrewl.aos2_client.render.chunk.ChunkMeshGenerator;
+import nl.andrewl.aos_core.model.Chunk;
+import nl.andrewl.aos_core.model.Player;
import nl.andrewl.aos_core.model.World;
+import nl.andrewl.aos_core.net.ChunkDataMessage;
+import nl.andrewl.aos_core.net.PlayerJoinMessage;
+import nl.andrewl.aos_core.net.PlayerLeaveMessage;
+import nl.andrewl.aos_core.net.udp.ChunkUpdateMessage;
+import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
+import org.joml.Vector3f;
+import org.joml.Vector3i;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * A client-side extension of the world model, with information that is only
+ * important for the client to know about, like other players and chunk render
+ * queues.
+ */
public class ClientWorld extends World {
+ private final Queue chunkUpdateQueue = new ConcurrentLinkedQueue<>();
+ private final Queue chunkRemovalQueue = new ConcurrentLinkedQueue<>();
+ private final ChunkMeshGenerator chunkMeshGenerator = new ChunkMeshGenerator();
+ private final Map chunkMeshes = new ConcurrentHashMap<>();
+ private final Map players = new HashMap<>();
+
+ public void playerJoined(PlayerJoinMessage joinMessage) {
+ Player p = joinMessage.toPlayer();
+ players.put(p.getId(), p);
+ }
+
+ public void playerLeft(PlayerLeaveMessage leaveMessage) {
+ players.remove(leaveMessage.id());
+ }
+
+ public void playerUpdated(PlayerUpdateMessage playerUpdate) {
+ Player p = players.get(playerUpdate.clientId());
+ if (p != null) {
+ playerUpdate.apply(p);
+ }
+ }
+
+ public Collection getPlayers() {
+ return players.values();
+ }
+
+ public void interpolatePlayers(float dt) {
+ Vector3f movement = new Vector3f();
+ for (var player : getPlayers()) {
+ movement.set(player.getVelocity()).mul(dt);
+ player.getPosition().add(movement);
+ }
+ }
+
+ @Override
+ public void addChunk(Chunk chunk) {
+ super.addChunk(chunk);
+ chunkUpdateQueue.add(chunk);
+ }
+
+ public void addChunk(ChunkDataMessage msg) {
+ addChunk(msg.toChunk());
+ }
+
+ @Override
+ public void removeChunk(Vector3i chunkPos) {
+ Chunk chunk = getChunkAt(chunkPos);
+ if (chunk != null) {
+ chunkRemovalQueue.add(chunk);
+ chunkUpdateQueue.remove(chunk);
+ }
+ super.removeChunk(chunkPos);
+ }
+
+ public void updateChunk(ChunkUpdateMessage update) {
+ Chunk chunk = getChunkAt(update.getChunkPos());
+ if (chunk != null) {
+ chunk.setBlockAt(update.lx(), update.ly(), update.lz(), update.newBlock());
+ List chunksToReRender = new ArrayList<>(7);
+ chunksToReRender.add(chunk);
+ // Check if neighboring chunks need to be re-rendered too.
+ if (update.lx() == 0) {
+ Chunk c = getChunkAt(update.cx() - 1, update.cy(), update.cz());
+ if (c != null) chunksToReRender.add(c);
+ }
+ if (update.ly() == 0) {
+ Chunk c = getChunkAt(update.cx(), update.cy() - 1, update.cz());
+ if (c != null) chunksToReRender.add(c);
+ }
+ if (update.lz() == 0) {
+ Chunk c = getChunkAt(update.cx(), update.cy(), update.cz() - 1);
+ if (c != null) chunksToReRender.add(c);
+ }
+ if (update.lx() == Chunk.SIZE - 1) {
+ Chunk c = getChunkAt(update.cx() + 1, update.cy(), update.cz());
+ if (c != null) chunksToReRender.add(c);
+ }
+ if (update.ly() == Chunk.SIZE - 1) {
+ Chunk c = getChunkAt(update.cx(), update.cy() + 1, update.cz());
+ if (c != null) chunksToReRender.add(c);
+ }
+ if (update.lz() == Chunk.SIZE - 1) {
+ Chunk c = getChunkAt(update.cx(), update.cy(), update.cz() + 1);
+ if (c != null) chunksToReRender.add(c);
+ }
+ chunkUpdateQueue.addAll(chunksToReRender);
+ }
+ }
+
+ /**
+ * Call this to process any queued chunk updates, and update chunk meshes.
+ * Only call this method on the main OpenGL context thread!
+ */
+ public void processQueuedChunkUpdates() {
+ while (!chunkRemovalQueue.isEmpty()) {
+ Chunk chunk = chunkRemovalQueue.remove();
+ ChunkMesh mesh = chunkMeshes.remove(chunk);
+ if (mesh != null) mesh.free();
+ }
+ while (!chunkUpdateQueue.isEmpty()) {
+ Chunk chunk = chunkUpdateQueue.remove();
+ ChunkMesh mesh = new ChunkMesh(chunk, this, chunkMeshGenerator);
+ ChunkMesh existingMesh = chunkMeshes.get(chunk);
+ if (existingMesh != null) existingMesh.free();
+ chunkMeshes.put(chunk, mesh);
+ }
+ }
+
+ public Collection getChunkMeshesToDraw() {
+ return chunkMeshes.values();
+ }
}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java
index b4c63fe..7f8a4f6 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java
@@ -14,7 +14,6 @@ public class PlayerInputMouseClickCallback implements GLFWMouseButtonCallbackI {
@Override
public void invoke(long window, int button, int action, int mods) {
- System.out.println("Click: " + button);
inputHandler.updateInputState(window);
}
}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java
index 58d5924..a66f5b1 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java
@@ -1,10 +1,11 @@
package nl.andrewl.aos2_client.render;
import nl.andrewl.aos2_client.Camera;
+import nl.andrewl.aos2_client.ClientWorld;
import nl.andrewl.aos2_client.render.chunk.ChunkRenderer;
import nl.andrewl.aos2_client.render.gui.GUIRenderer;
import nl.andrewl.aos2_client.render.gui.GUITexture;
-import nl.andrewl.aos_core.model.World;
+import nl.andrewl.aos2_client.render.model.Model;
import org.joml.Matrix4f;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.GL;
@@ -27,30 +28,33 @@ public class GameRenderer {
private static final float Z_NEAR = 0.01f;
private static final float Z_FAR = 500f;
- private final ChunkRenderer chunkRenderer;
- private final GUIRenderer guiRenderer;
+ private ChunkRenderer chunkRenderer;
+ private GUIRenderer guiRenderer;
+ private ModelRenderer modelRenderer;
private final Camera camera;
- private final World world;
+ private final ClientWorld world;
+ private Model playerModel; // Standard player model used to render all players.
private long windowHandle;
- private GLFWVidMode primaryMonitorSettings;
- private boolean fullscreen;
private int screenWidth = 800;
private int screenHeight = 600;
private float fov = 90f;
private final Matrix4f perspectiveTransform;
- public GameRenderer(World world) {
+ public GameRenderer(ClientWorld world) {
this.world = world;
- this.chunkRenderer = new ChunkRenderer();
- this.guiRenderer = new GUIRenderer();
this.camera = new Camera();
this.perspectiveTransform = new Matrix4f();
-
}
- public void setupWindow(GLFWCursorPosCallbackI viewCursorCallback, GLFWKeyCallbackI inputKeyCallback, GLFWMouseButtonCallbackI mouseButtonCallback) {
+ public void setupWindow(
+ GLFWCursorPosCallbackI viewCursorCallback,
+ GLFWKeyCallbackI inputKeyCallback,
+ GLFWMouseButtonCallbackI mouseButtonCallback,
+ boolean fullscreen,
+ boolean grabCursor
+ ) {
GLFWErrorCallback.createPrint(System.err).set();
if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW.");
glfwDefaultWindowHints();
@@ -58,21 +62,28 @@ public class GameRenderer {
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
long monitorId = glfwGetPrimaryMonitor();
- primaryMonitorSettings = glfwGetVideoMode(monitorId);
+ GLFWVidMode primaryMonitorSettings = glfwGetVideoMode(monitorId);
if (primaryMonitorSettings == null) throw new IllegalStateException("Could not get information about the primary monitory.");
log.debug("Primary monitor settings: Width: {}, Height: {}", primaryMonitorSettings.width(), primaryMonitorSettings.height());
- screenWidth = primaryMonitorSettings.width();
- screenHeight = primaryMonitorSettings.height();
- windowHandle = glfwCreateWindow(screenWidth, screenHeight, "Ace of Shades 2", monitorId, 0);
+ if (fullscreen) {
+ screenWidth = primaryMonitorSettings.width();
+ screenHeight = primaryMonitorSettings.height();
+ windowHandle = glfwCreateWindow(screenWidth, screenHeight, "Ace of Shades 2", monitorId, 0);
+ } else {
+ screenWidth = 1000;
+ screenHeight = 800;
+ windowHandle = glfwCreateWindow(screenWidth, screenHeight, "Ace of Shades 2", 0, 0);
+ }
if (windowHandle == 0) throw new RuntimeException("Failed to create GLFW window.");
- fullscreen = true;
log.debug("Initialized GLFW window.");
// Setup callbacks.
glfwSetKeyCallback(windowHandle, inputKeyCallback);
glfwSetCursorPosCallback(windowHandle, viewCursorCallback);
glfwSetMouseButtonCallback(windowHandle, mouseButtonCallback);
- glfwSetInputMode(windowHandle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ if (grabCursor) {
+ glfwSetInputMode(windowHandle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ }
glfwSetInputMode(windowHandle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
glfwSetCursorPos(windowHandle, 0, 0);
log.debug("Set up window callbacks.");
@@ -80,7 +91,6 @@ public class GameRenderer {
glfwMakeContextCurrent(windowHandle);
glfwSwapInterval(1);
glfwShowWindow(windowHandle);
- log.debug("Made window visible.");
GL.createCapabilities();
// GLUtil.setupDebugMessageCallback(System.out);
@@ -90,9 +100,9 @@ public class GameRenderer {
glCullFace(GL_BACK);
log.debug("Initialized OpenGL context.");
- chunkRenderer.setupShaderProgram();
+ this.chunkRenderer = new ChunkRenderer();
log.debug("Initialized chunk renderer.");
- guiRenderer.setup();
+ this.guiRenderer = new GUIRenderer();
// TODO: More organized way to load textures for GUI.
try {
var crosshairTexture = new GUITexture("gui/crosshair.png");
@@ -103,34 +113,13 @@ public class GameRenderer {
throw new RuntimeException(e);
}
log.debug("Initialized GUI renderer.");
- updatePerspective();
- }
-
- public void setFullscreen(boolean fullscreen) {
- if (windowHandle == 0) throw new IllegalStateException("Window not setup.");
- long monitor = glfwGetPrimaryMonitor();
- if (fullscreen) {
- log.debug("Changing to fullscreen: {} x {}", primaryMonitorSettings.width(), primaryMonitorSettings.height());
- glfwSetWindowMonitor(windowHandle, monitor, 0, 0, primaryMonitorSettings.width(), primaryMonitorSettings.height(), primaryMonitorSettings.refreshRate());
- screenWidth = primaryMonitorSettings.width();
- screenHeight = primaryMonitorSettings.height();
- updatePerspective();
- } else {
- log.debug("Changing to windowed mode.");
- screenWidth = 800;
- screenHeight = 600;
- int left = primaryMonitorSettings.width() / 2;
- int top = primaryMonitorSettings.height() / 2;
- glfwSetWindowMonitor(windowHandle, 0, left, top, screenWidth, screenHeight, primaryMonitorSettings.refreshRate());
- updatePerspective();
+ this.modelRenderer = new ModelRenderer();
+ try {
+ playerModel = new Model("model/player_simple.obj", "model/simple_player.png");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
- this.fullscreen = fullscreen;
- }
-
- public void setSize(int width, int height) {
- glfwSetWindowSize(windowHandle, width, height);
- this.screenWidth = width;
- this.screenHeight = height;
+ log.debug("Initialized model renderer.");
updatePerspective();
}
@@ -144,12 +133,18 @@ public class GameRenderer {
}
/**
- * Updates the rendering perspective used to render the game. Note: only
- * call this after calling {@link ChunkRenderer#setupShaderProgram()}.
+ * Updates the rendering perspective used to render the game.
*/
private void updatePerspective() {
perspectiveTransform.setPerspective(fov, getAspectRatio(), Z_NEAR, Z_FAR);
- chunkRenderer.setPerspective(perspectiveTransform);
+ float[] data = new float[16];
+ perspectiveTransform.get(data);
+ if (chunkRenderer != null) chunkRenderer.setPerspective(data);
+ if (modelRenderer != null) modelRenderer.setPerspective(data);
+ }
+
+ public Matrix4f getPerspectiveTransform() {
+ return perspectiveTransform;
}
public boolean windowShouldClose() {
@@ -160,14 +155,20 @@ public class GameRenderer {
return camera;
}
- public ChunkRenderer getChunkRenderer() {
- return chunkRenderer;
- }
-
public void draw() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ chunkRenderer.draw(camera, world.getChunkMeshesToDraw());
+
+ // Draw players.
+ modelRenderer.setView(camera.getViewTransformData());
+ playerModel.bind();
+ Matrix4f playerModelTransform = new Matrix4f();
+ for (var player : world.getPlayers()) {
+ playerModelTransform.identity().translate(player.getPosition());
+ modelRenderer.render(playerModel, playerModelTransform);
+ }
+ playerModel.unbind();
- chunkRenderer.draw(camera, world);
guiRenderer.draw();
glfwSwapBuffers(windowHandle);
@@ -175,8 +176,10 @@ public class GameRenderer {
}
public void freeWindow() {
- guiRenderer.free();
- chunkRenderer.free();
+ if (playerModel != null) playerModel.free();
+ if (modelRenderer != null) modelRenderer.free();
+ if (guiRenderer != null) guiRenderer.free();
+ if (chunkRenderer != null) chunkRenderer.free();
GL.destroy();
Callbacks.glfwFreeCallbacks(windowHandle);
glfwSetErrorCallback(null);
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/Mesh.java b/client/src/main/java/nl/andrewl/aos2_client/render/Mesh.java
deleted file mode 100644
index e815948..0000000
--- a/client/src/main/java/nl/andrewl/aos2_client/render/Mesh.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package nl.andrewl.aos2_client.render;
-
-import org.joml.Matrix4f;
-
-import static org.lwjgl.opengl.GL46.*;
-
-public class Mesh {
- private final int vboId;
- private final int vaoId;
- private final int eboId;
- private int indexCount;
- private final Matrix4f transform = new Matrix4f();
- private final float[] transformData = new float[16];
-
- public Mesh(MeshData initialData) {
- this.vboId = glGenBuffers();
- this.eboId = glGenBuffers();
- this.vaoId = glGenVertexArrays();
- load(initialData);
- initVertexArrayAttributes();
- }
-
- public void load(MeshData data) {
- indexCount = data.indexBuffer().limit();
- glBindBuffer(GL_ARRAY_BUFFER, vboId);
- glBufferData(GL_ARRAY_BUFFER, data.vertexBuffer(), GL_STATIC_DRAW);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.indexBuffer(), GL_STATIC_DRAW);
- }
-
- public Matrix4f getTransform() {
- return transform;
- }
-
- public void updateTransform() {
- transform.set(transformData);
- }
-
- public float[] getTransformData() {
- return transformData;
- }
-
- /**
- * Initializes this mesh's vertex array attribute settings.
- */
- private void initVertexArrayAttributes() {
- glBindVertexArray(vaoId);
- // Vertex position floats.
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 3, GL_FLOAT, false, 9 * Float.BYTES, 0);
- // Vertex color floats.
- glEnableVertexAttribArray(1);
- glVertexAttribPointer(1, 3, GL_FLOAT, false, 9 * Float.BYTES, 3 * Float.BYTES);
- // Vertex normal floats.
- glEnableVertexAttribArray(2);
- glVertexAttribPointer(2, 3, GL_FLOAT, false, 9 * Float.BYTES, 6 * Float.BYTES);
- }
-
- public void draw() {
- glBindVertexArray(vaoId);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
- glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
- }
-
- public void free() {
- glDeleteBuffers(vboId);
- glDeleteBuffers(eboId);
- glDeleteVertexArrays(vaoId);
- }
-}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/MeshData.java b/client/src/main/java/nl/andrewl/aos2_client/render/MeshData.java
deleted file mode 100644
index fde20be..0000000
--- a/client/src/main/java/nl/andrewl/aos2_client/render/MeshData.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package nl.andrewl.aos2_client.render;
-
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-
-public record MeshData(FloatBuffer vertexBuffer, IntBuffer indexBuffer) {}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/ModelRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/ModelRenderer.java
new file mode 100644
index 0000000..3c26344
--- /dev/null
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/ModelRenderer.java
@@ -0,0 +1,53 @@
+package nl.andrewl.aos2_client.render;
+
+import nl.andrewl.aos2_client.Camera;
+import nl.andrewl.aos2_client.render.model.Model;
+import org.joml.Matrix4f;
+
+import static org.lwjgl.opengl.GL46.*;
+
+/**
+ * A renderer that handles rendering of textured models.
+ */
+public class ModelRenderer {
+ private final ShaderProgram shaderProgram;
+ private final int projectionUniform;
+ private final int viewUniform;
+ private final int modelUniform;
+ private final int textureSamplerUniform;
+
+ public ModelRenderer() {
+ shaderProgram = new ShaderProgram.Builder()
+ .withShader("shader/model/vertex.glsl", GL_VERTEX_SHADER)
+ .withShader("shader/model/fragment.glsl", GL_FRAGMENT_SHADER)
+ .build();
+ projectionUniform = shaderProgram.getUniform("projectionTransform");
+ viewUniform = shaderProgram.getUniform("viewTransform");
+ modelUniform = shaderProgram.getUniform("modelTransform");
+ textureSamplerUniform = shaderProgram.getUniform("textureSampler");
+ }
+
+ public void setPerspective(float[] data) {
+ shaderProgram.use();
+ glUniformMatrix4fv(projectionUniform, false, data);
+ shaderProgram.stopUsing();
+ }
+
+ public void setView(float[] data) {
+ shaderProgram.use();
+ glUniformMatrix4fv(viewUniform, false, data);
+ shaderProgram.stopUsing();
+ }
+
+ public void render(Model model, Matrix4f modelTransform) {
+ shaderProgram.use();
+ glUniformMatrix4fv(modelUniform, false, modelTransform.get(new float[16]));
+ glUniform1i(textureSamplerUniform, 0);
+ model.draw();
+ shaderProgram.stopUsing();
+ }
+
+ public void free() {
+ shaderProgram.free();
+ }
+}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/PlayerMeshGenerator.java b/client/src/main/java/nl/andrewl/aos2_client/render/PlayerMeshGenerator.java
deleted file mode 100644
index db831ca..0000000
--- a/client/src/main/java/nl/andrewl/aos2_client/render/PlayerMeshGenerator.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package nl.andrewl.aos2_client.render;
-
-import org.joml.Vector3f;
-import org.joml.Vector3i;
-import org.lwjgl.BufferUtils;
-
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-
-public class PlayerMeshGenerator {
- private final FloatBuffer vertexBuffer;
- private final IntBuffer indexBuffer;
-
- private final Vector3i pos = new Vector3i();
- private final Vector3f color = new Vector3f();
- private final Vector3f norm = new Vector3f();
-
- public PlayerMeshGenerator() {
- vertexBuffer = BufferUtils.createFloatBuffer(1000);
- indexBuffer = BufferUtils.createIntBuffer(100);
- }
-
-// public PlayerMesh generateMesh() {
-//
-// }
-}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/ShaderProgram.java b/client/src/main/java/nl/andrewl/aos2_client/render/ShaderProgram.java
index 7b1f30e..2557509 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/ShaderProgram.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/ShaderProgram.java
@@ -32,6 +32,10 @@ public class ShaderProgram {
glUseProgram(0);
}
+ public int getId() {
+ return id;
+ }
+
public int getUniform(String name) {
return glGetUniformLocation(id, name);
}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkMesh.java b/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkMesh.java
index 4192bc9..5363b32 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkMesh.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkMesh.java
@@ -49,17 +49,16 @@ public class ChunkMesh {
* Generates and loads this chunk's mesh into the allocated OpenGL buffers.
*/
private void loadMesh(ChunkMeshGenerator meshGenerator) {
- long start = System.nanoTime();
+// long start = System.nanoTime();
var meshData = meshGenerator.generateMesh(chunk, world);
- double dur = (System.nanoTime() - start) / 1_000_000.0;
+// double dur = (System.nanoTime() - start) / 1_000_000.0;
this.indexCount = meshData.indexBuffer().limit();
- // Print some debug information.
- log.debug(
- "Generated mesh for chunk ({}, {}, {}) in {} ms. {} vertices and {} indices.",
- chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z,
- dur,
- meshData.vertexBuffer().limit() / 9, indexCount
- );
+// log.debug(
+// "Generated mesh for chunk ({}, {}, {}) in {} ms. {} vertices and {} indices.",
+// chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z,
+// dur,
+// meshData.vertexBuffer().limit() / 9, indexCount
+// );
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, meshData.vertexBuffer(), GL_STATIC_DRAW);
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkRenderer.java
index db3b697..0b8184e 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkRenderer.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/chunk/ChunkRenderer.java
@@ -7,7 +7,10 @@ import nl.andrewl.aos_core.model.World;
import org.joml.Matrix4f;
import org.joml.Vector3i;
-import java.util.*;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import static org.lwjgl.opengl.GL46.*;
@@ -18,17 +21,12 @@ import static org.lwjgl.opengl.GL46.*;
* be rendered each frame.
*/
public class ChunkRenderer {
- private final ChunkMeshGenerator chunkMeshGenerator = new ChunkMeshGenerator();
- private final Queue meshGenerationQueue = new ConcurrentLinkedQueue<>();
+ private final ShaderProgram shaderProgram;
+ private final int projectionTransformUniform;
+ private final int viewTransformUniform;
+ private final int chunkPositionUniform;
- private ShaderProgram shaderProgram;
- private int projectionTransformUniform;
- private int viewTransformUniform;
- private int chunkPositionUniform;
-
- private final Map chunkMeshes = new HashMap<>();
-
- public void setupShaderProgram() {
+ public ChunkRenderer() {
this.shaderProgram = new ShaderProgram.Builder()
.withShader("shader/chunk/vertex.glsl", GL_VERTEX_SHADER)
.withShader("shader/chunk/fragment.glsl", GL_FRAGMENT_SHADER)
@@ -38,39 +36,28 @@ public class ChunkRenderer {
this.viewTransformUniform = shaderProgram.getUniform("viewTransform");
this.chunkPositionUniform = shaderProgram.getUniform("chunkPosition");
int chunkSizeUniform = shaderProgram.getUniform("chunkSize");
-
// Set constant uniforms that don't change during runtime.
glUniform1i(chunkSizeUniform, Chunk.SIZE);
+ shaderProgram.stopUsing();
}
- public void queueChunkMesh(Chunk chunk) {
- meshGenerationQueue.add(chunk);
+ public void setPerspective(float[] data) {
+ shaderProgram.use();
+ glUniformMatrix4fv(projectionTransformUniform, false, data);
+ shaderProgram.stopUsing();
}
- public void setPerspective(Matrix4f projectionTransform) {
- glUniformMatrix4fv(projectionTransformUniform, false, projectionTransform.get(new float[16]));
- }
-
- public void draw(Camera cam, World world) {
- while (!meshGenerationQueue.isEmpty()) {
- Chunk chunk = meshGenerationQueue.remove();
- ChunkMesh mesh = new ChunkMesh(chunk, world, chunkMeshGenerator);
- ChunkMesh existingMesh = chunkMeshes.get(chunk.getPosition());
- if (existingMesh != null) existingMesh.free();
- chunkMeshes.put(chunk.getPosition(), mesh);
- }
+ public void draw(Camera cam, Collection chunkMeshes) {
shaderProgram.use();
glUniformMatrix4fv(viewTransformUniform, false, cam.getViewTransformData());
- for (var mesh : chunkMeshes.values()) {
+ for (var mesh : chunkMeshes) {
glUniform3iv(chunkPositionUniform, mesh.getPositionData());
mesh.draw();
}
+ shaderProgram.stopUsing();
}
public void free() {
- for (var mesh : chunkMeshes.values()) mesh.free();
- chunkMeshes.clear();
- meshGenerationQueue.clear();
shaderProgram.free();
}
}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUIRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUIRenderer.java
index a263a4c..ba6dc99 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUIRenderer.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUIRenderer.java
@@ -14,20 +14,16 @@ import static org.lwjgl.opengl.GL46.*;
* Manages rendering of 2D GUI components like cross-hairs, inventory stuff, etc.
*/
public class GUIRenderer {
- private int vaoId;
- private int vboId;
- private int vertexCount;
- private ShaderProgram shaderProgram;
- private int transformUniformLocation;
+ private final int vaoId;
+ private final int vboId;
+ private final int vertexCount;
+ private final ShaderProgram shaderProgram;
+ private final int transformUniformLocation;
private final List guiTextures = new ArrayList<>();
- public void addTexture(GUITexture texture) {
- guiTextures.add(texture);
- }
-
- public void setup() {
+ public GUIRenderer() {
vaoId = glGenVertexArrays();
vboId = glGenBuffers();
FloatBuffer buffer = BufferUtils.createFloatBuffer(8);
@@ -53,6 +49,10 @@ public class GUIRenderer {
shaderProgram.bindAttribute(0, "position");
}
+ public void addTexture(GUITexture texture) {
+ guiTextures.add(texture);
+ }
+
public void draw() {
shaderProgram.use();
glBindVertexArray(vaoId);
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java
index 4cc345d..59d15ce 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java
@@ -26,7 +26,7 @@ public class GUITexture {
textureId = glGenTextures();
glBindTexture(GL_TEXTURE_2D, textureId);
- glPixelStorei(GL_UNPACK_ALIGNMENT, textureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
var buf = ImageUtils.decodePng(img);
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/model/Model.java b/client/src/main/java/nl/andrewl/aos2_client/render/model/Model.java
new file mode 100644
index 0000000..205b76a
--- /dev/null
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/model/Model.java
@@ -0,0 +1,123 @@
+package nl.andrewl.aos2_client.render.model;
+
+import de.javagl.obj.Obj;
+import de.javagl.obj.ObjData;
+import de.javagl.obj.ObjReader;
+import de.javagl.obj.ObjUtils;
+import nl.andrewl.aos_core.ImageUtils;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.lwjgl.opengl.GL46.*;
+
+/**
+ * Represents a 3D model with a texture, which can be used to render one or
+ * more entities.
+ */
+public class Model {
+ private final int vaoId;
+ private final List vboIds;
+ private final int eboId;
+ private final int indexCount;
+ private final int textureId;
+
+ public Model(String resource, String textureResource) throws IOException {
+ try (
+ var in = Model.class.getClassLoader().getResourceAsStream(resource);
+ var imageIn = Model.class.getClassLoader().getResourceAsStream(textureResource)
+ ) {
+ if (in == null) throw new IOException("Could not load resource: " + resource);
+ if (imageIn == null) throw new IOException("Could not load texture image: " + textureResource);
+ Obj obj = ObjReader.read(in);
+ obj = ObjUtils.convertToRenderable(obj);
+ IntBuffer indices = ObjData.getFaceVertexIndices(obj, 3);
+ FloatBuffer vertices = ObjData.getVertices(obj);
+ FloatBuffer texCoords = ObjData.getTexCoords(obj, 2, true);
+ FloatBuffer normals = ObjData.getNormals(obj);
+ indexCount = indices.limit();
+
+ vboIds = new ArrayList<>(4);
+
+ vaoId = glGenVertexArrays();
+ glBindVertexArray(vaoId);
+
+ // Position data
+ int vboId = glGenBuffers();
+ vboIds.add(vboId);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+ glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
+
+ // Normal data
+ vboId = glGenBuffers();
+ vboIds.add(vboId);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+ glBufferData(GL_ARRAY_BUFFER, normals, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0);
+
+ // Texture data
+ vboId = glGenBuffers();
+ vboIds.add(vboId);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+ glBufferData(GL_ARRAY_BUFFER, texCoords, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(2); // Texture coordinates
+ glVertexAttribPointer(2, 2, GL_FLOAT, false, 0, 0);
+
+ // Index data
+ eboId = glGenBuffers();
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
+
+ textureId = glGenTextures();
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ BufferedImage img = ImageIO.read(imageIn);
+ int w = img.getWidth();
+ int h = img.getHeight();
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ ByteBuffer imageBuffer = ImageUtils.decodePng(img);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageBuffer);
+ }
+ }
+
+ public void bind() {
+ glBindVertexArray(vaoId);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glEnableVertexAttribArray(2);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
+ }
+
+ public void draw() {
+ glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
+ }
+
+ public void unbind() {
+ glBindVertexArray(0);
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(2);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ public void free() {
+ glDeleteTextures(textureId);
+ glDeleteBuffers(eboId);
+ for (var vboId : vboIds) {
+ glDeleteBuffers(vboId);
+ }
+ glDeleteVertexArrays(vaoId);
+ }
+}
diff --git a/client/src/main/resources/model/player_simple.mtl b/client/src/main/resources/model/player_simple.mtl
new file mode 100644
index 0000000..8f219e3
--- /dev/null
+++ b/client/src/main/resources/model/player_simple.mtl
@@ -0,0 +1,13 @@
+# Blender MTL File: 'player_simple.blend'
+# Material Count: 1
+
+newmtl Material.001
+Ns 225.000000
+Ka 1.000000 1.000000 1.000000
+Kd 0.800000 0.800000 0.800000
+Ks 0.500000 0.500000 0.500000
+Ke 0.000000 0.000000 0.000000
+Ni 1.450000
+d 1.000000
+illum 2
+map_Kd simple_player.png
diff --git a/client/src/main/resources/model/player_simple.obj b/client/src/main/resources/model/player_simple.obj
new file mode 100644
index 0000000..f232464
--- /dev/null
+++ b/client/src/main/resources/model/player_simple.obj
@@ -0,0 +1,604 @@
+# Blender v2.82 (sub 7) OBJ File: 'player_simple.blend'
+# www.blender.org
+mtllib player_simple.mtl
+o Cube
+v 0.307055 1.384430 -0.307055
+v 0.159554 0.004036 -0.159554
+v 0.307055 1.384430 0.307055
+v 0.159554 0.004036 0.159554
+v -0.307055 1.384430 -0.307055
+v -0.159554 0.004036 -0.159554
+v -0.307055 1.384430 0.307055
+v -0.159554 0.004036 0.159554
+v 0.000000 0.004036 -0.221565
+v 0.000000 1.384430 0.426394
+v 0.000000 0.004036 0.221565
+v 0.000000 1.384430 -0.426394
+v -0.221565 0.004036 0.000000
+v 0.426394 1.384430 0.000000
+v -0.426394 1.384430 0.000000
+v 0.221565 0.004036 0.000000
+v 0.000000 0.004036 0.000000
+v 0.125637 1.521320 -0.125637
+v 0.355691 0.694233 -0.355691
+v -0.355691 0.694233 0.355691
+v 0.355691 0.694233 0.355691
+v -0.355691 0.694233 -0.355691
+v 0.000000 0.694233 -0.493931
+v 0.000000 0.694233 0.493931
+v 0.493931 0.694233 -0.000000
+v -0.493931 0.694233 -0.000000
+v 0.342552 0.349135 -0.342552
+v 0.342552 0.349135 0.342552
+v -0.342552 0.349135 -0.342552
+v 0.000000 0.349135 -0.475686
+v 0.000000 0.349135 0.475686
+v 0.475686 0.349135 -0.000000
+v -0.475686 0.349135 -0.000000
+v -0.342552 0.349135 0.342552
+v -0.342552 1.039332 0.342552
+v 0.342552 1.039332 -0.342552
+v 0.342552 1.039332 0.342552
+v -0.342552 1.039332 -0.342552
+v 0.000000 1.039332 -0.475686
+v 0.000000 1.039332 0.475686
+v 0.475686 1.039332 -0.000000
+v -0.475686 1.039332 -0.000000
+v 0.324796 0.176514 -0.324796
+v 0.324796 0.176514 0.324796
+v -0.324796 0.176514 -0.324796
+v 0.000000 0.176514 -0.451029
+v 0.000000 0.176514 0.451029
+v 0.451029 0.176514 -0.000000
+v -0.451029 0.176514 -0.000000
+v -0.324796 0.176514 0.324796
+v 0.274970 0.090275 -0.274970
+v 0.274970 0.090275 0.274970
+v -0.274970 0.090275 -0.274970
+v 0.000000 0.090275 -0.381838
+v 0.000000 0.090275 0.381838
+v 0.381838 0.090275 -0.000000
+v -0.381838 0.090275 -0.000000
+v -0.274970 0.090275 0.274970
+v 0.230376 1.470570 -0.230376
+v 0.230376 1.470570 0.230376
+v -0.230376 1.470570 -0.230376
+v -0.230376 1.470570 0.230376
+v 0.000000 1.470570 0.319913
+v 0.000000 1.470570 -0.319913
+v 0.319913 1.470570 0.000000
+v -0.319913 1.470570 0.000000
+v 0.125637 1.556870 -0.125637
+v 0.125637 1.521320 0.125637
+v -0.125637 1.521320 -0.125637
+v -0.125637 1.521320 0.125637
+v 0.000000 1.521320 0.174467
+v 0.000000 1.521320 -0.174467
+v 0.174467 1.521320 0.000000
+v -0.174467 1.521320 0.000000
+v 0.169463 1.610223 -0.169463
+v 0.125637 1.556870 0.125637
+v -0.125637 1.556870 -0.125637
+v -0.125637 1.556870 0.125637
+v 0.000000 1.556870 0.174467
+v 0.000000 1.556870 -0.174467
+v 0.174467 1.556870 0.000000
+v -0.174467 1.556870 0.000000
+v 0.210794 1.686314 -0.210794
+v 0.169463 1.610223 0.169463
+v -0.169463 1.610223 -0.169463
+v -0.169463 1.610223 0.169463
+v 0.000000 1.610223 0.235325
+v 0.000000 1.610223 -0.235325
+v 0.235325 1.610223 0.000000
+v -0.235325 1.610223 0.000000
+v 0.223284 1.833569 -0.223284
+v 0.210794 1.686314 0.210794
+v -0.210794 1.686314 -0.210794
+v -0.210794 1.686314 0.210794
+v 0.000000 1.686314 0.292720
+v 0.000000 1.686314 -0.292720
+v 0.292720 1.686314 0.000000
+v -0.292720 1.686314 0.000000
+v 0.211578 1.977176 -0.211578
+v 0.223284 1.833569 0.223284
+v -0.223284 1.833569 -0.223284
+v -0.223284 1.833569 0.223284
+v 0.000000 1.833569 0.310064
+v 0.000000 1.833569 -0.310064
+v 0.310064 1.833569 0.000000
+v -0.310064 1.833569 0.000000
+v 0.172806 2.077383 -0.172806
+v 0.211578 1.977176 0.211578
+v -0.211578 1.977176 -0.211578
+v -0.211578 1.977176 0.211578
+v 0.000000 1.977176 0.293809
+v 0.000000 1.977176 -0.293809
+v 0.293809 1.977176 0.000000
+v -0.293809 1.977176 0.000000
+v 0.067534 2.131719 -0.067534
+v 0.172806 2.077383 0.172806
+v -0.172806 2.077383 -0.172806
+v -0.172806 2.077383 0.172806
+v 0.000000 2.077383 0.239968
+v 0.000000 2.077383 -0.239968
+v 0.239968 2.077383 0.000000
+v -0.239968 2.077383 0.000000
+v 0.067534 2.131719 0.067534
+v -0.067534 2.131719 -0.067534
+v -0.067534 2.131719 0.067534
+v 0.000000 2.131719 0.093781
+v 0.000000 2.131719 -0.093781
+v 0.093781 2.131719 0.000000
+v -0.093781 2.131719 0.000000
+v 0.000000 2.131719 0.000000
+vt 0.270204 0.781152
+vt 0.118734 0.781152
+vt 0.138824 0.736777
+vt 0.252468 0.736777
+vt 0.757867 0.465320
+vt 0.690242 0.473338
+vt 0.690321 0.364703
+vt 0.757955 0.344126
+vt 0.757867 0.142172
+vt 0.690242 0.150190
+vt 0.690321 0.041556
+vt 0.757955 0.020979
+vt 0.210691 0.204882
+vt 0.247595 0.112545
+vt 0.305645 0.164231
+vt 0.305645 0.240769
+vt 0.499666 0.142172
+vt 0.432041 0.150190
+vt 0.432120 0.041555
+vt 0.499754 0.020979
+vt 0.499666 0.465320
+vt 0.432041 0.473338
+vt 0.432120 0.364703
+vt 0.499754 0.344126
+vt 0.499754 0.629641
+vt 0.432120 0.620631
+vt 0.173786 0.297218
+vt 0.252494 0.297218
+vt 0.757955 0.629641
+vt 0.690321 0.620631
+vt 0.016446 0.672518
+vt 0.016446 0.525224
+vt 0.062080 0.544760
+vt 0.062080 0.655271
+vt 0.381919 0.534391
+vt 0.381919 0.681685
+vt 0.336285 0.662149
+vt 0.336285 0.551638
+vt 0.115736 0.245532
+vt 0.115736 0.168995
+vt 0.499754 0.306493
+vt 0.432120 0.297484
+vt 0.168888 0.112545
+vt 0.757955 0.306493
+vt 0.690321 0.297484
+vt 0.893270 0.306425
+vt 0.825604 0.309807
+vt 0.825512 0.139183
+vt 0.893182 0.142104
+vt 0.635069 0.306425
+vt 0.567403 0.309807
+vt 0.567311 0.139183
+vt 0.634981 0.142104
+vt 0.893270 0.629572
+vt 0.825604 0.632954
+vt 0.825512 0.462330
+vt 0.893182 0.465251
+vt 0.635069 0.629572
+vt 0.567403 0.632954
+vt 0.567311 0.462330
+vt 0.634981 0.465251
+vt 0.567403 0.336488
+vt 0.635069 0.344058
+vt 0.567403 0.013341
+vt 0.635069 0.020910
+vt 0.825604 0.013341
+vt 0.893270 0.020910
+vt 0.825604 0.336488
+vt 0.893270 0.344058
+vt 0.927041 0.469228
+vt 0.927124 0.354316
+vt 0.927041 0.146080
+vt 0.927124 0.031169
+vt 0.668840 0.146080
+vt 0.668923 0.031169
+vt 0.668840 0.469228
+vt 0.668923 0.354316
+vt 0.668923 0.625032
+vt 0.927124 0.625032
+vt 0.668923 0.301884
+vt 0.927124 0.301884
+vt 0.282733 0.364011
+vt 0.295787 0.392847
+vt 0.135566 0.392847
+vt 0.147091 0.364011
+vt 0.138649 0.045752
+vt 0.125594 0.016917
+vt 0.285815 0.016917
+vt 0.274291 0.045752
+vt 0.374332 0.134826
+vt 0.403985 0.122132
+vt 0.403985 0.277935
+vt 0.374332 0.266728
+vt 0.047049 0.274937
+vt 0.017396 0.287632
+vt 0.017397 0.131828
+vt 0.047049 0.143035
+vt 0.145897 0.470132
+vt 0.259541 0.470132
+vt 0.232099 0.530746
+vt 0.170123 0.530746
+vt 0.124413 0.631713
+vt 0.166266 0.676163
+vt 0.228242 0.676163
+vt 0.273952 0.635464
+vt 0.124413 0.571445
+vt 0.128161 0.425756
+vt 0.279631 0.425756
+vt 0.405682 0.414476
+vt 0.405650 0.458926
+vt 0.398680 0.458930
+vt 0.398712 0.414480
+vt 0.405682 0.528622
+vt 0.405650 0.573072
+vt 0.398680 0.573076
+vt 0.398712 0.528626
+vt 0.405682 0.519194
+vt 0.398712 0.519197
+vt 0.405682 0.633340
+vt 0.398712 0.633344
+vt 0.273952 0.575196
+vt 0.907386 0.807523
+vt 0.907386 0.747255
+vt 0.933468 0.736089
+vt 0.933468 0.817380
+vt 0.799699 0.706556
+vt 0.757846 0.751006
+vt 0.731765 0.741148
+vt 0.788217 0.681193
+vt 0.757846 0.811274
+vt 0.803557 0.851973
+vt 0.793420 0.877335
+vt 0.731765 0.822439
+vt 0.865533 0.851973
+vt 0.877016 0.877335
+vt 0.413282 0.458926
+vt 0.413314 0.519194
+vt 0.406344 0.519197
+vt 0.406312 0.458930
+vt 0.421034 0.458926
+vt 0.421066 0.519194
+vt 0.414097 0.519197
+vt 0.414065 0.458930
+vt 0.421066 0.414476
+vt 0.414097 0.414480
+vt 0.413314 0.414476
+vt 0.406344 0.414480
+vt 0.783860 0.901254
+vt 0.707168 0.832969
+vt 0.887844 0.901254
+vt 0.707168 0.731852
+vt 0.871813 0.681193
+vt 0.881373 0.657274
+vt 0.958065 0.725559
+vt 0.861676 0.706556
+vt 0.059593 0.887311
+vt 0.059648 0.988429
+vt 0.030770 0.991625
+vt 0.030712 0.884517
+vt 0.997431 0.015588
+vt 0.997377 0.090166
+vt 0.968496 0.087372
+vt 0.968554 0.008375
+vt 0.997431 0.421298
+vt 0.997377 0.495876
+vt 0.968496 0.493081
+vt 0.968554 0.414084
+vt 0.997431 0.191283
+vt 0.968554 0.194480
+vt 0.777388 0.657274
+vt 0.958065 0.826676
+vt 0.940352 0.495728
+vt 0.940407 0.420873
+vt 0.940407 0.191512
+vt 0.940352 0.090019
+vt 0.968554 0.600190
+vt 0.940407 0.597221
+vt 0.030770 0.805520
+vt 0.002569 0.887164
+vt 0.002623 0.812308
+vt 0.997377 0.293021
+vt 0.997431 0.394138
+vt 0.968554 0.397335
+vt 0.968496 0.290226
+vt 0.997431 0.218443
+vt 0.968554 0.211229
+vt 0.059648 0.812733
+vt 0.997431 0.596993
+vt 0.503196 0.657315
+vt 0.607567 0.657315
+vt 0.598599 0.679753
+vt 0.513354 0.679753
+vt 0.684545 0.725854
+vt 0.684545 0.827347
+vt 0.661471 0.818627
+vt 0.661471 0.735732
+vt 0.614063 0.902203
+vt 0.603905 0.879765
+vt 0.940407 0.015163
+vt 0.002623 0.988657
+vt 0.940407 0.394367
+vt 0.940352 0.292873
+vt 0.940407 0.218018
+vt 0.576323 0.818842
+vt 0.598820 0.794949
+vt 0.598820 0.762553
+vt 0.574250 0.740676
+vt 0.455788 0.823786
+vt 0.455788 0.740891
+vt 0.518438 0.764569
+vt 0.518438 0.796965
+vt 0.518660 0.879765
+vt 0.543009 0.818842
+vt 0.509692 0.902203
+vt 0.432714 0.833664
+vt 0.432714 0.732171
+vt 0.558629 0.779759
+vt 0.540936 0.740676
+vn -0.6110 0.7552 0.2374
+vn -0.3591 0.1320 0.9239
+vn -0.9239 0.1320 -0.3591
+vn 0.0000 -1.0000 0.0000
+vn 0.9239 0.1320 0.3591
+vn 0.3591 0.1320 -0.9239
+vn -0.3591 0.1320 -0.9239
+vn 0.3591 0.1320 0.9239
+vn 0.2374 0.7552 0.6110
+vn -0.2374 0.7552 -0.6110
+vn 0.9239 0.1320 -0.3591
+vn -0.9239 0.1320 0.3591
+vn -0.2374 0.7552 0.6110
+vn -0.9309 -0.0492 0.3618
+vn 0.9309 -0.0492 -0.3618
+vn 0.3618 -0.0492 0.9309
+vn -0.3618 -0.0492 -0.9309
+vn 0.3618 -0.0492 -0.9309
+vn 0.9309 -0.0492 0.3618
+vn -0.9309 -0.0492 -0.3618
+vn -0.3618 -0.0492 0.9309
+vn -0.3591 -0.1320 0.9239
+vn -0.9239 -0.1320 -0.3591
+vn 0.9239 -0.1320 0.3591
+vn 0.3591 -0.1320 -0.9239
+vn -0.3591 -0.1320 -0.9239
+vn 0.3591 -0.1320 0.9239
+vn 0.9239 -0.1320 -0.3591
+vn -0.9239 -0.1320 0.3591
+vn -0.9309 0.0492 0.3618
+vn 0.9309 0.0492 -0.3618
+vn 0.3618 0.0492 0.9309
+vn -0.3618 0.0492 -0.9309
+vn 0.3618 0.0492 -0.9309
+vn 0.9309 0.0492 0.3618
+vn -0.9309 0.0492 -0.3618
+vn -0.3618 0.0492 0.9309
+vn -0.7464 -0.5989 0.2901
+vn 0.7464 -0.5989 -0.2901
+vn 0.2901 -0.5989 0.7464
+vn -0.2901 -0.5989 -0.7464
+vn 0.2901 -0.5989 -0.7464
+vn 0.7464 -0.5989 0.2901
+vn -0.7464 -0.5989 -0.2901
+vn -0.2901 -0.5989 0.7464
+vn -0.1811 -0.8660 0.4660
+vn -0.4660 -0.8660 -0.1811
+vn 0.4660 -0.8660 0.1811
+vn 0.1811 -0.8660 -0.4660
+vn -0.1811 -0.8660 -0.4660
+vn 0.1811 -0.8660 0.4660
+vn 0.4660 -0.8660 -0.1811
+vn -0.4660 -0.8660 0.1811
+vn 0.3268 0.9365 -0.1270
+vn -0.1270 0.9365 0.3268
+vn -0.3268 0.9365 -0.1270
+vn 0.1270 0.9365 0.3268
+vn -0.6110 0.7552 -0.2374
+vn 0.6110 0.7552 0.2374
+vn 0.6110 0.7552 -0.2374
+vn 0.2374 0.7552 -0.6110
+vn -0.3623 0.0000 0.9321
+vn -0.9321 0.0000 -0.3623
+vn 0.3623 0.0000 0.9321
+vn -0.9321 0.0000 0.3623
+vn -0.1270 0.9365 -0.3268
+vn 0.1270 0.9365 -0.3268
+vn 0.3268 0.9365 0.1270
+vn -0.3268 0.9365 0.1270
+vn -0.6386 -0.7284 0.2482
+vn 0.6386 -0.7284 0.2482
+vn 0.2482 -0.7284 -0.6386
+vn -0.2482 -0.7284 -0.6386
+vn 0.9321 0.0000 -0.3623
+vn -0.3623 0.0000 -0.9321
+vn 0.3623 0.0000 -0.9321
+vn 0.9321 0.0000 0.3623
+vn 0.2963 -0.5751 -0.7625
+vn -0.2963 -0.5751 -0.7625
+vn 0.7625 -0.5751 -0.2963
+vn -0.2963 -0.5751 0.7625
+vn 0.2482 -0.7284 0.6386
+vn -0.6386 -0.7284 -0.2482
+vn -0.2482 -0.7284 0.6386
+vn 0.6386 -0.7284 -0.2482
+vn 0.9265 -0.1091 -0.3601
+vn -0.3601 -0.1091 0.9265
+vn -0.9265 -0.1091 -0.3601
+vn 0.3601 -0.1091 0.9265
+vn 0.7625 -0.5751 0.2963
+vn -0.7625 -0.5751 0.2963
+vn 0.2963 -0.5751 0.7625
+vn -0.7625 -0.5751 -0.2963
+vn -0.9269 0.1049 -0.3603
+vn 0.3603 0.1049 0.9269
+vn -0.9269 0.1049 0.3603
+vn 0.9269 0.1049 0.3603
+vn -0.3601 -0.1091 -0.9265
+vn 0.3601 -0.1091 -0.9265
+vn 0.9265 -0.1091 0.3601
+vn -0.9265 -0.1091 0.3601
+vn 0.8334 0.4478 0.3239
+vn 0.3239 0.4478 -0.8334
+vn -0.3239 0.4478 -0.8334
+vn 0.8334 0.4478 -0.3239
+vn -0.3603 0.1049 0.9269
+vn 0.9269 0.1049 -0.3603
+vn -0.3603 0.1049 -0.9269
+vn 0.3603 0.1049 -0.9269
+vn -0.1342 0.9289 -0.3452
+vn 0.3452 0.9289 -0.1342
+vn -0.1342 0.9289 0.3452
+vn -0.3452 0.9289 -0.1342
+vn -0.8334 0.4478 0.3239
+vn 0.3239 0.4478 0.8334
+vn -0.8334 0.4478 -0.3239
+vn -0.3239 0.4478 0.8334
+vn 0.0000 1.0000 0.0000
+vn 0.1342 0.9289 -0.3452
+vn 0.3452 0.9289 0.1342
+vn -0.3452 0.9289 0.1342
+vn 0.1342 0.9289 0.3452
+usemtl Material.001
+s off
+f 15/1/1 7/2/1 62/3/1 66/4/1
+f 40/5/2 10/6/2 7/7/2 35/8/2
+f 42/9/3 15/10/3 5/11/3 38/12/3
+f 17/13/4 16/14/4 4/15/4 11/16/4
+f 41/17/5 14/18/5 3/19/5 37/20/5
+f 39/21/6 12/22/6 1/23/6 36/24/6
+f 38/25/7 5/26/7 12/22/7 39/21/7
+f 13/27/4 17/13/4 11/16/4 8/28/4
+f 37/29/8 3/30/8 10/6/8 40/5/8
+f 10/31/9 3/32/9 60/33/9 63/34/9
+f 12/35/10 5/36/10 61/37/10 64/38/10
+f 6/39/4 9/40/4 17/13/4 13/27/4
+f 36/41/11 1/42/11 14/18/11 41/17/11
+f 9/40/4 2/43/4 16/14/4 17/13/4
+f 35/44/12 7/45/12 15/10/12 42/9/12
+f 7/2/13 10/31/13 63/34/13 62/3/13
+f 34/46/14 20/47/14 26/48/14 33/49/14
+f 27/50/15 19/51/15 25/52/15 32/53/15
+f 28/54/16 21/55/16 24/56/16 31/57/16
+f 29/58/17 22/59/17 23/60/17 30/61/17
+f 30/61/18 23/60/18 19/62/18 27/63/18
+f 32/53/19 25/52/19 21/64/19 28/65/19
+f 33/49/20 26/48/20 22/66/20 29/67/20
+f 31/57/21 24/56/21 20/68/21 34/69/21
+f 47/70/22 31/57/22 34/69/22 50/71/22
+f 49/72/23 33/49/23 29/67/23 45/73/23
+f 48/74/24 32/53/24 28/65/24 44/75/24
+f 46/76/25 30/61/25 27/63/25 43/77/25
+f 45/78/26 29/58/26 30/61/26 46/76/26
+f 44/79/27 28/54/27 31/57/27 47/70/27
+f 43/80/28 27/50/28 32/53/28 48/74/28
+f 50/81/29 34/46/29 33/49/29 49/72/29
+f 20/47/30 35/44/30 42/9/30 26/48/30
+f 19/51/31 36/41/31 41/17/31 25/52/31
+f 21/55/32 37/29/32 40/5/32 24/56/32
+f 22/59/33 38/25/33 39/21/33 23/60/33
+f 23/60/34 39/21/34 36/24/34 19/62/34
+f 25/52/35 41/17/35 37/20/35 21/64/35
+f 26/48/36 42/9/36 38/12/36 22/66/36
+f 24/56/37 40/5/37 35/8/37 20/68/37
+f 58/82/38 50/83/38 49/84/38 57/85/38
+f 51/86/39 43/87/39 48/88/39 56/89/39
+f 52/90/40 44/91/40 47/92/40 55/93/40
+f 53/94/41 45/95/41 46/96/41 54/97/41
+f 54/97/42 46/96/42 43/87/42 51/86/42
+f 56/89/43 48/88/43 44/91/43 52/90/43
+f 57/85/44 49/84/44 45/95/44 53/94/44
+f 55/93/45 47/92/45 50/83/45 58/82/45
+f 11/16/46 55/93/46 58/82/46 8/28/46
+f 13/27/47 57/85/47 53/94/47 6/39/47
+f 16/14/48 56/89/48 52/90/48 4/15/48
+f 9/40/49 54/97/49 51/86/49 2/43/49
+f 6/39/50 53/94/50 54/97/50 9/40/50
+f 4/15/51 52/90/51 55/93/51 11/16/51
+f 2/43/52 51/86/52 56/89/52 16/14/52
+f 8/28/53 58/82/53 57/85/53 13/27/53
+f 65/98/54 59/99/54 18/100/54 73/101/54
+f 62/3/55 63/34/55 71/102/55 70/103/55
+f 61/37/56 66/4/56 74/104/56 69/105/56
+f 63/34/57 60/33/57 68/106/57 71/102/57
+f 5/36/58 15/1/58 66/4/58 61/37/58
+f 3/32/59 14/107/59 65/98/59 60/33/59
+f 14/107/60 1/108/60 59/99/60 65/98/60
+f 1/108/61 12/35/61 64/38/61 59/99/61
+f 70/109/62 71/110/62 79/111/62 78/112/62
+f 69/113/63 74/114/63 82/115/63 77/116/63
+f 71/110/64 68/117/64 76/118/64 79/111/64
+f 74/114/65 70/119/65 78/120/65 82/115/65
+f 64/38/66 61/37/66 69/105/66 72/121/66
+f 59/99/67 64/38/67 72/121/67 18/100/67
+f 60/33/68 65/98/68 73/101/68 68/106/68
+f 66/4/69 62/3/69 70/103/69 74/104/69
+f 82/122/70 78/123/70 86/124/70 90/125/70
+f 76/126/71 81/127/71 89/128/71 84/129/71
+f 67/130/72 80/131/72 88/132/72 75/133/72
+f 80/131/73 77/134/73 85/135/73 88/132/73
+f 73/136/74 18/137/74 67/138/74 81/139/74
+f 72/140/75 69/141/75 77/142/75 80/143/75
+f 18/144/76 72/140/76 80/143/76 67/145/76
+f 68/146/77 73/136/77 81/139/77 76/147/77
+f 75/133/78 88/132/78 96/148/78 83/149/78
+f 88/132/79 85/135/79 93/150/79 96/148/79
+f 89/128/80 75/133/80 83/149/80 97/151/80
+f 86/124/81 87/152/81 95/153/81 94/154/81
+f 79/155/82 76/126/82 84/129/82 87/152/82
+f 77/134/83 82/122/83 90/125/83 85/135/83
+f 78/123/84 79/155/84 87/152/84 86/124/84
+f 81/127/85 67/130/85 75/133/85 89/128/85
+f 97/156/86 83/157/86 91/158/86 105/159/86
+f 94/160/87 95/161/87 103/162/87 102/163/87
+f 93/164/88 98/165/88 106/166/88 101/167/88
+f 95/161/89 92/168/89 100/169/89 103/162/89
+f 84/129/90 89/128/90 97/151/90 92/170/90
+f 90/125/91 86/124/91 94/154/91 98/171/91
+f 87/152/92 84/129/92 92/170/92 95/153/92
+f 85/135/93 90/125/93 98/171/93 93/150/93
+f 101/167/94 106/166/94 114/172/94 109/173/94
+f 103/162/95 100/169/95 108/174/95 111/175/95
+f 106/166/96 102/176/96 110/177/96 114/172/96
+f 100/178/97 105/159/97 113/179/97 108/180/97
+f 96/181/98 93/182/98 101/183/98 104/184/98
+f 83/185/99 96/181/99 104/184/99 91/186/99
+f 92/187/100 97/156/100 105/159/100 100/178/100
+f 98/165/101 94/188/101 102/176/101 106/166/101
+f 108/189/102 113/190/102 121/191/102 116/192/102
+f 99/193/103 112/194/103 120/195/103 107/196/103
+f 112/194/104 109/197/104 117/198/104 120/195/104
+f 113/190/105 99/193/105 107/196/105 121/191/105
+f 102/163/106 103/162/106 111/175/106 110/199/106
+f 105/159/107 91/158/107 99/200/107 113/179/107
+f 104/184/108 101/183/108 109/201/108 112/202/108
+f 91/186/109 104/184/109 112/202/109 99/203/109
+f 120/195/110 117/198/110 124/204/110 127/205/110
+f 121/191/111 107/196/111 115/206/111 128/207/111
+f 118/208/112 119/209/112 126/210/112 125/211/112
+f 117/198/113 122/212/113 129/213/113 124/204/113
+f 114/214/114 110/215/114 118/208/114 122/212/114
+f 111/216/115 108/189/115 116/192/115 119/209/115
+f 109/197/116 114/214/116 122/212/116 117/198/116
+f 110/215/117 111/216/117 119/209/117 118/208/117
+f 130/217/118 129/213/118 125/211/118 126/210/118
+f 128/207/118 130/217/118 126/210/118 123/218/118
+f 115/206/118 127/205/118 130/217/118 128/207/118
+f 127/205/118 124/204/118 129/213/118 130/217/118
+f 107/196/119 120/195/119 127/205/119 115/206/119
+f 116/192/120 121/191/120 128/207/120 123/218/120
+f 122/212/121 118/208/121 125/211/121 129/213/121
+f 119/209/122 116/192/122 123/218/122 126/210/122
diff --git a/client/src/main/resources/model/simple_player.png b/client/src/main/resources/model/simple_player.png
new file mode 100644
index 0000000..e7b7d62
Binary files /dev/null and b/client/src/main/resources/model/simple_player.png differ
diff --git a/client/src/main/resources/shader/model/fragment.glsl b/client/src/main/resources/shader/model/fragment.glsl
new file mode 100644
index 0000000..5235c00
--- /dev/null
+++ b/client/src/main/resources/shader/model/fragment.glsl
@@ -0,0 +1,20 @@
+#version 460 core
+
+in vec2 textureCoords;
+in vec3 vertexNormal;
+
+out vec4 fragmentColor;
+
+uniform sampler2D textureSampler;
+
+void main() {
+ vec4 baseColor = texture(textureSampler, textureCoords);
+ vec3 lightDirection = normalize(vec3(0.5, -1.0, -0.5));// 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 diffuseComponent = max(dot(vertexNormal * -1, lightDirection), 0.0) * lightColor;
+ // TODO: Add shading based on light.
+ // fragmentColor = vec4((ambientComponent + diffuseComponent), 1.0) * baseColor;
+ fragmentColor = baseColor;
+}
\ No newline at end of file
diff --git a/client/src/main/resources/shader/model/vertex.glsl b/client/src/main/resources/shader/model/vertex.glsl
new file mode 100644
index 0000000..9dad643
--- /dev/null
+++ b/client/src/main/resources/shader/model/vertex.glsl
@@ -0,0 +1,18 @@
+#version 460 core
+
+layout (location = 0) in vec3 vertexPositionIn;
+layout (location = 1) in vec3 vertexNormalIn;
+layout (location = 2) in vec2 textureCoordsIn;
+
+uniform mat4 projectionTransform;
+uniform mat4 viewTransform;
+uniform mat4 modelTransform;
+
+out vec2 textureCoords;
+out vec3 vertexNormal;
+
+void main() {
+ gl_Position = projectionTransform * viewTransform * modelTransform * vec4(vertexPositionIn, 1.0);
+ vertexNormal = vec3(modelTransform * vec4(vertexNormalIn, 1.0));
+ textureCoords = textureCoordsIn;
+}
\ No newline at end of file
diff --git a/core/src/main/java/nl/andrewl/aos_core/ImageUtils.java b/core/src/main/java/nl/andrewl/aos_core/ImageUtils.java
index 31f6d34..35f70a2 100644
--- a/core/src/main/java/nl/andrewl/aos_core/ImageUtils.java
+++ b/core/src/main/java/nl/andrewl/aos_core/ImageUtils.java
@@ -16,7 +16,7 @@ public class ImageUtils {
// ARGB format to -> RGBA
for( int h = 0; h < height; h++ )
for( int w = 0; w < width; w++ ) {
- int argb = image.getRGB( w, h );
+ int argb = image.getRGB(w, h);
buf.put( (byte) ( 0xFF & ( argb >> 16 ) ) );
buf.put( (byte) ( 0xFF & ( argb >> 8 ) ) );
buf.put( (byte) ( 0xFF & ( argb ) ) );
@@ -25,4 +25,14 @@ public class ImageUtils {
buf.flip();
return buf;
}
+
+ public static BufferedImage rotateClockwise90(BufferedImage src) {
+ int w = src.getWidth();
+ int h = src.getHeight();
+ BufferedImage dest = new BufferedImage(h, w, src.getType());
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ dest.setRGB(y, w - x - 1, src.getRGB(x, y));
+ return dest;
+ }
}
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
index ef16e08..df8eaf7 100644
--- a/core/src/main/java/nl/andrewl/aos_core/model/Chunk.java
+++ b/core/src/main/java/nl/andrewl/aos_core/model/Chunk.java
@@ -1,7 +1,6 @@
package nl.andrewl.aos_core.model;
import net.openhft.hashing.LongHashFunction;
-import org.joml.Vector3f;
import org.joml.Vector3i;
import java.util.Random;
@@ -115,6 +114,11 @@ public class Chunk {
return sb.toString();
}
+ @Override
+ public int hashCode() {
+ return position.hashCode();
+ }
+
public long blockHash() {
return LongHashFunction.xx3(0).hashBytes(blocks);
}
diff --git a/core/src/main/java/nl/andrewl/aos_core/net/PlayerJoinMessage.java b/core/src/main/java/nl/andrewl/aos_core/net/PlayerJoinMessage.java
index 07d10d2..1e927f9 100644
--- a/core/src/main/java/nl/andrewl/aos_core/net/PlayerJoinMessage.java
+++ b/core/src/main/java/nl/andrewl/aos_core/net/PlayerJoinMessage.java
@@ -21,4 +21,12 @@ public record PlayerJoinMessage(
player.getOrientation().x, player.getOrientation().y
);
}
+
+ public Player toPlayer() {
+ Player p = new Player(id, username);
+ p.getPosition().set(px, py, pz);
+ p.getVelocity().set(vx, vy, vz);
+ p.getOrientation().set(ox, oy);
+ return p;
+ }
}
diff --git a/core/src/main/java/nl/andrewl/aos_core/net/UdpReceiver.java b/core/src/main/java/nl/andrewl/aos_core/net/UdpReceiver.java
index aed09fe..e73d6df 100644
--- a/core/src/main/java/nl/andrewl/aos_core/net/UdpReceiver.java
+++ b/core/src/main/java/nl/andrewl/aos_core/net/UdpReceiver.java
@@ -35,7 +35,6 @@ public class UdpReceiver implements Runnable {
handler.handle(msg, packet);
} catch (SocketException e) {
if (e.getMessage().equals("Socket closed")) {
- System.out.println("Socket closed!");
break;
}
e.printStackTrace();
diff --git a/core/src/main/java/nl/andrewl/aos_core/net/udp/ChunkUpdateMessage.java b/core/src/main/java/nl/andrewl/aos_core/net/udp/ChunkUpdateMessage.java
index 2796b7c..0aa5ded 100644
--- a/core/src/main/java/nl/andrewl/aos_core/net/udp/ChunkUpdateMessage.java
+++ b/core/src/main/java/nl/andrewl/aos_core/net/udp/ChunkUpdateMessage.java
@@ -28,4 +28,12 @@ public record ChunkUpdateMessage(
world.getBlockAt(worldPos.x, worldPos.y, worldPos.z)
);
}
+
+ public Vector3i getChunkPos() {
+ return new Vector3i(cx, cy, cz);
+ }
+
+ public Vector3i getLocalPos() {
+ return new Vector3i(lx, ly, lz);
+ }
}
diff --git a/core/src/main/java/nl/andrewl/aos_core/net/udp/PlayerUpdateMessage.java b/core/src/main/java/nl/andrewl/aos_core/net/udp/PlayerUpdateMessage.java
index 9584fc0..803540b 100644
--- a/core/src/main/java/nl/andrewl/aos_core/net/udp/PlayerUpdateMessage.java
+++ b/core/src/main/java/nl/andrewl/aos_core/net/udp/PlayerUpdateMessage.java
@@ -1,5 +1,6 @@
package nl.andrewl.aos_core.net.udp;
+import nl.andrewl.aos_core.model.Player;
import nl.andrewl.record_net.Message;
/**
@@ -12,4 +13,11 @@ public record PlayerUpdateMessage(
float vx, float vy, float vz,
float ox, float oy,
boolean crouching
-) implements Message {}
+) implements Message {
+
+ public void apply(Player p) {
+ p.getPosition().set(px, py, pz);
+ p.getVelocity().set(vx, vy, vz);
+ p.getOrientation().set(ox, oy);
+ }
+}
diff --git a/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java b/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java
index b2caa60..5e9fce3 100644
--- a/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java
+++ b/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java
@@ -91,6 +91,13 @@ public class ClientCommunicationHandler {
log.debug("Sent connect accept message.");
sendTcpMessage(new WorldInfoMessage(server.getWorld()));
+ // Send join info for all players that are already connected.
+ for (var player : server.getPlayerManager().getPlayers()) {
+ if (player.getId() != this.player.getId()) {
+ sendTcpMessage(new PlayerJoinMessage(player));
+ }
+ }
+ // Send chunk data.
for (var chunk : server.getWorld().getChunkMap().values()) {
sendTcpMessage(new ChunkDataMessage(chunk));
}