#include "World.hpp" #include "Generation/ChunkMeshing.hpp" #include "Generation/Lighting.hpp" #include "../Time.hpp" namespace MC::World { std::vector World::get_visible_chunks(Vector<3> position) { load_finished_chunks_from_queue(); auto visible_chunks = get_visible_chunk_indices(position); std::vector chunks{}; chunks.reserve(visible_chunks.size()); for (auto index : visible_chunks) { auto& data = get(index); if (data.status == ChunkStatus::Empty) { request_generation(index, position.distance(index.middle())); data.status = ChunkStatus::WaitingForGeneration; continue; } if (data.status == ChunkStatus::WaitingForReification) { try_to_reify_chunk(data); } if (data.status == ChunkStatus::Done) { chunks.push_back(&data); } } 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 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); std::vector indices{}; indices.reserve(m_view_distance_radius * m_view_distance_radius * 4); auto radius = m_view_distance_radius; 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++) { indices.emplace_back(x + center_x, y + center_y); } } auto cmp = [=](const ChunkIndex& a, const ChunkIndex& b) -> bool { return position.distance(a.middle()) > position.distance(b.middle()); }; std::sort(indices.begin(), indices.end(), cmp); return indices; } void World::load_finished_chunks_from_queue() { auto results = m_queue.done(); for (auto& [id, res] : results) { get(id) = {id, ChunkStatus::WaitingForReification, {res.chunk}}; log_chunk_time(res.generation_duration); } } void World::request_generation(ChunkIndex index, Real priority) { m_queue.add(index, priority, [=]() -> GenerationResult { auto start = Time::now(); auto chunk = m_generator.generate(index.x, index.y); return {chunk, Time::now() - start}; }); } 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) { auto index = data.index; UInt neighbor_index = 0; std::array neighbors; for (I32 x = -1; x <= 1; x++) { for (I32 y = -1; y <= 1; y++) { if (x == 0 && y == 0) continue; auto& neighbor_data = 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(); } } // Layout of neighboring chunks in `neighbors` array: // (-1; -1) > (-1; 0) > (-1; 1) > (0; -1) // ( 0; 1) > ( 1; -1) > ( 1; 0) > (1; 1) Generation::ChunkNeighbors chunk_neighbors { neighbors[3], neighbors[6], neighbors[4], neighbors[1], neighbors[5], neighbors[7], neighbors[2], neighbors[0], }; // Lighting Generation::Lighting::light_chunk(data.chunk.value(), chunk_neighbors); // Meshing auto meshes = Generation::ChunkMeshing::mesh_chunk(data.chunk.value(), chunk_neighbors); data.land_mesh_data = meshes.land_mesh; data.land_mesh = GFX::Binder::load(data.land_mesh_data.value()); data.water_mesh_data = meshes.water_mesh; data.water_mesh = GFX::Binder::load(data.water_mesh_data.value()); data.status = ChunkStatus::Done; } void World::log_chunk_time(U64 chunk_time_ms) { m_statistics.chunk_time_sample_count++; m_statistics.average_chunk_time_ms += ((Real)chunk_time_ms - m_statistics.average_chunk_time_ms) / m_statistics.chunk_time_sample_count; } Real World::get_average_chunk_time() const { return m_statistics.average_chunk_time_ms; } }