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.cpp217
1 files changed, 139 insertions, 78 deletions
diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp
index 8996169..8944c53 100644
--- a/src/World/Generation/ChunkMeshing.cpp
+++ b/src/World/Generation/ChunkMeshing.cpp
@@ -1,6 +1,35 @@
 #include "ChunkMeshing.hpp"
 
-namespace MC::World::Generation {
+namespace MC::World::Generation::ChunkMeshing {
+
+Chunk::BlockData 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 {
+        if (c < 0) { c += max; return -1; }
+        if (c >= max) { c -= max; return 1; }
+        return 0;
+    };
+
+    auto xo = overflow(pos.x(), Chunk::Width);
+    auto yo = overflow(pos.y(), Chunk::Height);
+    auto zo = overflow(pos.z(), Chunk::Width);
+
+    // Blocks above and below a chunk are always Air.
+    if (yo != 0) return {};
+
+    if (xo == 1 && zo == 1) { chunk_to_ask = neighbors.south_east; }
+    else if (xo == 1 && zo == -1) { chunk_to_ask = neighbors.north_east; }
+    else if (xo == -1 && zo == 1) { chunk_to_ask = neighbors.south_west; }
+    else if (xo == -1 && zo == -1) { chunk_to_ask = neighbors.north_west; }
+    else if (xo == 1) { chunk_to_ask = neighbors.east; }
+    else if (xo == -1) { chunk_to_ask = neighbors.west; }
+    else if (zo == 1) { chunk_to_ask = neighbors.south; }
+    else if (zo == -1) { chunk_to_ask = neighbors.north; }
+    else { chunk_to_ask = &chunk; }
+
+    return chunk_to_ask->get(pos.x(), pos.y(), pos.z());
+}
 
 std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
     U8 atlas_width = 4;
@@ -9,15 +38,21 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
     Real width_step = 1.0f / atlas_width;
     Real height_step = 1.0f / atlas_height;
 
-    auto block_coords = [=](U8 x, U8 y) {
+    auto block_coords = [=](U8 x, U8 y) -> std::array<Vector<2, F32>, 4> {
         auto t = y * height_step;
         auto l = x * width_step;
         auto b = t + height_step;
         auto r = l + width_step;
 
-        return std::array<Vector<2, F32>, 4>{{
-            {l, b}, {r, b}, {r, t}, {l, t},
-        }};
+        // This is horrible and it was better before I restructured the vertex order...
+        // In the last version the front and back side pairs had the same structure in different winding,
+        // so a back_side check wasn't needed...
+        // Note: BlockSide::Front counts as a back side, because OpenGL has an inverted Z-axis compared to us.
+        Bool back_side = side == BlockSide::Front || side == BlockSide::Bottom || side == BlockSide::Left;
+        if (back_side) {
+            return {{{l, t}, {l, b}, {r, b}, {r, t}}};
+        }
+        return {{{r, t}, {l, t}, {l, b}, {r, b}}};
     };
 
     switch (type) {
@@ -25,15 +60,15 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
             return block_coords(1, 0);
         case BlockType::Grass:
             switch (side) {
-                case BlockSide::Front:
-                case BlockSide::Back:
-                case BlockSide::Left:
-                case BlockSide::Right:
-                    return block_coords(2, 0);
-                case BlockSide::Bottom:
-                    return block_coords(1, 0);
-                case BlockSide::Top:
-                    return block_coords(0, 0);
+            case BlockSide::Front:
+            case BlockSide::Back:
+            case BlockSide::Left:
+            case BlockSide::Right:
+                return block_coords(2, 0);
+            case BlockSide::Bottom:
+                return block_coords(1, 0);
+            case BlockSide::Top:
+                return block_coords(0, 0);
             }
         case BlockType::Stone:
             return block_coords(3, 0);
@@ -43,26 +78,26 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
             return block_coords(0, 1);
         case BlockType::Snow:
             switch (side) {
-                case BlockSide::Front:
-                case BlockSide::Back:
-                case BlockSide::Left:
-                case BlockSide::Right:
-                    return block_coords(3, 1);
-                case BlockSide::Bottom:
-                    return block_coords(1, 0);
-                case BlockSide::Top:
-                    return block_coords(2, 1);
+            case BlockSide::Front:
+            case BlockSide::Back:
+            case BlockSide::Left:
+            case BlockSide::Right:
+                return block_coords(3, 1);
+            case BlockSide::Bottom:
+                return block_coords(1, 0);
+            case BlockSide::Top:
+                return block_coords(2, 1);
             }
         case BlockType::Wood:
             switch (side) {
-                case BlockSide::Front:
-                case BlockSide::Back:
-                case BlockSide::Right:
-                case BlockSide::Left:
-                    return block_coords(0, 2);
-                case BlockSide::Bottom:
-                case BlockSide::Top:
-                    return block_coords(1, 2);
+            case BlockSide::Front:
+            case BlockSide::Back:
+            case BlockSide::Right:
+            case BlockSide::Left:
+                return block_coords(0, 2);
+            case BlockSide::Bottom:
+            case BlockSide::Top:
+                return block_coords(1, 2);
             }
         case BlockType::Leaves:
             return block_coords(2, 2);
@@ -84,76 +119,101 @@ std::array<Vector<3, F32>, 4> face_normals(BlockSide side) {
 }
 
 Bool is_face_visible(Chunk& chunk, const ChunkMeshing::ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+std::array<F32, 4> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+    std::array<Vector<3, I32>, 4> offsets{};
+    switch (side) {
+    case BlockSide::Front:
+        offsets = {{{0, 1, 1}, {-1, 0, 1}, {0, -1, 1}, {1, 0, 1}}}; // works!
+        break;
+    case BlockSide::Back:
+        offsets = {{{-1, 0, -1}, {0, 1, -1}, {1, 0, -1}, {0, -1, -1}}}; // works!
+        break;
+    case BlockSide::Top:
+        offsets = {{{-1, 1, 0}, {0, 1, 1}, {1, 1, 0}, {0, 1, -1}}};
+        break;
+    case BlockSide::Bottom:
+        offsets = {{{0, -1, 1}, {-1, -1, 0}, {0, -1, -1},  {1, -1, 0}}};
+        break;
+    case BlockSide::Right:
+        offsets = {{{1, 0, -1}, {1, 1, 0}, {1, 0, 1},  {1, -1, 0}}};
+        break;
+    case BlockSide::Left:
+        offsets = {{{-1, 1, 0}, {-1, 0, -1}, {-1, -1, 0},  {-1, 0, 1}}};
+        break;
+    }
+
+
+    std::array<F32, 4> vertex_ao{};
+
+    for (UInt vertex = 0; vertex < 4; vertex++) {
+        auto a = offsets[vertex];
+        auto b = offsets[(vertex + 1) % 4];
+
+        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()});
+
+        F32 occlusion_a = block_a.empty() ? 0 : 1;
+        F32 occlusion_b = block_b.empty() ? 0 : 1;
+
+        vertex_ao[vertex] = (occlusion_a + occlusion_b) / 2;
+    }
+
+    return vertex_ao;
+}
+
+Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
     Vector<3, I32> offset{};
     switch (side) {
-        case BlockSide::Front:
-            offset[2] = 1;
-            break;
-        case BlockSide::Back:
-            offset[2] = -1;
-            break;
-        case BlockSide::Top:
-            offset[1] = 1;
-            break;
-        case BlockSide::Bottom:
-            offset[1] = -1;
-            break;
-        case BlockSide::Left:
-            offset[0] = -1;
-            break;
-        case BlockSide::Right:
-            offset[0] = 1;
-            break;
+    case BlockSide::Front:
+        offset[2] = 1;
+        break;
+    case BlockSide::Back:
+        offset[2] = -1;
+        break;
+    case BlockSide::Top:
+        offset[1] = 1;
+        break;
+    case BlockSide::Bottom:
+        offset[1] = -1;
+        break;
+    case BlockSide::Right:
+        offset[0] = 1;
+        break;
+    case BlockSide::Left:
+        offset[0] = -1;
+        break;
     }
 
     Vector<3, I32> neighbor_pos{
-        (I32)x + offset.x(), (I32)y + offset.y(), (I32)z + offset.z(),
+        x + offset.x(), y + offset.y(), z + offset.z(),
     };
 
-    Chunk* chunk_to_ask;
-
-    if (neighbor_pos.z() < 0) {
-        chunk_to_ask = &neighbors.north;
-        neighbor_pos.z() += Chunk::Width;
-    } else if (neighbor_pos.x() >= (I32)Chunk::Width) {
-        chunk_to_ask = &neighbors.east;
-        neighbor_pos.x() -= Chunk::Width;
-    } else if (neighbor_pos.z() >= (I32)Chunk::Width) {
-        chunk_to_ask = &neighbors.south;
-        neighbor_pos.z() -= Chunk::Width;
-    } else if (neighbor_pos.x() < 0) {
-        chunk_to_ask = &neighbors.west;
-        neighbor_pos.x() += Chunk::Width;
-    } else {
-        chunk_to_ask = &chunk;
-    }
-
-    auto [neighbor] = chunk_to_ask->get(neighbor_pos.x(), neighbor_pos.y(), neighbor_pos.z());
+    auto [neighbor] = get_block_wrapping(chunk, neighbors, neighbor_pos);
     return neighbor.is_transparent();
 }
 
-GFX::Mesh ChunkMeshing::create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) {
+GFX::Mesh create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) {
     std::vector<Vector<3, F32>> positions{};
     std::vector<Vector<3, F32>> normals{};
     std::vector<Vector<2, F32>> tex_coords{};
+    std::vector<F32> ambient_occlusion_values{};
     std::vector<U32> indices{};
 
     for (Int x = 0; x < Chunk::Width; x++) {
         for (Int y = 0; y < Chunk::Height; y++) {
             for (Int z = 0; z < Chunk::Width; z++) {
-                auto type = chunk.get(x, y, z).type;
-                if (type == BlockType::Air) {
+                auto block = chunk.get(x, y, z);
+                if (block.empty())
                     continue;
-                }
 
                 for (auto side: BlockSide::all()) {
-                    if (!is_face_visible(chunk, neighbors, x, y, z, side)) {
+                    if (!is_face_visible(chunk, neighbors, x, y, z, side))
                         continue;
-                    }
 
                     auto side_positions = side.face();
                     auto side_normals = face_normals(side);
-                    auto side_tex_coords = face_tex_coords(type, side);
+                    auto side_tex_coords = face_tex_coords(block.type, side);
+                    auto side_ao = face_ao_values(chunk, neighbors, x, y, z, side);
 
                     for (auto& position : side_positions) {
                         position = position + Vector<3, F32>{x, y, z};
@@ -164,14 +224,15 @@ GFX::Mesh ChunkMeshing::create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors
                     positions.insert(positions.end(), side_positions.begin(), side_positions.end());
                     normals.insert(normals.end(), side_normals.begin(), side_normals.end());
                     tex_coords.insert(tex_coords.end(), side_tex_coords.begin(), side_tex_coords.end());
-                    indices.insert(indices.end(), {s, s + 1, s + 3, s + 1, s + 2, s + 3});
+                    ambient_occlusion_values.insert(ambient_occlusion_values.end(), side_ao.begin(), side_ao.end());
+                    indices.insert(indices.end(), {s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 });
                 }
             }
         }
     }
 
     return {
-        {positions, normals, tex_coords},
+        {positions, normals, tex_coords, ambient_occlusion_values},
         indices,
     };
 }