diff options
| author | Mel <einebeere@gmail.com> | 2023-07-07 21:39:42 +0200 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2023-07-07 21:39:42 +0200 |
| commit | f1fc192ddc4c739fa8b4b376c759b7d3218a34eb (patch) | |
| tree | 9e9afb9a21ba3ca27d1f25d46230aa9d27f8be39 /src/World/Generation/Decoration.cpp | |
| parent | 24b8124469350d1c80d0553cf3f4bf58cdb1489b (diff) | |
| download | meowcraft-f1fc192ddc4c739fa8b4b376c759b7d3218a34eb.tar.zst meowcraft-f1fc192ddc4c739fa8b4b376c759b7d3218a34eb.zip | |
Chunk-bound tree decoration
Diffstat (limited to 'src/World/Generation/Decoration.cpp')
| -rw-r--r-- | src/World/Generation/Decoration.cpp | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/World/Generation/Decoration.cpp b/src/World/Generation/Decoration.cpp new file mode 100644 index 0000000..26eb397 --- /dev/null +++ b/src/World/Generation/Decoration.cpp @@ -0,0 +1,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; +} + +} |
