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
|
#include "Decoration.hpp"
#include <iostream>
namespace MC::World::Generation {
void Decorator::put_block(Chunk& chunk, Pos pos, BlockType block) {
if (!Chunk::is_valid_position(pos.x(), pos.y(), pos.z())) {
return;
}
if (chunk.is_empty(pos.x(), pos.y(), pos.z())) {
chunk.set(pos.x(), pos.y(), pos.z(), {block});
}
}
void Decorator::draw_column(Chunk& chunk, Pos pos, uint height, BlockType block) {
Pos current_pos = pos;
for (uint i = 0; i < height; i++) {
put_block(chunk, current_pos, block);
current_pos += Pos::up();
}
}
void Decorator::draw_circle(Chunk& chunk, Pos pos, Vector<3> axis, float radius, BlockType block) {
auto normalized_axis = axis.normalize();
auto ortho1 = normalized_axis.any_orthogonal();
auto ortho2 = normalized_axis.cross(ortho1);
auto r = [](const float x) { return static_cast<uint>(std::round(x)); };
int radius_round = std::round(radius);
for (int32_t d1 = -radius_round; d1 <= radius_round; d1++) {
float height = std::sqrt(radius * radius - d1 * d1);
for (int32_t d2 = -height; d2 <= (int)height; d2++) {
auto p = ortho1 * d1 + ortho2 * d2;
Pos block_pos = pos + Pos{r(p.x()), r(p.y()), r(p.z())};
put_block(chunk, block_pos, block);
}
}
}
void TreeDecorator::decorate_chunk(Chunk& chunk) {
Pos last_tree = Pos::max();
for (uint x = 0; x < Chunk::Width; x++) {
for (uint z = 0; z < Chunk::Width; z++) {
for (uint y = Chunk::Height; y > 1; y--) {
Pos pos{x, y, z};
if (!is_valid_position(pos))
continue;
auto block_below = chunk.get(x, y-1, z);
if (block_below.empty())
continue;
auto type = block_below.type;
if (type != BlockType::Snow && type != BlockType::Grass && type != BlockType::Dirt)
break;
auto noise = m_tree_noise.at({(float)x, (float)z});
if (noise < 0.8f)
continue;
if (last_tree.distance(pos) < s_tree_radius * 3)
continue;
draw_tree(chunk, pos);
chunk.set(x, y-1, z, {BlockType::Dirt});
last_tree = pos;
break;
}
}
}
}
void TreeDecorator::draw_tree(Chunk& chunk, Pos pos) const {
auto noise = m_tree_noise.at({(float)pos.x(), (float)pos.z()});
uint height = std::round(10 * noise - 4.75f);
draw_column(chunk, pos, height, BlockType::Wood);
uint max_leaf_height = 4;
for (int x = 0; x < max_leaf_height; x++) {
Pos p{pos.x(), pos.y() + height + x - 2, pos.z()};
float radius = s_tree_radius - 0.5f + x * ((0.3f * x - 1.45f) * x + 1.25f);
draw_circle(chunk, p, Vector<3>::up(), radius, BlockType::Leaves);
}
}
bool TreeDecorator::is_valid_position(Vector<3, uint> pos) {
int tree_radius = s_tree_radius;
return (int)pos.x() - tree_radius >= 0
&& pos.x() + tree_radius <= Chunk::Width
&& (int)pos.z() - tree_radius >= 0
&& pos.z() + tree_radius <= Chunk::Width;
}
}
|