diff options
Diffstat (limited to 'src/World')
| -rw-r--r-- | src/World/Chunk.cpp | 8 | ||||
| -rw-r--r-- | src/World/Chunk.hpp | 9 | ||||
| -rw-r--r-- | src/World/ChunkRegistry.hpp | 17 | ||||
| -rw-r--r-- | src/World/Clouds.cpp | 6 | ||||
| -rw-r--r-- | src/World/Clouds.hpp | 8 | ||||
| -rw-r--r-- | src/World/Generation/ChunkMeshing.cpp | 88 | ||||
| -rw-r--r-- | src/World/Generation/ChunkMeshing.hpp | 42 | ||||
| -rw-r--r-- | src/World/Generation/ChunkNeighbors.cpp | 26 | ||||
| -rw-r--r-- | src/World/Generation/ChunkNeighbors.hpp | 13 | ||||
| -rw-r--r-- | src/World/Generation/Lighting.cpp | 9 | ||||
| -rw-r--r-- | src/World/Generation/Lighting.hpp | 4 | ||||
| -rw-r--r-- | src/World/Position.hpp | 10 | ||||
| -rw-r--r-- | src/World/World.cpp | 103 | ||||
| -rw-r--r-- | src/World/World.hpp | 24 |
14 files changed, 251 insertions, 116 deletions
diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index 31a2c90..8b204c0 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -31,6 +31,14 @@ Vector<3> Chunk::position() const { return m_position; } +Bool Chunk::is_damaged() const { + return m_damaged; +} + +void Chunk::damage() { + m_damaged = true; +} + Bool Chunk::is_valid_position(U32 x, U32 y, U32 z) { return x < Width && y < Height && z < Width; } diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index 32d3ab3..1aa2dd1 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -21,8 +21,8 @@ public: m_position{(Real)x * Width, 0.0f, (Real)y * Width} {} struct BlockData { - BlockData() : type(BlockType::Air) {} - BlockData(BlockType t) : type(t), light{} {} + BlockData() : type(BlockType::Air), light{200} {} + BlockData(BlockType t) : type(t), light{200} {} BlockType type; U8 light; @@ -52,6 +52,9 @@ public: ChunkIndex index() const; Vector<3> position() const; + Bool is_damaged() const; + void damage(); + static Bool is_valid_position(U32 x, U32 y, U32 z); private: static U64 pos(U32 x, U32 y, U32 z); @@ -60,6 +63,8 @@ private: Vector<3> m_position; std::vector<BlockData> m_blocks; + Bool m_damaged = false; + Details m_details; }; diff --git a/src/World/ChunkRegistry.hpp b/src/World/ChunkRegistry.hpp index 86e5d06..2c515c5 100644 --- a/src/World/ChunkRegistry.hpp +++ b/src/World/ChunkRegistry.hpp @@ -5,7 +5,6 @@ #include "Chunk.hpp" #include "ChunkIndex.hpp" #include "Position.hpp" -#include "../GFX/Binder.hpp" namespace MC::World { @@ -14,7 +13,9 @@ public: enum class Status { Empty, WaitingForGeneration, + NeedsReification, WaitingForReification, + Damaged, Done }; @@ -24,11 +25,17 @@ public: Status status; std::optional<Chunk> chunk = {}; - std::optional<GFX::Mesh> land_mesh_data = {}; - std::optional<GFX::Mesh> water_mesh_data = {}; + std::optional<GFX::Mesh> land_mesh = {}; + std::optional<GFX::Mesh> water_mesh = {}; - std::optional<GFX::BindableMesh> land_mesh = {}; - std::optional<GFX::BindableMesh> water_mesh = {}; + Status get_status() const { + if (status == Status::Done && chunk.value().is_damaged()) { return Status::Damaged; } + return status; + } + + void damage() { + if (status == Status::Done) chunk.value().damage(); + } }; Data& get(ChunkIndex index); diff --git a/src/World/Clouds.cpp b/src/World/Clouds.cpp index 2e0f76c..2567985 100644 --- a/src/World/Clouds.cpp +++ b/src/World/Clouds.cpp @@ -15,7 +15,7 @@ Clouds::Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_co {GFX::Shading::Shader::Type::Vertex, vertex}, {GFX::Shading::Shader::Type::Fragment, fragment} ), - m_mesh(GFX::Binder::load(create_mesh(create_cloud_matrix()))), + m_mesh(create_mesh(create_cloud_matrix())), m_model_uniform(), m_view_uniform(), m_projection_uniform() { m_program.bind(); @@ -38,7 +38,7 @@ void Clouds::update(const Time& time) { m_x_offset += 5.0 * time.delta(); } -void Clouds::render(const GFX::Camera& camera) const { +void Clouds::render(const GFX::Camera& camera) { auto position = camera.position(); I32 center_x = std::round((position.x() - m_x_offset) / TileSize); @@ -51,7 +51,7 @@ void Clouds::render(const GFX::Camera& camera) const { } } -void Clouds::render_single_instance(const GFX::Camera& camera, Int x, Int y) const { +void Clouds::render_single_instance(const GFX::Camera& camera, Int x, Int y) { Vector<3> position{TileSize * x + m_x_offset, Height, TileSize * y}; m_program.bind(); diff --git a/src/World/Clouds.hpp b/src/World/Clouds.hpp index 35e7155..4bd0637 100644 --- a/src/World/Clouds.hpp +++ b/src/World/Clouds.hpp @@ -1,10 +1,10 @@ #pragma once #include "../Time.hpp" -#include "../GFX/Binder.hpp" #include "../GFX/Shading/Program.hpp" #include "../GFX/Shading/Uniform.hpp" #include "../GFX/Camera.hpp" +#include "../GFX/Mesh.hpp" namespace MC::World { @@ -13,7 +13,7 @@ public: Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color, Vector<3, F32> sun_direction); void update(const Time& time); - void render(const GFX::Camera& camera) const; + void render(const GFX::Camera& camera); private: constexpr static U32 CloudMatrixSize = 128; constexpr static Int Height = 200; @@ -22,7 +22,7 @@ private: using CloudMatrix = Matrix<CloudMatrixSize, CloudMatrixSize, Bool>; - void render_single_instance(const GFX::Camera& camera, Int x, Int y) const; + void render_single_instance(const GFX::Camera& camera, Int x, Int y); static CloudMatrix create_cloud_matrix(); static GFX::Mesh create_mesh(const CloudMatrix& cloud_matrix); @@ -33,7 +33,7 @@ private: Real m_x_offset = 0.0; GFX::Shading::Program m_program; - GFX::BindableMesh m_mesh; + GFX::Mesh m_mesh; GFX::Shading::Uniform m_model_uniform; GFX::Shading::Uniform m_view_uniform; 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; } 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 <typename T> using Face = std::array<T, 4>; template <typename Decisions> -GFX::Mesh create_mesh(Chunk& chunk, const ChunkNeighbors& neighbors) { +GFX::Mesh create_mesh(Chunk& chunk, const SurroundingContext& context) { GFX::Util::MeshBuilder<Normal, TexCoord, Light, AO> 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<Vertex> face_positions(BlockSide side, U32 x, U32 y, U32 z); static Face<TexCoord> face_tex_coords(BlockType type, BlockSide side); static Face<Normal> face_normals(BlockSide side); - static Face<Light> face_light(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); - static Face<AO> face_ao_values(Chunk& chunk, const ChunkNeighbors& neighbors, U32 x, U32 y, U32 z, BlockSide side); + static Face<Light> face_light(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side); + static Face<AO> 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<Chunk*, 8> 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<Operation> m_queue; }; diff --git a/src/World/Position.hpp b/src/World/Position.hpp index 49cbbb6..b603ee4 100644 --- a/src/World/Position.hpp +++ b/src/World/Position.hpp @@ -9,9 +9,9 @@ namespace MC::World::Position { using World = Vector<3>; // Offset between block positions within single chunk. -class OffsetBlock : public Vector<3, I8> { +class BlockOffset : public Vector<3, I16> { public: - OffsetBlock(I8 x, I8 y, I8 z) : Vector(x, y, z) {} + BlockOffset(I16 x, I16 y, I16 z) : Vector(x, y, z) {} Bool fits_within_chunk() const { using namespace ChunkDimensions; return x() >= 0 && x() < Width && y() >= 0 && y() < Height && z() >= 0 && z() < Width; @@ -28,8 +28,8 @@ public: class BlockLocal : public Vector<3, U8> { public: BlockLocal(U8 x, U8 y, U8 z) : Vector(x, y, z) {} - BlockLocal(OffsetBlock offset) : BlockLocal(offset.x(), offset.y(), offset.z()) {} - OffsetBlock offset(OffsetBlock by) { + BlockLocal(BlockOffset offset) : BlockLocal(offset.x(), offset.y(), offset.z()) {} + BlockOffset offset(BlockOffset by) { return { static_cast<I8>(x() + by.x()), static_cast<I8>(y() + by.y()), @@ -38,7 +38,7 @@ public: } }; -const std::array<OffsetBlock, 6> axis_directions = {{ +const std::array<BlockOffset, 6> axis_directions = {{ {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}, }}; diff --git a/src/World/World.cpp b/src/World/World.cpp index 5548866..3d273bd 100644 --- a/src/World/World.cpp +++ b/src/World/World.cpp @@ -5,8 +5,8 @@ namespace MC::World { -std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Vector<3> position) { - load_finished_chunks_from_queue(); +std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Position::World position) { + process_chunk_updates(); auto visible_chunks = get_visible_chunk_indices(position); @@ -14,24 +14,30 @@ std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Vector<3> position) chunks.reserve(visible_chunks.size()); for (auto index : visible_chunks) { auto& data = m_registry.get(index); - if (data.status == ChunkRegistry::Status::Empty) { - request_generation(index, position.distance(index.middle())); + + if (data.get_status() == ChunkRegistry::Status::Empty) { + request_generation(index, calculate_priority(index, position, RequestType::Initial)); data.status = ChunkRegistry::Status::WaitingForGeneration; continue; } - if (data.status == ChunkRegistry::Status::WaitingForReification) { - try_to_reify_chunk(data); - } - if (data.status == ChunkRegistry::Status::Done) { - chunks.push_back(&data); + if (data.get_status() == ChunkRegistry::Status::NeedsReification || data.get_status() == ChunkRegistry::Status::Damaged) { + auto do_all_exist = Generation::find_chunk_neighbors(index, m_registry).all_exist(); + if (m_reification_queue.size() <= 50 && do_all_exist) { + auto request_type = data.get_status() == ChunkRegistry::Status::Damaged ? RequestType::Update : RequestType::Initial; + request_reification(index, calculate_priority(index, position, request_type)); + data.status = ChunkRegistry::Status::WaitingForReification; + } } + + // TODO: Use a better indicator than `land_mesh.has_value()`. + if (data.land_mesh.has_value()) chunks.push_back(&data); } return chunks; } -std::vector<ChunkIndex> World::get_visible_chunk_indices(const Vector<3> position) const { +std::vector<ChunkIndex> World::get_visible_chunk_indices(const Position::World position) const { I32 center_x = std::round(position.x() / Chunk::Width); I32 center_y = std::round(position.z() / Chunk::Width); @@ -53,61 +59,62 @@ std::vector<ChunkIndex> World::get_visible_chunk_indices(const Vector<3> positio return indices; } -void World::load_finished_chunks_from_queue() { - auto results = m_queue.done(); - for (auto& [id, res] : results) { - m_registry.get(id) = {id, ChunkRegistry::Status::WaitingForReification, {res.chunk}}; +void World::process_chunk_updates() { + auto generation_results = m_generation_queue.done(); + for (auto& [id, res] : generation_results) { + m_registry.get(id) = {id, ChunkRegistry::Status::NeedsReification, {res.chunk}}; + log_chunk_time(res.generation_duration); } + + auto reification_results = m_reification_queue.done(); + for (auto& [id, res] : reification_results) { + m_registry.get(id) = { + id, ChunkRegistry::Status::Done, + {res.chunk}, + res.chunk_mesh.land_mesh, + res.chunk_mesh.water_mesh + }; + + // TODO: Damage surrounding chunks. + } } void World::request_generation(ChunkIndex index, Real priority) { - m_queue.add(index, priority, [=]() -> GenerationResult { + m_generation_queue.add(index, priority, [=]() -> GenerationResult { auto start = Time::now(); auto chunk = m_generator.generate(index.x, index.y); return {chunk, Time::now() - start}; }); } -void World::try_to_reify_chunk(ChunkRegistry::Data& data) { - auto index = data.index; +void World::request_reification(ChunkIndex index, Real priority) { + auto& data = m_registry.get(index); auto& chunk = data.chunk.value(); - UInt neighbor_index = 0; - std::array<Chunk*, 8> neighbors; - for (I32 x = -1; x <= 1; x++) { - for (I32 y = -1; y <= 1; y++) { - if (x == 0 && y == 0) continue; + auto neighbors = Generation::find_chunk_neighbors(index, m_registry); + auto meshing_context = Generation::ChunkMeshing::create_meshing_context(data.chunk.value(), neighbors); + m_reification_queue.add(index, priority, [=]() mutable -> ReificationResult { + Generation::Lighting intitial_lighting{}; + intitial_lighting.add_chunk(chunk); + intitial_lighting.illuminate(chunk); - auto& neighbor_data = m_registry.get({index.x + x, index.y + y}); - if (!neighbor_data.chunk.has_value()) return; // All neighbors need to be generated first. - - 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) - Generation::ChunkNeighbors chunk_neighbors { - neighbors[3], neighbors[6], neighbors[4], neighbors[1], - neighbors[5], neighbors[7], neighbors[2], neighbors[0], - }; - - // Lighting - m_lighting.add_chunk(chunk); - m_lighting.illuminate(m_registry); - - // Meshing - auto meshes = Generation::ChunkMeshing::mesh_chunk(chunk, chunk_neighbors); + auto meshes = Generation::ChunkMeshing::mesh_chunk(chunk, meshing_context); + return {chunk, meshes}; + }); +} - data.land_mesh_data = meshes.land_mesh; - data.land_mesh = GFX::Binder::load(data.land_mesh_data.value()); +Real World::calculate_priority(ChunkIndex chunk, Position::World player_position, RequestType request_type) { + auto chunk_position = chunk.middle(); + auto flat_distance = Position::World{player_position.x(), chunk_position.y(), player_position.z()}.distance(chunk_position); - data.water_mesh_data = meshes.water_mesh; - data.water_mesh = GFX::Binder::load(data.water_mesh_data.value()); + Real request_type_penalty; + switch (request_type) { + case RequestType::Initial: request_type_penalty = 0; break; + case RequestType::Update: request_type_penalty = 500; break; + } - data.status = ChunkRegistry::Status::Done; + return flat_distance + request_type_penalty; } void World::log_chunk_time(U64 chunk_time_ms) { diff --git a/src/World/World.hpp b/src/World/World.hpp index db9d0db..92367f6 100644 --- a/src/World/World.hpp +++ b/src/World/World.hpp @@ -4,33 +4,41 @@ #include "ChunkIndex.hpp" #include "ChunkRegistry.hpp" #include "../Compute/Queue.hpp" +#include "Generation/ChunkMeshing.hpp" #include "Generation/Lighting.hpp" namespace MC::World { class World { public: - World() : m_queue(8) {} - - std::vector<ChunkRegistry::Data*> get_visible_chunks(Vector<3> position); + std::vector<ChunkRegistry::Data*> get_visible_chunks(Position::World position); Real get_average_chunk_time() const; private: - std::vector<ChunkIndex> get_visible_chunk_indices(Vector<3> position) const; + std::vector<ChunkIndex> get_visible_chunk_indices(Position::World position) const; - void load_finished_chunks_from_queue(); + void process_chunk_updates(); void request_generation(ChunkIndex index, Real priority); - void try_to_reify_chunk(ChunkRegistry::Data& data); + void request_reification(ChunkIndex index, Real priority); + + enum class RequestType { Initial, Update }; + static Real calculate_priority(ChunkIndex chunk, Position::World player_position, RequestType request_type); void log_chunk_time(U64 chunk_time_ms); - U8 m_view_distance_radius = 10; + U8 m_view_distance_radius = 12; struct GenerationResult { Chunk chunk; U64 generation_duration; }; - Compute::Queue<GenerationResult, ChunkIndex> m_queue; + Compute::Queue<GenerationResult, ChunkIndex> m_generation_queue{4}; + struct ReificationResult { + Chunk chunk; + Generation::ChunkMeshing::ChunkMesh chunk_mesh; + }; + Compute::Queue<ReificationResult, ChunkIndex> m_reification_queue{4}; + Generation::Generator m_generator; Generation::Lighting m_lighting; ChunkRegistry m_registry; |
