diff --git a/assets/texture/block/water/back.png b/assets/texture/block/water/back.png new file mode 100644 index 0000000..ddec446 Binary files /dev/null and b/assets/texture/block/water/back.png differ diff --git a/assets/texture/block/water/base.png b/assets/texture/block/water/base.png new file mode 100644 index 0000000..ddec446 Binary files /dev/null and b/assets/texture/block/water/base.png differ diff --git a/assets/texture/block/water/front.png b/assets/texture/block/water/front.png new file mode 100644 index 0000000..ddec446 Binary files /dev/null and b/assets/texture/block/water/front.png differ diff --git a/assets/texture/block/water/left.png b/assets/texture/block/water/left.png new file mode 100644 index 0000000..ddec446 Binary files /dev/null and b/assets/texture/block/water/left.png differ diff --git a/assets/texture/block/water/right.png b/assets/texture/block/water/right.png new file mode 100644 index 0000000..ddec446 Binary files /dev/null and b/assets/texture/block/water/right.png differ diff --git a/assets/texture/block/water/top.png b/assets/texture/block/water/top.png new file mode 100644 index 0000000..ddec446 Binary files /dev/null and b/assets/texture/block/water/top.png differ diff --git a/assets/texture/item/block/water.png b/assets/texture/item/block/water.png new file mode 100644 index 0000000..8eb8b80 Binary files /dev/null and b/assets/texture/item/block/water.png differ diff --git a/include/Cubed/constants.hpp b/include/Cubed/constants.hpp index 8e0efe9..fc98789 100644 --- a/include/Cubed/constants.hpp +++ b/include/Cubed/constants.hpp @@ -4,8 +4,9 @@ namespace Cubed { constexpr int WORLD_SIZE_Y = 256; constexpr int CHUCK_SIZE = 16; +constexpr int SEA_LEVEL = 64; -constexpr int MAX_BLOCK_NUM = 7; +constexpr int MAX_BLOCK_NUM = 8; constexpr int MAX_UI_NUM = 1; constexpr int MAX_BLOCK_STATUS = 1; constexpr int MAX_BIOME_SUM = 4; diff --git a/include/Cubed/gameplay/biome.hpp b/include/Cubed/gameplay/biome.hpp index e403d3e..68a8057 100644 --- a/include/Cubed/gameplay/biome.hpp +++ b/include/Cubed/gameplay/biome.hpp @@ -12,7 +12,7 @@ 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, NONE }; +enum class Biome { PLAIN = 0, FOREST, DESERT, MOUNTAIN, RIVER, NONE }; struct BiomeHeightRange { int base_y; @@ -26,10 +26,10 @@ struct BiomeNonAdjacent { }; static inline const std::vector NON_ADJACENT{ - {{Biome::PLAIN, {Biome::NONE}, Biome::PLAIN}, - {Biome::FOREST, {Biome::DESERT}, Biome::PLAIN}, - {Biome::DESERT, {Biome::MOUNTAIN, Biome::FOREST}, Biome::PLAIN}, - {Biome::MOUNTAIN, {Biome::DESERT}, Biome::PLAIN}}}; + {{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}}}; struct BaseBiomeParams { Biome biome; @@ -49,6 +49,8 @@ struct DesertParams : public BaseBiomeParams {}; 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); @@ -61,5 +63,5 @@ PlainParams& plain_params(); ForestParams& forest_params(); DesertParams& desert_params(); MountainParams& mountain_params(); - +RiverParams& river_params(); } // namespace Cubed diff --git a/include/Cubed/gameplay/block.hpp b/include/Cubed/gameplay/block.hpp index e369c51..86c5846 100644 --- a/include/Cubed/gameplay/block.hpp +++ b/include/Cubed/gameplay/block.hpp @@ -39,7 +39,7 @@ struct LookBlock { }; constexpr std::array BLOCK_REISTER{ - "air", "grass_block", "dirt", "stone", "sand", "log", "leaf"}; + "air", "grass_block", "dirt", "stone", "sand", "log", "leaf", "water"}; const std::array TRANSPARENT_MAP{ true, false, false, false, false, false, true}; diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index 6fc7de5..feb2528 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -1,6 +1,7 @@ #pragma once #include "Cubed/constants.hpp" +#include "Cubed/gameplay/biome.hpp" #include "Cubed/tools/cubed_random.hpp" #include @@ -49,6 +50,9 @@ private: 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}; + bool is_neighbor_river = false; }; } // namespace Cubed \ No newline at end of file diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index 65f2cfd..44d4d41 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -223,6 +223,32 @@ void DevPanel::show_biome_table_bar() { AMPLITUDE_MIN, AMPLITUDE_MAX); ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("River")) { + ImGui::SliderFloat("MinTemp##river", &river_params().temp.first, + TEMP_MIN, TEMP_MAX); + ImGui::SliderFloat("MaxTemp##river", &river_params().temp.second, + TEMP_MIN, TEMP_MAX); + ImGui::SliderFloat("MinHumid##river", &river_params().humid.first, + HUMID_MIN, HUMID_MAX); + ImGui::SliderFloat("MaxHumid##river", &river_params().humid.second, + HUMID_MIN, HUMID_MAX); + ImGui::SliderFloat("Freq One##river", + &river_params().frequencies[0], FREQ1_MIN, + FREQ1_MAX); + ImGui::SliderFloat("Freq Two##river", + &river_params().frequencies[1], FREQ2_MIN, + FREQ2_MAX); + ImGui::SliderFloat("Freq Three##river", + &river_params().frequencies[2], FREQ3_MIN, + FREQ3_MAX); + ImGui::SliderInt("Base Y##river", + &river_params().height_range.base_y, + HEIGHT_BASE_MIN, HEIGHT_BASE_MAX); + ImGui::SliderInt("Amplitude##river", + &river_params().height_range.amplitude, + AMPLITUDE_MIN, AMPLITUDE_MAX); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } } diff --git a/src/gameplay/biome.cpp b/src/gameplay/biome.cpp index e91e490..b48447c 100644 --- a/src/gameplay/biome.cpp +++ b/src/gameplay/biome.cpp @@ -19,7 +19,7 @@ static ForestParams forest{{Biome::FOREST, {0.5f, 1.0f}, {0.5f, 1.0f}, {0.004f, 0.010f, 0.020f}, - {62, 12}}, + {62, 8}}, 0.1f }; @@ -36,6 +36,12 @@ static MountainParams mountain{{Biome::MOUNTAIN, {0.006f, 0.014f, 0.010f}, {70, 70}}}; +static RiverParams river{{Biome::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 str; using enum Biome; @@ -52,6 +58,9 @@ std::string get_biome_str(Biome biome) { case MOUNTAIN: str = "Mountain"; break; + case RIVER: + str = "River"; + break; case NONE: str = "Unknown"; break; @@ -109,6 +118,8 @@ std::array get_noise_frequencies_for_biome(Biome biome) { return desert.frequencies; case MOUNTAIN: return mountain.frequencies; + case RIVER: + return river.frequencies; case NONE: ASSERT_MSG(false, "Chunk Biome is None"); throw std::invalid_argument{"Chunk Biome is None"}; @@ -128,6 +139,8 @@ BiomeHeightRange get_biome_height_range(Biome biome) { return desert.height_range; case MOUNTAIN: return mountain.height_range; + case RIVER: + return river.height_range; case NONE: ASSERT_MSG(false, "Chunk Biome is None"); throw std::invalid_argument{"Chunk Biome is None"}; @@ -139,7 +152,7 @@ BiomeHeightRange get_biome_height_range(Biome biome) { Biome safe_int_to_biome(int x) { using enum Biome; static const std::unordered_map INT_TO_BIOME_MAP{ - {0, PLAIN}, {1, FOREST}, {2, DESERT}, {3, MOUNTAIN}}; + {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"); @@ -194,5 +207,5 @@ PlainParams& plain_params() { return plain; } ForestParams& forest_params() { return forest; } DesertParams& desert_params() { return desert; } MountainParams& mountain_params() { return mountain; } - +RiverParams& river_params() { return river; } } // namespace Cubed diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index 2d0d160..10f3536 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -7,7 +7,7 @@ #include namespace Cubed { - +constexpr int BLEND_RADIUS = 12; ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) { ASSERT_MSG(is_init, "ChunksGenerator is not init"); ChunkPos pos = m_chunk.get_chunk_pos(); @@ -52,11 +52,16 @@ void ChunkGenerator::assign_chunk_biome() { void ChunkGenerator::resolve_biome_adjacency_conflict( const std::array& adj_chunks) { auto m_biome = m_chunk.biome(); - for (auto& chunk : adj_chunks) { + for (int i = 0; i < 4; i++) { + auto& chunk = adj_chunks[i]; if (chunk == nullptr) { continue; } Biome biome = chunk->get_biome(); + neighbor_biome[i] = biome; + if (biome == Biome::RIVER) { + is_neighbor_river = true; + } for (const auto& non : NON_ADJACENT) { if (m_biome != non.first) { continue; @@ -104,9 +109,8 @@ void ChunkGenerator::generate_heightmap() { void ChunkGenerator::blend_heightmap_boundaries( const std::array, 4>& neighbor_heightmap) { auto& m_heightmap = m_chunk.heightmap(); - + auto m_biome = m_chunk.biome(); // Width of interpolation influence (in number of cells) - constexpr int BLEND_RADIUS = 12; for (int x = 0; x < SIZE_X; x++) { for (int z = 0; z < SIZE_Z; z++) { @@ -116,7 +120,8 @@ void ChunkGenerator::blend_heightmap_boundaries( // --- Right neighbor neighbor[0]: (1, 0) --- // Blend when x is close to SIZE_X-1 - if (neighbor_heightmap[0] != std::nullopt) { + if (neighbor_heightmap[0] != std::nullopt && + neighbor_biome[0] != m_biome) { int dist = (SIZE_X - 1) - x; // distance from right border if (dist < BLEND_RADIUS) { // Neighbor's boundary row is its x=0 column @@ -133,7 +138,8 @@ void ChunkGenerator::blend_heightmap_boundaries( } // --- Left neighbor neighbor[1]: (-1, 0) --- - if (neighbor_heightmap[1] != std::nullopt) { + if (neighbor_heightmap[1] != std::nullopt && + neighbor_biome[1] != m_biome) { int dist = x; // distance from left border if (dist < BLEND_RADIUS) { float neighbor_h = static_cast( @@ -146,7 +152,8 @@ void ChunkGenerator::blend_heightmap_boundaries( } // --- Front neighbor neighbor[2]: (0, 1) --- - if (neighbor_heightmap[2] != std::nullopt) { + if (neighbor_heightmap[2] != std::nullopt && + neighbor_biome[2] != m_biome) { int dist = (SIZE_Z - 1) - z; if (dist < BLEND_RADIUS) { float neighbor_h = @@ -159,7 +166,8 @@ void ChunkGenerator::blend_heightmap_boundaries( } // --- Back neighbor neighbor[3]: (0, -1) --- - if (neighbor_heightmap[3] != std::nullopt) { + if (neighbor_heightmap[3] != std::nullopt && + neighbor_biome[3] != m_biome) { int dist = z; if (dist < BLEND_RADIUS) { float neighbor_h = static_cast( @@ -213,6 +221,17 @@ void ChunkGenerator::generate_terrain_blocks() { 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; @@ -230,7 +249,6 @@ void ChunkGenerator::blend_surface_blocks_borders( auto& m_blocks = m_chunk.blocks(); auto& m_heightmap = m_chunk.heightmap(); - constexpr int BLEND_RADIUS = 12; constexpr int WORLD_HEIGHT = WORLD_SIZE_Y; // Helper lambda: get top block type from a neighbor's block data at (nx, @@ -325,7 +343,19 @@ 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) { + final_type = 2; + } + if (is_neighbor_river && final_type == 1) { + if (top_y < SEA_LEVEL) { + final_type = 2; + } else { + final_type = 1; + } + } m_blocks[Chunk::get_index(x, top_y, z)] = final_type; + // bottom block unsigned fill_type = 2; if (final_type == 1) { fill_type = 2; @@ -342,6 +372,7 @@ 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; @@ -352,9 +383,36 @@ void ChunkGenerator::generate_vegetation() { std::shuffle(z_arr.begin(), z_arr.end(), m_random.engine()); for (auto x : x_arr) { for (auto z : z_arr) { - if (m_random.random_bool(forest_params().tree_frequency)) { - build_tree(m_chunk, - {x, static_cast(m_heightmap[x][z]), z}); + 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 == 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; } } }