summary refs log tree commit diff
path: root/src/World/World.cpp
blob: 554886660c7328342e427a05b7cb157eedfbca29 (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
#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(Vector<3> position) {
    load_finished_chunks_from_queue();

    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.status == ChunkRegistry::Status::Empty) {
            request_generation(index, position.distance(index.middle()));
            data.status = ChunkRegistry::Status::WaitingForGeneration;
            continue;
        }

        if (data.status == ChunkRegistry::Status::WaitingForReification) {
            try_to_reify_chunk(data);
        }
        if (data.status == ChunkRegistry::Status::Done) {
            chunks.push_back(&data);
        }
    }

    return chunks;
}

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

void World::try_to_reify_chunk(ChunkRegistry::Data& data) {
    auto index = data.index;
    auto& chunk = data.chunk.value();

    UInt neighbor_index = 0;
    std::array<Chunk*, 8> neighbors;
    for (I32 x = -1; x <= 1; x++) {
        for (I32 y = -1; y <= 1; y++) {
            if (x == 0 && y == 0) continue;

            auto& neighbor_data = m_registry.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
    m_lighting.add_chunk(chunk);
    m_lighting.illuminate(m_registry);

    // Meshing
    auto meshes = Generation::ChunkMeshing::mesh_chunk(chunk, 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 = ChunkRegistry::Status::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;
}

}