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