From 56d8c65e0e7739c6a0d7e5ca2ac3d692812a6fe3 Mon Sep 17 00:00:00 2001 From: valoeghese Date: Fri, 28 Feb 2020 22:42:00 +1300 Subject: [PATCH] start dynamic chunkloading. crashes. --- .../github/halotroop/litecraft/Litecraft.java | 5 + .../halotroop/litecraft/world/World.java | 116 +++++++++++------- .../engine/elements/objects/Player.java | 40 +++++- 3 files changed, 115 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/github/halotroop/litecraft/Litecraft.java b/src/main/java/com/github/halotroop/litecraft/Litecraft.java index 5e52f79..8c6009f 100644 --- a/src/main/java/com/github/halotroop/litecraft/Litecraft.java +++ b/src/main/java/com/github/halotroop/litecraft/Litecraft.java @@ -182,4 +182,9 @@ public class Litecraft extends Game ginger3D.setGingerPlayer(this.world.player); } } + + public World getWorld() + { + return this.world; + } } \ No newline at end of file diff --git a/src/main/java/com/github/halotroop/litecraft/world/World.java b/src/main/java/com/github/halotroop/litecraft/world/World.java index c815ff5..b8aa718 100644 --- a/src/main/java/com/github/halotroop/litecraft/world/World.java +++ b/src/main/java/com/github/halotroop/litecraft/world/World.java @@ -1,6 +1,6 @@ package com.github.halotroop.litecraft.world; -import java.util.Random; +import java.util.*; import java.util.function.LongConsumer; import org.joml.Vector3f; @@ -26,9 +26,9 @@ public class World implements BlockAccess, WorldGenConstants private final long seed; private final int dimension; public Player player; + private final int renderSize; - // This will likely become the main public constructor after we add dynamic chunkloading - private World(long seed, Dimension dim, LitecraftSave save) + public World(long seed, int renderSize, Dimension dim, LitecraftSave save) { this.chunks = new Long2ObjectArrayMap<>(); this.seed = seed; @@ -37,36 +37,7 @@ public class World implements BlockAccess, WorldGenConstants this.genBlockAccess = new GenerationWorld(this); this.save = save; this.dimension = dim.id; - } - - public void spawnPlayer() - { - int y = this.findAir(0, 0); - if (y == -1) - y = 300; // yeet - - this.spawnPlayer(0, y, -3); - } - - public Player spawnPlayer(float x, float y, float z) - { - TexturedModel dirtModel = ModelLoader.loadGenericCube("block/cubes/soil/dirt.png"); - this.player = new Player(dirtModel, new Vector3f(x, y, z), 0, 180f, 0, new Vector3f(0.2f, 0.2f, 0.2f)); - this.player.isVisible = false; - return this.player; - } - - // this constructor will likely not be neccesary when we have dynamic chunkloading - public World(long seed, int size, Dimension dim, LitecraftSave save) - { - this(seed, dim, save); - long time = System.currentTimeMillis(); - System.out.println("Generating world!"); - for (int i = (0 - (size / 2)); i < (size / 2); i++) - for (int k = (0 - (size / 2)); k < (size / 2); k++) - for (int y = -2; y < 2; ++y) - this.loadChunk(i, y, k).setRender(true); - System.out.println("Generated world in " + (System.currentTimeMillis() - time) + " milliseconds"); + this.renderSize = renderSize; } public int findAir(int x, int z) @@ -84,6 +55,30 @@ public class World implements BlockAccess, WorldGenConstants return -1; // if it fails, returns -1 } + public void spawnPlayer() + { + int y = this.findAir(0, 0); + if (y == -1) + y = 300; // yeet + + this.spawnPlayer(0, y, -3); + } + + public Player spawnPlayer(float x, float y, float z) + { + // Player model and stuff + TexturedModel dirtModel = ModelLoader.loadGenericCube("block/cubes/soil/dirt.png"); + this.player = new Player(dirtModel, new Vector3f(x, y, z), 0, 180f, 0, new Vector3f(0.2f, 0.2f, 0.2f)); + this.player.isVisible = false; + // Generate world around player + long time = System.currentTimeMillis(); + System.out.println("Generating world!"); + this.updateLoadedChunks(this.player.getChunkX(), this.player.getChunkY(), this.player.getChunkZ()); + System.out.println("Generated world in " + (System.currentTimeMillis() - time) + " milliseconds"); + // return player + return this.player; + } + public Chunk getChunk(int chunkX, int chunkY, int chunkZ) { Chunk chunk = this.chunks.computeIfAbsent(posHash(chunkX, chunkY, chunkZ), pos -> @@ -97,19 +92,21 @@ public class World implements BlockAccess, WorldGenConstants return chunk; } - public Chunk loadChunk(int chunkX, int chunkY, int chunkZ) + public Chunk getChunkToLoad(int chunkX, int chunkY, int chunkZ) { - return this.chunks.computeIfAbsent(posHash(chunkX, chunkY, chunkZ), pos -> - { - Chunk readChunk = save.readChunk(chunkX, chunkY, chunkZ, this.dimension); - return readChunk == null ? this.chunkGenerator.generateChunk(chunkX, chunkY, chunkZ) : readChunk; - }); + // try get an already loaded chunk + Chunk result = this.chunks.get(posHash(chunkX, chunkY, chunkZ)); + if (result != null) + return result; + // try read a chunk from memory + result = save.readChunk(chunkX, chunkY, chunkZ, this.dimension); + // if neither of those work, generate the chunk + return result == null ? this.chunkGenerator.generateChunk(chunkX, chunkY, chunkZ) : result; } /** @return whether the chunk was unloaded without errors. Will often, but not always, be equal to whether the chunk was already in memory. */ - public boolean unloadChunk(int chunkX, int chunkY, int chunkZ) + private boolean unloadChunk(long posHash) { - long posHash = posHash(chunkX, chunkY, chunkZ); Chunk chunk = this.chunks.get(posHash); // If the chunk is not in memory, it does not need to be unloaded if (chunk == null) return false; @@ -119,6 +116,11 @@ public class World implements BlockAccess, WorldGenConstants return result; } + private void populateChunk(Chunk chunk) + { + this.populateChunk(chunk.chunkX, chunk.chunkY, chunk.chunkZ, chunk.chunkStartX, chunk.chunkStartY, chunk.chunkStartZ); + } + private void populateChunk(int chunkX, int chunkY, int chunkZ, int chunkStartX, int chunkStartY, int chunkStartZ) { Random rand = new Random(this.seed + 5828671L * chunkX + -47245139L * chunkY + 8972357 * (long) chunkZ); @@ -176,6 +178,38 @@ public class World implements BlockAccess, WorldGenConstants public static final int SEA_LEVEL = 0; + public void updateLoadedChunks(int newChunkX, int newChunkY, int newChunkZ) + { + List toKeep = new ArrayList<>(); + // loop over rendered area, adding chunks that are needed + for (int x = -renderSize / 2; x < renderSize / 2; x++) + for (int z = -renderSize / 2; z < renderSize / 2; z++) + for (int y = -2; y < 2; ++y) + toKeep.add(this.getChunkToLoad(x, y, z)); + // list of keys to remove + LongList toRemove = new LongArrayList(); + // check which loaded chunks are not neccesary + this.chunks.forEach((pos, chunk) -> + { + if (!toKeep.contains(chunk)) + toRemove.add((long) pos); + }); + // unload unneccesary chunks from chunk array + toRemove.forEach((LongConsumer) pos -> this.unloadChunk(pos)); + // populate chunks to render if they are not rendered, then render them + toKeep.forEach(chunk -> { + if (!chunk.isFullyGenerated()) + { + this.populateChunk(chunk); + chunk.setFullyGenerated(true); + } + boolean alreadyRendering = chunk.doRender(); // if it's already rendering then it's most likely in the map + chunk.setRender(true); + if (!alreadyRendering) + this.chunks.put(posHash(chunk.chunkX, chunk.chunkY, chunk.chunkZ), chunk); + }); + } + private static final class GenerationWorld implements BlockAccess, WorldGenConstants { GenerationWorld(World parent) diff --git a/src/main/java/com/github/hydos/ginger/engine/elements/objects/Player.java b/src/main/java/com/github/hydos/ginger/engine/elements/objects/Player.java index ac409b3..bc992c5 100644 --- a/src/main/java/com/github/hydos/ginger/engine/elements/objects/Player.java +++ b/src/main/java/com/github/hydos/ginger/engine/elements/objects/Player.java @@ -2,20 +2,29 @@ package com.github.hydos.ginger.engine.elements.objects; import org.joml.Vector3f; +import com.github.halotroop.litecraft.Litecraft; import com.github.halotroop.litecraft.util.RelativeDirection; +import com.github.halotroop.litecraft.world.World; +import com.github.halotroop.litecraft.world.gen.WorldGenConstants; import com.github.hydos.ginger.engine.api.GingerRegister; import com.github.hydos.ginger.engine.io.Window; import com.github.hydos.ginger.engine.render.models.TexturedModel; import com.github.hydos.ginger.main.settings.Constants; -public class Player extends RenderObject +public class Player extends RenderObject implements WorldGenConstants { private boolean isInAir = false; private double upwardsSpeed; private boolean noWeight = true; // because the force of gravity on an object's mass is called WEIGHT, not gravity + private int chunkX, chunkY, chunkZ; public Player(TexturedModel model, Vector3f position, float rotX, float rotY, float rotZ, Vector3f scale) - { super(model, position, rotX, rotY, rotZ, scale); } + { + super(model, position, rotX, rotY, rotZ, scale); + this.chunkX = (int) position.x >> POS_SHIFT; + this.chunkY = (int) position.y >> POS_SHIFT; + this.chunkZ = (int) position.z >> POS_SHIFT; + } public void move(RelativeDirection direction) { @@ -32,12 +41,12 @@ public class Player extends RenderObject position.x -= Math.sin(ry) * Constants.movementSpeed; break; case LEFT: - ry -= NINETY_DEGREES; + ry -= RIGHT_ANGLE; position.z -= Math.cos(ry) * Constants.movementSpeed; position.x += Math.sin(ry) * Constants.movementSpeed; break; case RIGHT: - ry += NINETY_DEGREES; + ry += RIGHT_ANGLE; position.z -= Math.cos(ry) * Constants.movementSpeed; position.x += Math.sin(ry) * Constants.movementSpeed; break; @@ -51,7 +60,7 @@ public class Player extends RenderObject } } - private static final float NINETY_DEGREES = (float) (Math.PI / 2f); + private static final float RIGHT_ANGLE = (float) (Math.PI / 2f); private void jump() { @@ -62,11 +71,32 @@ public class Player extends RenderObject } } + public int getChunkX() + { return this.chunkX; } + + public int getChunkY() + { return this.chunkY; } + + public int getChunkZ() + { return this.chunkZ; } + public void updateMovement() { super.increasePosition(0, (float) (upwardsSpeed * (Window.getTime())), 0); upwardsSpeed += Constants.gravity.y() * Window.getTime(); isInAir = false; upwardsSpeed = 0; + + int newChunkX = (int) position.x >> POS_SHIFT; + int newChunkY = (int) position.y >> POS_SHIFT; + int newChunkZ = (int) position.z >> POS_SHIFT; + + if (newChunkX != this.chunkX || newChunkY != this.chunkY || newChunkZ != this.chunkZ) + { + Litecraft.getInstance().getWorld().updateLoadedChunks(newChunkX, newChunkY, newChunkZ); + this.chunkX = newChunkX; + this.chunkY = newChunkY; + this.chunkZ = newChunkZ; + } } }