summary refs log tree commit diff
path: root/src/World/World.cpp
blob: 3d273bde64e1dced5ca3f49a97e921992cb392d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "World.hpp"
#include "Generation/ChunkMeshing.hpp"
#include "Generation/Lighting.hpp"
#include "../Time.hpp"

namespace MC::World {

std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Position::World position) {
    process_chunk_updates();

    auto visible_chunks = get_visible_chunk_indices(position);

    std::vector<ChunkRegistry::Data*> chunks{};
    chunks.reserve(visible_chunks.size());
    for (auto index : visible_chunks) {
        auto& data = m_registry.get(index);

        if (data.get_status() == ChunkRegistry::Status::Empty) {
            request_generation(index, calculate_priority(index, position, RequestType::Initial));
            data.status = ChunkRegistry::Status::WaitingForGeneration;
            continue;
        }

        if (data.get_status() == ChunkRegistry::Status::NeedsReification || data.get_status() == ChunkRegistry::Status::Damaged) {
            auto do_all_exist = Generation::find_chunk_neighbors(index, m_registry).all_exist();
            if (m_reification_queue.size() <= 50 && do_all_exist) {
                auto request_type = data.get_status() == ChunkRegistry::Status::Damaged ? RequestType::Update : RequestType::Initial;
                request_reification(index, calculate_priority(index, position, request_type));
                data.status = ChunkRegistry::Status::WaitingForReification;
            }
        }

        // TODO: Use a better indicator than `land_mesh.has_value()`.
        if (data.land_mesh.has_value()) chunks.push_back(&data);
    }

    return chunks;
}

std::vector<ChunkIndex> World::get_visible_chunk_indices(const Position::World position) const {
    I32 center_x = std::round(position.x() / Chunk::Width);
    I32 center_y = std::round(position.z() / Chunk::Width);

    std::vector<ChunkIndex> 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::process_chunk_updates() {
    auto generation_results = m_generation_queue.done();
    for (auto& [id, res] : generation_results) {
        m_registry.get(id) = {id, ChunkRegistry::Status::NeedsReification, {res.chunk}};

        log_chunk_time(res.generation_duration);
    }

    auto reification_results = m_reification_queue.done();
    for (auto& [id, res] : reification_results) {
        m_registry.get(id) = {
            id, ChunkRegistry::Status::Done,
            {res.chunk},
            res.chunk_mesh.land_mesh,
            res.chunk_mesh.water_mesh
        };

        // TODO: Damage surrounding chunks.
    }
}

void World::request_generation(ChunkIndex index, Real priority) {
    m_generation_queue.add(index, priority, [=]() -> GenerationResult {
        auto start = Time::now();
        auto chunk = m_generator.generate(index.x, index.y);
        return {chunk, Time::now() - start};
    });
}

void World::request_reification(ChunkIndex index, Real priority) {
    auto& data = m_registry.get(index);
    auto& chunk = data.chunk.value();

    auto neighbors = Generation::find_chunk_neighbors(index, m_registry);
    auto meshing_context = Generation::ChunkMeshing::create_meshing_context(data.chunk.value(), neighbors);
    m_reification_queue.add(index, priority, [=]() mutable -> ReificationResult {
        Generation::Lighting intitial_lighting{};
        intitial_lighting.add_chunk(chunk);
        intitial_lighting.illuminate(chunk);

        auto meshes = Generation::ChunkMeshing::mesh_chunk(chunk, meshing_context);
        return {chunk, meshes};
    });
}

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);

    Real request_type_penalty;
    switch (request_type) {
    case RequestType::Initial: request_type_penalty = 0; break;
    case RequestType::Update: request_type_penalty = 500; break;
    }

    return flat_distance + request_type_penalty;
}

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;
}

}