Added improved physics, and optimized chunk mesh generation.
This commit is contained in:
parent
f2b0e09979
commit
f3c9a4ad92
|
@ -1,6 +1,7 @@
|
||||||
package nl.andrewl.aos2_client;
|
package nl.andrewl.aos2_client;
|
||||||
|
|
||||||
import nl.andrewl.aos2_client.render.ChunkMesh;
|
import nl.andrewl.aos2_client.render.ChunkMesh;
|
||||||
|
import nl.andrewl.aos2_client.render.ChunkMeshGenerator;
|
||||||
import nl.andrewl.aos2_client.render.ChunkRenderer;
|
import nl.andrewl.aos2_client.render.ChunkRenderer;
|
||||||
import nl.andrewl.aos2_client.render.WindowUtils;
|
import nl.andrewl.aos2_client.render.WindowUtils;
|
||||||
import nl.andrewl.aos_core.model.World;
|
import nl.andrewl.aos_core.model.World;
|
||||||
|
@ -22,10 +23,10 @@ public class Client implements Runnable {
|
||||||
client.run();
|
client.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private InetAddress serverAddress;
|
private final InetAddress serverAddress;
|
||||||
private int serverPort;
|
private final int serverPort;
|
||||||
private String username;
|
private final String username;
|
||||||
private CommunicationHandler communicationHandler;
|
private final CommunicationHandler communicationHandler;
|
||||||
private ChunkRenderer chunkRenderer;
|
private ChunkRenderer chunkRenderer;
|
||||||
private int clientId;
|
private int clientId;
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ public class Client implements Runnable {
|
||||||
var windowInfo = WindowUtils.initUI();
|
var windowInfo = WindowUtils.initUI();
|
||||||
long windowHandle = windowInfo.windowHandle();
|
long windowHandle = windowInfo.windowHandle();
|
||||||
chunkRenderer = new ChunkRenderer(windowInfo.width(), windowInfo.height());
|
chunkRenderer = new ChunkRenderer(windowInfo.width(), windowInfo.height());
|
||||||
|
ChunkMeshGenerator meshGenerator = new ChunkMeshGenerator();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.clientId = communicationHandler.establishConnection(serverAddress, serverPort, username);
|
this.clientId = communicationHandler.establishConnection(serverAddress, serverPort, username);
|
||||||
|
@ -62,7 +64,7 @@ public class Client implements Runnable {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
for (var chunk : world.getChunkMap().values()) {
|
for (var chunk : world.getChunkMap().values()) {
|
||||||
chunkRenderer.addChunkMesh(new ChunkMesh(chunk));
|
chunkRenderer.addChunkMesh(new ChunkMesh(chunk, meshGenerator));
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwSetCursorPosCallback(windowHandle, cam);
|
glfwSetCursorPosCallback(windowHandle, cam);
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class ChunkMesh {
|
||||||
private final int[] positionData;
|
private final int[] positionData;
|
||||||
private final Chunk chunk;
|
private final Chunk chunk;
|
||||||
|
|
||||||
public ChunkMesh(Chunk chunk) {
|
public ChunkMesh(Chunk chunk, ChunkMeshGenerator meshGenerator) {
|
||||||
this.chunk = chunk;
|
this.chunk = chunk;
|
||||||
this.positionData = new int[]{chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z};
|
this.positionData = new int[]{chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ public class ChunkMesh {
|
||||||
this.eboId = glGenBuffers();
|
this.eboId = glGenBuffers();
|
||||||
this.vaoId = glGenVertexArrays();
|
this.vaoId = glGenVertexArrays();
|
||||||
|
|
||||||
loadMesh();
|
loadMesh(meshGenerator);
|
||||||
|
|
||||||
initVertexArrayAttributes();
|
initVertexArrayAttributes();
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,9 @@ public class ChunkMesh {
|
||||||
/**
|
/**
|
||||||
* Generates and loads this chunk's mesh into the allocated OpenGL buffers.
|
* Generates and loads this chunk's mesh into the allocated OpenGL buffers.
|
||||||
*/
|
*/
|
||||||
private void loadMesh() {
|
private void loadMesh(ChunkMeshGenerator meshGenerator) {
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
var meshData = ChunkMeshGenerator.generateMesh(chunk);
|
var meshData = meshGenerator.generateMesh(chunk);
|
||||||
double dur = (System.nanoTime() - start) / 1_000_000.0;
|
double dur = (System.nanoTime() - start) / 1_000_000.0;
|
||||||
this.indexCount = meshData.indexBuffer().limit();
|
this.indexCount = meshData.indexBuffer().limit();
|
||||||
// Print some debug information.
|
// Print some debug information.
|
||||||
|
|
|
@ -2,22 +2,35 @@ package nl.andrewl.aos2_client.render;
|
||||||
|
|
||||||
import nl.andrewl.aos_core.model.Chunk;
|
import nl.andrewl.aos_core.model.Chunk;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
import org.joml.Vector3i;
|
||||||
import org.lwjgl.BufferUtils;
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highly-optimized class for generating chunk meshes, without any heap
|
||||||
|
* allocations at runtime. Not thread safe.
|
||||||
|
*/
|
||||||
public final class ChunkMeshGenerator {
|
public final class ChunkMeshGenerator {
|
||||||
private ChunkMeshGenerator() {}
|
private final FloatBuffer vertexBuffer;
|
||||||
|
private final IntBuffer indexBuffer;
|
||||||
|
|
||||||
public static ChunkMeshData generateMesh(Chunk chunk) {
|
private final Vector3i pos = new Vector3i();
|
||||||
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(300000);
|
private final Vector3f color = new Vector3f();
|
||||||
IntBuffer indexBuffer = BufferUtils.createIntBuffer(100000);
|
private final Vector3f norm = new Vector3f();
|
||||||
|
|
||||||
|
public ChunkMeshGenerator() {
|
||||||
|
vertexBuffer = BufferUtils.createFloatBuffer(300_000);
|
||||||
|
indexBuffer = BufferUtils.createIntBuffer(100_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkMeshData generateMesh(Chunk chunk) {
|
||||||
|
vertexBuffer.clear();
|
||||||
|
indexBuffer.clear();
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (int i = 0; i < Chunk.TOTAL_SIZE; i++) {
|
for (int i = 0; i < Chunk.TOTAL_SIZE; i++) {
|
||||||
var pos = Chunk.idxToXyz(i);
|
Chunk.idxToXyz(i, pos);
|
||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
int y = pos.y;
|
int y = pos.y;
|
||||||
int z = pos.z;
|
int z = pos.z;
|
||||||
|
@ -26,52 +39,82 @@ public final class ChunkMeshGenerator {
|
||||||
continue; // Don't render empty blocks.
|
continue; // Don't render empty blocks.
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3f color = Chunk.getColor(block);
|
Chunk.getColor(block, color);
|
||||||
|
|
||||||
// See /design/block_rendering.svg for a diagram of how these vertices are defined.
|
// See /design/block_rendering.svg for a diagram of how these vertices are defined.
|
||||||
var a = new Vector3f(x, y + 1, z + 1);
|
// var a = new Vector3f(x, y + 1, z + 1);
|
||||||
var b = new Vector3f(x, y + 1, z);
|
// var b = new Vector3f(x, y + 1, z);
|
||||||
var c = new Vector3f(x, y, z);
|
// var c = new Vector3f(x, y, z);
|
||||||
var d = new Vector3f(x, y, z + 1);
|
// var d = new Vector3f(x, y, z + 1);
|
||||||
var e = new Vector3f(x + 1, y + 1, z);
|
// var e = new Vector3f(x + 1, y + 1, z);
|
||||||
var f = new Vector3f(x + 1, y + 1, z + 1);
|
// var f = new Vector3f(x + 1, y + 1, z + 1);
|
||||||
var g = new Vector3f(x + 1, y, z + 1);
|
// var g = new Vector3f(x + 1, y, z + 1);
|
||||||
var h = new Vector3f(x + 1, y, z);
|
// var h = new Vector3f(x + 1, y, z);
|
||||||
|
|
||||||
// Top
|
// Top
|
||||||
if (chunk.getBlockAt(x, y + 1, z) == 0) {
|
if (chunk.getBlockAt(x, y + 1, z) == 0) {
|
||||||
var norm = new Vector3f(0, 1, 0);
|
norm.set(0, 1, 0);
|
||||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(a, f, e, b));
|
genFace(idx,
|
||||||
|
x, y+1, z+1, // a
|
||||||
|
x+1, y+1, z+1, // f
|
||||||
|
x+1, y+1, z, // e
|
||||||
|
x, y+1, z // b
|
||||||
|
);
|
||||||
idx += 4;
|
idx += 4;
|
||||||
}
|
}
|
||||||
// Bottom
|
// Bottom
|
||||||
if (chunk.getBlockAt(x, y - 1, z) == 0) {
|
if (chunk.getBlockAt(x, y - 1, z) == 0) {
|
||||||
var norm = new Vector3f(0, -1, 0);
|
norm.set(0, -1, 0);// c h g d
|
||||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(c, h, g, d));
|
genFace(idx,
|
||||||
|
x, y, z, // c
|
||||||
|
x+1, y, z, // h
|
||||||
|
x+1, y, z+1, // g
|
||||||
|
x, y, z+1 // d
|
||||||
|
);
|
||||||
idx += 4;
|
idx += 4;
|
||||||
}
|
}
|
||||||
// Positive z
|
// Positive z
|
||||||
if (chunk.getBlockAt(x, y, z + 1) == 0) {
|
if (chunk.getBlockAt(x, y, z + 1) == 0) {
|
||||||
var norm = new Vector3f(0, 0, 1);
|
norm.set(0, 0, 1);
|
||||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(f, a, d, g));
|
genFace(idx,
|
||||||
|
x+1, y+1, z+1, // f
|
||||||
|
x, y+1, z+1, // a
|
||||||
|
x, y, z+1, // d
|
||||||
|
x+1, y, z+1 // g
|
||||||
|
);
|
||||||
idx += 4;
|
idx += 4;
|
||||||
}
|
}
|
||||||
// Negative z
|
// Negative z
|
||||||
if (chunk.getBlockAt(x, y, z - 1) == 0) {
|
if (chunk.getBlockAt(x, y, z - 1) == 0) {
|
||||||
var norm = new Vector3f(0, 0, -1);
|
norm.set(0, 0, -1);
|
||||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(b, e, h, c));
|
genFace(idx,
|
||||||
|
x, y+1, z, // b
|
||||||
|
x+1, y+1, z, // e
|
||||||
|
x+1, y, z, // h
|
||||||
|
x, y, z // c
|
||||||
|
);
|
||||||
idx += 4;
|
idx += 4;
|
||||||
}
|
}
|
||||||
// Positive x
|
// Positive x
|
||||||
if (chunk.getBlockAt(x + 1, y, z) == 0) {
|
if (chunk.getBlockAt(x + 1, y, z) == 0) {
|
||||||
var norm = new Vector3f(1, 0, 0);
|
norm.set(1, 0, 0);
|
||||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(e, f, g, h));
|
genFace(idx,
|
||||||
|
x+1, y+1, z, // e
|
||||||
|
x+1, y+1, z+1, // f
|
||||||
|
x+1, y, z+1, // g
|
||||||
|
x+1, y, z // h
|
||||||
|
);
|
||||||
idx += 4;
|
idx += 4;
|
||||||
}
|
}
|
||||||
// Negative x
|
// Negative x
|
||||||
if (chunk.getBlockAt(x - 1, y, z) == 0) {
|
if (chunk.getBlockAt(x - 1, y, z) == 0) {
|
||||||
var norm = new Vector3f(-1, 0, 0);
|
norm.set(-1, 0, 0);
|
||||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(a, b, c, d));
|
genFace(idx,
|
||||||
|
x, y+1, z+1, // a
|
||||||
|
x, y+1, z, // b
|
||||||
|
x, y, z, // c
|
||||||
|
x, y, z+1 // d
|
||||||
|
);
|
||||||
idx += 4;
|
idx += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,11 +122,11 @@ public final class ChunkMeshGenerator {
|
||||||
return new ChunkMeshData(vertexBuffer.flip(), indexBuffer.flip());
|
return new ChunkMeshData(vertexBuffer.flip(), indexBuffer.flip());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void genFace(FloatBuffer vertexBuffer, IntBuffer indexBuffer, int currentIndex, Vector3f color, Vector3f norm, List<Vector3f> vertices) {
|
private void genFace(int currentIndex, float... vertices) {
|
||||||
for (var vertex : vertices) {
|
for (int i = 0; i < 12; i += 3) {
|
||||||
vertexBuffer.put(vertex.x);
|
vertexBuffer.put(vertices[i]);
|
||||||
vertexBuffer.put(vertex.y);
|
vertexBuffer.put(vertices[i+1]);
|
||||||
vertexBuffer.put(vertex.z);
|
vertexBuffer.put(vertices[i+2]);
|
||||||
vertexBuffer.put(color.x);
|
vertexBuffer.put(color.x);
|
||||||
vertexBuffer.put(color.y);
|
vertexBuffer.put(color.y);
|
||||||
vertexBuffer.put(color.z);
|
vertexBuffer.put(color.z);
|
||||||
|
|
|
@ -67,12 +67,17 @@ public class Chunk {
|
||||||
* @return The 3D coordinate, or -1, -1, -1 if the index is out of bounds.
|
* @return The 3D coordinate, or -1, -1, -1 if the index is out of bounds.
|
||||||
*/
|
*/
|
||||||
public static Vector3i idxToXyz(int idx) {
|
public static Vector3i idxToXyz(int idx) {
|
||||||
if (idx < 0 || idx >= TOTAL_SIZE) return new Vector3i(-1, -1, -1);
|
Vector3i vec = new Vector3i(-1, -1, -1);
|
||||||
int x = idx / (SIZE * SIZE);
|
idxToXyz(idx, vec);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void idxToXyz(int idx, Vector3i vec) {
|
||||||
|
if (idx < 0 || idx >= TOTAL_SIZE) return;
|
||||||
|
vec.x = idx / (SIZE * SIZE);
|
||||||
int remainder = idx % (SIZE * SIZE);
|
int remainder = idx % (SIZE * SIZE);
|
||||||
int y = remainder / SIZE;
|
vec.y = remainder / SIZE;
|
||||||
int z = remainder % SIZE;
|
vec.z = remainder % SIZE;
|
||||||
return new Vector3i(x, y, z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte getBlockAt(int x, int y, int z) {
|
public byte getBlockAt(int x, int y, int z) {
|
||||||
|
@ -126,4 +131,9 @@ public class Chunk {
|
||||||
float v = blockValue / 127.0f;
|
float v = blockValue / 127.0f;
|
||||||
return new Vector3f(v);
|
return new Vector3f(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void getColor(byte blockValue, Vector3f vec) {
|
||||||
|
float v = blockValue / 127.0f;
|
||||||
|
vec.set(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package nl.andrewl.aos_core.model;
|
package nl.andrewl.aos_core.model;
|
||||||
|
|
||||||
|
import org.joml.Math;
|
||||||
|
import org.joml.Vector3f;
|
||||||
import org.joml.Vector3i;
|
import org.joml.Vector3i;
|
||||||
import org.joml.Vector3ic;
|
import org.joml.Vector3ic;
|
||||||
|
|
||||||
|
@ -17,6 +19,30 @@ public class World {
|
||||||
return chunkMap;
|
return chunkMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte getBlockAt(Vector3f pos) {
|
||||||
|
Vector3i chunkPos = getChunkPosAt(pos);
|
||||||
|
Chunk chunk = chunkMap.get(chunkPos);
|
||||||
|
if (chunk == null) return 0;
|
||||||
|
Vector3i blockPos = new Vector3i(
|
||||||
|
(int) Math.floor(pos.x - chunkPos.x * Chunk.SIZE),
|
||||||
|
(int) Math.floor(pos.y - chunkPos.y * Chunk.SIZE),
|
||||||
|
(int) Math.floor(pos.z - chunkPos.z * Chunk.SIZE)
|
||||||
|
);
|
||||||
|
return chunk.getBlockAt(blockPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlockAt(Vector3f pos, byte block) {
|
||||||
|
Vector3i chunkPos = getChunkPosAt(pos);
|
||||||
|
Chunk chunk = chunkMap.get(chunkPos);
|
||||||
|
if (chunk == null) return;
|
||||||
|
Vector3i blockPos = new Vector3i(
|
||||||
|
(int) Math.floor(pos.x - chunkPos.x * Chunk.SIZE),
|
||||||
|
(int) Math.floor(pos.y - chunkPos.y * Chunk.SIZE),
|
||||||
|
(int) Math.floor(pos.z - chunkPos.z * Chunk.SIZE)
|
||||||
|
);
|
||||||
|
chunk.setBlockAt(blockPos.x, blockPos.y, blockPos.z, block);
|
||||||
|
}
|
||||||
|
|
||||||
public byte getBlockAt(int x, int y, int z) {
|
public byte getBlockAt(int x, int y, int z) {
|
||||||
int chunkX = x / Chunk.SIZE;
|
int chunkX = x / Chunk.SIZE;
|
||||||
int localX = x % Chunk.SIZE;
|
int localX = x % Chunk.SIZE;
|
||||||
|
@ -33,4 +59,17 @@ public class World {
|
||||||
public Chunk getChunkAt(Vector3i chunkPos) {
|
public Chunk getChunkAt(Vector3i chunkPos) {
|
||||||
return chunkMap.get(chunkPos);
|
return chunkMap.get(chunkPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the coordinates of a chunk at a given world position.
|
||||||
|
* @param worldPos The world position.
|
||||||
|
* @return The chunk position. Note that this may not correspond to any existing chunk.
|
||||||
|
*/
|
||||||
|
public static Vector3i getChunkPosAt(Vector3f worldPos) {
|
||||||
|
return new Vector3i(
|
||||||
|
(int) Math.floor(worldPos.x / Chunk.SIZE),
|
||||||
|
(int) Math.floor(worldPos.y / Chunk.SIZE),
|
||||||
|
(int) Math.floor(worldPos.z / Chunk.SIZE)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package nl.andrewl.aos_core.model;
|
||||||
|
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.joml.Vector3i;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class WorldTest {
|
||||||
|
@Test
|
||||||
|
public void testGetBlockAt() {
|
||||||
|
Chunk chunk = new Chunk(0, 0, 0);
|
||||||
|
chunk.setBlockAt(1, 0, 0, (byte) 1);
|
||||||
|
World world = new World();
|
||||||
|
world.addChunk(chunk);
|
||||||
|
assertEquals(1, world.getBlockAt(new Vector3f(1, 0, 0)));
|
||||||
|
assertEquals(1, world.getBlockAt(new Vector3f(1.9f, 0, 0)));
|
||||||
|
assertEquals(1, world.getBlockAt(new Vector3f(1.5f, 0.7f, 0.3f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetChunkPosAt() {
|
||||||
|
assertEquals(new Vector3i(0, 0, 0), World.getChunkPosAt(new Vector3f(0, 0, 0)));
|
||||||
|
assertEquals(new Vector3i(0, 0, 0), World.getChunkPosAt(new Vector3f(1, 0, 0)));
|
||||||
|
assertEquals(new Vector3i(0, 0, 0), World.getChunkPosAt(new Vector3f(0, 0.5f, 0)));
|
||||||
|
assertEquals(new Vector3i(0, 0, 0), World.getChunkPosAt(new Vector3f(Chunk.SIZE - 1, 0, 0)));
|
||||||
|
assertEquals(new Vector3i(1, 0, 0), World.getChunkPosAt(new Vector3f(Chunk.SIZE, 0, 0)));
|
||||||
|
assertEquals(new Vector3i(0, 0, -1), World.getChunkPosAt(new Vector3f(0, 0, -0.0001f)));
|
||||||
|
assertEquals(new Vector3i(0, 0, 0), World.getChunkPosAt(new Vector3f(Chunk.SIZE / 2f, Chunk.SIZE / 2f, Chunk.SIZE / 2f)));
|
||||||
|
assertEquals(new Vector3i(1, 1, 1), World.getChunkPosAt(new Vector3f(Chunk.SIZE, Chunk.SIZE, Chunk.SIZE)));
|
||||||
|
assertEquals(new Vector3i(4, 4, 4), World.getChunkPosAt(new Vector3f(Chunk.SIZE * 5 - 1, Chunk.SIZE * 5 - 1, Chunk.SIZE * 5 - 1)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,4 +23,31 @@
|
||||||
<version>${parent.version}</version>
|
<version>${parent.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>nl.andrewl.aos2_server.Server</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
|
@ -8,6 +8,7 @@ import nl.andrewl.aos_core.net.udp.ClientOrientationState;
|
||||||
import nl.andrewl.aos_core.net.udp.DatagramInit;
|
import nl.andrewl.aos_core.net.udp.DatagramInit;
|
||||||
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
||||||
import nl.andrewl.record_net.Message;
|
import nl.andrewl.record_net.Message;
|
||||||
|
import org.joml.Vector3f;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -39,16 +40,21 @@ public class Server implements Runnable {
|
||||||
Random rand = new Random(1);
|
Random rand = new Random(1);
|
||||||
this.world = new World();
|
this.world = new World();
|
||||||
for (int x = -5; x <= 5; x++) {
|
for (int x = -5; x <= 5; x++) {
|
||||||
for (int y = 0; y <= 3; y++) {
|
for (int y = 0; y <= 5; y++) {
|
||||||
for (int z = -3; z <= 3; z++) {
|
for (int z = -3; z <= 3; z++) {
|
||||||
Chunk chunk = new Chunk(x, y, z);
|
Chunk chunk = new Chunk(x, y, z);
|
||||||
for (int i = 0; i < Chunk.TOTAL_SIZE; i++) {
|
if (y <= 3) {
|
||||||
chunk.getBlocks()[i] = (byte) rand.nextInt(20, 40);
|
for (int i = 0; i < Chunk.TOTAL_SIZE; i++) {
|
||||||
|
chunk.getBlocks()[i] = (byte) rand.nextInt(20, 40);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
world.addChunk(chunk);
|
world.addChunk(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
world.setBlockAt(new Vector3f(5, 64, 5), (byte) 50);
|
||||||
|
world.setBlockAt(new Vector3f(5, 65, 6), (byte) 50);
|
||||||
|
world.setBlockAt(new Vector3f(5, 66, 7), (byte) 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package nl.andrewl.aos2_server;
|
package nl.andrewl.aos2_server;
|
||||||
|
|
||||||
|
import nl.andrewl.aos_core.model.World;
|
||||||
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
||||||
|
import org.joml.Math;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -60,37 +62,76 @@ public class WorldUpdater implements Runnable {
|
||||||
private void updatePlayerMovement(ServerPlayer player) {
|
private void updatePlayerMovement(ServerPlayer player) {
|
||||||
boolean updated = false;
|
boolean updated = false;
|
||||||
var v = player.getVelocity();
|
var v = player.getVelocity();
|
||||||
|
var hv = new Vector3f(v.x, 0, v.z);
|
||||||
var p = player.getPosition();
|
var p = player.getPosition();
|
||||||
|
|
||||||
// Apply deceleration to the player before computing any input-derived acceleration.
|
// Check if we have a negative velocity that will cause us to fall through a block next tick.
|
||||||
if (v.length() > 0) {
|
float nextTickY = p.y + v.y;
|
||||||
Vector3f deceleration = new Vector3f(v).negate().normalize().mul(0.1f);
|
if (server.getWorld().getBlockAt(new Vector3f(p.x, nextTickY, p.z)) != 0) {
|
||||||
v.add(deceleration);
|
// Find the first block we'll hit and set the player down on that.
|
||||||
if (v.length() < 0.1f) {
|
int floorY = (int) Math.floor(p.y) - 1;
|
||||||
v.set(0);
|
while (true) {
|
||||||
|
if (server.getWorld().getBlockAt(new Vector3f(p.x, floorY, p.z)) != 0) {
|
||||||
|
p.y = floorY + 1f;
|
||||||
|
v.y = 0;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
floorY--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the player is on the ground.
|
||||||
|
boolean grounded = (Math.floor(p.y) == p.y && server.getWorld().getBlockAt(new Vector3f(p.x, p.y - 0.0001f, p.z)) != 0);
|
||||||
|
|
||||||
|
if (!grounded) {
|
||||||
|
v.y -= 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply horizontal deceleration to the player before computing any input-derived acceleration.
|
||||||
|
if (hv.length() > 0) {
|
||||||
|
Vector3f deceleration = new Vector3f(hv).negate().normalize().mul(0.1f);
|
||||||
|
hv.add(deceleration);
|
||||||
|
if (hv.length() < 0.1f) {
|
||||||
|
hv.set(0);
|
||||||
|
}
|
||||||
|
v.x = hv.x;
|
||||||
|
v.z = hv.z;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3f a = new Vector3f();
|
Vector3f a = new Vector3f();
|
||||||
var inputState = player.getLastInputState();
|
var inputState = player.getLastInputState();
|
||||||
|
if (inputState.jumping() && grounded) {
|
||||||
|
v.y = 0.6f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute horizontal motion separately.
|
||||||
if (inputState.forward()) a.z -= 1;
|
if (inputState.forward()) a.z -= 1;
|
||||||
if (inputState.backward()) a.z += 1;
|
if (inputState.backward()) a.z += 1;
|
||||||
if (inputState.left()) a.x -= 1;
|
if (inputState.left()) a.x -= 1;
|
||||||
if (inputState.right()) a.x += 1;
|
if (inputState.right()) a.x += 1;
|
||||||
if (inputState.jumping()) a.y += 1; // TODO: check if on ground.
|
// if (inputState.crouching()) a.y -= 1; // TODO: do crouching instead of down.
|
||||||
if (inputState.crouching()) a.y -= 1; // TODO: do crouching instead of down.
|
|
||||||
if (a.lengthSquared() > 0) {
|
if (a.lengthSquared() > 0) {
|
||||||
a.normalize();
|
a.normalize();
|
||||||
Matrix4f moveTransform = new Matrix4f();
|
Matrix4f moveTransform = new Matrix4f();
|
||||||
moveTransform.rotate(player.getOrientation().x, new Vector3f(0, 1, 0));
|
moveTransform.rotate(player.getOrientation().x, new Vector3f(0, 1, 0));
|
||||||
moveTransform.transformDirection(a);
|
moveTransform.transformDirection(a);
|
||||||
v.add(a);
|
hv.add(a);
|
||||||
|
|
||||||
final float maxSpeed = 0.25f; // Blocks per tick.
|
final float maxSpeed = 0.25f; // Blocks per tick.
|
||||||
if (v.length() > maxSpeed) v.normalize(maxSpeed);
|
if (hv.length() > maxSpeed) {
|
||||||
|
hv.normalize(maxSpeed);
|
||||||
|
}
|
||||||
|
v.x = hv.x;
|
||||||
|
v.z = hv.z;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the player is colliding with the world.
|
||||||
|
|
||||||
|
|
||||||
|
// Apply velocity to the player's position.
|
||||||
if (v.lengthSquared() > 0) {
|
if (v.lengthSquared() > 0) {
|
||||||
p.add(v);
|
p.add(v);
|
||||||
updated = true;
|
updated = true;
|
||||||
|
|
Loading…
Reference in New Issue