summary refs log tree commit diff
path: root/src/World/Generation/ChunkMeshing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/World/Generation/ChunkMeshing.cpp')
-rw-r--r--src/World/Generation/ChunkMeshing.cpp88
1 files changed, 69 insertions, 19 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;
 }