From 2eef7cf49b7a15559ee7bb6719411bcf67386213 Mon Sep 17 00:00:00 2001 From: Mel Date: Sat, 22 Jul 2023 17:35:00 +0200 Subject: Propagation in lighting system --- src/World/BlockType.hpp | 2 +- src/World/Chunk.cpp | 12 +++++++++ src/World/Chunk.hpp | 12 +++++++-- src/World/ChunkDimensions.hpp | 14 ++++++++++ src/World/ChunkIndex.hpp | 11 +++++++- src/World/ChunkRegistry.cpp | 28 ++++++++++++++++++++ src/World/ChunkRegistry.hpp | 42 ++++++++++++++++++++++++++++++ src/World/Generation/Lighting.cpp | 54 ++++++++++++++++++++++++++++++++++----- src/World/Generation/Lighting.hpp | 30 ++++++++++++++++++---- src/World/Position.hpp | 45 ++++++++++++++++++++++++++++++++ src/World/World.cpp | 50 +++++++++++------------------------- src/World/World.hpp | 37 +++++---------------------- 12 files changed, 256 insertions(+), 81 deletions(-) create mode 100644 src/World/ChunkDimensions.hpp create mode 100644 src/World/ChunkRegistry.cpp create mode 100644 src/World/ChunkRegistry.hpp create mode 100644 src/World/Position.hpp (limited to 'src/World') diff --git a/src/World/BlockType.hpp b/src/World/BlockType.hpp index a63d96a..2a9a652 100644 --- a/src/World/BlockType.hpp +++ b/src/World/BlockType.hpp @@ -35,7 +35,7 @@ public: case Water: return 0.05; case Leaves: - return 0.2; + return 0.3; default: return 1.0; } diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index ab727bd..31a2c90 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -11,10 +11,22 @@ Chunk::BlockData& Chunk::at(U32 x, U32 y, U32 z) { return m_blocks.at(pos(x, y, z)); } +const Chunk::BlockData& Chunk::at(Position::BlockLocal pos) const { + return at(pos.x(), pos.y(), pos.z()); +} + +Chunk::BlockData& Chunk::at(Position::BlockLocal pos) { + return at(pos.x(), pos.y(), pos.z()); +} + Bool Chunk::is_empty(U32 x, U32 y, U32 z) const { return at(x, y, z).empty(); } +ChunkIndex Chunk::index() const { + return m_index; +} + Vector<3> Chunk::position() const { return m_position; } diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index 5f4844e..32d3ab3 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -1,19 +1,23 @@ #pragma once #include "../Common/Sizes.hpp" +#include "ChunkDimensions.hpp" #include "BiomeType.hpp" #include "BlockType.hpp" +#include "ChunkIndex.hpp" +#include "Position.hpp" #include "../GFX/Mesh.hpp" namespace MC::World { class Chunk { public: - static constexpr U32 Width = 16; - static constexpr U32 Height = 128; + static constexpr U32 Width = ChunkDimensions::Width; + static constexpr U32 Height = ChunkDimensions::Height; Chunk(I64 x, I64 y) : m_blocks{Width * Height * Width, {BlockType::Air}}, + m_index(x, y), m_position{(Real)x * Width, 0.0f, (Real)y * Width} {} struct BlockData { @@ -28,6 +32,8 @@ public: const BlockData& at(U32 x, U32 y, U32 z) const; BlockData& at(U32 x, U32 y, U32 z); + const BlockData& at(Position::BlockLocal pos) const; + BlockData& at(Position::BlockLocal pos); Bool is_empty(U32 x, U32 y, U32 z) const; @@ -43,12 +49,14 @@ public: void set_details(const Details& details) { m_details = details; } Details& details(){ return m_details; } + ChunkIndex index() const; Vector<3> position() const; static Bool is_valid_position(U32 x, U32 y, U32 z); private: static U64 pos(U32 x, U32 y, U32 z); + ChunkIndex m_index; Vector<3> m_position; std::vector m_blocks; diff --git a/src/World/ChunkDimensions.hpp b/src/World/ChunkDimensions.hpp new file mode 100644 index 0000000..99f2824 --- /dev/null +++ b/src/World/ChunkDimensions.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "../Common/Sizes.hpp" + +// This file defines chunk dimensions outside of the `Chunk` class. +// We need this to avoid cyclic dependencies for headers on which Chunk depends, +// but which need the dimensions, too (i.e. `ChunkIndex`). +// `Chunk` re-exports these so other units may use `Chunk::Width` and `Chunk::Height` as before. +namespace MC::World::ChunkDimensions { + +static constexpr U32 Width = 16; +static constexpr U32 Height = 128; + +} \ No newline at end of file diff --git a/src/World/ChunkIndex.hpp b/src/World/ChunkIndex.hpp index 7d6fdbf..ed3347b 100644 --- a/src/World/ChunkIndex.hpp +++ b/src/World/ChunkIndex.hpp @@ -1,6 +1,9 @@ #pragma once #include "../Common/Sizes.hpp" +#include "../Math/Vector.hpp" +#include "ChunkDimensions.hpp" +#include "Position.hpp" namespace MC::World { @@ -9,7 +12,13 @@ struct ChunkIndex { ChunkIndex(I32 x, I32 y) : x(x), y(y) {} Vector<3> middle() const { - return {(x + 0.5f) * Chunk::Width, Chunk::Height / 2.0f, (y + 0.5f) * Chunk::Width}; + using namespace ChunkDimensions; + return {(x + 0.5f) * Width, Height / 2.0f, (y + 0.5f) * Width}; + } + + Position::BlockWorld world(Position::BlockLocal local) const { + using namespace ChunkDimensions; + return {x * Width + local.x(), local.y(), y * Width + local.z()}; } I32 x, y; diff --git a/src/World/ChunkRegistry.cpp b/src/World/ChunkRegistry.cpp new file mode 100644 index 0000000..41fd1b0 --- /dev/null +++ b/src/World/ChunkRegistry.cpp @@ -0,0 +1,28 @@ +#include "ChunkRegistry.hpp" + +namespace MC::World { + +ChunkRegistry::Data& ChunkRegistry::get(ChunkIndex index) { + auto entry = m_chunks.find(index); + if (entry == m_chunks.end()) { + Data data{index, Status::Empty}; + + m_chunks.insert({index, std::move(data)}); + return m_chunks.at(index); + } + + return entry->second; +} + +ChunkRegistry::Data& ChunkRegistry::find(Position::BlockWorld pos) { + return get({ + static_cast(pos.x() / Chunk::Width), + static_cast(pos.z() / Chunk::Width) + }); +} + +ChunkRegistry::Data& ChunkRegistry::find(ChunkIndex chunk, Position::BlockLocal pos) { + return find(chunk.world(pos)); +} + +} \ No newline at end of file diff --git a/src/World/ChunkRegistry.hpp b/src/World/ChunkRegistry.hpp new file mode 100644 index 0000000..86e5d06 --- /dev/null +++ b/src/World/ChunkRegistry.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include "Chunk.hpp" +#include "ChunkIndex.hpp" +#include "Position.hpp" +#include "../GFX/Binder.hpp" + +namespace MC::World { + +class ChunkRegistry { +public: + enum class Status { + Empty, + WaitingForGeneration, + WaitingForReification, + Done + }; + + // I think a Chunk entity should just store all this by itself... + struct Data { + ChunkIndex index; + Status status; + std::optional chunk = {}; + + std::optional land_mesh_data = {}; + std::optional water_mesh_data = {}; + + std::optional land_mesh = {}; + std::optional water_mesh = {}; + }; + + Data& get(ChunkIndex index); + + Data& find(Position::BlockWorld pos); + Data& find(ChunkIndex chunk, Position::BlockLocal pos); +private: + std::unordered_map m_chunks; +}; + +} diff --git a/src/World/Generation/Lighting.cpp b/src/World/Generation/Lighting.cpp index 8df6e2a..39d8320 100644 --- a/src/World/Generation/Lighting.cpp +++ b/src/World/Generation/Lighting.cpp @@ -1,20 +1,60 @@ #include "Lighting.hpp" -namespace MC::World::Generation::Lighting { +namespace MC::World::Generation { -void light_chunk(Chunk& chunk, ChunkNeighbors& _) { - for (UInt x = 0; x < Chunk::Width; x++) { - for (UInt z = 0; z < Chunk::Width; z++) { - U8 current_light_exposure = LightSun; - for (UInt y = Chunk::Height - 1; y != 0; y--) { +void Lighting::add_chunk(Chunk& chunk) { + for (U8 x = 0; x < Chunk::Width; x++) { + for (U8 z = 0; z < Chunk::Width; z++) { + U8 current_light_exposure = SunBrightness; + for (U8 y = Chunk::Height - 1; y != 0; y--) { auto& block = chunk.at(x, y, z); if (!block.type.is_translucent()) break; current_light_exposure = (Real)current_light_exposure * (1 - block.type.opacity()); - block.light = current_light_exposure; + add_block(chunk, {x, y, z}, current_light_exposure); } } } } +void Lighting::add_block(Chunk& chunk, Position::BlockLocal position, U8 light) { + auto& block = chunk.at(position); + block.light = light; + enqueue(chunk.index(), position, light); +} + +void Lighting::illuminate(ChunkRegistry& chunks) { + while (!m_queue.empty()) { + auto op = m_queue.front(); + m_queue.pop(); + + process(chunks, op); + } +} + +void Lighting::enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origin) { + 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(); + + for (auto direction : Position::axis_directions) { + auto neighbor_pos = op.position.offset(direction); + if (!neighbor_pos.fits_within_chunk()) continue; + + auto& neighbor = chunk.at(neighbor_pos); + if (!neighbor.type.is_translucent()) continue; + + U8 falloff = (neighbor.type.opacity() + 1) * DefaultFalloff; + U8 target = std::max(op.origin - falloff, 0); + + if (neighbor.light < target) { + neighbor.light = target; + enqueue(op.chunk, neighbor_pos, target); + } + } +} + } diff --git a/src/World/Generation/Lighting.hpp b/src/World/Generation/Lighting.hpp index 7bb8fcc..72af0e1 100644 --- a/src/World/Generation/Lighting.hpp +++ b/src/World/Generation/Lighting.hpp @@ -1,13 +1,33 @@ #pragma once +#include #include "../Chunk.hpp" -#include "ChunkNeighbors.hpp" +#include "../ChunkIndex.hpp" +#include "../ChunkRegistry.hpp" +#include "../Position.hpp" -namespace MC::World::Generation::Lighting { +namespace MC::World::Generation { -constexpr U8 LightSun = 200; -constexpr U8 LightTorch = 100; +class Lighting { +public: + void add_chunk(Chunk& chunk); + void add_block(Chunk& chunk, Position::BlockLocal position, U8 light); -void light_chunk(Chunk& chunk, ChunkNeighbors& neighbors); + void illuminate(ChunkRegistry& chunks); +private: + static constexpr U8 SunBrightness = 200; + static constexpr U8 DefaultFalloff = 10; + + struct Operation { + ChunkIndex chunk; + Position::BlockLocal position; + U8 origin; + }; + + void enqueue(ChunkIndex chunk, Position::BlockLocal position, U8 origin); + void process(ChunkRegistry& chunks, Operation op); + + std::queue m_queue; +}; } diff --git a/src/World/Position.hpp b/src/World/Position.hpp new file mode 100644 index 0000000..49cbbb6 --- /dev/null +++ b/src/World/Position.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "array" +#include "../Math/Vector.hpp" + +namespace MC::World::Position { + +// Position within entire world. +using World = Vector<3>; + +// Offset between block positions within single chunk. +class OffsetBlock : public Vector<3, I8> { +public: + OffsetBlock(I8 x, I8 y, I8 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; + } +}; + +// Position of a block within entire world. +class BlockWorld : public Vector<3, I64> { +public: + BlockWorld(I64 x, I64 y, I64 z) : Vector(x, y, z) {} +}; + +// Position of a block within single chunk. +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) { + return { + static_cast(x() + by.x()), + static_cast(y() + by.y()), + static_cast(z() + by.z()) + }; + } +}; + +const std::array 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 912a5cf..5548866 100644 --- a/src/World/World.cpp +++ b/src/World/World.cpp @@ -5,25 +5,25 @@ namespace MC::World { -std::vector World::get_visible_chunks(Vector<3> position) { +std::vector World::get_visible_chunks(Vector<3> position) { load_finished_chunks_from_queue(); auto visible_chunks = get_visible_chunk_indices(position); - std::vector chunks{}; + std::vector chunks{}; chunks.reserve(visible_chunks.size()); for (auto index : visible_chunks) { - auto& data = get(index); - if (data.status == ChunkStatus::Empty) { + auto& data = m_registry.get(index); + if (data.status == ChunkRegistry::Status::Empty) { request_generation(index, position.distance(index.middle())); - data.status = ChunkStatus::WaitingForGeneration; + data.status = ChunkRegistry::Status::WaitingForGeneration; continue; } - if (data.status == ChunkStatus::WaitingForReification) { + if (data.status == ChunkRegistry::Status::WaitingForReification) { try_to_reify_chunk(data); } - if (data.status == ChunkStatus::Done) { + if (data.status == ChunkRegistry::Status::Done) { chunks.push_back(&data); } } @@ -31,16 +31,6 @@ std::vector World::get_visible_chunks(Vector<3> position) { return chunks; } -Chunk* World::get_chunk_for_position(Vector<3> position) { - I32 x = std::round(position.x() / Chunk::Width); - I32 y = std::round(position.z() / Chunk::Width); - auto& data = get({x, y}); - if (data.chunk.has_value()) { - return &data.chunk.value(); - } - return nullptr; -} - std::vector World::get_visible_chunk_indices(const Vector<3> position) const { I32 center_x = std::round(position.x() / Chunk::Width); I32 center_y = std::round(position.z() / Chunk::Width); @@ -66,7 +56,7 @@ std::vector World::get_visible_chunk_indices(const Vector<3> positio void World::load_finished_chunks_from_queue() { auto results = m_queue.done(); for (auto& [id, res] : results) { - get(id) = {id, ChunkStatus::WaitingForReification, {res.chunk}}; + m_registry.get(id) = {id, ChunkRegistry::Status::WaitingForReification, {res.chunk}}; log_chunk_time(res.generation_duration); } } @@ -79,20 +69,9 @@ void World::request_generation(ChunkIndex index, Real priority) { }); } -World::ChunkData& World::get(ChunkIndex index) { - auto entry = m_chunks.find(index); - if (entry == m_chunks.end()) { - ChunkData data{index, ChunkStatus::Empty}; - - m_chunks.insert({index, std::move(data)}); - return m_chunks.at(index); - } - - return entry->second; -} - -void World::try_to_reify_chunk(ChunkData& data) { +void World::try_to_reify_chunk(ChunkRegistry::Data& data) { auto index = data.index; + auto& chunk = data.chunk.value(); UInt neighbor_index = 0; std::array neighbors; @@ -100,7 +79,7 @@ void World::try_to_reify_chunk(ChunkData& data) { for (I32 y = -1; y <= 1; y++) { if (x == 0 && y == 0) continue; - auto& neighbor_data = get({index.x + x, index.y + y}); + 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(); @@ -116,10 +95,11 @@ void World::try_to_reify_chunk(ChunkData& data) { }; // Lighting - Generation::Lighting::light_chunk(data.chunk.value(), chunk_neighbors); + m_lighting.add_chunk(chunk); + m_lighting.illuminate(m_registry); // Meshing - auto meshes = Generation::ChunkMeshing::mesh_chunk(data.chunk.value(), chunk_neighbors); + auto meshes = Generation::ChunkMeshing::mesh_chunk(chunk, chunk_neighbors); data.land_mesh_data = meshes.land_mesh; data.land_mesh = GFX::Binder::load(data.land_mesh_data.value()); @@ -127,7 +107,7 @@ void World::try_to_reify_chunk(ChunkData& data) { data.water_mesh_data = meshes.water_mesh; data.water_mesh = GFX::Binder::load(data.water_mesh_data.value()); - data.status = ChunkStatus::Done; + data.status = ChunkRegistry::Status::Done; } void World::log_chunk_time(U64 chunk_time_ms) { diff --git a/src/World/World.hpp b/src/World/World.hpp index 3635d8a..db9d0db 100644 --- a/src/World/World.hpp +++ b/src/World/World.hpp @@ -1,12 +1,10 @@ #pragma once -#include -#include -#include #include "Generation/Generator.hpp" #include "ChunkIndex.hpp" -#include "../GFX/Binder.hpp" +#include "ChunkRegistry.hpp" #include "../Compute/Queue.hpp" +#include "Generation/Lighting.hpp" namespace MC::World { @@ -14,39 +12,18 @@ class World { public: World() : m_queue(8) {} - enum class ChunkStatus { - Empty, - WaitingForGeneration, - WaitingForReification, - Done - }; - - struct ChunkData { - ChunkIndex index; - ChunkStatus status; - std::optional chunk = {}; - - std::optional land_mesh_data = {}; - std::optional water_mesh_data = {}; - - std::optional land_mesh = {}; - std::optional water_mesh = {}; - }; - - std::vector get_visible_chunks(Vector<3> position); - Chunk* get_chunk_for_position(Vector<3> position); + std::vector get_visible_chunks(Vector<3> position); Real get_average_chunk_time() const; private: std::vector get_visible_chunk_indices(Vector<3> position) const; + void load_finished_chunks_from_queue(); void request_generation(ChunkIndex index, Real priority); - void try_to_reify_chunk(ChunkData& data); + void try_to_reify_chunk(ChunkRegistry::Data& data); void log_chunk_time(U64 chunk_time_ms); - ChunkData& get(ChunkIndex index); - U8 m_view_distance_radius = 10; struct GenerationResult { @@ -55,8 +32,8 @@ private: }; Compute::Queue m_queue; Generation::Generator m_generator; - - std::unordered_map m_chunks; + Generation::Lighting m_lighting; + ChunkRegistry m_registry; struct Statistics { UInt chunk_time_sample_count; -- cgit 1.4.1