#include "ChunkMeshing.hpp" namespace MC::World::Generation { std::array, 4> face_tex_coords(BlockType type, BlockSide side) { uint8_t atlas_width = 4; uint8_t atlas_height = 4; float width_step = 1.0f / atlas_width; float height_step = 1.0f / atlas_height; auto block_coords = [=](uint8_t x, uint8_t y) { auto t = y * height_step; auto l = x * width_step; auto b = t + height_step; auto r = l + width_step; return std::array, 4>{{ {l, b}, {r, b}, {r, t}, {l, t}, }}; }; switch (type) { 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 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, 4> face_normals(BlockSide side) { auto is_side = [=](BlockSide s) -> float { return s == side; }; Vector<3> 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), }; return {normal, normal, normal, normal}; } bool is_face_visible(Chunk& chunk, const ChunkMeshing::ChunkNeighbors& neighbors, uint32_t x, uint32_t y, uint32_t z, BlockSide side) { Vector<3, int32_t> 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; } Vector<3, int32_t> neighbor_pos{ (int32_t)x + offset.x(), (int32_t)y + offset.y(), (int32_t)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() >= (int32_t)Chunk::Width) { chunk_to_ask = &neighbors.east; neighbor_pos.x() -= Chunk::Width; } else if (neighbor_pos.z() >= (int32_t)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()); if (neighbor == BlockType::Air) { return true; } return false; } GFX::Mesh ChunkMeshing::create_mesh_for_chunk(Chunk& chunk, const ChunkNeighbors& neighbors) { std::vector> positions{}; std::vector> normals{}; std::vector> tex_coords{}; std::vector 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) { 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(type, side); for (auto& position : side_positions) { position = position + Vector<3>{static_cast(x), static_cast(y), static_cast(z)}; } uint32_t 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()); indices.insert(indices.end(), {s, s + 1, s + 3, s + 1, s + 2, s + 3}); } } } } return GFX::Mesh{ {positions, normals, tex_coords}, indices, }; } }