From a54e87dbc6bed7887f448d907406023910834407 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Sat, 23 May 2026 10:33:52 +0800 Subject: [PATCH] refactor: terrain generation (#9) * feat: add BlockType * refactor: use fBM for heightmap generation * feat: improve mountain realism * refactor: adjust mountain spawn probability * feat: add biome boundary blending * refactor: remove resolve_biome_adjacency_conflict function * feat: add snowy plain * perf: speed up world generation * refactor: lower overall terrain height --- CMakeLists.txt | 1 + .../texture/block/snowy_grass_block/back.png | Bin 0 -> 568 bytes .../texture/block/snowy_grass_block/base.png | Bin 0 -> 482 bytes .../texture/block/snowy_grass_block/front.png | Bin 0 -> 568 bytes .../texture/block/snowy_grass_block/left.png | Bin 0 -> 568 bytes .../texture/block/snowy_grass_block/right.png | Bin 0 -> 568 bytes .../texture/block/snowy_grass_block/top.png | Bin 0 -> 394 bytes .../texture/item/block/snowy_grass_block.png | Bin 0 -> 499 bytes include/Cubed/constants.hpp | 2 +- include/Cubed/gameplay/biome.hpp | 26 +++- include/Cubed/gameplay/block.hpp | 7 +- .../gameplay/builders/snowy_plain_builder.hpp | 21 +++ include/Cubed/gameplay/chunk.hpp | 30 +++-- include/Cubed/gameplay/chunk_generator.hpp | 5 +- include/Cubed/tools/perlin_noise.hpp | 17 ++- src/gameplay/biome.cpp | 56 ++++---- src/gameplay/builders/biome_builder.cpp | 4 +- src/gameplay/builders/desert_builder.cpp | 4 +- src/gameplay/builders/forest_builder.cpp | 6 +- src/gameplay/builders/mountain_builder.cpp | 16 +-- src/gameplay/builders/plain_builder.cpp | 6 +- src/gameplay/builders/river_builder.cpp | 10 +- src/gameplay/builders/snowy_plain_builder.cpp | 38 ++++++ src/gameplay/chunk.cpp | 73 +++++++--- src/gameplay/chunk_generator.cpp | 125 ++++++++++++++---- src/gameplay/tree.cpp | 6 +- src/gameplay/world.cpp | 86 +++++++----- src/tools/perlin_noise.cpp | 71 ++++++++-- 28 files changed, 455 insertions(+), 155 deletions(-) create mode 100644 assets/texture/block/snowy_grass_block/back.png create mode 100644 assets/texture/block/snowy_grass_block/base.png create mode 100644 assets/texture/block/snowy_grass_block/front.png create mode 100644 assets/texture/block/snowy_grass_block/left.png create mode 100644 assets/texture/block/snowy_grass_block/right.png create mode 100644 assets/texture/block/snowy_grass_block/top.png create mode 100644 assets/texture/item/block/snowy_grass_block.png create mode 100644 include/Cubed/gameplay/builders/snowy_plain_builder.hpp create mode 100644 src/gameplay/builders/snowy_plain_builder.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a6fdcfa..d89a0b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,7 @@ add_executable(${PROJECT_NAME} src/gameplay/builders/forest_builder.cpp src/gameplay/cave_carver.cpp src/gameplay/cave_path.cpp + src/gameplay/builders/snowy_plain_builder.cpp ) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/assets/texture/block/snowy_grass_block/back.png b/assets/texture/block/snowy_grass_block/back.png new file mode 100644 index 0000000000000000000000000000000000000000..41b3e578b6256e22082fd9769b06a7a3b32d3b94 GIT binary patch literal 568 zcmV-80>}M{P)Px$@<~KNR5*=okB6}eIdnDLx#!F|bH-9iNt&i)S;pNmC+WtfMp49K zu^@^fT-RkbzXTxZ#%K<8U5ipmF0N*xloAp7Z`J>8yoiW>bv-AFBBQLGoF4Vf``f2? zOvfYcmN~BLlBOw1H{LiB5xKaU$z<3wij!eaP7d}&|0$(pGVDnkYaC=*#`nnyMcEj| zsuse)VK%>HIv$axDMi_EdekTB#sEymBWCkU+`DBCKo~eo$0NePp{mz(;vLif{AQmp zaEwS%HiUsg(v7hu!=6;NP}OU~z`=bMv-qnX2MNAsQIrk7XXASofawF+FZO$eT-Czr zA;*D9VOToB&Xi4fg%*jj{LoJK|zvaR>(9`vhry;g6 z?VhcHo1`s#&oboaZ$ALwd$w_-*V6;-4Zz~n!N4eJpbqjW$@eVw-hbI#p(q;w{#^e8 zpc5xscJ+mP&muTHHUhtY{@7kY@1?_O2damZ=VxX`ecpfLW}UNKH`}=Y0000Px$oJmAMR5*=olf7%(Kp4h?z$SJE%dw{y>No`0VMcd8V`*^2C zvXv z492thpjf;`nNyiRuYU{_`nCcgEyW`@0pRA>A1$t%JM3afl(`46fvQl5QvBrb)gvCM z4Y~dNNh6fRfSm=|EQNjoB3G1^RmXO-Cd%B7!S`?1gBwLn5*uEgT__*1^z`{N0NNto zF-UDlS#=tvob1OnaudCvEpqCnR~}|{a?s1IK1@F9#sB~S literal 0 HcmV?d00001 diff --git a/assets/texture/block/snowy_grass_block/front.png b/assets/texture/block/snowy_grass_block/front.png new file mode 100644 index 0000000000000000000000000000000000000000..41b3e578b6256e22082fd9769b06a7a3b32d3b94 GIT binary patch literal 568 zcmV-80>}M{P)Px$@<~KNR5*=okB6}eIdnDLx#!F|bH-9iNt&i)S;pNmC+WtfMp49K zu^@^fT-RkbzXTxZ#%K<8U5ipmF0N*xloAp7Z`J>8yoiW>bv-AFBBQLGoF4Vf``f2? zOvfYcmN~BLlBOw1H{LiB5xKaU$z<3wij!eaP7d}&|0$(pGVDnkYaC=*#`nnyMcEj| zsuse)VK%>HIv$axDMi_EdekTB#sEymBWCkU+`DBCKo~eo$0NePp{mz(;vLif{AQmp zaEwS%HiUsg(v7hu!=6;NP}OU~z`=bMv-qnX2MNAsQIrk7XXASofawF+FZO$eT-Czr zA;*D9VOToB&Xi4fg%*jj{LoJK|zvaR>(9`vhry;g6 z?VhcHo1`s#&oboaZ$ALwd$w_-*V6;-4Zz~n!N4eJpbqjW$@eVw-hbI#p(q;w{#^e8 zpc5xscJ+mP&muTHHUhtY{@7kY@1?_O2damZ=VxX`ecpfLW}UNKH`}=Y0000}M{P)Px$@<~KNR5*=okB6}eIdnDLx#!F|bH-9iNt&i)S;pNmC+WtfMp49K zu^@^fT-RkbzXTxZ#%K<8U5ipmF0N*xloAp7Z`J>8yoiW>bv-AFBBQLGoF4Vf``f2? zOvfYcmN~BLlBOw1H{LiB5xKaU$z<3wij!eaP7d}&|0$(pGVDnkYaC=*#`nnyMcEj| zsuse)VK%>HIv$axDMi_EdekTB#sEymBWCkU+`DBCKo~eo$0NePp{mz(;vLif{AQmp zaEwS%HiUsg(v7hu!=6;NP}OU~z`=bMv-qnX2MNAsQIrk7XXASofawF+FZO$eT-Czr zA;*D9VOToB&Xi4fg%*jj{LoJK|zvaR>(9`vhry;g6 z?VhcHo1`s#&oboaZ$ALwd$w_-*V6;-4Zz~n!N4eJpbqjW$@eVw-hbI#p(q;w{#^e8 zpc5xscJ+mP&muTHHUhtY{@7kY@1?_O2damZ=VxX`ecpfLW}UNKH`}=Y0000}M{P)Px$@<~KNR5*=okB6}eIdnDLx#!F|bH-9iNt&i)S;pNmC+WtfMp49K zu^@^fT-RkbzXTxZ#%K<8U5ipmF0N*xloAp7Z`J>8yoiW>bv-AFBBQLGoF4Vf``f2? zOvfYcmN~BLlBOw1H{LiB5xKaU$z<3wij!eaP7d}&|0$(pGVDnkYaC=*#`nnyMcEj| zsuse)VK%>HIv$axDMi_EdekTB#sEymBWCkU+`DBCKo~eo$0NePp{mz(;vLif{AQmp zaEwS%HiUsg(v7hu!=6;NP}OU~z`=bMv-qnX2MNAsQIrk7XXASofawF+FZO$eT-Czr zA;*D9VOToB&Xi4fg%*jj{LoJK|zvaR>(9`vhry;g6 z?VhcHo1`s#&oboaZ$ALwd$w_-*V6;-4Zz~n!N4eJpbqjW$@eVw-hbI#p(q;w{#^e8 zpc5xscJ+mP&muTHHUhtY{@7kY@1?_O2damZ=VxX`ecpfLW}UNKH`}=Y0000Px$L`g(JR5*==lgp}uFc5~nN*+Lh`T#B>wD12WRY(@@6GggDA!ku$h&@8xWHPz^ zH*Y!TVvJ!93)AR0j)U#B)3@De1hk$;hhGEJ=-}z+^7L~lr4*@i&i{H+#~5`UV=Rr_ zJcot8?Eo;w05FGzhr8?gG&)5vhlTC6bBi&S8rBu$x_-F3KDNE~4~1V)g;LJB#29t1 z6yEPQ0MqD5DJfKeE`s?rV6DZkfrq=Zy>^T*Qogke;KcO-`d=selDWyRagKX?oLXHVHnO10FC%9T&=ZwxXysJR&ybT zr_p^x6z+1c4wAK21+KM{UL&`qlun!EV!sBpS%OqsPc781f8a{c+tbgbZ#xahcclBa oQ}N`hW6if!VpAR6D;ovvA0jpg=N_LNY5)KL07*qoM6N<$f_8DRQ2+n{ literal 0 HcmV?d00001 diff --git a/assets/texture/item/block/snowy_grass_block.png b/assets/texture/item/block/snowy_grass_block.png new file mode 100644 index 0000000000000000000000000000000000000000..51212c86a34285df3cc055a75796408ef928797b GIT binary patch literal 499 zcmVPx$tw}^dR5*>rlCf{oP!Ps{XPee_aAOjuxOfO88%w)(=v1joH`Yq9z=HTA`ai(H z%D_}Xr!GjHS<6zUAVq50w4qWP6E|rbAA=RAk|`VFo8G(o?z`{aJNTDae`VB7au89xrYOnzneen)Xbp&Sa=5!9Q@pzz||Y%J@x9p4X7Jp~}@fQZoV zq*!j&u-(dXgMOEygS`r>O05=P?^ba`!Mv|DSC^?b1FBzq0P4*EbFY#MN@IK*u z?I(@b0ljaa3ccPLzW)$3YjVsDUdbp52m%UAUww`bZeSxD)84HzkOTxg$KWzf8I36l z09?i?o?~EJ0tXem1%qJ%wv{=&ij}fs0JGJ|y(o(37!1eC{5>)eVBUh^SZQo-=l?FZ z)+ZNuP%^Q+CG0t2lw^cq0#|Vg!1w)+lpV>N6&7iVjmN$M`9qDykgBpUC#YtHpO1QS pr@$1?p4N0m5 get_noise_frequencies_for_biome(BiomeType biome); -BiomeHeightRange get_biome_height_range(BiomeType biome); +// 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); +BiomeType determine_biome(const BiomeConditions& conditions); + PlainParams& plain_params(); ForestParams& forest_params(); DesertParams& desert_params(); diff --git a/include/Cubed/gameplay/block.hpp b/include/Cubed/gameplay/block.hpp index 86c5846..dc6d7d7 100644 --- a/include/Cubed/gameplay/block.hpp +++ b/include/Cubed/gameplay/block.hpp @@ -10,6 +10,8 @@ namespace Cubed { +using BlockType = uint8_t; + struct BlockTexture { std::string name; unsigned id; @@ -39,10 +41,11 @@ struct LookBlock { }; constexpr std::array BLOCK_REISTER{ - "air", "grass_block", "dirt", "stone", "sand", "log", "leaf", "water"}; + "air", "grass_block", "dirt", "stone", "sand", "log", "leaf", + "water", "snowy_grass_block"}; const std::array TRANSPARENT_MAP{ - true, false, false, false, false, false, true}; + true, false, false, false, false, false, true, false, false}; inline bool is_in_transparent_map(unsigned id) { ASSERT_MSG(id < MAX_BLOCK_NUM, "ID is invaild"); diff --git a/include/Cubed/gameplay/builders/snowy_plain_builder.hpp b/include/Cubed/gameplay/builders/snowy_plain_builder.hpp new file mode 100644 index 0000000..a716794 --- /dev/null +++ b/include/Cubed/gameplay/builders/snowy_plain_builder.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "Cubed/gameplay/builders/biome_builder.hpp" +namespace Cubed { + +class ChunkGenerator; + +class SnowyPlainBuilder : public BiomeBuilder { +public: + SnowyPlainBuilder(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 3557fea..f4b464c 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -7,7 +7,6 @@ #include "Cubed/primitive_data.hpp" #include -#include namespace Cubed { @@ -32,13 +31,16 @@ private: World& m_world; HeightMapArray m_heightmap; // the index is a array of block id - std::vector m_blocks; + std::vector m_blocks; GLuint m_vbo = 0; std::vector m_vertexs_data; float frequency = 0.01f; float height = 80; unsigned m_seed = 0; + + BiomeConditions m_conditions; + void clear_dirty(); public: @@ -49,12 +51,21 @@ public: Chunk(Chunk&&) noexcept; Chunk& operator=(Chunk&&) noexcept; + static std::tuple world_to_block(int world_x, int world_y, + int world_z, int chunk_x, + int chunk_z); + static std::tuple world_to_block(const glm::ivec3& block_pos, + ChunkPos chunk_pos); + static std::tuple block_to_world(int x, int y, int z, + int chunk_x, int chunk_z); + static std::tuple block_to_world(const glm::ivec3& block_pos, + ChunkPos chunk_pos); BiomeType get_biome() const; ChunkPos get_chunk_pos() const; - const std::vector& get_chunk_blocks() const; + const std::vector& get_chunk_blocks() const; HeightMapArray get_heightmap() const; - static int get_index(int x, int y, int z); - static int get_index(const glm::vec3& pos); + static int index(int x, int y, int z); + static int index(const glm::vec3& pos); // Init Chunk // Determine biome from temperature and humidity noise void gen_phase_one(); @@ -69,8 +80,8 @@ public: // Generate terrain blocks from heightmap and biome void gen_phase_five(); // Blend surface blocks at chunk borders with neighbors - void gen_phase_six(const std::array>, 4>& - neighbor_block); + void gen_phase_six(const std::array>, + 4>& neighbor_block); // Generate biome-specific vegetation/structures void gen_phase_seven(); // void gen_vertex_data(); @@ -79,7 +90,7 @@ public: // 2 : (0, 1) // 3 : (0, -1) void gen_vertex_data( - const std::array*, 4>& neighbor_block); + const std::array*, 4>& neighbor_block); void upload_to_gpu(); GLuint get_vbo() const; @@ -97,9 +108,10 @@ public: BiomeType biome() const; void biome(BiomeType b); HeightMapArray& heightmap(); - std::vector& blocks(); + std::vector& blocks(); World& world(); unsigned seed() const; + BiomeConditions& conditions(); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index 391a25e..4352497 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -2,6 +2,7 @@ #include "Cubed/constants.hpp" #include "Cubed/gameplay/biome.hpp" +#include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/builders/biome_builder.hpp" #include "Cubed/tools/cubed_random.hpp" @@ -36,7 +37,7 @@ public: void generate_terrain_blocks(); // Adjust Block; void blend_surface_blocks_borders( - const std::array>, 4>& + const std::array>, 4>& neighbor_block); // Generate Structure void generate_vegetation(); @@ -44,6 +45,7 @@ public: Chunk& chunk(); Random& random(); const std::array& neighbor_biome() const; + void generate_cave(); private: static inline std::atomic is_init{false}; @@ -57,7 +59,6 @@ private: unsigned m_chunk_seed = 0; void make_biome_builder(); - void generate_cave(); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/perlin_noise.hpp b/include/Cubed/tools/perlin_noise.hpp index 078eef9..9a0e6a3 100644 --- a/include/Cubed/tools/perlin_noise.hpp +++ b/include/Cubed/tools/perlin_noise.hpp @@ -4,7 +4,7 @@ namespace Cubed { -class PerlinNoise { +class PerlinNoise3D { public: static void init(unsigned seed); static float noise(float x, float y, float z); @@ -18,4 +18,19 @@ private: static float grad(int hash, float x, float y, float z); }; +class PerlinNoise2D { +public: + static void init(unsigned seed); + static float noise(float x, float y); + static void reload(unsigned seed); + +private: + static inline std::atomic is_init = false; + static inline std::vector p; + + static float fade(float t); + static float lerp(float t, float a, float b); + static float grad(int hash, float x, float y); +}; + } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/biome.cpp b/src/gameplay/biome.cpp index f9736f1..39fdeb9 100644 --- a/src/gameplay/biome.cpp +++ b/src/gameplay/biome.cpp @@ -1,12 +1,13 @@ #include "Cubed/gameplay/biome.hpp" #include "Cubed/tools/cubed_assert.hpp" -#include "Cubed/tools/log.hpp" #include namespace Cubed { +using enum BiomeType; + static PlainParams plain{{BiomeType::PLAIN, {0.0f, 0.5f}, {0.0f, 0.5f}, @@ -59,6 +60,8 @@ std::string get_biome_str(BiomeType biome) { case RIVER: str = "River"; break; + case SNOWY_PLAIN: + str = "Snowy Plain"; case NONE: str = "Unknown"; break; @@ -84,27 +87,7 @@ Biome get_biome_from_noise(float temp, float humid) { return Biome::FOREST; } */ -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; - } - if (forest.temp.first <= temp && temp < forest.temp.second && - forest.humid.first <= humid && humid < forest.humid.second) { - return FOREST; - } - if (desert.temp.first <= temp && temp < desert.temp.second && - desert.humid.first <= humid && humid < desert.humid.second) { - return DESERT; - } - if (mountain.temp.first <= temp && temp <= mountain.temp.second && - mountain.humid.first <= humid && humid <= mountain.humid.second) { - return MOUNTAIN; - } - Logger::warn("Invail Temp {} or Humid {}", temp, humid); - return PLAIN; -} +/* std::array get_noise_frequencies_for_biome(BiomeType biome) { using enum BiomeType; switch (biome) { @@ -125,7 +108,8 @@ std::array get_noise_frequencies_for_biome(BiomeType biome) { Logger::warn("Unknown Biome"); return {0.003f, 0.015f, 0.06f}; } - +*/ +/* BiomeHeightRange get_biome_height_range(BiomeType biome) { using enum BiomeType; switch (biome) { @@ -146,7 +130,7 @@ BiomeHeightRange get_biome_height_range(BiomeType biome) { Logger::warn("Unknown Biome"); return {62, 4}; } - +*/ BiomeType safe_int_to_biome(int x) { using enum BiomeType; static const std::unordered_map INT_TO_BIOME_MAP{ @@ -201,6 +185,30 @@ int get_interpolated_height(float world_x, float world_z, float temp, return static_cast(h); } */ + +BiomeType determine_biome(const BiomeConditions& conditions) { + if (conditions.mountainous > 0.75) { + return MOUNTAIN; + } + auto temp = conditions.temp; + auto humid = conditions.humid; + if (temp < 0.5) { + if (humid < 0.5) { + return SNOWY_PLAIN; + } else { + return PLAIN; + } + } else { + if (humid < 0.5) { + return DESERT; + } else { + return FOREST; + } + } + + return PLAIN; +} + 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 index e9772ea..6582982 100644 --- a/src/gameplay/builders/biome_builder.cpp +++ b/src/gameplay/builders/biome_builder.cpp @@ -10,7 +10,7 @@ void BiomeBuilder::build_bottom() { for (int x = 0; x < CHUNK_SIZE; x++) { for (int y = 0; y < 5; y++) { for (int z = 0; z < CHUNK_SIZE; z++) { - m_blocks[Chunk::get_index(x, y, z)] = 3; + m_blocks[Chunk::index(x, y, z)] = 3; } } } @@ -30,7 +30,7 @@ void BiomeBuilder::fill_water() { } int height = heightmap[x][z]; for (int y = height; y < SEA_LEVEL; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 7; + m_blocks[Chunk::index(x, y, z)] = 7; } } } diff --git a/src/gameplay/builders/desert_builder.cpp b/src/gameplay/builders/desert_builder.cpp index bb33e9f..25aefe0 100644 --- a/src/gameplay/builders/desert_builder.cpp +++ b/src/gameplay/builders/desert_builder.cpp @@ -19,11 +19,11 @@ void DesertBuilder::build_blocks() { for (int z = 0; z < CHUNK_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; + m_blocks[Chunk::index(x, y, z)] = 3; } for (int y = height - 5; y <= height; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 4; + m_blocks[Chunk::index(x, y, z)] = 4; } } } diff --git a/src/gameplay/builders/forest_builder.cpp b/src/gameplay/builders/forest_builder.cpp index 6df931d..34eda3f 100644 --- a/src/gameplay/builders/forest_builder.cpp +++ b/src/gameplay/builders/forest_builder.cpp @@ -23,12 +23,12 @@ void ForestBuilder::build_blocks() { for (int z = 0; z < CHUNK_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; + m_blocks[Chunk::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::index(x, y, z)] = 2; } - m_blocks[Chunk::get_index(x, height, z)] = 1; + m_blocks[Chunk::index(x, height, z)] = 1; } } } diff --git a/src/gameplay/builders/mountain_builder.cpp b/src/gameplay/builders/mountain_builder.cpp index 22f06b0..1af20c5 100644 --- a/src/gameplay/builders/mountain_builder.cpp +++ b/src/gameplay/builders/mountain_builder.cpp @@ -18,20 +18,8 @@ void MountainBuilder::build_blocks() { for (int x = 0; x < CHUNK_SIZE; x++) { for (int z = 0; z < CHUNK_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, z)] = 3; - } else { - m_blocks[Chunk::get_index(x, height, z)] = 1; + for (int y = 5; y <= height; y++) { + m_blocks[Chunk::index(x, y, z)] = 3; } } } diff --git a/src/gameplay/builders/plain_builder.cpp b/src/gameplay/builders/plain_builder.cpp index 4cc6186..6c20fbd 100644 --- a/src/gameplay/builders/plain_builder.cpp +++ b/src/gameplay/builders/plain_builder.cpp @@ -19,12 +19,12 @@ void PlainBuilder::build_blocks() { for (int z = 0; z < CHUNK_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; + m_blocks[Chunk::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::index(x, y, z)] = 2; } - m_blocks[Chunk::get_index(x, height, z)] = 1; + m_blocks[Chunk::index(x, height, z)] = 1; } } } diff --git a/src/gameplay/builders/river_builder.cpp b/src/gameplay/builders/river_builder.cpp index 9d9a807..3e7fa6e 100644 --- a/src/gameplay/builders/river_builder.cpp +++ b/src/gameplay/builders/river_builder.cpp @@ -19,16 +19,16 @@ void RiverBuilder::build_blocks() { for (int z = 0; z < CHUNK_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; + m_blocks[Chunk::index(x, y, z)] = 3; } for (int y = height - 5; y <= height - 1; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 2; + m_blocks[Chunk::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; + m_blocks[Chunk::index(x, y, z)] = 1; } else { - m_blocks[Chunk::get_index(x, y, z)] = 2; + m_blocks[Chunk::index(x, y, z)] = 2; } } } @@ -46,7 +46,7 @@ void RiverBuilder::build_vegetation() { continue; } for (int y = height + 1; y < SEA_LEVEL; y++) { - m_blocks[Chunk::get_index(x, y, z)] = 7; + m_blocks[Chunk::index(x, y, z)] = 7; } } } diff --git a/src/gameplay/builders/snowy_plain_builder.cpp b/src/gameplay/builders/snowy_plain_builder.cpp new file mode 100644 index 0000000..9a46298 --- /dev/null +++ b/src/gameplay/builders/snowy_plain_builder.cpp @@ -0,0 +1,38 @@ +#include "Cubed/gameplay/builders/snowy_plain_builder.hpp" + +#include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/chunk_generator.hpp" +namespace Cubed { +SnowyPlainBuilder::SnowyPlainBuilder(ChunkGenerator& chunk_generator) + : m_chunk_generator(chunk_generator) {} + +void SnowyPlainBuilder::build_biome() { + BiomeBuilder::build_bottom(); + build_blocks(); +}; + +void SnowyPlainBuilder::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 < CHUNK_SIZE; x++) { + for (int z = 0; z < CHUNK_SIZE; z++) { + int height = static_cast(m_heightmap[x][z]); + for (int y = 5; y < height - 5; y++) { + m_blocks[Chunk::index(x, y, z)] = 3; + } + for (int y = height - 5; y < height; y++) { + m_blocks[Chunk::index(x, y, z)] = 2; + } + m_blocks[Chunk::index(x, height, z)] = 8; + } + } +} + +void SnowyPlainBuilder::build_vegetation() { fill_water(); } + +ChunkGenerator& SnowyPlainBuilder::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 10ffd6b..bc3bd0c 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -24,7 +24,8 @@ Chunk::Chunk(Chunk&& other) noexcept m_chunk_pos(std::move(other.m_chunk_pos)), m_world(other.m_world), m_heightmap(std::move(other.m_heightmap)), m_blocks(std::move(other.m_blocks)), m_vbo(other.m_vbo), - m_vertexs_data(std::move(other.m_vertexs_data)), m_seed(other.m_seed) { + m_vertexs_data(std::move(other.m_vertexs_data)), m_seed(other.m_seed), + m_conditions(other.m_conditions) { other.m_vbo = 0; } @@ -44,14 +45,46 @@ Chunk& Chunk::operator=(Chunk&& other) noexcept { m_need_upload = other.m_need_upload.load(); m_vertex_sum = other.m_vertex_sum.load(); m_seed = other.m_seed; + m_conditions = other.m_conditions; return *this; } +std::tuple Chunk::world_to_block(int world_x, int world_y, + int world_z, int chunk_x, + int chunk_z) { + int x, y, z; + y = world_y; + x = world_x - chunk_x * CHUNK_SIZE; + z = world_z - chunk_z * CHUNK_SIZE; + return {x, y, z}; +} + +std::tuple Chunk::world_to_block(const glm::ivec3& block_pos, + ChunkPos chunk_pos) { + return world_to_block(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x, + chunk_pos.z); +} + +std::tuple Chunk::block_to_world(int x, int y, int z, + int chunk_x, int chunk_z) { + int world_x = x + chunk_x * CHUNK_SIZE; + int world_z = z + chunk_z * CHUNK_SIZE; + int world_y = y; + return {world_x, world_y, world_z}; +} +std::tuple Chunk::block_to_world(const glm::ivec3& block_pos, + ChunkPos chunk_pos) { + return block_to_world(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x, + chunk_pos.z); +} + BiomeType Chunk::get_biome() const { return m_biome.load(); } ChunkPos Chunk::get_chunk_pos() const { return m_chunk_pos; } -const std::vector& Chunk::get_chunk_blocks() const { return m_blocks; } +const std::vector& Chunk::get_chunk_blocks() const { + return m_blocks; +} HeightMapArray Chunk::get_heightmap() const { // Logger::info("Chunk pos {} {} in get_heightmap this {}", m_chunk_pos.x, @@ -59,7 +92,7 @@ HeightMapArray Chunk::get_heightmap() const { return m_heightmap; } -int Chunk::get_index(int x, int y, int z) { +int Chunk::index(int x, int y, int z) { ASSERT(!(x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE)); if ((x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z < 0 || @@ -71,12 +104,12 @@ int Chunk::get_index(int x, int y, int z) { return (x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z; } -int Chunk::get_index(const glm::vec3& pos) { - return Chunk::get_index(pos.x, pos.y, pos.z); +int Chunk::index(const glm::vec3& pos) { + return Chunk::index(pos.x, pos.y, pos.z); } void Chunk::gen_vertex_data( - const std::array*, 4>& neighbor_block) { + const std::array*, 4>& neighbor_block) { if (m_is_on_gen_vertex_data) { return; } @@ -93,7 +126,7 @@ void Chunk::gen_vertex_data( int world_x = x + m_chunk_pos.x * CHUNK_SIZE; int world_z = z + m_chunk_pos.z * CHUNK_SIZE; int world_y = y; - int cur_id = m_blocks[get_index(x, y, z)]; + int cur_id = m_blocks[index(x, y, z)]; // air if (cur_id == 0) { continue; @@ -116,7 +149,7 @@ void Chunk::gen_vertex_data( World::chunk_pos(world_nx, world_nz); auto is_cull = - [&](const std::vector* chunk_blocks) { + [&](const std::vector* chunk_blocks) { if (chunk_blocks == nullptr) { return false; } @@ -130,12 +163,12 @@ void Chunk::gen_vertex_data( return false; } - int idx = Chunk::get_index(x, y, z); + int idx = Chunk::index(x, y, z); // not init if (static_cast(idx) >= chunk_blocks->size()) { - Logger::warn("not init"); - return false; + // Logger::warn("not init"); + return true; } auto id = (*chunk_blocks)[idx]; if (is_in_transparent_map(id)) { @@ -162,7 +195,7 @@ void Chunk::gen_vertex_data( // neighbor_cull = m_world.is_block(glm::ivec3(world_x, // world_y, world_z) + DIR[face]); } else { - auto id = m_blocks[get_index(nx, ny, nz)]; + auto id = m_blocks[index(nx, ny, nz)]; if (!is_in_transparent_map(id)) { neighbor_cull = true; } else { @@ -222,7 +255,7 @@ void Chunk::gen_phase_two(const std::array& adj_chunks) { Logger::error("ChunkGenerator is Nullptr"); return; } - m_generator->resolve_biome_adjacency_conflict(adj_chunks); + // m_generator->resolve_biome_adjacency_conflict(adj_chunks); } void Chunk::gen_phase_three() { @@ -240,7 +273,8 @@ void Chunk::gen_phase_four( Logger::error("ChunkGenerator is Nullptr"); return; } - m_generator->blend_heightmap_boundaries(neighbor_heightmap, neighbor_biome); + // m_generator->blend_heightmap_boundaries(neighbor_heightmap, + // neighbor_biome); } void Chunk::gen_phase_five() { @@ -252,12 +286,14 @@ void Chunk::gen_phase_five() { } void Chunk::gen_phase_six( - const std::array>, 4>& neighbor_block) { + const std::array>, 4>& + neighbor_block) { if (!m_generator) { Logger::error("ChunkGenerator is Nullptr"); return; } - // m_generator->blend_surface_blocks_borders(neighbor_block); + m_generator->blend_surface_blocks_borders(neighbor_block); + m_generator->generate_cave(); } void Chunk::gen_phase_seven() { @@ -308,7 +344,7 @@ BiomeType Chunk::biome() const { return m_biome; } void Chunk::biome(BiomeType b) { m_biome = b; } HeightMapArray& Chunk::heightmap() { return m_heightmap; } -std::vector& Chunk::blocks() { return m_blocks; } +std::vector& Chunk::blocks() { return m_blocks; } World& Chunk::world() { return m_world; } unsigned Chunk::seed() const { if (m_seed == 0) { @@ -316,4 +352,7 @@ unsigned Chunk::seed() const { } return m_seed; } + +BiomeConditions& Chunk::conditions() { return m_conditions; } + } // namespace Cubed diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index f8a67ad..1132fc7 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -5,10 +5,12 @@ #include "Cubed/gameplay/builders/mountain_builder.hpp" #include "Cubed/gameplay/builders/plain_builder.hpp" #include "Cubed/gameplay/builders/river_builder.hpp" +#include "Cubed/gameplay/builders/snowy_plain_builder.hpp" #include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/tree.hpp" #include "Cubed/gameplay/world.hpp" #include "Cubed/tools/cubed_hash.hpp" +#include "Cubed/tools/math_tools.hpp" #include "Cubed/tools/perlin_noise.hpp" namespace Cubed { @@ -27,7 +29,8 @@ void ChunkGenerator::init() { std::random_device d; m_generator_seed = d(); Logger::info("Chunk Generator Seed {}", m_generator_seed); - PerlinNoise::init(m_generator_seed); + PerlinNoise3D::init(m_generator_seed); + PerlinNoise2D::init(m_generator_seed); is_init = true; } @@ -35,7 +38,7 @@ void ChunkGenerator::reload() { if (!is_seed_change) { return; } - PerlinNoise::reload(m_generator_seed); + PerlinNoise3D::reload(m_generator_seed); is_seed_change = false; } @@ -54,11 +57,20 @@ void ChunkGenerator::assign_chunk_biome() { auto m_chunk_pos = m_chunk.chunk_pos(); float x = static_cast(m_chunk_pos.x); float z = static_cast(m_chunk_pos.z); - float temp = PerlinNoise::noise(x * BIOME_NOISE_FREQUENCY, 0.0f, - z * BIOME_NOISE_FREQUENCY); - float humid = PerlinNoise::noise(x * BIOME_NOISE_FREQUENCY, 1.0f, - z * BIOME_NOISE_FREQUENCY); - auto biome = get_biome_from_noise(temp, humid); + float temp = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 0.0f, + z * BIOME_NOISE_FREQUENCY); + float humid = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 1.0f, + z * BIOME_NOISE_FREQUENCY); + float center_x = static_cast(SIZE_X / 2) + x * CHUNK_SIZE + 0.5f; + float center_z = static_cast(SIZE_Z / 2) + z * CHUNK_SIZE + 0.5f; + float mountainous = + PerlinNoise2D::noise(center_x * MOUNTAINOUS_NOISE_FREQUENCY, + center_z * MOUNTAINOUS_NOISE_FREQUENCY); + auto& conditions = m_chunk.conditions(); + conditions.mountainous = mountainous; + conditions.humid = humid; + conditions.temp = temp; + auto biome = determine_biome(conditions); m_chunk.biome(biome); } @@ -86,6 +98,7 @@ void ChunkGenerator::resolve_biome_adjacency_conflict( } } +/* void ChunkGenerator::generate_heightmap() { auto m_chunk_pos = m_chunk.chunk_pos(); @@ -114,6 +127,63 @@ void ChunkGenerator::generate_heightmap() { } } } +*/ + +void ChunkGenerator::generate_heightmap() { + auto chunk_pos = m_chunk.chunk_pos(); + auto& heightmap = m_chunk.heightmap(); + + for (int x = 0; x < CHUNK_SIZE; ++x) { + for (int z = 0; z < CHUNK_SIZE; ++z) { + float world_x = static_cast(x + chunk_pos.x * CHUNK_SIZE); + float world_z = static_cast(z + chunk_pos.z * CHUNK_SIZE); + + auto fbm_height = [](float x, float y, int octaves, + float lacunarity, float gain, float amplitude, + float frequency) -> float { + float value = 0.0f; + for (int i = 0; i < octaves; i++) { + value += amplitude * + PerlinNoise2D::noise(x * frequency, y * frequency); + frequency *= lacunarity; + amplitude *= gain; + } + return value; + }; + + int octaves = 4; + float lacunarity = 2.0f; + float gain = 0.5f; + float base_y = 64; + float amplitude = 40.0f; + float mountainous = + PerlinNoise2D::noise(world_x * MOUNTAINOUS_NOISE_FREQUENCY, + world_z * MOUNTAINOUS_NOISE_FREQUENCY); + /* + float t = Math::smootherstep(0.6, 0.7, mountainous); + base_y = std::lerp(64, 85, t); + amplitude = std::lerp(10, 40, t); + */ + float t; + if (mountainous >= 0.7f) { + t = Math::smootherstep(0.7f, 0.75, mountainous); + base_y = std::lerp(70, 88, t); + amplitude = std::lerp(28, 48, t); + } else if (mountainous >= 0.65f) { + t = Math::smootherstep(0.65f, 0.7f, mountainous); + base_y = std::lerp(66, 70, t); + amplitude = std::lerp(18, 28, t); + } else { + t = Math::smootherstep(0.55, 0.65, mountainous); + base_y = std::lerp(58, 66, t); + amplitude = std::lerp(8, 18, t); + } + heightmap[x][z] = + base_y + fbm_height(world_x, world_z, octaves, lacunarity, gain, + amplitude, 0.005f); + } + } +} void ChunkGenerator::blend_heightmap_boundaries( const std::array, 8>& neighbor_heightmap, @@ -362,11 +432,11 @@ void ChunkGenerator::generate_terrain_blocks() { } m_chunk.blocks().assign(CHUNK_SIZE * CHUNK_SIZE * WORLD_SIZE_Y, 0); m_biome_builder->build_biome(); - generate_cave(); } void ChunkGenerator::blend_surface_blocks_borders( - const std::array>, 4>& neighbor_block) { + const std::array>, 4>& + neighbor_block) { auto& m_blocks = m_chunk.blocks(); auto& m_heightmap = m_chunk.heightmap(); @@ -374,12 +444,12 @@ void ChunkGenerator::blend_surface_blocks_borders( // Helper lambda: get top block type from a neighbor's block data at (nx, // nz) - auto get_top_block_from_neighbor = [&](const std::vector& blocks, - int nx, int nz) -> uint8_t { + auto get_top_block_from_neighbor = [&](const std::vector& blocks, + int nx, int nz) -> BlockType { // Search from topmost y downwards for the first non-zero block for (int y = WORLD_HEIGHT - 1; y >= 0; --y) { - int idx = Chunk::get_index( - nx, y, nz); // linear index: y * area + z * size + x + int idx = Chunk::index(nx, y, + nz); // linear index: y * area + z * size + x if (idx >= 0 && idx < static_cast(blocks.size()) && blocks[idx] != 0) { return blocks[idx]; @@ -392,16 +462,16 @@ void ChunkGenerator::blend_surface_blocks_borders( for (int x = 0; x < CHUNK_SIZE; ++x) { for (int z = 0; z < CHUNK_SIZE; ++z) { // Get the current top block type of this column from m_blocks - uint8_t type_self = 0; + BlockType type_self = 0; int top_y = -1; top_y = m_heightmap[x][z]; - type_self = m_blocks[Chunk::get_index(x, top_y, z)]; + type_self = m_blocks[Chunk::index(x, top_y, z)]; if (top_y == -1) continue; // no block? skip // Weight map: type -> total weight - std::unordered_map weights; + std::unordered_map weights; weights[type_self] = 1.0f; // self weight // --- Right neighbor (index 0) --- @@ -410,7 +480,7 @@ void ChunkGenerator::blend_surface_blocks_borders( float t = 1.0f - static_cast(dist) / BLEND_RADIUS; t = t * t * (3.0f - 2.0f * t); // smoothstep if (t > 0.0f) { - uint8_t type_neighbor = + BlockType type_neighbor = get_top_block_from_neighbor(*neighbor_block[0], 0, z); weights[type_neighbor] += t; } @@ -422,7 +492,7 @@ void ChunkGenerator::blend_surface_blocks_borders( float t = 1.0f - static_cast(dist) / BLEND_RADIUS; t = t * t * (3.0f - 2.0f * t); if (t > 0.0f) { - uint8_t type_neighbor = get_top_block_from_neighbor( + BlockType type_neighbor = get_top_block_from_neighbor( *neighbor_block[1], CHUNK_SIZE - 1, z); weights[type_neighbor] += t; } @@ -434,7 +504,7 @@ void ChunkGenerator::blend_surface_blocks_borders( float t = 1.0f - static_cast(dist) / BLEND_RADIUS; t = t * t * (3.0f - 2.0f * t); if (t > 0.0f) { - uint8_t type_neighbor = + BlockType type_neighbor = get_top_block_from_neighbor(*neighbor_block[2], x, 0); weights[type_neighbor] += t; } @@ -446,14 +516,14 @@ void ChunkGenerator::blend_surface_blocks_borders( float t = 1.0f - static_cast(dist) / BLEND_RADIUS; t = t * t * (3.0f - 2.0f * t); if (t > 0.0f) { - uint8_t type_neighbor = get_top_block_from_neighbor( + BlockType type_neighbor = get_top_block_from_neighbor( *neighbor_block[3], x, CHUNK_SIZE - 1); weights[type_neighbor] += t; } } // Find type with maximum total weight - uint8_t final_type = type_self; + BlockType final_type = type_self; float max_weight = weights[type_self]; for (const auto& [type, w] : weights) { if (w > max_weight) { @@ -462,6 +532,10 @@ void ChunkGenerator::blend_surface_blocks_borders( } } + if (final_type == 0) { + return; + } + // Update the top block if the type changed if (final_type != type_self) { // top block @@ -469,7 +543,7 @@ void ChunkGenerator::blend_surface_blocks_borders( final_type = 2; } - m_blocks[Chunk::get_index(x, top_y, z)] = final_type; + m_blocks[Chunk::index(x, top_y, z)] = final_type; // bottom block unsigned fill_type = 2; if (final_type == 1) { @@ -478,7 +552,7 @@ void ChunkGenerator::blend_surface_blocks_borders( fill_type = 4; } for (int y = top_y - 5; y < top_y; y++) { - m_blocks[Chunk::get_index(x, y, z)] = fill_type; + m_blocks[Chunk::index(x, y, z)] = fill_type; } } } @@ -512,6 +586,9 @@ void ChunkGenerator::make_biome_builder() { case RIVER: m_biome_builder = std::make_unique(*this); break; + case SNOWY_PLAIN: + m_biome_builder = std::make_unique(*this); + break; case NONE: m_biome_builder = nullptr; break; @@ -563,7 +640,7 @@ void ChunkGenerator::generate_cave() { if (y == 0) { continue; } - blocks[Chunk::get_index(x, y, z)] = 0; + blocks[Chunk::index(x, y, z)] = 0; } } } diff --git a/src/gameplay/tree.cpp b/src/gameplay/tree.cpp index 3aae5be..8a50744 100644 --- a/src/gameplay/tree.cpp +++ b/src/gameplay/tree.cpp @@ -30,7 +30,7 @@ static constexpr std::array TREE{{ bool build_tree(Chunk& chunk, const glm::ivec3& pos) { auto& block = chunk.get_chunk_blocks(); - if (block[Chunk::get_index(pos)] != 1) { + if (block[Chunk::index(pos)] != 1) { Logger::info("Root is not Grass Block"); return false; } @@ -43,13 +43,13 @@ bool build_tree(Chunk& chunk, const glm::ivec3& pos) { z >= CHUNK_SIZE) { return false; } - if (block[Chunk::get_index(tree_node)] != 0) { + if (block[Chunk::index(tree_node)] != 0) { return false; } } for (const auto& d : TREE) { auto tree_node = pos + d.offset; - chunk.set_chunk_block(Chunk::get_index(tree_node), d.id); + chunk.set_chunk_block(Chunk::index(tree_node), d.id); } return true; } diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index a0b63a3..7510248 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -10,7 +10,7 @@ namespace Cubed { struct ChunkRenderData { - std::array*, 4> neighbor_block; + std::array*, 4> neighbor_block; Chunk* chunk; }; @@ -86,6 +86,7 @@ void World::init_chunks() { std::this_thread::sleep_for(std::chrono::microseconds(200)); } } + /* void World::init_chunks() { @@ -230,7 +231,7 @@ void World::init_chunks() { for (auto& [pos, chunks] : temp_neighbor) { chunks.gen_phase_five(); } - std::array>, 4> neighbor_block; + std::array>, 4> neighbor_block; for (auto& [pos, chunks] : m_chunks) { for (int i = 0; i < 4; i++) { auto neighbor_pos = pos + CHUNK_DIR[i]; @@ -332,6 +333,8 @@ ChunkPos World::chunk_pos(int world_x, int world_z) { return {chunk_x, chunk_z}; } +#pragma region ChunkGenerate + void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.0f; m_chunk_gen_finished = false; @@ -344,35 +347,43 @@ void World::gen_chunks_internal() { sync_and_collect_missing_chunks(need_gen_chunks_pos, required_chunks); Logger::info("New Gen Chunks Sum: {}", need_gen_chunks_pos.size()); + if (need_gen_chunks_pos.empty()) { m_could_gen = true; m_chunk_gen_fraction = 1.0f; return; } + m_chunk_gen_fraction = 0.1f; + ChunkUpdateList new_chunks; for (auto& pos : need_gen_chunks_pos) { new_chunks.push_back({pos, Chunk(*this, pos)}); } ConstChunkMap new_chunks_neighbor; - // affected neighbor + // affected neighbor ChunkPtrUpdateList affected_neighbor; ChunkHashMap temp_neighbor; + build_neighbor_context_for_new_chunks( new_chunks_neighbor, affected_neighbor, new_chunks, temp_neighbor); - Logger::info("Temp neighbor sum {}", temp_neighbor.size()); - // build new chunk, but the neighbor in m_chunks also need to re-build + + // Logger::info("Temp neighbor sum {}", temp_neighbor.size()); + // build new chunk, but the neighbor in m_chunks also need to re-build for (auto& [pos, chunk] : new_chunks) { chunk.gen_phase_one(); m_cave_carcer.try_to_add_path(pos, chunk.seed()); } - for (auto& [pos, chunk] : temp_neighbor) { - chunk.gen_phase_one(); - m_cave_carcer.try_to_add_path(pos, chunk.seed()); - } + + // for (auto& [pos, chunk] : temp_neighbor) { + // chunk.gen_phase_one(); + // m_cave_carcer.try_to_add_path(pos, chunk.seed()); + // } + m_chunk_gen_fraction = 0.2f; + std::array neighbor_chunks; for (auto& [pos, chunks] : new_chunks) { for (int i = 0; i < 8; i++) { @@ -387,6 +398,8 @@ void World::gen_chunks_internal() { } chunks.gen_phase_two(neighbor_chunks); } + + /* for (auto& [pos, chunks] : temp_neighbor) { for (int i = 0; i < 8; i++) { auto neighbor_pos = pos + CHUNK_DIR[i]; @@ -399,14 +412,20 @@ void World::gen_chunks_internal() { } chunks.gen_phase_two(neighbor_chunks); } + */ + m_chunk_gen_fraction = 0.3f; + for (auto& [pos, chunks] : new_chunks) { chunks.gen_phase_three(); } - for (auto& [pos, chunks] : temp_neighbor) { - chunks.gen_phase_three(); - } + // for (auto& [pos, chunks] : temp_neighbor) { + // chunks.gen_phase_three(); + // } + m_chunk_gen_fraction = 0.4f; + + /* for (int i = 0; i < 4; i++) { for (auto& [pos, chunks] : temp_neighbor) { std::array, 8> @@ -448,15 +467,20 @@ void World::gen_chunks_internal() { chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome); } } + */ m_chunk_gen_fraction = 0.5f; for (auto& [pos, chunks] : new_chunks) { chunks.gen_phase_five(); } + + /* for (auto& [pos, chunks] : temp_neighbor) { chunks.gen_phase_five(); } - std::array>, 4> neighbor_blocks_data; + */ + + std::array>, 4> neighbor_blocks_data; for (auto& [pos, chunks] : new_chunks) { { // std::lock_guard lk(m_chunks_mutex); @@ -472,12 +496,14 @@ void World::gen_chunks_internal() { } chunks.gen_phase_six(neighbor_blocks_data); } + for (auto& [pos, chunks] : new_chunks) { chunks.gen_phase_seven(); } m_chunk_gen_fraction = 0.6f; - std::array*, 4> neighbor_block; + + std::array*, 4> neighbor_block; for (auto& [pos, chunk] : new_chunks) { for (int i = 0; i < 4; i++) { auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); @@ -489,10 +515,14 @@ void World::gen_chunks_internal() { } chunk.gen_vertex_data(neighbor_block); } + m_chunk_gen_fraction = 0.7f; + build_neighbor_context_for_affected_neighbors(affected_neighbor, new_chunks_neighbor); + m_chunk_gen_fraction = 0.8f; + for (auto& [pos, chunk] : affected_neighbor) { for (int i = 0; i < 4; i++) { auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); @@ -505,7 +535,9 @@ void World::gen_chunks_internal() { chunk->gen_vertex_data(neighbor_block); chunk->need_upload(); } + m_chunk_gen_fraction = 0.9f; + { std::lock_guard lk(m_new_chunk_queue_mutex); for (auto& x : new_chunks) { @@ -597,6 +629,8 @@ void World::build_neighbor_context_for_affected_neighbors( } } +#pragma endregion + void World::start_gen_thread() { m_gen_running = true; Logger::info("Gen Thread Started"); @@ -667,15 +701,12 @@ int World::get_block(const glm::ivec3& block_pos) const { } const auto& chunk_blocks = it->second.get_chunk_blocks(); - int x, y, z; - y = block_pos.y; - x = block_pos.x - chunk_x * CHUNK_SIZE; - z = block_pos.z - chunk_z * CHUNK_SIZE; + auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z}); if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE) { return 0; } - return chunk_blocks[Chunk::get_index(x, y, z)]; + return chunk_blocks[Chunk::index(x, y, z)]; } bool World::is_block(const glm::ivec3& block_pos) const { @@ -687,15 +718,12 @@ bool World::is_block(const glm::ivec3& block_pos) const { return false; } const auto& chunk_blocks = it->second.get_chunk_blocks(); - int x, y, z; - y = block_pos.y; - x = block_pos.x - chunk_x * CHUNK_SIZE; - z = block_pos.z - chunk_z * CHUNK_SIZE; + auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z}); if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE) { return false; } - auto id = chunk_blocks[Chunk::get_index(x, y, z)]; + auto id = chunk_blocks[Chunk::index(x, y, z)]; if (id == 0) { return false; } else { @@ -718,16 +746,14 @@ void World::set_block(const glm::ivec3& block_pos, unsigned id) { return; } - int x, y, z; - y = world_y; - x = world_x - chunk_x * CHUNK_SIZE; - z = world_z - chunk_z * CHUNK_SIZE; + auto [x, y, z] = + Chunk::world_to_block(world_x, world_y, world_z, chunk_x, chunk_z); if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE) { return; } - it->second.set_chunk_block(Chunk::get_index(x, y, z), id); + it->second.set_chunk_block(Chunk::index(x, y, z), id); static const glm::ivec3 NEIGHBOR_DIRS[] = { {1, 0, 0}, {-1, 0, 0}, {0, 0, -1}, {0, 0, 1}}; @@ -784,7 +810,7 @@ void World::update(float delta_time) { for (auto& [pos, chunk] : m_chunks) { if (chunk.is_dirty()) { // the curial fator influence - std::array*, 4> neighbor_block; + std::array*, 4> neighbor_block; for (int i = 0; i < 4; i++) { auto it = m_chunks.find(pos + CHUNK_DIR[i]); if (it != m_chunks.end()) { diff --git a/src/tools/perlin_noise.cpp b/src/tools/perlin_noise.cpp index 114e833..77e7b72 100644 --- a/src/tools/perlin_noise.cpp +++ b/src/tools/perlin_noise.cpp @@ -1,8 +1,6 @@ #include "Cubed/tools/perlin_noise.hpp" -#include "Cubed/config.hpp" #include "Cubed/tools/cubed_assert.hpp" -#include "Cubed/tools/cubed_random.hpp" #include #include @@ -10,7 +8,7 @@ namespace Cubed { -void PerlinNoise::init(unsigned seed) { +void PerlinNoise3D::init(unsigned seed) { p.resize(256); std::iota(p.begin(), p.end(), 0); Logger::info("Init Perlin Noise With Seed {}", seed); @@ -20,7 +18,7 @@ void PerlinNoise::init(unsigned seed) { is_init = true; } -float PerlinNoise::noise(float x, float y, float z) { +float PerlinNoise3D::noise(float x, float y, float z) { ASSERT_MSG(is_init, "The PerlinNoise don't init!"); int ix = static_cast(std::floor(x)) & 255; int iy = static_cast(std::floor(y)) & 255; @@ -55,11 +53,13 @@ float PerlinNoise::noise(float x, float y, float z) { return (res + 1.0f) / 2.0f; } -float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } +float PerlinNoise3D::fade(float t) { + return t * t * t * (t * (t * 6 - 15) + 10); +} -float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); } +float PerlinNoise3D::lerp(float t, float a, float b) { return a + t * (b - a); } -float PerlinNoise::grad(int hash, float x, float y, float z) { +float PerlinNoise3D::grad(int hash, float x, float y, float z) { int h = hash & 15; float u = h < 8 ? x : y; @@ -68,7 +68,7 @@ float PerlinNoise::grad(int hash, float x, float y, float z) { return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } -void PerlinNoise::reload(unsigned seed) { +void PerlinNoise3D::reload(unsigned seed) { is_init = false; p.resize(256); std::iota(p.begin(), p.end(), 0); @@ -79,4 +79,59 @@ void PerlinNoise::reload(unsigned seed) { is_init = true; } +void PerlinNoise2D::init(unsigned seed) { + p.resize(256); + std::iota(p.begin(), p.end(), 0); + Logger::info("Init Perlin Noise With Seed {}", seed); + std::shuffle(p.begin(), p.end(), std::mt19937(seed)); + + p.insert(p.end(), p.begin(), p.end()); // 扩展到 512,方便索引 + is_init = true; +} + +float PerlinNoise2D::noise(float x, float y) { + ASSERT_MSG(is_init, "The PerlinNoise2D don't init!"); + + int ix = static_cast(std::floor(x)) & 255; + int iy = static_cast(std::floor(y)) & 255; + + x -= std::floor(x); + y -= std::floor(y); + + float u = fade(x); + float v = fade(y); + + int a = p[ix] + iy; + int b = p[ix + 1] + iy; + + float res = + lerp(v, lerp(u, grad(p[a], x, y), grad(p[b], x - 1, y)), + lerp(u, grad(p[a + 1], x, y - 1), grad(p[b + 1], x - 1, y - 1))); + + return (res + 1.0f) / 2.0f; // 映射到 [0, 1] +} + +float PerlinNoise2D::fade(float t) { + return t * t * t * (t * (t * 6 - 15) + 10); +} + +float PerlinNoise2D::lerp(float t, float a, float b) { return a + t * (b - a); } + +float PerlinNoise2D::grad(int hash, float x, float y) { + int h = hash & 3; // 使用低 2 位选择 4 个梯度方向 + float u = (h & 1) ? -x : x; + float v = (h & 2) ? -y : y; + return u + v; +} + +void PerlinNoise2D::reload(unsigned seed) { + is_init = false; + p.resize(256); + std::iota(p.begin(), p.end(), 0); + Logger::info("Reload Perlin Noise With Seed {}", seed); + std::shuffle(p.begin(), p.end(), std::mt19937(seed)); + + p.insert(p.end(), p.begin(), p.end()); + is_init = true; +} } // namespace Cubed \ No newline at end of file