summary refs log tree commit diff
path: root/src/World/Generation
diff options
context:
space:
mode:
Diffstat (limited to 'src/World/Generation')
-rw-r--r--src/World/Generation/ChunkMeshing.cpp88
-rw-r--r--src/World/Generation/ChunkMeshing.hpp42
-rw-r--r--src/World/Generation/ChunkNeighbors.cpp26
-rw-r--r--src/World/Generation/ChunkNeighbors.hpp13
-rw-r--r--src/World/Generation/Lighting.cpp9
-rw-r--r--src/World/Generation/Lighting.hpp4
6 files changed, 141 insertions, 41 deletions
diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp
index 002dd79..3855de4 100644
--- a/src/World/Generation/ChunkMeshing.cpp
+++ b/src/World/Generation/ChunkMeshing.cpp
@@ -2,15 +2,53 @@
 
 namespace MC::World::Generation::ChunkMeshing {
 
-ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) {
+ChunkMesh mesh_chunk(Chunk& chunk, const SurroundingContext& context) {
     using namespace Detail;
 
     return {
-        create_mesh<DefaultMeshDecisions>(chunk, neighbors),
-        create_mesh<WaterMeshDecisions>(chunk, neighbors)
+        create_mesh<DefaultMeshDecisions>(chunk, context),
+        create_mesh<WaterMeshDecisions>(chunk, context)
     };
 }
 
+SurroundingContext::Block& SurroundingContext::at(Position::BlockOffset p) {
+    return m_blocks[pos(p)];
+}
+
+const SurroundingContext::Block& SurroundingContext::at(Position::BlockOffset p) const {
+    return m_blocks[pos(p)];
+}
+
+USize SurroundingContext::pos(Position::BlockOffset p) {
+    // First we calculate the index as if there were no gaps.
+    USize pos = 0;
+    pos += p.x() + 1;
+    pos += (p.z() + 1) * (Chunk::Width + 2);
+
+    // Then we substract the gaps.
+    Int g = (p.z() + 1) - (p.x() < 0);
+    UInt gap_count = std::clamp<Int>(g, 0, Chunk::Width);
+    pos -= gap_count * Chunk::Width;
+
+    pos += p.y() * surrounding_block_count;
+    return pos;
+}
+
+SurroundingContext create_meshing_context(const Chunk& chunk, ChunkNeighbors& neighbors) {
+    SurroundingContext context{};
+    for (I16 x = -1; x <= (I16)Chunk::Width; x++) {
+        for (I16 z = -1; z <= (I16)Chunk::Width; z++) {
+            for (I16 y = 0; y < (I16)Chunk::Height; y++) {
+                Position::BlockOffset pos{x, y, z};
+                if (pos.fits_within_chunk()) continue;
+                auto [does_exist, block] = get_block_wrapping(chunk, neighbors, {pos.x(), pos.y(), pos.z()});
+                context.at(pos) = {does_exist, block};
+            }
+        }
+    }
+    return context;
+}
+
 namespace Detail {
 
 Face<Vertex> DefaultMeshDecisions::face_positions(BlockSide side, U32 x, U32 y, U32 z) {
@@ -124,15 +162,15 @@ Face<Normal> DefaultMeshDecisions::face_normals(BlockSide side) {
     return {normal, normal, normal, normal};
 }
 
-Face<Light> DefaultMeshDecisions::face_light(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
-    auto neighbor = get_opposing_neighbor(chunk, neighbors, x, y, z, side);
+Face<Light> DefaultMeshDecisions::face_light(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) {
+    auto neighbor = get_opposing_neighbor(chunk, context, x, y, z, side).block;
     if (!neighbor.type.is_translucent()) return {};
 
     auto light = (F32)neighbor.light / (F32)255;
     return {light, light, light, light};
 }
 
-Face<AO> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+Face<AO> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) {
     std::array<Vector<3, I32>, 8> offsets{};
     // Given a block position, these offsets can be added to it to get the 8 blocks necessary to calculate AO.
     // There are 4 corners and 4 sides, corners are visually distinguished by the lack of spaces and
@@ -167,13 +205,15 @@ Face<AO> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors
         auto b = offsets[++offset_index]; // corner
         auto c = offsets[++offset_index % 8];
 
-        auto block_a = get_block_wrapping(chunk, neighbors, {x + a.x(), y + a.y(), z + a.z()});
-        auto block_b = get_block_wrapping(chunk, neighbors, {x + b.x(), y + b.y(), z + b.z()});
-        auto block_c = get_block_wrapping(chunk, neighbors, {x + c.x(), y + c.y(), z + c.z()});
+        auto p = [=](auto o) -> Position::BlockOffset { return {(I16)((I16)x + o.x()), (I16)((I16)y + o.y()), (I16)((I16)z + o.z())}; };
+        auto block_a = get_block_from_chunk_or_context(chunk, context, p(a));
+        auto block_b = get_block_from_chunk_or_context(chunk, context, p(b));
+        auto block_c = get_block_from_chunk_or_context(chunk, context, p(c));
 
-        F32 occlusion_a = block_a.empty() ? 0 : 1;
-        F32 occlusion_b = block_b.empty() ? 0 : 1;
-        F32 occlusion_c = block_c.empty() ? 0 : 1;
+        auto is_occupied = [](auto b) -> F32 { return b.does_exist && !b.block.empty(); };
+        F32 occlusion_a = is_occupied(block_a);
+        F32 occlusion_b = is_occupied(block_b);
+        F32 occlusion_c = is_occupied(block_c);
 
         if (occlusion_a + occlusion_c == 2.0f) {
             vertex_ao[vertex] = 1.0f;
@@ -195,15 +235,24 @@ Vector<3, I32> DefaultMeshDecisions::get_face_normal(BlockSide side) {
     };
 }
 
-Chunk::BlockData DefaultMeshDecisions::get_opposing_neighbor(const Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+SurroundingContext::Block DefaultMeshDecisions::get_block_from_chunk_or_context(
+    const Chunk& chunk, const SurroundingContext& context, Position::BlockOffset pos
+) {
+    if (pos.fits_within_chunk()) return {true, chunk.at(pos)};
+    return context.at(pos);
+}
+
+SurroundingContext::Block DefaultMeshDecisions::get_opposing_neighbor(const Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) {
     Vector<3, I32> offset = get_face_normal(side);
-    auto neighbor_position = offset + Vector<3, I32>{x, y, z};
+    auto pos = offset + Vector<3, I32>{x, y, z};
 
-    return get_block_wrapping(chunk, neighbors, neighbor_position);
+    return get_block_from_chunk_or_context(chunk, context, {(I16)pos.x(), (I16)pos.y(), (I16)pos.z()});
 }
 
-Bool DefaultMeshDecisions::is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
-    auto block = get_opposing_neighbor(chunk, neighbors, x, y, z, side);
+Bool DefaultMeshDecisions::is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) {
+    auto [does_exist, block] = get_opposing_neighbor(chunk, context, x, y, z, side);
+    // Faces on the chunk border are hidden by default, even if the opposing chunk does not exist.
+    if (!does_exist) return false;
     return block.type.is_translucent();
 }
 
@@ -211,8 +260,9 @@ Bool DefaultMeshDecisions::should_ignore_block(Chunk::BlockData block) {
     return block.empty() || block.type == BlockType::Water;
 }
 
-Bool WaterMeshDecisions::is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
-    auto block = get_opposing_neighbor(chunk, neighbors, x, y, z, side);
+Bool WaterMeshDecisions::is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) {
+    auto [does_exist, block] = get_opposing_neighbor(chunk, context, x, y, z, side);
+    if (!does_exist) return false; // See above.
     return block.type.is_translucent() && block.type != BlockType::Water;
 }
 
diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp
index fb5fea9..5401580 100644
--- a/src/World/Generation/ChunkMeshing.hpp
+++ b/src/World/Generation/ChunkMeshing.hpp
@@ -5,11 +5,32 @@
 #include "../BlockSide.hpp"
 #include "../Chunk.hpp"
 #include "ChunkNeighbors.hpp"
+#include "../ChunkRegistry.hpp"
 
 namespace MC::World::Generation::ChunkMeshing {
 
+// Literally just the blocks squarely surrounding a chunk.
+// Example context for a 2x1x2 chunk:
+// c c c c
+// c x x c
+// c x x c
+// c c c c
+class SurroundingContext {
+public:
+    struct Block { Bool does_exist; Chunk::BlockData block; };
+
+    Block& at(Position::BlockOffset p);
+    const Block& at(Position::BlockOffset p) const;
+private:
+    static USize pos(Position::BlockOffset p);
+    static constexpr USize surrounding_block_count = Chunk::Width * 4 + 4;
+
+    Block m_blocks[surrounding_block_count * Chunk::Height] = {};
+};
+SurroundingContext create_meshing_context(const Chunk& chunk, ChunkNeighbors& neighbors);
+
 struct ChunkMesh { GFX::Mesh land_mesh, water_mesh; };
-ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors);
+ChunkMesh mesh_chunk(Chunk& chunk, const SurroundingContext& context);
 
 namespace Detail {
 
@@ -23,7 +44,7 @@ template <typename T>
 using Face = std::array<T, 4>;
 
 template <typename Decisions>
-GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) {
+GFX::Mesh create_mesh(Chunk& chunk, const SurroundingContext& context) {
     GFX::Util::MeshBuilder<Normal, TexCoord, Light, AO> builder;
 
     for (Int x = 0; x < Chunk::Width; x++) {
@@ -34,7 +55,7 @@ GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) {
                     continue;
 
                 for (auto side: BlockSide::all()) {
-                    if (!Decisions::is_face_visible(chunk, neighbors, x, y, z, side))
+                    if (!Decisions::is_face_visible(chunk, context, x, y, z, side))
                         continue;
 
                     U32 s = builder.vertex_count();
@@ -42,8 +63,8 @@ GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) {
                     builder.positions(Decisions::face_positions(side, x, y, z));
                     builder.attributes<0>(Decisions::face_normals(side));
                     builder.attributes<1>(Decisions::face_tex_coords(block.type, side));
-                    builder.attributes<2>(Decisions::face_light(chunk, neighbors, x, y, z, side));
-                    builder.attributes<3>(Decisions::face_ao_values(chunk, neighbors, x, y, z, side));
+                    builder.attributes<2>(Decisions::face_light(chunk, context, x, y, z, side));
+                    builder.attributes<3>(Decisions::face_ao_values(chunk, context, x, y, z, side));
                     builder.indices(std::array{ s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 });
                 }
             }
@@ -58,19 +79,20 @@ public:
     static Face<Vertex> face_positions(BlockSide side, U32 x, U32 y, U32 z);
     static Face<TexCoord> face_tex_coords(BlockType type, BlockSide side);
     static Face<Normal> face_normals(BlockSide side);
-    static Face<Light> face_light(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
-    static Face<AO> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
+    static Face<Light> face_light(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
+    static Face<AO> face_ao_values(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
 
     static Vector<3, I32> get_face_normal(BlockSide side);
-    static Chunk::BlockData get_opposing_neighbor(const Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
+    static SurroundingContext::Block get_block_from_chunk_or_context(const Chunk& chunk, const SurroundingContext& context, Position::BlockOffset pos);
+    static SurroundingContext::Block get_opposing_neighbor(const Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
 
-    static Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
+    static Bool is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
     static Bool should_ignore_block(Chunk::BlockData block);
 };
 
 class WaterMeshDecisions final : public DefaultMeshDecisions {
 public:
-    static Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side);
+    static Bool is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
     static Bool should_ignore_block(Chunk::BlockData block);
 };
 
diff --git a/src/World/Generation/ChunkNeighbors.cpp b/src/World/Generation/ChunkNeighbors.cpp
index f34466e..c3ae5cb 100644
--- a/src/World/Generation/ChunkNeighbors.cpp
+++ b/src/World/Generation/ChunkNeighbors.cpp
@@ -2,7 +2,28 @@
 
 namespace MC::World::Generation {
 
-Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos) {
+ChunkNeighbors find_chunk_neighbors(ChunkIndex chunk, ChunkRegistry& chunks) {
+    UInt neighbor_index = 0;
+    std::array<Chunk*, 8> neighbors{};
+    for (I32 x = -1; x <= 1; x++) {
+        for (I32 y = -1; y <= 1; y++) {
+            if (x == 0 && y == 0) continue;
+
+            auto& neighbor_data = chunks.get({chunk.x + x, chunk.y + y});
+            if (neighbor_data.chunk.has_value()) neighbors[neighbor_index++] = &neighbor_data.chunk.value();
+        }
+    }
+
+    // Layout of neighboring chunks in `neighbors` array:
+    // (-1; -1) > (-1;  0) > (-1; 1) > (0; -1)
+    // ( 0;  1) > ( 1; -1) > ( 1; 0) > (1;  1)
+    return {
+        neighbors[3], neighbors[6], neighbors[4], neighbors[1],
+        neighbors[5], neighbors[7], neighbors[2], neighbors[0],
+    };
+}
+
+GetBlockWrappingResult get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos) {
     const Chunk* chunk_to_ask;
 
     auto overflow = [](I32& c, I32 max) -> I8 {
@@ -28,7 +49,8 @@ Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& ne
     else if (zo == -1) { chunk_to_ask = neighbors.north; }
     else { chunk_to_ask = &chunk; }
 
-    return chunk_to_ask->at(pos.x(), pos.y(), pos.z());
+    if (!chunk_to_ask) return {false};
+    return {true, chunk_to_ask->at(pos.x(), pos.y(), pos.z())};
 }
 
 }
\ No newline at end of file
diff --git a/src/World/Generation/ChunkNeighbors.hpp b/src/World/Generation/ChunkNeighbors.hpp
index 1207c60..83fca4c 100644
--- a/src/World/Generation/ChunkNeighbors.hpp
+++ b/src/World/Generation/ChunkNeighbors.hpp
@@ -1,13 +1,22 @@
 #pragma once
 
 #include "../Chunk.hpp"
+#include "../ChunkRegistry.hpp"
 
 namespace MC::World::Generation {
 struct ChunkNeighbors {
     Chunk *north, *east, *south, *west;
     Chunk *north_east, *south_east, *south_west, *north_west;
+
+    Bool all_exist() const { return north && east && south && west && north_east && south_east && south_west && north_west; }
 };
 
-Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos);
+ChunkNeighbors find_chunk_neighbors(ChunkIndex chunk, ChunkRegistry& chunks);
+
+struct GetBlockWrappingResult {
+    bool does_exist;
+    Chunk::BlockData block;
+};
+GetBlockWrappingResult get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos);
 
-}
\ No newline at end of file
+}
diff --git a/src/World/Generation/Lighting.cpp b/src/World/Generation/Lighting.cpp
index 39d8320..f7c7bbf 100644
--- a/src/World/Generation/Lighting.cpp
+++ b/src/World/Generation/Lighting.cpp
@@ -23,12 +23,12 @@ void Lighting::add_block(Chunk& chunk, Position::BlockLocal position, U8 light)
     enqueue(chunk.index(), position, light);
 }
 
-void Lighting::illuminate(ChunkRegistry& chunks) {
+void Lighting::illuminate(Chunk& chunk) {
     while (!m_queue.empty()) {
         auto op = m_queue.front();
         m_queue.pop();
 
-        process(chunks, op);
+        process(chunk, op);
     }
 }
 
@@ -36,10 +36,7 @@ void Lighting::enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origi
     m_queue.push({chunk, position, origin});
 }
 
-void Lighting::process(ChunkRegistry& chunks, Operation op) {
-    auto& chunk_data = chunks.get(op.chunk);
-    auto& chunk = chunk_data.chunk.value();
-
+void Lighting::process(Chunk& chunk, Operation op) {
     for (auto direction : Position::axis_directions) {
         auto neighbor_pos = op.position.offset(direction);
         if (!neighbor_pos.fits_within_chunk()) continue;
diff --git a/src/World/Generation/Lighting.hpp b/src/World/Generation/Lighting.hpp
index 72af0e1..4587266 100644
--- a/src/World/Generation/Lighting.hpp
+++ b/src/World/Generation/Lighting.hpp
@@ -13,7 +13,7 @@ public:
     void add_chunk(Chunk& chunk);
     void add_block(Chunk& chunk, Position::BlockLocal position, U8 light);
 
-    void illuminate(ChunkRegistry& chunks);
+    void illuminate(Chunk& chunk);
 private:
     static constexpr U8 SunBrightness = 200;
     static constexpr U8 DefaultFalloff = 10;
@@ -25,7 +25,7 @@ private:
     };
 
     void enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origin);
-    void process(ChunkRegistry& chunks, Operation op);
+    void process(Chunk& chunk, Operation op);
 
     std::queue<Operation> m_queue;
 };