summaryrefslogtreecommitdiff
path: root/src/World
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-07-22 17:35:00 +0200
committerMel <einebeere@gmail.com>2023-07-22 17:35:00 +0200
commit2eef7cf49b7a15559ee7bb6719411bcf67386213 (patch)
tree11eb7a4f437da7bfdde620c10a043960fd423cfb /src/World
parent23d88e5f1c8f0c8652a07050fcfa8ff126e85d4a (diff)
downloadmeowcraft-2eef7cf49b7a15559ee7bb6719411bcf67386213.tar.zst
meowcraft-2eef7cf49b7a15559ee7bb6719411bcf67386213.zip
Propagation in lighting system
Diffstat (limited to 'src/World')
-rw-r--r--src/World/BlockType.hpp2
-rw-r--r--src/World/Chunk.cpp12
-rw-r--r--src/World/Chunk.hpp12
-rw-r--r--src/World/ChunkDimensions.hpp14
-rw-r--r--src/World/ChunkIndex.hpp11
-rw-r--r--src/World/ChunkRegistry.cpp28
-rw-r--r--src/World/ChunkRegistry.hpp42
-rw-r--r--src/World/Generation/Lighting.cpp54
-rw-r--r--src/World/Generation/Lighting.hpp30
-rw-r--r--src/World/Position.hpp45
-rw-r--r--src/World/World.cpp50
-rw-r--r--src/World/World.hpp37
12 files changed, 256 insertions, 81 deletions
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<BlockData> 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<I32>(pos.x() / Chunk::Width),
+ static_cast<I32>(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 <optional>
+#include <unordered_map>
+#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> chunk = {};
+
+ std::optional<GFX::Mesh> land_mesh_data = {};
+ std::optional<GFX::Mesh> water_mesh_data = {};
+
+ std::optional<GFX::BindableMesh> land_mesh = {};
+ std::optional<GFX::BindableMesh> water_mesh = {};
+ };
+
+ Data& get(ChunkIndex index);
+
+ Data& find(Position::BlockWorld pos);
+ Data& find(ChunkIndex chunk, Position::BlockLocal pos);
+private:
+ std::unordered_map<ChunkIndex, Data> 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 <queue>
#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<Operation> 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<I8>(x() + by.x()),
+ static_cast<I8>(y() + by.y()),
+ static_cast<I8>(z() + by.z())
+ };
+ }
+};
+
+const std::array<OffsetBlock, 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 912a5cf..5548866 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -5,25 +5,25 @@
namespace MC::World {
-std::vector<World::ChunkData*> World::get_visible_chunks(Vector<3> position) {
+std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Vector<3> position) {
load_finished_chunks_from_queue();
auto visible_chunks = get_visible_chunk_indices(position);
- std::vector<ChunkData*> chunks{};
+ std::vector<ChunkRegistry::Data*> 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::ChunkData*> 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<ChunkIndex> 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<ChunkIndex> 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<Chunk*, 8> 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 <unordered_map>
-#include <unordered_set>
-#include <optional>
#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> chunk = {};
-
- std::optional<GFX::Mesh> land_mesh_data = {};
- std::optional<GFX::Mesh> water_mesh_data = {};
-
- std::optional<GFX::BindableMesh> land_mesh = {};
- std::optional<GFX::BindableMesh> water_mesh = {};
- };
-
- std::vector<ChunkData*> get_visible_chunks(Vector<3> position);
- Chunk* get_chunk_for_position(Vector<3> position);
+ std::vector<ChunkRegistry::Data*> get_visible_chunks(Vector<3> position);
Real get_average_chunk_time() const;
private:
std::vector<ChunkIndex> 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<GenerationResult, ChunkIndex> m_queue;
Generation::Generator m_generator;
-
- std::unordered_map<ChunkIndex, ChunkData> m_chunks;
+ Generation::Lighting m_lighting;
+ ChunkRegistry m_registry;
struct Statistics {
UInt chunk_time_sample_count;