diff options
| -rw-r--r-- | src/Compute/Queue.hpp | 31 | ||||
| -rw-r--r-- | src/World/Position.hpp | 8 | ||||
| -rw-r--r-- | src/World/World.cpp | 42 | ||||
| -rw-r--r-- | src/World/World.hpp | 15 |
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; |
