#include "World.hpp" namespace MC::World { std::vector World::get_visible_chunks(Vector<3> position) { auto finished_chunks = load_finished_chunks_from_queue(); auto visible_chunks = get_visible_chunk_indices(position); auto updates = visible_chunks; for (auto index : m_visible_chunks) { updates.erase(index); } for (auto index : finished_chunks) { updates.insert(index); } if (!updates.empty()) { process_chunk_visibility_updates(updates, position); m_visible_chunks = visible_chunks; } std::vector chunks{}; chunks.reserve(visible_chunks.size()); for (auto index : visible_chunks) { auto& data = get(index); if (data.status == ChunkStatus::Done) { chunks.push_back(data); } } return chunks; } Chunk* World::get_chunk_for_positon(Vector<3> position) { int32_t x = std::round(position.x() / Chunk::Width); int32_t y = std::round(position.z() / Chunk::Width); auto& data = get({x, y}); if (data.chunk.has_value()) { return &data.chunk.value(); } return nullptr; } void World::process_chunk_visibility_updates(std::unordered_set& new_chunks, Vector<3> player) { for (auto new_index: new_chunks) { auto& data = get(new_index); switch (data.status) { case ChunkStatus::Empty: request_generation(new_index, player.distance(new_index.middle())); data.status = ChunkStatus::WaitingForGeneration; break; case ChunkStatus::Done: data.mesh = GFX::Binder::load(data.chunk.value().mesh()); data.status = ChunkStatus::Done; break; case ChunkStatus::WaitingForGeneration: break; } } } std::unordered_set World::get_visible_chunk_indices(Vector<3> position) const { int32_t center_x = std::round(position.x() / Chunk::Width); int32_t center_y = std::round(position.z() / Chunk::Width); std::unordered_set indices{}; indices.reserve(m_view_distance_radius * m_view_distance_radius * 4); auto radius = m_view_distance_radius; for (int32_t x = -radius; x <= radius; x++) { int32_t height = std::round(std::sqrt(radius * radius - x * x) + 0.5); for (int32_t y = -height; y <= height; y++) { indices.emplace(x + center_x, y + center_y); } } return indices; } std::unordered_set World::load_finished_chunks_from_queue() { std::unordered_set indices; auto results = m_queue.done(); for (auto& result : results) { auto& data = get(result.id); data.chunk = {result.res}; data.status = ChunkStatus::Done; indices.insert(result.id); } return indices; } void World::request_generation(ChunkIndex index, float priority) { m_queue.add(index, priority, [=]() { return m_generator.generate(index.x, index.y); }); } 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, data}); return m_chunks.at(index); } return entry->second; } }