diff options
Diffstat (limited to 'src/World/Generation/ChunkMeshing.cpp')
| -rw-r--r-- | src/World/Generation/ChunkMeshing.cpp | 217 |
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, }; } |
