From 6b69d4b5b648253f894707723af0e2eae9f71445 Mon Sep 17 00:00:00 2001 From: Mel Date: Sat, 29 Jul 2023 03:31:42 +0200 Subject: Move chunk reification to worker threads and set stage for chunk-unbound lighting --- src/World/Generation/ChunkMeshing.cpp | 88 ++++++++++++++++++++++++++------- src/World/Generation/ChunkMeshing.hpp | 42 ++++++++++++---- src/World/Generation/ChunkNeighbors.cpp | 26 +++++++++- src/World/Generation/ChunkNeighbors.hpp | 13 ++++- src/World/Generation/Lighting.cpp | 9 ++-- src/World/Generation/Lighting.hpp | 4 +- 6 files changed, 141 insertions(+), 41 deletions(-) (limited to 'src/World/Generation') 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(chunk, neighbors), - create_mesh(chunk, neighbors) + create_mesh(chunk, context), + create_mesh(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(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 DefaultMeshDecisions::face_positions(BlockSide side, U32 x, U32 y, U32 z) { @@ -124,15 +162,15 @@ Face DefaultMeshDecisions::face_normals(BlockSide side) { return {normal, normal, normal, normal}; } -Face 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 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 DefaultMeshDecisions::face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side) { +Face DefaultMeshDecisions::face_ao_values(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side) { std::array, 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 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; } diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp index fb5fea9..5401580 100644 --- a/src/World/Generation/ChunkMeshing.hpp +++ b/src/World/Generation/ChunkMeshing.hpp @@ -5,11 +5,32 @@ #include "../BlockSide.hpp" #include "../Chunk.hpp" #include "ChunkNeighbors.hpp" +#include "../ChunkRegistry.hpp" namespace MC::World::Generation::ChunkMeshing { +// Literally just the blocks squarely surrounding a chunk. +// Example context for a 2x1x2 chunk: +// c c c c +// c x x c +// c x x c +// c c c c +class SurroundingContext { +public: + struct Block { Bool does_exist; Chunk::BlockData block; }; + + Block& at(Position::BlockOffset p); + const Block& at(Position::BlockOffset p) const; +private: + static USize pos(Position::BlockOffset p); + static constexpr USize surrounding_block_count = Chunk::Width * 4 + 4; + + Block m_blocks[surrounding_block_count * Chunk::Height] = {}; +}; +SurroundingContext create_meshing_context(const Chunk& chunk, ChunkNeighbors& neighbors); + struct ChunkMesh { GFX::Mesh land_mesh, water_mesh; }; -ChunkMesh mesh_chunk(Chunk& chunk, const ChunkNeighbors& neighbors); +ChunkMesh mesh_chunk(Chunk& chunk, const SurroundingContext& context); namespace Detail { @@ -23,7 +44,7 @@ template using Face = std::array; template -GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) { +GFX::Mesh create_mesh(Chunk& chunk, const SurroundingContext& context) { GFX::Util::MeshBuilder builder; for (Int x = 0; x < Chunk::Width; x++) { @@ -34,7 +55,7 @@ GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) { continue; for (auto side: BlockSide::all()) { - if (!Decisions::is_face_visible(chunk, neighbors, x, y, z, side)) + if (!Decisions::is_face_visible(chunk, context, x, y, z, side)) continue; U32 s = builder.vertex_count(); @@ -42,8 +63,8 @@ GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) { builder.positions(Decisions::face_positions(side, x, y, z)); builder.attributes<0>(Decisions::face_normals(side)); builder.attributes<1>(Decisions::face_tex_coords(block.type, side)); - builder.attributes<2>(Decisions::face_light(chunk, neighbors, x, y, z, side)); - builder.attributes<3>(Decisions::face_ao_values(chunk, neighbors, x, y, z, side)); + builder.attributes<2>(Decisions::face_light(chunk, context, x, y, z, side)); + builder.attributes<3>(Decisions::face_ao_values(chunk, context, x, y, z, side)); builder.indices(std::array{ s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 }); } } @@ -58,19 +79,20 @@ public: static Face face_positions(BlockSide side, U32 x, U32 y, U32 z); static Face face_tex_coords(BlockType type, BlockSide side); static Face face_normals(BlockSide side); - static Face face_light(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); - static Face face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); + static Face face_light(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); + static Face face_ao_values(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); static Vector<3, I32> get_face_normal(BlockSide side); - static Chunk::BlockData get_opposing_neighbor(const Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); + static SurroundingContext::Block get_block_from_chunk_or_context(const Chunk& chunk, const SurroundingContext& context, Position::BlockOffset pos); + static SurroundingContext::Block get_opposing_neighbor(const Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); - static Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); + static Bool is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); static Bool should_ignore_block(Chunk::BlockData block); }; class WaterMeshDecisions final : public DefaultMeshDecisions { public: - static Bool is_face_visible(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); + static Bool is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); static Bool should_ignore_block(Chunk::BlockData block); }; diff --git a/src/World/Generation/ChunkNeighbors.cpp b/src/World/Generation/ChunkNeighbors.cpp index f34466e..c3ae5cb 100644 --- a/src/World/Generation/ChunkNeighbors.cpp +++ b/src/World/Generation/ChunkNeighbors.cpp @@ -2,7 +2,28 @@ namespace MC::World::Generation { -Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos) { +ChunkNeighbors find_chunk_neighbors(ChunkIndex chunk, ChunkRegistry& chunks) { + UInt neighbor_index = 0; + std::array neighbors{}; + for (I32 x = -1; x <= 1; x++) { + for (I32 y = -1; y <= 1; y++) { + if (x == 0 && y == 0) continue; + + auto& neighbor_data = chunks.get({chunk.x + x, chunk.y + y}); + if (neighbor_data.chunk.has_value()) neighbors[neighbor_index++] = &neighbor_data.chunk.value(); + } + } + + // Layout of neighboring chunks in `neighbors` array: + // (-1; -1) > (-1; 0) > (-1; 1) > (0; -1) + // ( 0; 1) > ( 1; -1) > ( 1; 0) > (1; 1) + return { + neighbors[3], neighbors[6], neighbors[4], neighbors[1], + neighbors[5], neighbors[7], neighbors[2], neighbors[0], + }; +} + +GetBlockWrappingResult 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 { @@ -28,7 +49,8 @@ Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& ne else if (zo == -1) { chunk_to_ask = neighbors.north; } else { chunk_to_ask = &chunk; } - return chunk_to_ask->at(pos.x(), pos.y(), pos.z()); + if (!chunk_to_ask) return {false}; + return {true, chunk_to_ask->at(pos.x(), pos.y(), pos.z())}; } } \ No newline at end of file diff --git a/src/World/Generation/ChunkNeighbors.hpp b/src/World/Generation/ChunkNeighbors.hpp index 1207c60..83fca4c 100644 --- a/src/World/Generation/ChunkNeighbors.hpp +++ b/src/World/Generation/ChunkNeighbors.hpp @@ -1,13 +1,22 @@ #pragma once #include "../Chunk.hpp" +#include "../ChunkRegistry.hpp" namespace MC::World::Generation { struct ChunkNeighbors { Chunk *north, *east, *south, *west; Chunk *north_east, *south_east, *south_west, *north_west; + + Bool all_exist() const { return north && east && south && west && north_east && south_east && south_west && north_west; } }; -Chunk::BlockData get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos); +ChunkNeighbors find_chunk_neighbors(ChunkIndex chunk, ChunkRegistry& chunks); + +struct GetBlockWrappingResult { + bool does_exist; + Chunk::BlockData block; +}; +GetBlockWrappingResult get_block_wrapping(const Chunk& chunk, const ChunkNeighbors& neighbors, Vector<3, I32> pos); -} \ No newline at end of file +} diff --git a/src/World/Generation/Lighting.cpp b/src/World/Generation/Lighting.cpp index 39d8320..f7c7bbf 100644 --- a/src/World/Generation/Lighting.cpp +++ b/src/World/Generation/Lighting.cpp @@ -23,12 +23,12 @@ void Lighting::add_block(Chunk& chunk, Position::BlockLocal position, U8 light) enqueue(chunk.index(), position, light); } -void Lighting::illuminate(ChunkRegistry& chunks) { +void Lighting::illuminate(Chunk& chunk) { while (!m_queue.empty()) { auto op = m_queue.front(); m_queue.pop(); - process(chunks, op); + process(chunk, op); } } @@ -36,10 +36,7 @@ void Lighting::enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origi m_queue.push({chunk, position, origin}); } -void Lighting::process(ChunkRegistry& chunks, Operation op) { - auto& chunk_data = chunks.get(op.chunk); - auto& chunk = chunk_data.chunk.value(); - +void Lighting::process(Chunk& chunk, Operation op) { for (auto direction : Position::axis_directions) { auto neighbor_pos = op.position.offset(direction); if (!neighbor_pos.fits_within_chunk()) continue; diff --git a/src/World/Generation/Lighting.hpp b/src/World/Generation/Lighting.hpp index 72af0e1..4587266 100644 --- a/src/World/Generation/Lighting.hpp +++ b/src/World/Generation/Lighting.hpp @@ -13,7 +13,7 @@ public: void add_chunk(Chunk& chunk); void add_block(Chunk& chunk, Position::BlockLocal position, U8 light); - void illuminate(ChunkRegistry& chunks); + void illuminate(Chunk& chunk); private: static constexpr U8 SunBrightness = 200; static constexpr U8 DefaultFalloff = 10; @@ -25,7 +25,7 @@ private: }; void enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origin); - void process(ChunkRegistry& chunks, Operation op); + void process(Chunk& chunk, Operation op); std::queue m_queue; }; -- cgit 1.4.1