summary refs log tree commit diff
path: root/src/World/World.cpp
blob: 03ce60ecc36c7cdabc00700faf3334c3033cce2e (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
#include "World.hpp"

namespace MC::World {

std::vector<World::ChunkData> 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<ChunkData> 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<ChunkIndex>& 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<ChunkIndex> 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<ChunkIndex> 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<ChunkIndex> World::load_finished_chunks_from_queue() {
    std::unordered_set<ChunkIndex> 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;
}

}