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;
|
||||
|
||||
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.WindowUtils;
|
||||
import nl.andrewl.aos_core.model.World;
|
||||
|
@ -22,10 +23,10 @@ public class Client implements Runnable {
|
|||
client.run();
|
||||
}
|
||||
|
||||
private InetAddress serverAddress;
|
||||
private int serverPort;
|
||||
private String username;
|
||||
private CommunicationHandler communicationHandler;
|
||||
private final InetAddress serverAddress;
|
||||
private final int serverPort;
|
||||
private final String username;
|
||||
private final CommunicationHandler communicationHandler;
|
||||
private ChunkRenderer chunkRenderer;
|
||||
private int clientId;
|
||||
|
||||
|
@ -46,6 +47,7 @@ public class Client implements Runnable {
|
|||
var windowInfo = WindowUtils.initUI();
|
||||
long windowHandle = windowInfo.windowHandle();
|
||||
chunkRenderer = new ChunkRenderer(windowInfo.width(), windowInfo.height());
|
||||
ChunkMeshGenerator meshGenerator = new ChunkMeshGenerator();
|
||||
|
||||
try {
|
||||
this.clientId = communicationHandler.establishConnection(serverAddress, serverPort, username);
|
||||
|
@ -62,7 +64,7 @@ public class Client implements Runnable {
|
|||
e.printStackTrace();
|
||||
}
|
||||
for (var chunk : world.getChunkMap().values()) {
|
||||
chunkRenderer.addChunkMesh(new ChunkMesh(chunk));
|
||||
chunkRenderer.addChunkMesh(new ChunkMesh(chunk, meshGenerator));
|
||||
}
|
||||
|
||||
glfwSetCursorPosCallback(windowHandle, cam);
|
||||
|
|
|
@ -17,7 +17,7 @@ public class ChunkMesh {
|
|||
private final int[] positionData;
|
||||
private final Chunk chunk;
|
||||
|
||||
public ChunkMesh(Chunk chunk) {
|
||||
public ChunkMesh(Chunk chunk, ChunkMeshGenerator meshGenerator) {
|
||||
this.chunk = chunk;
|
||||
this.positionData = new int[]{chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z};
|
||||
|
||||
|
@ -25,7 +25,7 @@ public class ChunkMesh {
|
|||
this.eboId = glGenBuffers();
|
||||
this.vaoId = glGenVertexArrays();
|
||||
|
||||
loadMesh();
|
||||
loadMesh(meshGenerator);
|
||||
|
||||
initVertexArrayAttributes();
|
||||
}
|
||||
|
@ -37,9 +37,9 @@ public class ChunkMesh {
|
|||
/**
|
||||
* Generates and loads this chunk's mesh into the allocated OpenGL buffers.
|
||||
*/
|
||||
private void loadMesh() {
|
||||
private void loadMesh(ChunkMeshGenerator meshGenerator) {
|
||||
long start = System.nanoTime();
|
||||
var meshData = ChunkMeshGenerator.generateMesh(chunk);
|
||||
var meshData = meshGenerator.generateMesh(chunk);
|
||||
double dur = (System.nanoTime() - start) / 1_000_000.0;
|
||||
this.indexCount = meshData.indexBuffer().limit();
|
||||
// Print some debug information.
|
||||
|
|
|
@ -2,22 +2,35 @@ package nl.andrewl.aos2_client.render;
|
|||
|
||||
import nl.andrewl.aos_core.model.Chunk;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
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 {
|
||||
private ChunkMeshGenerator() {}
|
||||
private final FloatBuffer vertexBuffer;
|
||||
private final IntBuffer indexBuffer;
|
||||
|
||||
public static ChunkMeshData generateMesh(Chunk chunk) {
|
||||
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(300000);
|
||||
IntBuffer indexBuffer = BufferUtils.createIntBuffer(100000);
|
||||
private final Vector3i pos = new Vector3i();
|
||||
private final Vector3f color = new Vector3f();
|
||||
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;
|
||||
for (int i = 0; i < Chunk.TOTAL_SIZE; i++) {
|
||||
var pos = Chunk.idxToXyz(i);
|
||||
Chunk.idxToXyz(i, pos);
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
int z = pos.z;
|
||||
|
@ -26,52 +39,82 @@ public final class ChunkMeshGenerator {
|
|||
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.
|
||||
var a = new Vector3f(x, y + 1, z + 1);
|
||||
var b = new Vector3f(x, y + 1, z);
|
||||
var c = new Vector3f(x, y, z);
|
||||
var d = new Vector3f(x, y, z + 1);
|
||||
var e = new Vector3f(x + 1, y + 1, z);
|
||||
var f = new Vector3f(x + 1, y + 1, z + 1);
|
||||
var g = new Vector3f(x + 1, y, z + 1);
|
||||
var h = new Vector3f(x + 1, y, z);
|
||||
// var a = new Vector3f(x, y + 1, z + 1);
|
||||
// var b = new Vector3f(x, y + 1, z);
|
||||
// var c = new Vector3f(x, y, z);
|
||||
// var d = new Vector3f(x, y, z + 1);
|
||||
// var e = new Vector3f(x + 1, y + 1, z);
|
||||
// var f = new Vector3f(x + 1, y + 1, z + 1);
|
||||
// var g = new Vector3f(x + 1, y, z + 1);
|
||||
// var h = new Vector3f(x + 1, y, z);
|
||||
|
||||
// Top
|
||||
if (chunk.getBlockAt(x, y + 1, z) == 0) {
|
||||
var norm = new Vector3f(0, 1, 0);
|
||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(a, f, e, b));
|
||||
norm.set(0, 1, 0);
|
||||
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;
|
||||
}
|
||||
// Bottom
|
||||
if (chunk.getBlockAt(x, y - 1, z) == 0) {
|
||||
var norm = new Vector3f(0, -1, 0);
|
||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(c, h, g, d));
|
||||
norm.set(0, -1, 0);// 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;
|
||||
}
|
||||
// Positive z
|
||||
if (chunk.getBlockAt(x, y, z + 1) == 0) {
|
||||
var norm = new Vector3f(0, 0, 1);
|
||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(f, a, d, g));
|
||||
norm.set(0, 0, 1);
|
||||
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;
|
||||
}
|
||||
// Negative z
|
||||
if (chunk.getBlockAt(x, y, z - 1) == 0) {
|
||||
var norm = new Vector3f(0, 0, -1);
|
||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(b, e, h, c));
|
||||
norm.set(0, 0, -1);
|
||||
genFace(idx,
|
||||
x, y+1, z, // b
|
||||
x+1, y+1, z, // e
|
||||
x+1, y, z, // h
|
||||
x, y, z // c
|
||||
);
|
||||
idx += 4;
|
||||
}
|
||||
// Positive x
|
||||
if (chunk.getBlockAt(x + 1, y, z) == 0) {
|
||||
var norm = new Vector3f(1, 0, 0);
|
||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(e, f, g, h));
|
||||
norm.set(1, 0, 0);
|
||||
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;
|
||||
}
|
||||
// Negative x
|
||||
if (chunk.getBlockAt(x - 1, y, z) == 0) {
|
||||
var norm = new Vector3f(-1, 0, 0);
|
||||
genFace(vertexBuffer, indexBuffer, idx, color, norm, List.of(a, b, c, d));
|
||||
norm.set(-1, 0, 0);
|
||||
genFace(idx,
|
||||
x, y+1, z+1, // a
|
||||
x, y+1, z, // b
|
||||
x, y, z, // c
|
||||
x, y, z+1 // d
|
||||
);
|
||||
idx += 4;
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +122,11 @@ public final class ChunkMeshGenerator {
|
|||
return new ChunkMeshData(vertexBuffer.flip(), indexBuffer.flip());
|
||||
}
|
||||
|
||||
private static void genFace(FloatBuffer vertexBuffer, IntBuffer indexBuffer, int currentIndex, Vector3f color, Vector3f norm, List<Vector3f> vertices) {
|
||||
for (var vertex : vertices) {
|
||||
vertexBuffer.put(vertex.x);
|
||||
vertexBuffer.put(vertex.y);
|
||||
vertexBuffer.put(vertex.z);
|
||||
private void genFace(int currentIndex, float... vertices) {
|
||||
for (int i = 0; i < 12; i += 3) {
|
||||
vertexBuffer.put(vertices[i]);
|
||||
vertexBuffer.put(vertices[i+1]);
|
||||
vertexBuffer.put(vertices[i+2]);
|
||||
vertexBuffer.put(color.x);
|
||||
vertexBuffer.put(color.y);
|
||||
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.
|
||||
*/
|
||||
public static Vector3i idxToXyz(int idx) {
|
||||
if (idx < 0 || idx >= TOTAL_SIZE) return new Vector3i(-1, -1, -1);
|
||||
int x = idx / (SIZE * SIZE);
|
||||
Vector3i vec = new Vector3i(-1, -1, -1);
|
||||
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 y = remainder / SIZE;
|
||||
int z = remainder % SIZE;
|
||||
return new Vector3i(x, y, z);
|
||||
vec.y = remainder / SIZE;
|
||||
vec.z = remainder % SIZE;
|
||||
}
|
||||
|
||||
public byte getBlockAt(int x, int y, int z) {
|
||||
|
@ -126,4 +131,9 @@ public class Chunk {
|
|||
float v = blockValue / 127.0f;
|
||||
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;
|
||||
|
||||
import org.joml.Math;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
import org.joml.Vector3ic;
|
||||
|
||||
|
@ -17,6 +19,30 @@ public class World {
|
|||
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) {
|
||||
int chunkX = x / Chunk.SIZE;
|
||||
int localX = x % Chunk.SIZE;
|
||||
|
@ -33,4 +59,17 @@ public class World {
|
|||
public Chunk getChunkAt(Vector3i 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>
|
||||
</dependency>
|
||||
</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>
|
|
@ -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.PlayerUpdateMessage;
|
||||
import nl.andrewl.record_net.Message;
|
||||
import org.joml.Vector3f;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -39,16 +40,21 @@ public class Server implements Runnable {
|
|||
Random rand = new Random(1);
|
||||
this.world = new World();
|
||||
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++) {
|
||||
Chunk chunk = new Chunk(x, y, z);
|
||||
if (y <= 3) {
|
||||
for (int i = 0; i < Chunk.TOTAL_SIZE; i++) {
|
||||
chunk.getBlocks()[i] = (byte) rand.nextInt(20, 40);
|
||||
}
|
||||
}
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package nl.andrewl.aos2_server;
|
||||
|
||||
import nl.andrewl.aos_core.model.World;
|
||||
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
||||
import org.joml.Math;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -60,37 +62,76 @@ public class WorldUpdater implements Runnable {
|
|||
private void updatePlayerMovement(ServerPlayer player) {
|
||||
boolean updated = false;
|
||||
var v = player.getVelocity();
|
||||
var hv = new Vector3f(v.x, 0, v.z);
|
||||
var p = player.getPosition();
|
||||
|
||||
// Apply deceleration to the player before computing any input-derived acceleration.
|
||||
if (v.length() > 0) {
|
||||
Vector3f deceleration = new Vector3f(v).negate().normalize().mul(0.1f);
|
||||
v.add(deceleration);
|
||||
if (v.length() < 0.1f) {
|
||||
v.set(0);
|
||||
// Check if we have a negative velocity that will cause us to fall through a block next tick.
|
||||
float nextTickY = p.y + v.y;
|
||||
if (server.getWorld().getBlockAt(new Vector3f(p.x, nextTickY, p.z)) != 0) {
|
||||
// Find the first block we'll hit and set the player down on that.
|
||||
int floorY = (int) Math.floor(p.y) - 1;
|
||||
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;
|
||||
}
|
||||
|
||||
Vector3f a = new Vector3f();
|
||||
var inputState = player.getLastInputState();
|
||||
if (inputState.jumping() && grounded) {
|
||||
v.y = 0.6f;
|
||||
}
|
||||
|
||||
// Compute horizontal motion separately.
|
||||
if (inputState.forward()) a.z -= 1;
|
||||
if (inputState.backward()) a.z += 1;
|
||||
if (inputState.left()) 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) {
|
||||
a.normalize();
|
||||
Matrix4f moveTransform = new Matrix4f();
|
||||
moveTransform.rotate(player.getOrientation().x, new Vector3f(0, 1, 0));
|
||||
moveTransform.transformDirection(a);
|
||||
v.add(a);
|
||||
hv.add(a);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Check if the player is colliding with the world.
|
||||
|
||||
|
||||
// Apply velocity to the player's position.
|
||||
if (v.lengthSquared() > 0) {
|
||||
p.add(v);
|
||||
updated = true;
|
||||
|
|
Loading…
Reference in New Issue