From a02bfad639c0eaa3241a4c7ef6dcce305697b311 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Sat, 2 May 2026 13:40:46 +0800 Subject: [PATCH] refactor: biome build (#5) * refactor: rename Biome to BiomeType * feat: add biome builder --- CMakeLists.txt | 6 + include/Cubed/constants.hpp | 3 + include/Cubed/gameplay/biome.hpp | 38 ++--- .../Cubed/gameplay/builders/biome_builder.hpp | 16 ++ .../gameplay/builders/desert_builder.hpp | 21 +++ .../gameplay/builders/forest_builder.hpp | 21 +++ .../gameplay/builders/mountain_builder.hpp | 21 +++ .../Cubed/gameplay/builders/plain_builder.hpp | 21 +++ .../Cubed/gameplay/builders/river_builder.hpp | 21 +++ include/Cubed/gameplay/chunk.hpp | 8 +- include/Cubed/gameplay/chunk_generator.hpp | 19 ++- src/gameplay/biome.cpp | 48 +++--- src/gameplay/builders/biome_builder.cpp | 18 ++ src/gameplay/builders/desert_builder.cpp | 55 ++++++ src/gameplay/builders/forest_builder.cpp | 72 ++++++++ src/gameplay/builders/mountain_builder.cpp | 63 +++++++ src/gameplay/builders/plain_builder.cpp | 55 ++++++ src/gameplay/builders/river_builder.cpp | 59 +++++++ src/gameplay/chunk.cpp | 6 +- src/gameplay/chunk_generator.cpp | 156 ++++++------------ 20 files changed, 563 insertions(+), 164 deletions(-) create mode 100644 include/Cubed/gameplay/builders/biome_builder.hpp create mode 100644 include/Cubed/gameplay/builders/desert_builder.hpp create mode 100644 include/Cubed/gameplay/builders/forest_builder.hpp create mode 100644 include/Cubed/gameplay/builders/mountain_builder.hpp create mode 100644 include/Cubed/gameplay/builders/plain_builder.hpp create mode 100644 include/Cubed/gameplay/builders/river_builder.hpp create mode 100644 src/gameplay/builders/biome_builder.cpp create mode 100644 src/gameplay/builders/desert_builder.cpp create mode 100644 src/gameplay/builders/forest_builder.cpp create mode 100644 src/gameplay/builders/mountain_builder.cpp create mode 100644 src/gameplay/builders/plain_builder.cpp create mode 100644 src/gameplay/builders/river_builder.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5eb4925..faa26af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,12 @@ add_executable(${PROJECT_NAME} src/tools/perlin_noise.cpp src/ui/text.cpp src/window.cpp + src/gameplay/builders/biome_builder.cpp + src/gameplay/builders/plain_builder.cpp + src/gameplay/builders/mountain_builder.cpp + src/gameplay/builders/river_builder.cpp + src/gameplay/builders/desert_builder.cpp + src/gameplay/builders/forest_builder.cpp ) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/include/Cubed/constants.hpp b/include/Cubed/constants.hpp index fc98789..60f32ce 100644 --- a/include/Cubed/constants.hpp +++ b/include/Cubed/constants.hpp @@ -22,6 +22,9 @@ constexpr float DEFAULT_MAX_RUN_SPEED = 7.0f; constexpr float DEFAULT_ACCELERATION = 10.0f; constexpr float DEFAULT_DECELERATION = 15.0f; constexpr float DEFAULT_G = 22.5f; +static constexpr int SIZE_X = CHUCK_SIZE; +static constexpr int SIZE_Y = WORLD_SIZE_Y; +static constexpr int SIZE_Z = CHUCK_SIZE; using HeightMapArray = std::array, CHUCK_SIZE>; diff --git a/include/Cubed/gameplay/biome.hpp b/include/Cubed/gameplay/biome.hpp index 68a8057..100d6d8 100644 --- a/include/Cubed/gameplay/biome.hpp +++ b/include/Cubed/gameplay/biome.hpp @@ -7,12 +7,7 @@ namespace Cubed { constexpr float BIOME_NOISE_FREQUENCY = 0.03f; -constexpr float PLAIN_FREQ = 0.4f; -constexpr float FOREST_FREQ = 1.2f; -constexpr float DESERT_FREQ = 1.2f; -constexpr float MOUNTAIN_FREQ = 2.0f; - -enum class Biome { PLAIN = 0, FOREST, DESERT, MOUNTAIN, RIVER, NONE }; +enum class BiomeType { PLAIN = 0, FOREST, DESERT, MOUNTAIN, RIVER, NONE }; struct BiomeHeightRange { int base_y; @@ -20,19 +15,23 @@ struct BiomeHeightRange { }; struct BiomeNonAdjacent { - Biome first; - std::vector second; - Biome replace; + BiomeType first; + std::vector second; + BiomeType replace; }; static inline const std::vector NON_ADJACENT{ - {{Biome::PLAIN, {Biome::DESERT}, Biome::RIVER}, - {Biome::FOREST, {Biome::DESERT}, Biome::RIVER}, - {Biome::DESERT, {Biome::MOUNTAIN, Biome::FOREST}, Biome::RIVER}, - {Biome::MOUNTAIN, {Biome::DESERT, Biome::FOREST}, Biome::RIVER}}}; + {{BiomeType::PLAIN, {BiomeType::DESERT}, BiomeType::RIVER}, + {BiomeType::FOREST, {BiomeType::DESERT}, BiomeType::RIVER}, + {BiomeType::DESERT, + {BiomeType::MOUNTAIN, BiomeType::FOREST}, + BiomeType::RIVER}, + {BiomeType::MOUNTAIN, + {BiomeType::DESERT, BiomeType::FOREST}, + BiomeType::RIVER}}}; struct BaseBiomeParams { - Biome biome; + BiomeType biome; std::pair temp; std::pair humid; std::array frequencies; @@ -51,11 +50,11 @@ struct MountainParams : public BaseBiomeParams {}; struct RiverParams : public BaseBiomeParams {}; -std::string get_biome_str(Biome biome); -Biome get_biome_from_noise(float temp, float humid); -std::array get_noise_frequencies_for_biome(Biome biome); -BiomeHeightRange get_biome_height_range(Biome biome); -Biome safe_int_to_biome(int x); +std::string get_biome_str(BiomeType biome); +BiomeType get_biome_from_noise(float temp, float humid); +std::array get_noise_frequencies_for_biome(BiomeType biome); +BiomeHeightRange get_biome_height_range(BiomeType biome); +BiomeType safe_int_to_biome(int x); int get_interpolated_height(float world_x, float world_z, float temp, float humid); @@ -64,4 +63,5 @@ ForestParams& forest_params(); DesertParams& desert_params(); MountainParams& mountain_params(); RiverParams& river_params(); + } // namespace Cubed diff --git a/include/Cubed/gameplay/builders/biome_builder.hpp b/include/Cubed/gameplay/builders/biome_builder.hpp new file mode 100644 index 0000000..694572f --- /dev/null +++ b/include/Cubed/gameplay/builders/biome_builder.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace Cubed { +class ChunkGenerator; +class BiomeBuilder { +public: + BiomeBuilder() = default; + virtual ~BiomeBuilder() = default; + virtual ChunkGenerator& get_chunk_generator() = 0; + virtual void build_biome() = 0; + virtual void build_vegetation() = 0; + +protected: + void build_bottom(); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/builders/desert_builder.hpp b/include/Cubed/gameplay/builders/desert_builder.hpp new file mode 100644 index 0000000..68d81a8 --- /dev/null +++ b/include/Cubed/gameplay/builders/desert_builder.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Cubed/gameplay/builders/biome_builder.hpp" +namespace Cubed { + +class ChunkGenerator; + +class DesertBuilder : public BiomeBuilder { +public: + DesertBuilder(ChunkGenerator& chunk_generator); + void build_biome() override; + ChunkGenerator& get_chunk_generator() override; + void build_vegetation() override; + +private: + ChunkGenerator& m_chunk_generator; + + void build_blocks(); +}; + +} // namespace Cubed diff --git a/include/Cubed/gameplay/builders/forest_builder.hpp b/include/Cubed/gameplay/builders/forest_builder.hpp new file mode 100644 index 0000000..5793daa --- /dev/null +++ b/include/Cubed/gameplay/builders/forest_builder.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Cubed/gameplay/builders/biome_builder.hpp" +namespace Cubed { + +class ChunkGenerator; + +class ForestBuilder : public BiomeBuilder { +public: + ForestBuilder(ChunkGenerator& chunk_generator); + void build_biome() override; + ChunkGenerator& get_chunk_generator() override; + void build_vegetation() override; + +private: + ChunkGenerator& m_chunk_generator; + + void build_blocks(); +}; + +} // namespace Cubed diff --git a/include/Cubed/gameplay/builders/mountain_builder.hpp b/include/Cubed/gameplay/builders/mountain_builder.hpp new file mode 100644 index 0000000..6fa599b --- /dev/null +++ b/include/Cubed/gameplay/builders/mountain_builder.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Cubed/gameplay/builders/biome_builder.hpp" +namespace Cubed { + +class ChunkGenerator; + +class MountainBuilder : public BiomeBuilder { +public: + MountainBuilder(ChunkGenerator& chunk_generator); + void build_biome() override; + ChunkGenerator& get_chunk_generator() override; + void build_vegetation() override; + +private: + ChunkGenerator& m_chunk_generator; + + void build_blocks(); +}; + +} // namespace Cubed diff --git a/include/Cubed/gameplay/builders/plain_builder.hpp b/include/Cubed/gameplay/builders/plain_builder.hpp new file mode 100644 index 0000000..47e1304 --- /dev/null +++ b/include/Cubed/gameplay/builders/plain_builder.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Cubed/gameplay/builders/biome_builder.hpp" +namespace Cubed { + +class ChunkGenerator; + +class PlainBuilder : public BiomeBuilder { +public: + PlainBuilder(ChunkGenerator& chunk_generator); + void build_biome() override; + ChunkGenerator& get_chunk_generator() override; + void build_vegetation() override; + +private: + ChunkGenerator& m_chunk_generator; + + void build_blocks(); +}; + +} // namespace Cubed diff --git a/include/Cubed/gameplay/builders/river_builder.hpp b/include/Cubed/gameplay/builders/river_builder.hpp new file mode 100644 index 0000000..97450a0 --- /dev/null +++ b/include/Cubed/gameplay/builders/river_builder.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Cubed/gameplay/builders/biome_builder.hpp" +namespace Cubed { + +class ChunkGenerator; + +class RiverBuilder : public BiomeBuilder { +public: + RiverBuilder(ChunkGenerator& chunk_generator); + void build_biome() override; + ChunkGenerator& get_chunk_generator() override; + void build_vegetation() override; + +private: + ChunkGenerator& m_chunk_generator; + + void build_blocks(); +}; + +} // namespace Cubed diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp index c8911d9..4a39aee 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -24,7 +24,7 @@ private: std::atomic m_need_upload{true}; std::atomic m_is_on_gen_vertex_data{false}; std::atomic m_vertex_sum = 0; - std::atomic m_biome = Biome::PLAIN; + std::atomic m_biome = BiomeType::PLAIN; std::mutex m_vertexs_data_mutex; std::unique_ptr m_generator; @@ -50,7 +50,7 @@ public: Chunk(Chunk&&) noexcept; Chunk& operator=(Chunk&&) noexcept; - Biome get_biome() const; + BiomeType get_biome() const; ChunkPos get_chunk_pos() const; const std::vector& get_chunk_blocks() const; HeightMapArray get_heightmap() const; @@ -94,8 +94,8 @@ public: void set_chunk_block(int index, unsigned id); ChunkPos chunk_pos() const; - Biome biome() const; - void biome(Biome b); + BiomeType biome() const; + void biome(BiomeType b); HeightMapArray& heightmap(); std::vector& blocks(); }; diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index feb2528..c9f33c1 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -2,21 +2,17 @@ #include "Cubed/constants.hpp" #include "Cubed/gameplay/biome.hpp" +#include "Cubed/gameplay/builders/biome_builder.hpp" #include "Cubed/tools/cubed_random.hpp" #include +#include #include namespace Cubed { class Chunk; class ChunkGenerator { - static constexpr int SIZE_X = CHUCK_SIZE; - static constexpr int SIZE_Y = WORLD_SIZE_Y; - static constexpr int SIZE_Z = CHUCK_SIZE; - using HeightMapArray = - std::array, CHUCK_SIZE>; - public: ChunkGenerator(Chunk& chunk); @@ -44,15 +40,22 @@ public: // Generate Structure void generate_vegetation(); + Chunk& chunk(); + Random& random(); + bool neighbor_river() const; + private: static inline std::atomic is_init{false}; static inline unsigned m_generator_seed{0}; static inline std::atomic is_seed_change{false}; Chunk& m_chunk; Random m_random; - std::array neighbor_biome{Biome::NONE, Biome::NONE, Biome::NONE, - Biome::NONE}; + std::array neighbor_biome{BiomeType::NONE, BiomeType::NONE, + BiomeType::NONE, BiomeType::NONE}; + std::unique_ptr m_biome_builder{nullptr}; bool is_neighbor_river = false; + + void make_biome_builder(); }; } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/biome.cpp b/src/gameplay/biome.cpp index b48447c..f9736f1 100644 --- a/src/gameplay/biome.cpp +++ b/src/gameplay/biome.cpp @@ -2,20 +2,18 @@ #include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/log.hpp" -#include "Cubed/tools/perlin_noise.hpp" -#include #include namespace Cubed { -static PlainParams plain{{Biome::PLAIN, +static PlainParams plain{{BiomeType::PLAIN, {0.0f, 0.5f}, {0.0f, 0.5f}, {0.003f, 0.010f, 0.020f}, {62, 8}}}; -static ForestParams forest{{Biome::FOREST, +static ForestParams forest{{BiomeType::FOREST, {0.5f, 1.0f}, {0.5f, 1.0f}, {0.004f, 0.010f, 0.020f}, @@ -24,27 +22,27 @@ static ForestParams forest{{Biome::FOREST, }; -static DesertParams desert{{Biome::DESERT, +static DesertParams desert{{BiomeType::DESERT, {0.5f, 1.0f}, {0.0f, 0.5f}, {0.003f, 0.010f, 0.020f}, {61, 12}}}; -static MountainParams mountain{{Biome::MOUNTAIN, +static MountainParams mountain{{BiomeType::MOUNTAIN, {0.0f, 0.5f}, {0.5f, 1.0f}, {0.006f, 0.014f, 0.010f}, {70, 70}}}; -static RiverParams river{{Biome::RIVER, +static RiverParams river{{BiomeType::RIVER, {-0.1f, -0.1f}, {-0.1f, -0.1f}, {0.003f, 0.010f, 0.020f}, {50, 6}}}; -std::string get_biome_str(Biome biome) { +std::string get_biome_str(BiomeType biome) { std::string str; - using enum Biome; + using enum BiomeType; switch (biome) { case PLAIN: str = "Plain"; @@ -86,8 +84,8 @@ Biome get_biome_from_noise(float temp, float humid) { return Biome::FOREST; } */ -Biome get_biome_from_noise(float temp, float humid) { - using enum Biome; +BiomeType get_biome_from_noise(float temp, float humid) { + using enum BiomeType; if (plain.temp.first <= temp && temp < plain.temp.second && plain.humid.first <= humid && humid < plain.humid.second) { return PLAIN; @@ -107,8 +105,8 @@ Biome get_biome_from_noise(float temp, float humid) { Logger::warn("Invail Temp {} or Humid {}", temp, humid); return PLAIN; } -std::array get_noise_frequencies_for_biome(Biome biome) { - using enum Biome; +std::array get_noise_frequencies_for_biome(BiomeType biome) { + using enum BiomeType; switch (biome) { case PLAIN: return plain.frequencies; @@ -128,8 +126,8 @@ std::array get_noise_frequencies_for_biome(Biome biome) { return {0.003f, 0.015f, 0.06f}; } -BiomeHeightRange get_biome_height_range(Biome biome) { - using enum Biome; +BiomeHeightRange get_biome_height_range(BiomeType biome) { + using enum BiomeType; switch (biome) { case PLAIN: return plain.height_range; @@ -149,16 +147,16 @@ BiomeHeightRange get_biome_height_range(Biome biome) { return {62, 4}; } -Biome safe_int_to_biome(int x) { - using enum Biome; - static const std::unordered_map INT_TO_BIOME_MAP{ +BiomeType safe_int_to_biome(int x) { + using enum BiomeType; + static const std::unordered_map INT_TO_BIOME_MAP{ {0, PLAIN}, {1, FOREST}, {2, DESERT}, {3, MOUNTAIN}, {4, RIVER}}; auto it = INT_TO_BIOME_MAP.find(x); ASSERT_MSG(it != INT_TO_BIOME_MAP.end(), ":Can't Find"); return it->second; } - +/* int get_interpolated_height(float world_x, float world_z, float temp, float humid) { @@ -186,7 +184,7 @@ int get_interpolated_height(float world_x, float world_z, float temp, w_desert /= total; w_forest /= total; - auto sample_height = [&](Biome b) -> float { + auto sample_height = [&](BiomeType b) -> float { auto range = get_biome_height_range(b); auto [f1, f2, f3] = get_noise_frequencies_for_biome(b); float n = 1.00f * PerlinNoise::noise(world_x * f1, 0.5f, world_z * f1) + @@ -196,13 +194,13 @@ int get_interpolated_height(float world_x, float world_z, float temp, return range.base_y + n * range.amplitude; }; - float h = w_mountain * sample_height(Biome::MOUNTAIN) + - w_plain * sample_height(Biome::PLAIN) + - w_desert * sample_height(Biome::DESERT) + - w_forest * sample_height(Biome::FOREST); + float h = w_mountain * sample_height(BiomeType::MOUNTAIN) + + w_plain * sample_height(BiomeType::PLAIN) + + w_desert * sample_height(BiomeType::DESERT) + + w_forest * sample_height(BiomeType::FOREST); return static_cast(h); } - +*/ PlainParams& plain_params() { return plain; } ForestParams& forest_params() { return forest; } DesertParams& desert_params() { return desert; } diff --git a/src/gameplay/builders/biome_builder.cpp b/src/gameplay/builders/biome_builder.cpp new file mode 100644 index 0000000..3314569 --- /dev/null +++ b/src/gameplay/builders/biome_builder.cpp @@ -0,0 +1,18 @@ +#include "Cubed/gameplay/builders/biome_builder.hpp" + +#include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/chunk_generator.hpp" +namespace Cubed { +void BiomeBuilder::build_bottom() { + ChunkGenerator& chunk_generator = get_chunk_generator(); + Chunk& chunk = chunk_generator.chunk(); + auto& m_blocks = chunk.blocks(); + for (int x = 0; x < CHUCK_SIZE; x++) { + for (int y = 0; y < 5; y++) { + for (int z = 0; z < CHUCK_SIZE; z++) { + m_blocks[Chunk::get_index(x, y, z)] = 3; + } + } + } +} +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/builders/desert_builder.cpp b/src/gameplay/builders/desert_builder.cpp new file mode 100644 index 0000000..bc1a511 --- /dev/null +++ b/src/gameplay/builders/desert_builder.cpp @@ -0,0 +1,55 @@ +#include "Cubed/gameplay/builders/desert_builder.hpp" + +#include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/chunk_generator.hpp" +namespace Cubed { +DesertBuilder::DesertBuilder(ChunkGenerator& chunk_generator) + : m_chunk_generator(chunk_generator) {} + +void DesertBuilder::build_biome() { + BiomeBuilder::build_bottom(); + build_blocks(); +}; + +void DesertBuilder::build_blocks() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + for (int x = 0; x < CHUCK_SIZE; x++) { + for (int z = 0; z < CHUCK_SIZE; z++) { + int height = static_cast(m_heightmap[x][z]); + for (int y = 5; y < height - 5; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 3; + } + + for (int y = height - 5; y <= height; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 4; + } + } + } +} + +void DesertBuilder::build_vegetation() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + if (m_chunk_generator.neighbor_river()) { + for (int x = 0; x < SIZE_X; x++) { + for (int z = 0; z < SIZE_Z; z++) { + int height = static_cast(m_heightmap[x][z]); + if (height >= SEA_LEVEL) { + continue; + } + for (int y = height + 1; y < SEA_LEVEL; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 7; + } + } + } + } +} + +ChunkGenerator& DesertBuilder::get_chunk_generator() { + return m_chunk_generator; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/builders/forest_builder.cpp b/src/gameplay/builders/forest_builder.cpp new file mode 100644 index 0000000..5cdf9a7 --- /dev/null +++ b/src/gameplay/builders/forest_builder.cpp @@ -0,0 +1,72 @@ +#include "Cubed/gameplay/builders/forest_builder.hpp" + +#include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/chunk_generator.hpp" +#include "Cubed/gameplay/tree.hpp" +namespace Cubed { +ForestBuilder::ForestBuilder(ChunkGenerator& chunk_generator) + : m_chunk_generator(chunk_generator) {} + +void ForestBuilder::build_biome() { + BiomeBuilder::build_bottom(); + build_blocks(); +}; + +void ForestBuilder::build_blocks() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + for (int x = 0; x < CHUCK_SIZE; x++) { + for (int z = 0; z < CHUCK_SIZE; z++) { + int height = static_cast(m_heightmap[x][z]); + for (int y = 5; y < height - 5; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 3; + } + for (int y = height - 5; y < height; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 2; + } + m_blocks[Chunk::get_index(x, height, z)] = 1; + } + } +} + +void ForestBuilder::build_vegetation() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_heightmap = m_chunk.heightmap(); + auto& m_random = m_chunk_generator.random(); + std::array x_arr; + std::iota(x_arr.begin(), x_arr.end(), 0); + std::shuffle(x_arr.begin(), x_arr.end(), m_random.engine()); + std::array z_arr; + std::iota(z_arr.begin(), z_arr.end(), 0); + std::shuffle(z_arr.begin(), z_arr.end(), m_random.engine()); + for (auto x : x_arr) { + for (auto z : z_arr) { + int y = static_cast(m_heightmap[x][z]); + if (m_random.random_bool(forest_params().tree_frequency) && + y >= SEA_LEVEL) { + build_tree(m_chunk, {x, y, z}); + } + } + } + auto& m_blocks = m_chunk.blocks(); + if (m_chunk_generator.neighbor_river()) { + for (int x = 0; x < SIZE_X; x++) { + for (int z = 0; z < SIZE_Z; z++) { + int height = static_cast(m_heightmap[x][z]); + if (height >= SEA_LEVEL) { + continue; + } + for (int y = height + 1; y < SEA_LEVEL; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 7; + } + } + } + } +} + +ChunkGenerator& ForestBuilder::get_chunk_generator() { + return m_chunk_generator; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/builders/mountain_builder.cpp b/src/gameplay/builders/mountain_builder.cpp new file mode 100644 index 0000000..68ba103 --- /dev/null +++ b/src/gameplay/builders/mountain_builder.cpp @@ -0,0 +1,63 @@ +#include "Cubed/gameplay/builders/mountain_builder.hpp" + +#include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/chunk_generator.hpp" +namespace Cubed { +MountainBuilder::MountainBuilder(ChunkGenerator& chunk_generator) + : m_chunk_generator(chunk_generator) {} + +void MountainBuilder::build_biome() { + BiomeBuilder::build_bottom(); + build_blocks(); +}; + +void MountainBuilder::build_blocks() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + for (int x = 0; x < CHUCK_SIZE; x++) { + for (int z = 0; z < CHUCK_SIZE; z++) { + int height = static_cast(m_heightmap[x][z]); + for (int y = 5; y < height - 5; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 3; + } + for (int y = height - 5; y <= height - 1; y++) { + if (y > 110) { + m_blocks[Chunk::get_index(x, y, z)] = 3; + } else { + m_blocks[Chunk::get_index(x, y, z)] = 2; + } + } + if (height > 110) { + m_blocks[Chunk::get_index(x, height - 1, z)] = 3; + } else { + m_blocks[Chunk::get_index(x, height - 1, z)] = 1; + } + } + } +} + +void MountainBuilder::build_vegetation() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + if (m_chunk_generator.neighbor_river()) { + for (int x = 0; x < SIZE_X; x++) { + for (int z = 0; z < SIZE_Z; z++) { + int height = static_cast(m_heightmap[x][z]); + if (height >= SEA_LEVEL) { + continue; + } + for (int y = height + 1; y < SEA_LEVEL; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 7; + } + } + } + } +} + +ChunkGenerator& MountainBuilder::get_chunk_generator() { + return m_chunk_generator; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/builders/plain_builder.cpp b/src/gameplay/builders/plain_builder.cpp new file mode 100644 index 0000000..147980d --- /dev/null +++ b/src/gameplay/builders/plain_builder.cpp @@ -0,0 +1,55 @@ +#include "Cubed/gameplay/builders/plain_builder.hpp" + +#include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/chunk_generator.hpp" +namespace Cubed { +PlainBuilder::PlainBuilder(ChunkGenerator& chunk_generator) + : m_chunk_generator(chunk_generator) {} + +void PlainBuilder::build_biome() { + BiomeBuilder::build_bottom(); + build_blocks(); +}; + +void PlainBuilder::build_blocks() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + for (int x = 0; x < CHUCK_SIZE; x++) { + for (int z = 0; z < CHUCK_SIZE; z++) { + int height = static_cast(m_heightmap[x][z]); + for (int y = 5; y < height - 5; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 3; + } + for (int y = height - 5; y < height; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 2; + } + m_blocks[Chunk::get_index(x, height, z)] = 1; + } + } +} + +void PlainBuilder::build_vegetation() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + if (m_chunk_generator.neighbor_river()) { + for (int x = 0; x < SIZE_X; x++) { + for (int z = 0; z < SIZE_Z; z++) { + int height = static_cast(m_heightmap[x][z]); + if (height >= SEA_LEVEL) { + continue; + } + for (int y = height + 1; y < SEA_LEVEL; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 7; + } + } + } + } +} + +ChunkGenerator& PlainBuilder::get_chunk_generator() { + return m_chunk_generator; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/builders/river_builder.cpp b/src/gameplay/builders/river_builder.cpp new file mode 100644 index 0000000..15b92d4 --- /dev/null +++ b/src/gameplay/builders/river_builder.cpp @@ -0,0 +1,59 @@ +#include "Cubed/gameplay/builders/river_builder.hpp" + +#include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/chunk_generator.hpp" +namespace Cubed { +RiverBuilder::RiverBuilder(ChunkGenerator& chunk_generator) + : m_chunk_generator(chunk_generator) {} + +void RiverBuilder::build_biome() { + BiomeBuilder::build_bottom(); + build_blocks(); +}; + +void RiverBuilder::build_blocks() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + for (int x = 0; x < CHUCK_SIZE; x++) { + for (int z = 0; z < CHUCK_SIZE; z++) { + int height = static_cast(m_heightmap[x][z]); + for (int y = 5; y < height - 5; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 3; + } + for (int y = height - 5; y <= height - 1; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 2; + } + for (int y = height; y <= height; y++) { + if (y >= SEA_LEVEL - 1) { + m_blocks[Chunk::get_index(x, y, z)] = 1; + } else { + m_blocks[Chunk::get_index(x, y, z)] = 2; + } + } + } + } +} + +void RiverBuilder::build_vegetation() { + auto& m_chunk = m_chunk_generator.chunk(); + auto& m_blocks = m_chunk.blocks(); + auto& m_heightmap = m_chunk.heightmap(); + for (int x = 0; x < SIZE_X; x++) { + for (int z = 0; z < SIZE_Z; z++) { + int height = static_cast(m_heightmap[x][z]); + if (height >= SEA_LEVEL) { + continue; + } + for (int y = height + 1; y < SEA_LEVEL; y++) { + m_blocks[Chunk::get_index(x, y, z)] = 7; + } + } + } +} + +ChunkGenerator& RiverBuilder::get_chunk_generator() { + return m_chunk_generator; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index a5f5eb7..8da69d9 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -46,7 +46,7 @@ Chunk& Chunk::operator=(Chunk&& other) noexcept { return *this; } -Biome Chunk::get_biome() const { return m_biome.load(); } +BiomeType Chunk::get_biome() const { return m_biome.load(); } ChunkPos Chunk::get_chunk_pos() const { return m_chunk_pos; } @@ -300,9 +300,9 @@ void Chunk::set_chunk_block(int index, unsigned id) { ChunkPos Chunk::chunk_pos() const { return m_chunk_pos; } -Biome Chunk::biome() const { return m_biome; } +BiomeType Chunk::biome() const { return m_biome; } -void Chunk::biome(Biome b) { m_biome = b; } +void Chunk::biome(BiomeType b) { m_biome = b; } HeightMapArray& Chunk::heightmap() { return m_heightmap; } std::vector& Chunk::blocks() { return m_blocks; } diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index 10f3536..2345f28 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -1,12 +1,19 @@ #include "Cubed/gameplay/chunk_generator.hpp" +#include "Cubed/gameplay/builders/desert_builder.hpp" +#include "Cubed/gameplay/builders/forest_builder.hpp" +#include "Cubed/gameplay/builders/mountain_builder.hpp" +#include "Cubed/gameplay/builders/plain_builder.hpp" +#include "Cubed/gameplay/builders/river_builder.hpp" #include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/tree.hpp" #include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/perlin_noise.hpp" -#include namespace Cubed { + +using enum BiomeType; + constexpr int BLEND_RADIUS = 12; ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) { ASSERT_MSG(is_init, "ChunksGenerator is not init"); @@ -57,9 +64,9 @@ void ChunkGenerator::resolve_biome_adjacency_conflict( if (chunk == nullptr) { continue; } - Biome biome = chunk->get_biome(); + BiomeType biome = chunk->get_biome(); neighbor_biome[i] = biome; - if (biome == Biome::RIVER) { + if (biome == BiomeType::RIVER) { is_neighbor_river = true; } for (const auto& non : NON_ADJACENT) { @@ -89,7 +96,7 @@ void ChunkGenerator::generate_heightmap() { float world_x = static_cast(x + m_chunk_pos.x * CHUCK_SIZE); float world_z = static_cast(z + m_chunk_pos.z * CHUCK_SIZE); - auto sample_height = [&](Biome b) -> float { + auto sample_height = [&](BiomeType b) -> float { auto range = get_biome_height_range(b); auto [f1, f2, f3] = get_noise_frequencies_for_biome(b); float n = 1.00f * PerlinNoise::noise(world_x * f1, 0.5f, @@ -185,63 +192,13 @@ void ChunkGenerator::blend_heightmap_boundaries( } void ChunkGenerator::generate_terrain_blocks() { - auto& m_blocks = m_chunk.blocks(); - auto& m_heightmap = m_chunk.heightmap(); - auto m_biome = m_chunk.biome(); - // bottom - m_blocks.assign(CHUCK_SIZE * CHUCK_SIZE * WORLD_SIZE_Y, 0); - for (int x = 0; x < CHUCK_SIZE; x++) { - for (int y = 0; y < 5; y++) { - for (int z = 0; z < CHUCK_SIZE; z++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; - } - } - } - - for (int x = 0; x < CHUCK_SIZE; x++) { - for (int z = 0; z < CHUCK_SIZE; z++) { - int height = static_cast(m_heightmap[x][z]); - for (int y = 5; y < height - 5; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; - } - if (m_biome == Biome::MOUNTAIN) { - for (int y = height - 5; y <= height - 1; y++) { - if (y > 110) { - m_blocks[Chunk::get_index(x, y, z)] = 3; - } else { - m_blocks[Chunk::get_index(x, y, z)] = 2; - } - } - if (height > 110) { - m_blocks[Chunk::get_index(x, height - 1, z)] = 3; - } else { - m_blocks[Chunk::get_index(x, height - 1, z)] = 1; - } - } else if (m_biome == Biome::DESERT) { - for (int y = height - 5; y <= height; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 4; - } - } else if (m_biome == Biome::RIVER) { - for (int y = height - 5; y <= height - 1; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 2; - } - for (int y = height; y <= height; y++) { - if (y >= SEA_LEVEL - 1) { - m_blocks[Chunk::get_index(x, y, z)] = 1; - } else { - m_blocks[Chunk::get_index(x, y, z)] = 2; - } - } - } else { - for (int y = height - 5; y <= height - 1; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 2; - } - for (int y = height; y <= height; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 1; - } - } - } + make_biome_builder(); + if (!m_biome_builder) { + Logger::error("BiomeBuilder is nullptr"); + return; } + m_chunk.blocks().assign(CHUCK_SIZE * CHUCK_SIZE * WORLD_SIZE_Y, 0); + m_biome_builder->build_biome(); } void ChunkGenerator::blend_surface_blocks_borders( @@ -344,7 +301,7 @@ void ChunkGenerator::blend_surface_blocks_borders( // Update the top block if the type changed if (final_type != type_self) { // top block - if (m_chunk.biome() == Biome::RIVER && final_type == 1) { + if (m_chunk.biome() == BiomeType::RIVER && final_type == 1) { final_type = 2; } if (is_neighbor_river && final_type == 1) { @@ -371,52 +328,41 @@ void ChunkGenerator::blend_surface_blocks_borders( } void ChunkGenerator::generate_vegetation() { - auto m_biome = m_chunk.biome(); - auto& m_blocks = m_chunk.blocks(); - auto& m_heightmap = m_chunk.heightmap(); - if (m_biome == Biome::FOREST) { - std::array x_arr; - std::iota(x_arr.begin(), x_arr.end(), 0); - std::shuffle(x_arr.begin(), x_arr.end(), m_random.engine()); - std::array z_arr; - std::iota(z_arr.begin(), z_arr.end(), 0); - std::shuffle(z_arr.begin(), z_arr.end(), m_random.engine()); - for (auto x : x_arr) { - for (auto z : z_arr) { - int y = static_cast(m_heightmap[x][z]); - if (m_random.random_bool(forest_params().tree_frequency) && - y >= SEA_LEVEL) { - build_tree(m_chunk, {x, y, z}); - } - } - } + + if (!m_biome_builder) { + Logger::error("BiomeBuilder is nullptr"); + return; } - if (m_biome == Biome::RIVER) { - for (int x = 0; x < SIZE_X; x++) { - for (int z = 0; z < SIZE_Z; z++) { - int height = static_cast(m_heightmap[x][z]); - if (height >= SEA_LEVEL) { - continue; - } - for (int y = height + 1; y < SEA_LEVEL; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 7; - } - } - } - } - if (is_neighbor_river) { - for (int x = 0; x < SIZE_X; x++) { - for (int z = 0; z < SIZE_Z; z++) { - int height = static_cast(m_heightmap[x][z]); - if (height >= SEA_LEVEL) { - continue; - } - for (int y = height + 1; y < SEA_LEVEL; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 7; - } - } - } + m_biome_builder->build_vegetation(); +} + +void ChunkGenerator::make_biome_builder() { + auto biome = m_chunk.biome(); + switch (biome) { + case PLAIN: + m_biome_builder = std::make_unique(*this); + break; + case DESERT: + m_biome_builder = std::make_unique(*this); + break; + case FOREST: + m_biome_builder = std::make_unique(*this); + break; + case MOUNTAIN: + m_biome_builder = std::make_unique(*this); + break; + case RIVER: + m_biome_builder = std::make_unique(*this); + break; + case NONE: + m_biome_builder = nullptr; + break; } } +Chunk& ChunkGenerator::chunk() { return m_chunk; } + +Random& ChunkGenerator::random() { return m_random; } +bool ChunkGenerator::neighbor_river() const { return is_neighbor_river; } + } // namespace Cubed \ No newline at end of file