summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-08-03 02:29:44 +0200
committerMel <einebeere@gmail.com>2023-08-03 02:31:12 +0200
commit1d574e5e8fe3f08d13f1b8c4444fc4cb02cf0faf (patch)
treed151baab68d6aa444a5968a1b579c5d48025084e
parent15352db7bc03a176ea7a184f40a7f4e72b54b5fe (diff)
downloadmeowcraft-1d574e5e8fe3f08d13f1b8c4444fc4cb02cf0faf.tar.zst
meowcraft-1d574e5e8fe3f08d13f1b8c4444fc4cb02cf0faf.zip
World can now reassess chunk generation priorities on player movement and cancel no longer needed jobs
-rw-r--r--src/Compute/Queue.hpp31
-rw-r--r--src/World/Position.hpp8
-rw-r--r--src/World/World.cpp42
-rw-r--r--src/World/World.hpp15
4 files changed, 92 insertions, 4 deletions
diff --git a/src/Compute/Queue.hpp b/src/Compute/Queue.hpp
index e201932..799df73 100644
--- a/src/Compute/Queue.hpp
+++ b/src/Compute/Queue.hpp
@@ -46,6 +46,37 @@ public:
return done_results;
}
+ struct Reassession {
+ enum { Keep, Reassess, Cancel } type;
+ Real new_priority = 0;
+ };
+
+ template <typename F>
+ void reassess(F assess) {
+ decltype(m_control->work.jobs) new_jobs{};
+
+ std::scoped_lock work_lock(m_control->work.mutex);
+ while (!m_control->work.jobs.empty()) { // I don't like std::priority_queue :(
+ Job job = m_control->work.jobs.top();
+ m_control->work.jobs.pop();
+
+ Reassession reassession = assess(job.id);
+
+ switch (reassession.type) {
+ case Reassession::Keep:
+ new_jobs.push(job);
+ break;
+ case Reassession::Reassess:
+ new_jobs.emplace(job.id, reassession.new_priority, job.execute);
+ break;
+ default:
+ break;
+ }
+ }
+
+ m_control->work.jobs = std::move(new_jobs);
+ }
+
private:
struct Job {
Job() = default;
diff --git a/src/World/Position.hpp b/src/World/Position.hpp
index b603ee4..07de6f3 100644
--- a/src/World/Position.hpp
+++ b/src/World/Position.hpp
@@ -1,19 +1,23 @@
#pragma once
#include "array"
+#include "ChunkDimensions.hpp"
#include "../Math/Vector.hpp"
-namespace MC::World::Position {
+namespace MC::Position {
// Position within entire world.
using World = Vector<3>;
+// Offset between two world positions.
+using WorldOffset = Vector<3>;
+
// Offset between block positions within single chunk.
class BlockOffset : public Vector<3, I16> {
public:
BlockOffset(I16 x, I16 y, I16 z) : Vector(x, y, z) {}
Bool fits_within_chunk() const {
- using namespace ChunkDimensions;
+ using namespace MC::World::ChunkDimensions;
return x() >= 0 && x() < Width && y() >= 0 && y() < Height && z() >= 0 && z() < Width;
}
};
diff --git a/src/World/World.cpp b/src/World/World.cpp
index 3d273bd..0f83310 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -8,6 +8,10 @@ namespace MC::World {
std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Position::World position) {
process_chunk_updates();
+ if (should_reassess_priorities(position)) {
+ reassess_priorities(position);
+ }
+
auto visible_chunks = get_visible_chunk_indices(position);
std::vector<ChunkRegistry::Data*> chunks{};
@@ -47,6 +51,8 @@ std::vector<ChunkIndex> World::get_visible_chunk_indices(const Position::World p
for (I32 x = -radius; x <= radius; x++) {
I32 height = std::round(std::sqrt(radius * radius - x * x) + 0.5);
for (I32 y = -height; y <= height; y++) {
+ // Needed so that this function and is_chunk_in_radius forcibly agree.
+ if (!is_chunk_in_radius(position, {x + center_x, y + center_y})) continue;
indices.emplace_back(x + center_x, y + center_y);
}
}
@@ -104,6 +110,33 @@ void World::request_reification(ChunkIndex index, Real priority) {
});
}
+Bool World::should_reassess_priorities(Position::World player_position) const {
+ return player_position.distance(m_last_priority_reassession_at) > m_priority_reassession_distance_threshold;
+}
+
+void World::reassess_priorities(Position::World player_position) {
+ m_last_priority_reassession_at = player_position;
+
+ // TODO: How do we know a chunk has been requested as an update?
+ m_generation_queue.reassess([&](ChunkIndex id) -> GenerationQueue::Reassession {
+ if (!is_chunk_in_radius(player_position, id)) return {GenerationQueue::Reassession::Cancel};
+
+ return {
+ GenerationQueue::Reassession::Reassess,
+ calculate_priority(id, player_position, RequestType::Initial)
+ };
+ });
+
+ m_reification_queue.reassess([&](ChunkIndex id) -> ReificationQueue::Reassession {
+ if (!is_chunk_in_radius(player_position, id)) return {ReificationQueue::Reassession::Cancel};
+
+ return {
+ ReificationQueue::Reassession::Reassess,
+ calculate_priority(id, player_position, RequestType::Initial)
+ };
+ });
+}
+
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);
@@ -126,4 +159,13 @@ Real World::get_average_chunk_time() const {
return m_statistics.average_chunk_time_ms;
}
+Bool World::is_chunk_in_radius(Position::World position, ChunkIndex chunk) const {
+ Real max_distance = (m_view_distance_radius + 2) * Chunk::Width; // TODO: Tune this so that m_view_distance_radius represents the exact radius.
+ auto chunk_position = chunk.middle();
+ // TODO: Create a flat() method in Position::World for things like this. See also: priorities.
+ Position::World flat_position{position.x(), chunk_position.y(), position.z()};
+
+ return chunk_position.distance(flat_position) < max_distance;
+}
+
}
diff --git a/src/World/World.hpp b/src/World/World.hpp
index 92367f6..e3b08f5 100644
--- a/src/World/World.hpp
+++ b/src/World/World.hpp
@@ -21,6 +21,9 @@ private:
void request_generation(ChunkIndex index, Real priority);
void request_reification(ChunkIndex index, Real priority);
+ Bool should_reassess_priorities(Position::World player_position) const;
+ void reassess_priorities(Position::World player_position);
+
enum class RequestType { Initial, Update };
static Real calculate_priority(ChunkIndex chunk, Position::World player_position, RequestType request_type);
@@ -28,16 +31,24 @@ private:
U8 m_view_distance_radius = 12;
+ Bool is_chunk_in_radius(Position::World position, ChunkIndex chunk) const;
+
struct GenerationResult {
Chunk chunk;
U64 generation_duration;
};
- Compute::Queue<GenerationResult, ChunkIndex> m_generation_queue{4};
+ using GenerationQueue = Compute::Queue<GenerationResult, ChunkIndex>;
+ GenerationQueue m_generation_queue{4};
+
struct ReificationResult {
Chunk chunk;
Generation::ChunkMeshing::ChunkMesh chunk_mesh;
};
- Compute::Queue<ReificationResult, ChunkIndex> m_reification_queue{4};
+ using ReificationQueue = Compute::Queue<ReificationResult, ChunkIndex>;
+ ReificationQueue m_reification_queue{4};
+
+ Real m_priority_reassession_distance_threshold = 50;
+ Position::World m_last_priority_reassession_at = {};
Generation::Generator m_generator;
Generation::Lighting m_lighting;