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