summary refs log tree commit diff
path: root/src/World/Generation/Decoration.cpp
blob: ecfa49bc334cefefb1015bde1ae624af6b7243f6 (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
#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)) {
        return;
    }

    auto& place = chunk.at(pos);
    if (place.empty()) {
        place = {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, Real radius, BlockType block) {
    auto normalized_axis = axis.normalize();

    auto ortho1 = normalized_axis.any_orthogonal();
    auto ortho2 = normalized_axis.cross(ortho1);

    auto r = [](const Real x) { return static_cast<UInt>(std::round(x)); };

    Int radius_round = std::round(radius);
    for (I32 d1 = -radius_round; d1 <= radius_round; d1++) {
        Real height = std::sqrt(radius * radius - d1 * d1);
        for (I32 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();
    chunk.for_each_by_column([&](Pos pos, Chunk::BlockData& block) {
        auto pos_above = pos + Pos::up();
        if (!is_valid_position(pos_above))
            return Chunk::ColumnIteration::Continue;

        if (block.empty())
            return Chunk::ColumnIteration::Continue;

        auto type = block.type;
        if (type != BlockType::Snow && type != BlockType::Grass && type != BlockType::Dirt)
            return Chunk::ColumnIteration::SkipColumn;

        auto noise = m_tree_noise.at({TO(Real, pos.x()), TO(Real, pos.z())});
        if (noise < 0.8f)
            return Chunk::ColumnIteration::Continue;

        if (last_tree.distance(pos_above) < s_tree_radius * 3)
            return Chunk::ColumnIteration::Continue;

        draw_tree(chunk, pos_above);
        block = {BlockType::Dirt};
        last_tree = pos_above;

        return Chunk::ColumnIteration::SkipColumn;
    });
}

void TreeDecorator::draw_tree(Chunk& chunk, Pos pos) const {
    auto noise = m_tree_noise.at({(Real)pos.x(), (Real)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()};
        Real 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(Pos 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;
}

void DefaultLightDecorator::decorate_chunk(Chunk& chunk) {
    chunk.for_each_by_column([&](Pos pos, Chunk::BlockData& block) {
        if (!block.type.is_translucent()) return Chunk::ColumnIteration::Break;
        block.light = 200;
        return Chunk::ColumnIteration::Continue;
    });
}

}