summary refs log tree commit diff
path: root/src/World/Generation/ChunkMeshing.cpp
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-07-10 04:42:49 +0200
committerMel <einebeere@gmail.com>2023-07-10 04:42:49 +0200
commit354a49d852d8f9ed9b66d7780ba43ce3a9ec59d7 (patch)
tree5f311f6aa47db9a231164a82403cc0e1e53864e8 /src/World/Generation/ChunkMeshing.cpp
parent22aeb320b193705f493747714404c8ddc752919b (diff)
downloadmeowcraft-354a49d852d8f9ed9b66d7780ba43ce3a9ec59d7.tar.zst
meowcraft-354a49d852d8f9ed9b66d7780ba43ce3a9ec59d7.zip
Separate transparent water mesh (not sorted, bad)
Diffstat (limited to 'src/World/Generation/ChunkMeshing.cpp')
-rw-r--r--src/World/Generation/ChunkMeshing.cpp286
1 files changed, 140 insertions, 146 deletions
diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp
index 130bbad..7158734 100644
--- a/src/World/Generation/ChunkMeshing.cpp
+++ b/src/World/Generation/ChunkMeshing.cpp
@@ -2,36 +2,49 @@
 
 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;
+ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) {
+    using namespace Detail;
 
-    auto overflow = [](I32& c, I32 max) -> I8 {
-        if (c < 0) { c += max; return -1; }
-        if (c >= max) { c -= max; return 1; }
-        return 0;
+    return {
+        create_mesh<DefaultMeshDecisions>(chunk, neighbors),
+        create_mesh<WaterMeshDecisions>(chunk, neighbors)
     };
+}
 
-    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 {};
+namespace Detail {
 
-    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; }
+std::array<Vector<3, F32>, 4> DefaultMeshDecisions::face_positions(BlockSide side, U32 x, U32 y, U32 z) {
+    // Winding order: (0, 1, 2) (2, 3, 0)
+    // Note: OpenGL Coordinate system has a flipped z axis.
+    std::array<Vector<3, F32>, 4> face{};
+    switch (side) {
+    case BlockSide::Front:
+        face = {{{0, 1, 1}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}}};
+        break;
+    case BlockSide::Back:
+        face = {{{0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}};
+        break;
+    case BlockSide::Top:
+        face = {{{0, 1, 1}, {1, 1, 1}, {1, 1, 0}, {0, 1, 0}}};
+        break;
+    case BlockSide::Bottom:
+        face = {{{0, 0, 1}, {0, 0, 0}, {1, 0, 0}, {1, 0, 1}}};
+        break;
+    case BlockSide::Right:
+        face = {{{1, 1, 0}, {1, 1, 1}, {1, 0, 1}, {1, 0, 0}}};
+        break;
+    case BlockSide::Left:
+        face = {{{0, 1, 0}, {0, 0, 0}, {0, 0, 1}, {0, 1, 1}}};
+        break;
+    }
 
-    return chunk_to_ask->get(pos.x(), pos.y(), pos.z());
+    for (auto& p : face) {
+        p += {x, y, z};
+    }
+    return face;
 }
 
-std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
+std::array<Vector<2, F32>, 4> DefaultMeshDecisions::face_tex_coords(BlockType type, BlockSide side) {
     U8 atlas_width = 4;
     U8 atlas_height = 4;
 
@@ -56,75 +69,67 @@ std::array<Vector<2, F32>, 4> face_tex_coords(BlockType type, BlockSide side) {
     };
 
     switch (type) {
-        case BlockType::Dirt:
+    case BlockType::Dirt:
+        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 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 BlockType::Stone:
-            return block_coords(3, 0);
-        case BlockType::Sand:
-            return block_coords(1, 1);
-        case BlockType::Water:
-            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 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 BlockType::Leaves:
-            return block_coords(2, 2);
-        case BlockType::Air:
-            return {};
+        case BlockSide::Top:
+            return block_coords(0, 0);
+        }
+    case BlockType::Stone:
+        return block_coords(3, 0);
+    case BlockType::Sand:
+        return block_coords(1, 1);
+    case BlockType::Water:
+        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 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 BlockType::Leaves:
+        return block_coords(2, 2);
+    case BlockType::Air:
+        return {};
     }
 }
 
-std::array<Vector<3, F32>, 4> face_normals(BlockSide side) {
-    auto is_side = [=](BlockSide s) -> Real { return s == side; };
-
-    Vector<3, F32> normal = {
-        is_side(BlockSide::Right) - is_side(BlockSide::Left),
-        is_side(BlockSide::Top) - is_side(BlockSide::Bottom),
-        is_side(BlockSide::Front) - is_side(BlockSide::Back),
-    };
-
+std::array<Vector<3, F32>, 4> DefaultMeshDecisions::face_normals(BlockSide side) {
+    Vector<3, F32> normal{get_face_normal(side)};
     return {normal, normal, normal, normal};
 }
 
-std::array<F32, 4> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) {
+std::array<F32, 4> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, 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
     // but you can recognize them by the fact that they have no 0 offsets.
     // Note: Is there a way to compute these? I tried but I couldn't... If the vertex windings ever change again I don't want to hardcode these...
-    auto nowhere = Vector<3, I32>::max();
     switch (side) {
     case BlockSide::Front:
         offsets = {{{0, 1, 1}, {-1,1,1}, {-1, 0, 1}, {-1,-1,1}, {0, -1, 1}, {1,-1,1}, {1, 0, 1}, {1,1,1}}};
@@ -172,80 +177,69 @@ std::array<F32, 4> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors,
     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::Right:
-        offset[0] = 1;
-        break;
-    case BlockSide::Left:
-        offset[0] = -1;
-        break;
-    }
+Chunk::BlockData DefaultMeshDecisions::get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos) {
+    const Chunk* chunk_to_ask;
 
-    Vector<3, I32> neighbor_pos{
-        x + offset.x(), y + offset.y(), z + offset.z(),
+    auto overflow = [](I32& c, I32 max) -> I8 {
+        if (c < 0) { c += max; return -1; }
+        if (c >= max) { c -= max; return 1; }
+        return 0;
     };
 
-    auto [neighbor] = get_block_wrapping(chunk, neighbors, neighbor_pos);
-    return neighbor.is_transparent();
+    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());
 }
 
-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 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))
-                        continue;
-
-                    auto side_positions = side.face();
-                    auto side_normals = face_normals(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};
-                    }
-
-                    U32 s = positions.size();
-
-                    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());
-                    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 });
-                }
-            }
-        }
-    }
+Vector<3, I32> DefaultMeshDecisions::get_face_normal(BlockSide side) {
+    auto is_side = [=](BlockSide s) -> I8 { return s == side; };
 
     return {
-        {positions, normals, tex_coords, ambient_occlusion_values},
-        indices,
+        is_side(BlockSide::Right) - is_side(BlockSide::Left),
+        is_side(BlockSide::Top) - is_side(BlockSide::Bottom),
+        is_side(BlockSide::Front) - is_side(BlockSide::Back),
     };
 }
 
+Bool DefaultMeshDecisions::is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, 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 block = get_block_wrapping(chunk, neighbors, neighbor_position);
+    return block.type.is_transparent();
+}
+
+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) {
+    Vector<3, I32> offset = get_face_normal(side);
+    auto neighbor_position = offset + Vector<3, I32>{x, y, z};
+
+    auto [neighbor] = get_block_wrapping(chunk, neighbors, neighbor_position);
+    return neighbor.is_transparent() && neighbor != BlockType::Water;
+}
+
+Bool WaterMeshDecisions::should_ignore_block(Chunk::BlockData block) {
+    return block.type != BlockType::Water;
+}
+
+}
+
 }
\ No newline at end of file