From c9b325da3af1bf76a49a209bd2292886950eb38a Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Fri, 15 Jul 2022 17:10:23 +0200 Subject: [PATCH] Added raycasting method! I think... --- .../andrewl/aos2_client/render/ChunkMesh.java | 4 + .../aos2_client/render/ChunkRenderer.java | 17 ++-- .../java/nl/andrewl/aos_core/model/World.java | 83 ++++++++++++++++--- .../nl/andrewl/aos_core/model/WorldTest.java | 79 ++++++++++++++++++ 4 files changed, 163 insertions(+), 20 deletions(-) diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/ChunkMesh.java b/client/src/main/java/nl/andrewl/aos2_client/render/ChunkMesh.java index 1221e4e..e592d8b 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/render/ChunkMesh.java +++ b/client/src/main/java/nl/andrewl/aos2_client/render/ChunkMesh.java @@ -37,6 +37,10 @@ public class ChunkMesh { return positionData; } + public Chunk getChunk() { + return chunk; + } + /** * Generates and loads this chunk's mesh into the allocated OpenGL buffers. */ diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/ChunkRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/ChunkRenderer.java index fc4d8e3..0f527f3 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/render/ChunkRenderer.java +++ b/client/src/main/java/nl/andrewl/aos2_client/render/ChunkRenderer.java @@ -4,10 +4,9 @@ import nl.andrewl.aos2_client.Camera; import nl.andrewl.aos_core.model.Chunk; import nl.andrewl.aos_core.model.World; import org.joml.Matrix4f; +import org.joml.Vector3i; -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; +import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; import static org.lwjgl.opengl.GL46.*; @@ -26,7 +25,7 @@ public class ChunkRenderer { private int viewTransformUniform; private int chunkPositionUniform; - private final List chunkMeshes = new ArrayList<>(); + private final Map chunkMeshes = new HashMap<>(); public void setupShaderProgram() { this.shaderProgram = new ShaderProgram.Builder() @@ -53,18 +52,22 @@ public class ChunkRenderer { public void draw(Camera cam, World world) { while (!meshGenerationQueue.isEmpty()) { - chunkMeshes.add(new ChunkMesh(meshGenerationQueue.remove(), world, chunkMeshGenerator)); + Chunk chunk = meshGenerationQueue.remove(); + ChunkMesh mesh = new ChunkMesh(chunk, world, chunkMeshGenerator); + chunkMeshes.put(chunk.getPosition(), mesh); } shaderProgram.use(); glUniformMatrix4fv(viewTransformUniform, false, cam.getViewTransformData()); - for (var mesh : chunkMeshes) { + for (var mesh : chunkMeshes.values()) { glUniform3iv(chunkPositionUniform, mesh.getPositionData()); mesh.draw(); } } public void free() { - for (var mesh : chunkMeshes) mesh.free(); + for (var mesh : chunkMeshes.values()) mesh.free(); + chunkMeshes.clear(); + meshGenerationQueue.clear(); shaderProgram.free(); } } diff --git a/core/src/main/java/nl/andrewl/aos_core/model/World.java b/core/src/main/java/nl/andrewl/aos_core/model/World.java index 97d79fa..0e002a6 100644 --- a/core/src/main/java/nl/andrewl/aos_core/model/World.java +++ b/core/src/main/java/nl/andrewl/aos_core/model/World.java @@ -82,23 +82,80 @@ public class World { setBlockAt(new Vector3f(x, y, z), block); } -// public byte getBlockAt(int x, int y, int z) { -//// int chunkX = x / Chunk.SIZE; -//// int localX = x % Chunk.SIZE; -//// int chunkY = y / Chunk.SIZE; -//// int localY = y % Chunk.SIZE; -//// int chunkZ = z / Chunk.SIZE; -//// int localZ = z % Chunk.SIZE; -//// Vector3i chunkPos = new Vector3i(chunkX, chunkY, chunkZ); -//// Chunk chunk = chunkMap.get(chunkPos); -//// if (chunk == null) return 0; -//// return chunk.getBlockAt(localX, localY, localZ); -// } - public Chunk getChunkAt(Vector3i chunkPos) { return chunkMap.get(chunkPos); } + /** + * Gets the position that a system is looking at, within a distance limit. + * Usually used to determine where a player has interacted/clicked in the + * world. + * @param eyePos The origin point to look from. + * @param eyeDir The direction to look towards. This should be normalized. + * @param limit The radius out from the origin to look. Blocks outside this + * limit will not be returned. + * @return The location of the block that is looked at, or null if none + * could be found. + */ + public Vector3i getLookingAtPos(Vector3f eyePos, Vector3f eyeDir, float limit) { + if (eyeDir.lengthSquared() == 0 || limit <= 0) return null; + Vector3f pos = new Vector3f(eyePos); + Vector3f movement = new Vector3f(); // Pre-allocate this vector. + while (pos.distance(eyePos) < limit) { + // Find the coordinates of the next blocks on the x, y, and z axes. + float stepX = getNextStep(pos.x, eyeDir.x); + float stepY = getNextStep(pos.y, eyeDir.y); + float stepZ = getNextStep(pos.z, eyeDir.z); + // Get the distance from our current position to the next block on the x, y, and z axes. + float distX = Math.abs(pos.x - stepX); + float distY = Math.abs(pos.y - stepY); + float distZ = Math.abs(pos.z - stepZ); + // Get the factor required to multiply each component by to get to its next step. + float factorX = Math.abs(distX / eyeDir.x); + float factorY = Math.abs(distY / eyeDir.y); + float factorZ = Math.abs(distZ / eyeDir.z); + float minFactor = Float.MAX_VALUE; + if (factorX > 0 && factorX < minFactor) minFactor = factorX; + if (factorY > 0 && factorY < minFactor) minFactor = factorY; + if (factorZ > 0 && factorZ < minFactor) minFactor = factorZ; + // We should add dir * lowest factor to step to the first next block. + movement.set(eyeDir).mul(minFactor); + pos.add(movement); + if (getBlockAt(pos) > 0) { + return new Vector3i( + (int) Math.floor(pos.x), + (int) Math.floor(pos.y), + (int) Math.floor(pos.z) + ); + } + } + return null; + } + + /** + * Helper function to find the next whole number, given a current number and + * an indication of which direction the number is increasing. + * @param n The current number. + * @param sign An indication of which way the number is increasing. + * @return The next whole number up from the current number. + */ + private static float getNextStep(float n, float sign) { + if (sign > 0) { + if (Math.ceil(n) == n) { + return n + 1; + } else { + return Math.ceil(n); + } + } else if (sign < 0) { + if (Math.floor(n) == n) { + return n - 1; + } else { + return Math.floor(n); + } + } + return n; + } + /** * Gets the coordinates of a chunk at a given world position. * @param worldPos The world position. diff --git a/core/src/test/java/nl/andrewl/aos_core/model/WorldTest.java b/core/src/test/java/nl/andrewl/aos_core/model/WorldTest.java index 043b9b3..76edc2c 100644 --- a/core/src/test/java/nl/andrewl/aos_core/model/WorldTest.java +++ b/core/src/test/java/nl/andrewl/aos_core/model/WorldTest.java @@ -5,6 +5,7 @@ import org.joml.Vector3i; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class WorldTest { @Test @@ -31,4 +32,82 @@ public class WorldTest { 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))); } + + @Test + public void testGetLookingAtPos() { + World world = Worlds.testingWorld(); + // Spawn a block high in the air. + Vector3i blockPos = new Vector3i(20, 20, 20); + world.setBlockAt(blockPos.x, blockPos.y, blockPos.z, (byte) 5); + assertEquals( // Looking straight down onto block. + blockPos, + world.getLookingAtPos( + new Vector3f(20.5f, 25, 20.5f), + new Vector3f(0, -1, 0), + 15 + ) + ); + assertEquals( // Looking straight up. + blockPos, + world.getLookingAtPos( + new Vector3f(20.5f, 15, 20.5f), + new Vector3f(0, 1, 0), + 15 + ) + ); + assertEquals( // Looking towards -Z + blockPos, + world.getLookingAtPos( + new Vector3f(20.5f, 20.5f, 26f), + new Vector3f(0, 0, -1), + 15 + ) + ); + assertEquals( // Looking towards +Z + blockPos, + world.getLookingAtPos( + new Vector3f(20.5f, 20.5f, 15f), + new Vector3f(0, 0, 1), + 15 + ) + ); + assertEquals( // Looking towards -X + blockPos, + world.getLookingAtPos( + new Vector3f(26f, 20.5f, 20.5f), + new Vector3f(-1, 0, 0), + 15 + ) + ); + assertEquals( // Looking towards +X + blockPos, + world.getLookingAtPos( + new Vector3f(15f, 20.5f, 20.5f), + new Vector3f(1, 0, 0), + 15 + ) + ); + + // Looking up into the void. + assertNull(world.getLookingAtPos( + new Vector3f(0, 30, 0), + new Vector3f(0, 1, 0), + 10 + )); + + // Looking straight down, but too far away. + assertNull(world.getLookingAtPos( + new Vector3f(0.5f, 20, 0.5f), + new Vector3f(0, -1, 0), + 15 + )); + assertEquals( // Standing in the corner, looking into center of block. + blockPos, + world.getLookingAtPos( + new Vector3f(20f, 21f, 20f), + new Vector3f(1, -2, 1).normalize(), + 15 + ) + ); + } }