summary refs log tree commit diff
diff options
context:
space:
mode:
-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;