diff --git a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/world/WorldRenderer.java b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/world/WorldRenderer.java index 9b0c1f4..d0745cb 100644 --- a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/world/WorldRenderer.java +++ b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/world/WorldRenderer.java @@ -17,8 +17,12 @@ import io.github.xenfork.freeworld.client.render.model.VertexLayouts; import io.github.xenfork.freeworld.client.world.chunk.ClientChunk; import io.github.xenfork.freeworld.core.math.AABBox; +import io.github.xenfork.freeworld.util.Direction; import io.github.xenfork.freeworld.world.World; +import io.github.xenfork.freeworld.world.WorldListener; import io.github.xenfork.freeworld.world.block.BlockType; +import io.github.xenfork.freeworld.world.chunk.Chunk; +import io.github.xenfork.freeworld.world.chunk.ChunkPos; import org.jetbrains.annotations.NotNull; import org.joml.*; @@ -31,7 +35,7 @@ * @author squid233 * @since 0.1.0 */ -public final class WorldRenderer implements GLResource { +public final class WorldRenderer implements GLResource, WorldListener { private final GameRenderer gameRenderer; private final World world; private final ExecutorService executor; @@ -46,6 +50,8 @@ public final class WorldRenderer implements GLResource { public WorldRenderer(GameRenderer gameRenderer, World world) { this.gameRenderer = gameRenderer; this.world = world; + world.addListener(this); + final int processors = Runtime.getRuntime().availableProcessors(); this.executor = new ThreadPoolExecutor(processors, processors, @@ -65,9 +71,7 @@ public Thread newThread(@NotNull Runnable r) { for (int x = 0; x < world.xChunks; x++) { for (int y = 0; y < world.yChunks; y++) { for (int z = 0; z < world.zChunks; z++) { - final ClientChunk chunk = new ClientChunk(world, x, y, z); - this.chunks[(y * world.zChunks + z) * world.xChunks + x] = chunk; - chunk.copyFrom(world.getChunk(x, y, z)); + this.chunks[(y * world.zChunks + z) * world.xChunks + x] = new ClientChunk(world, x, y, z); } } } @@ -80,6 +84,10 @@ private static DefaultVertexBuilder createVertexBuilder() { public void compileChunks() { for (ClientChunk chunk : chunks) { if (chunk.shouldRecompile && !chunk.submitted) { + final Chunk chunk1 = world.getChunk(chunk.x(), chunk.y(), chunk.z()); + if (chunk1 != null) { + chunk.copyFrom(chunk1); + } chunk.future = executor.submit(new ChunkCompileTask(gameRenderer, this, chunk)); chunk.submitted = true; } @@ -166,6 +174,39 @@ public HitResult selectBlock() { return new HitResult(nearestBlock, nearestX, nearestY, nearestZ, nearestBlock == null); } + @Override + public void onBlockChanged(int x, int y, int z) { + final ClientChunk chunk = getChunkByAbsolutePos(x, y, z); + if (chunk != null) { + chunk.markDirty(); + } + for (Direction direction : Direction.LIST) { + final ClientChunk chunk1 = getChunkByAbsolutePos( + x + direction.axisX(), + y + direction.axisY(), + z + direction.axisZ() + ); + if (chunk1 != null) { + chunk1.markDirty(); + } + } + } + + private ClientChunk getChunk(int x, int y, int z) { + if (x >= 0 && x < world.xChunks && y >= 0 && y < world.yChunks && z >= 0 && z < world.zChunks) { + return chunks[(y * world.zChunks + z) * world.xChunks + x]; + } + return null; + } + + private ClientChunk getChunkByAbsolutePos(int x, int y, int z) { + return getChunk( + ChunkPos.absoluteToChunk(x), + ChunkPos.absoluteToChunk(y), + ChunkPos.absoluteToChunk(z) + ); + } + public VertexBuilderPool vertexBuilderPool() { return vertexBuilderPool; } diff --git a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/world/chunk/ClientChunk.java b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/world/chunk/ClientChunk.java index f5a5ebe..3f06deb 100644 --- a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/world/chunk/ClientChunk.java +++ b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/world/chunk/ClientChunk.java @@ -84,6 +84,12 @@ public void render(GLStateMgr gl) { } } + @Override + public void markDirty() { + super.markDirty(); + shouldRecompile = true; + } + @Override public void close(GLStateMgr gl) { gl.deleteVertexArrays(vao); diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java index 6141c37..01299fb 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java @@ -11,6 +11,7 @@ package io.github.xenfork.freeworld.world; import io.github.xenfork.freeworld.world.block.BlockType; +import io.github.xenfork.freeworld.world.block.BlockTypes; import io.github.xenfork.freeworld.world.chunk.Chunk; import io.github.xenfork.freeworld.world.chunk.ChunkPos; import io.github.xenfork.freeworld.world.entity.Entity; @@ -36,6 +37,7 @@ public final class World { public final Chunk[] chunks = new Chunk[xChunks * yChunks * zChunks]; public final List entities = new ArrayList<>(); private final MotionSystem motionSystem = new MotionSystem(); + private final List listeners = new ArrayList<>(); public World(String name) { for (int x = 0; x < xChunks; x++) { @@ -49,6 +51,10 @@ public World(String name) { } } + public void addListener(WorldListener listener) { + listeners.add(listener); + } + public void tick() { motionSystem.process(entities); } @@ -63,7 +69,10 @@ public Entity createEntity(EntityType type, double x, double y, double z) { } public Chunk getChunk(int x, int y, int z) { - return chunks[(y * zChunks + z) * xChunks + x]; + if (x >= 0 && x < xChunks && y >= 0 && y < yChunks && z >= 0 && z < zChunks) { + return chunks[(y * zChunks + z) * xChunks + x]; + } + return null; } public Chunk getChunkByAbsolutePos(int x, int y, int z) { @@ -75,22 +84,30 @@ public Chunk getChunkByAbsolutePos(int x, int y, int z) { } public BlockType getBlockType(int x, int y, int z) { - return getChunkByAbsolutePos(x, y, z).getBlockType( - ChunkPos.absoluteToRelative(x), - ChunkPos.absoluteToRelative(y), - ChunkPos.absoluteToRelative(z) - ); + final Chunk chunk = getChunkByAbsolutePos(x, y, z); + if (chunk != null) { + return chunk.getBlockType( + ChunkPos.absoluteToRelative(x), + ChunkPos.absoluteToRelative(y), + ChunkPos.absoluteToRelative(z) + ); + } + return BlockTypes.AIR; } public void setBlockType(int x, int y, int z, BlockType blockType) { final Chunk chunk = getChunkByAbsolutePos(x, y, z); - chunk.setBlockType( - ChunkPos.absoluteToRelative(x), - ChunkPos.absoluteToRelative(y), - ChunkPos.absoluteToRelative(z), - blockType - ); - chunk.markDirty(); + if (chunk != null) { + chunk.setBlockType( + ChunkPos.absoluteToRelative(x), + ChunkPos.absoluteToRelative(y), + ChunkPos.absoluteToRelative(z), + blockType + ); + for (WorldListener listener : listeners) { + listener.onBlockChanged(x, y, z); + } + } } public Chunk createChunk(int x, int y, int z) { diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/WorldListener.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/WorldListener.java new file mode 100644 index 0000000..b930972 --- /dev/null +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/WorldListener.java @@ -0,0 +1,19 @@ +/* + * freeworld - 3D sandbox game + * Copyright (C) 2024 XenFork Union + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +package io.github.xenfork.freeworld.world; + +/** + * @author squid233 + * @since 0.1.0 + */ +public interface WorldListener { + void onBlockChanged(int x, int y, int z); +}