diff --git a/CMakeLists.txt b/CMakeLists.txt index d89a0b3..ee7027f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,8 @@ add_executable(${PROJECT_NAME} src/gameplay/cave_carver.cpp src/gameplay/cave_path.cpp src/gameplay/builders/snowy_plain_builder.cpp + src/gameplay/river_worm.cpp + src/gameplay/river_path.cpp ) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/assets/texture/item/block/snowy_grass_block.png b/assets/texture/item/block/snowy_grass_block.png index 51212c8..a45dd10 100644 Binary files a/assets/texture/item/block/snowy_grass_block.png and b/assets/texture/item/block/snowy_grass_block.png differ diff --git a/include/Cubed/dev_panel.hpp b/include/Cubed/dev_panel.hpp index dfa484d..4756805 100644 --- a/include/Cubed/dev_panel.hpp +++ b/include/Cubed/dev_panel.hpp @@ -47,6 +47,7 @@ private: void show_about_table_bar(); void show_biome_table_bar(); void show_cave_table_bar(); + void show_river_table_bar(); void show_settings_tab_item(); void show_world_tab_item(); void show_player_tab_item(); diff --git a/include/Cubed/gameplay/builders/biome_builder.hpp b/include/Cubed/gameplay/builders/biome_builder.hpp index 7df2309..694572f 100644 --- a/include/Cubed/gameplay/builders/biome_builder.hpp +++ b/include/Cubed/gameplay/builders/biome_builder.hpp @@ -12,6 +12,5 @@ public: protected: void build_bottom(); - void fill_water(); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/cave_carver.hpp b/include/Cubed/gameplay/cave_carver.hpp index c340660..76b397e 100644 --- a/include/Cubed/gameplay/cave_carver.hpp +++ b/include/Cubed/gameplay/cave_carver.hpp @@ -4,10 +4,10 @@ namespace Cubed { class CaveCarver { public: CaveCarver(); - std::unordered_map& paths(); + std::unordered_map& paths(); void init(unsigned world_seed); void reload(unsigned world_seed); - void add_path(const glm::vec3& pos); + void add_path(const glm::vec3& pos, unsigned chunk_seed); void try_to_add_path(const ChunkPos& pos, unsigned chunk_seed); void cleanup_finished_caves(); @@ -15,7 +15,7 @@ public: float& cave_probability(); private: - std::unordered_map m_paths; + std::unordered_map m_paths; unsigned m_seed = 0; int m_sum = 0; Random m_random; diff --git a/include/Cubed/gameplay/cave_path.hpp b/include/Cubed/gameplay/cave_path.hpp index 269c760..85ad01a 100644 --- a/include/Cubed/gameplay/cave_path.hpp +++ b/include/Cubed/gameplay/cave_path.hpp @@ -1,47 +1,13 @@ #pragma once #include "Cubed/gameplay/chunk_pos.hpp" +#include "Cubed/gameplay/path_point.hpp" #include "Cubed/tools/cubed_random.hpp" #include #include namespace Cubed { -struct PathPoint { - glm::vec3 pos; - glm::vec3 tangent{0.0f, 0.0f, 1.0f}; - float rad_xz; - float rad_y; - PathPoint(const glm::vec3& p, float rx, float ry) - : pos(p), rad_xz(rx), rad_y(ry) {} - bool contains(const glm::vec3& other_pos) const { - glm::vec3 to_point = other_pos - pos; - - glm::vec3 world_up(0.0f, 1.0f, 0.0f); - - glm::vec3 right = glm::normalize(glm::cross(tangent, world_up)); - - if (glm::length(right) < 0.001f) { - glm::vec3 alt_up(1.0f, 0.0f, 0.0f); - right = glm::normalize(glm::cross(tangent, alt_up)); - } - - glm::vec3 up = glm::normalize(glm::cross(right, tangent)); - - float horizontal_dist = glm::dot(to_point, right); - float vertical_dist = glm::dot(to_point, up); - - float a = rad_xz; - float b = rad_y; - if (a <= 0.0f || b <= 0.0f) - return false; - - float check = (horizontal_dist * horizontal_dist) / (a * a) + - (vertical_dist * vertical_dist) / (b * b); - return check <= 1.0f; - } -}; - class CavePath { public: CavePath(unsigned int world_seed, int path_id, const glm::vec3& start_pos); diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index 4352497..396b6f8 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -46,6 +46,7 @@ public: Random& random(); const std::array& neighbor_biome() const; void generate_cave(); + void generate_river(); private: static inline std::atomic is_init{false}; diff --git a/include/Cubed/gameplay/path_point.hpp b/include/Cubed/gameplay/path_point.hpp new file mode 100644 index 0000000..6e3b9d1 --- /dev/null +++ b/include/Cubed/gameplay/path_point.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +struct PathPoint { + glm::vec3 pos; + glm::vec3 tangent{0.0f, 0.0f, 1.0f}; + float rad_xz; + float rad_y; + PathPoint(const glm::vec3& p, float rx, float ry) + : pos(p), rad_xz(rx), rad_y(ry) {} + bool contains(const glm::vec3& other_pos) const { + glm::vec3 to_point = other_pos - pos; + + glm::vec3 world_up(0.0f, 1.0f, 0.0f); + + glm::vec3 right = glm::normalize(glm::cross(tangent, world_up)); + + if (glm::length(right) < 0.001f) { + glm::vec3 alt_up(1.0f, 0.0f, 0.0f); + right = glm::normalize(glm::cross(tangent, alt_up)); + } + + glm::vec3 up = glm::normalize(glm::cross(right, tangent)); + + float horizontal_dist = glm::dot(to_point, right); + float vertical_dist = glm::dot(to_point, up); + + float a = rad_xz; + float b = rad_y; + if (a <= 0.0f || b <= 0.0f) + return false; + + float check = (horizontal_dist * horizontal_dist) / (a * a) + + (vertical_dist * vertical_dist) / (b * b); + return check <= 1.0f; + } +}; \ No newline at end of file diff --git a/include/Cubed/gameplay/river.path.hpp b/include/Cubed/gameplay/river.path.hpp new file mode 100644 index 0000000..89837eb --- /dev/null +++ b/include/Cubed/gameplay/river.path.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "Cubed/gameplay/chunk_pos.hpp" +#include "Cubed/gameplay/path_point.hpp" +#include "Cubed/tools/cubed_random.hpp" + +#include +#include + +namespace Cubed { +class RiverPath { + +public: + RiverPath(unsigned int world_seed, int path_id, const glm::vec3& start_pos); + const std::vector& points() const; + void clear_chunk(const ChunkPos& pos); + bool is_finished() const; + + static float& radius_xz_min(); + static float& radius_xz_max(); + static float& radius_y_min(); + static float& radius_y_max(); + static float& delta_angle_min(); + static float& delta_angle_max(); + static int& step_min(); + static int& step_max(); + +private: + static inline float m_radius_xz_min = 5.0f; + static inline float m_radius_xz_max = 10.0f; + static inline float m_radius_y_min = 4.0f; + static inline float m_radius_y_max = 8.0f; + static inline float m_delta_angle_min = -3.0f; + static inline float m_delta_angle_max = 3.0f; + static inline int m_step_min = 150; + static inline int m_step_max = 400; + + int m_path_id = 0; + unsigned int m_seed = 0; + float m_yaw = 0.0f; + float m_pitch = 0.0f; + int m_step = 0; + float m_step_len = 1.0f; + PathPoint m_start_path_point{{0.0f, 0.0f, 0.0f}, 0.0f, 0.0f}; + Random m_random; + + std::vector m_points; + std::unordered_set m_pending_chunks; + void collect_path_points(); + void precompute_chunk_coverage(); +}; +} // namespace Cubed diff --git a/include/Cubed/gameplay/river_worm.hpp b/include/Cubed/gameplay/river_worm.hpp new file mode 100644 index 0000000..281cc32 --- /dev/null +++ b/include/Cubed/gameplay/river_worm.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "Cubed/gameplay/chunk_pos.hpp" +#include "Cubed/gameplay/river.path.hpp" +#include "Cubed/tools/cubed_random.hpp" + +#include +#include +namespace Cubed { + +class RiverWorm { +public: + RiverWorm(); + std::unordered_map& paths(); + void init(unsigned world_seed); + void reload(unsigned world_seed); + void add_path(const glm::vec3& pos, unsigned chunk_seed); + void try_to_add_path(const ChunkPos& pos, unsigned chunk_seed); + void cleanup_finished_rivers(); + + int river_sum() const; + float& river_probability(); + +private: + std::unordered_map m_paths; + unsigned m_seed = 0; + int m_sum = 0; + Random m_random; + float m_probability = 0.01f; +}; + +}; // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index adea6a2..d5e0570 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -2,6 +2,7 @@ #include "Cubed/AABB.hpp" #include "Cubed/gameplay/cave_carver.hpp" #include "Cubed/gameplay/chunk.hpp" +#include "Cubed/gameplay/river_worm.hpp" #include #include @@ -56,19 +57,19 @@ private: std::vector> m_new_chunk_queue; CaveCarver m_cave_carcer; - + RiverWorm m_river_worm; void init_chunks(); void gen_chunks_internal(); void sync_player_pos(glm::vec3& player_pos); - void compute_required_chunks(ChunkPosSet& required_chunks); + void compute_required_chunks(ChunkPosSet& required_chunks, + ChunkHashMap& temp_neighbor); void sync_and_collect_missing_chunks(std::vector&, const ChunkPosSet&); void build_neighbor_context_for_new_chunks(ConstChunkMap& new_chunks_neighbor, ChunkPtrUpdateList& affected_neighbor, - const ChunkUpdateList& new_chunks, - ChunkHashMap& temp_neighbor); + const ChunkUpdateList& new_chunks); void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&, ConstChunkMap&); @@ -112,6 +113,7 @@ public: void stop_gen_thread(); CaveCarver& cave_carcer(); + RiverWorm& river_worm(); }; } // namespace Cubed diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index 0375378..ab689a4 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -34,16 +34,16 @@ constexpr int AMPLITUDE_MAX = 80; constexpr float TREE_FREQ_MIM = 0.001f; constexpr float TREE_FREQ_MAX = 0.3f; -constexpr float CAVE_PROBABILITY_MIN = 0.005f; -constexpr float CAVE_PROBABILITY_MAX = 0.1f; +constexpr float PATH_PROBABILITY_MIN = 0.005f; +constexpr float PATH_PROBABILITY_MAX = 0.1f; constexpr float RADIUS_XZ_MIN = 1.0f; constexpr float RADIUS_XZ_MAX = 50.0f; constexpr float RADIUS_Y_MIN = 1.0f; constexpr float RADIUS_Y_MAX = 50.0f; constexpr float DELTA_ANGLE_MIN = -30.0f; constexpr float DELTA_ANGLE_MAX = 30.0f; -constexpr int CAVE_STEP_MIN = 1; -constexpr int CAVE_STEP_MAX = 1000; +constexpr int PATH_STEP_MIN = 1; +constexpr int PATH_STEP_MAX = 1000; static int filter_unsigned(ImGuiInputTextCallbackData* data) { if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) { @@ -269,7 +269,7 @@ void DevPanel::show_cave_table_bar() { ImGui::Text("Total Cave Sum %d", cave_carcer.cave_sum()); ImGui::SliderFloat("Cave Probability", &cave_carcer.cave_probability(), - CAVE_PROBABILITY_MIN, CAVE_PROBABILITY_MAX); + PATH_PROBABILITY_MIN, PATH_PROBABILITY_MAX); ImGui::SliderFloat("Radius XZ Min", &CavePath::radius_xz_min(), RADIUS_XZ_MIN, RADIUS_XZ_MAX); ImGui::SliderFloat("Radius XZ Max", &CavePath::radius_xz_max(), @@ -282,10 +282,34 @@ void DevPanel::show_cave_table_bar() { DELTA_ANGLE_MIN, 0.0f); ImGui::SliderFloat("Delta Angle Max", &CavePath::delta_angle_max(), 0.0f, DELTA_ANGLE_MAX); - ImGui::SliderInt("Step Min", &CavePath::step_min(), CAVE_STEP_MIN, - CAVE_STEP_MAX); - ImGui::SliderInt("Step Max", &CavePath::step_max(), CAVE_STEP_MIN, - CAVE_STEP_MAX); + ImGui::SliderInt("Step Min", &CavePath::step_min(), PATH_STEP_MIN, + PATH_STEP_MAX); + ImGui::SliderInt("Step Max", &CavePath::step_max(), PATH_STEP_MIN, + PATH_STEP_MAX); +} + +void DevPanel::show_river_table_bar() { + auto& river_wrom = m_app.world().river_worm(); + + ImGui::Text("Total River Sum %d", river_wrom.river_sum()); + ImGui::SliderFloat("River Probability", &river_wrom.river_probability(), + PATH_PROBABILITY_MIN, PATH_PROBABILITY_MAX); + ImGui::SliderFloat("Radius XZ Min##river", &RiverPath::radius_xz_min(), + RADIUS_XZ_MIN, RADIUS_XZ_MAX); + ImGui::SliderFloat("Radius XZ Max##river", &RiverPath::radius_xz_max(), + RADIUS_XZ_MIN, RADIUS_XZ_MAX); + ImGui::SliderFloat("Radius Y Min##river", &RiverPath::radius_y_min(), + RADIUS_Y_MIN, RADIUS_Y_MAX); + ImGui::SliderFloat("Radius Y Max##river", &RiverPath::radius_y_max(), + RADIUS_Y_MIN, RADIUS_Y_MAX); + ImGui::SliderFloat("Delta Angle Min##river", &RiverPath::delta_angle_min(), + DELTA_ANGLE_MIN, 0.0f); + ImGui::SliderFloat("Delta Angle Max##river", &RiverPath::delta_angle_max(), + 0.0f, DELTA_ANGLE_MAX); + ImGui::SliderInt("Step Min##river", &RiverPath::step_min(), PATH_STEP_MIN, + PATH_STEP_MAX); + ImGui::SliderInt("Step Max##river", &RiverPath::step_max(), PATH_STEP_MIN, + PATH_STEP_MAX); } void DevPanel::show_settings_tab_item() { @@ -438,6 +462,10 @@ void DevPanel::show_world_tab_item() { show_cave_table_bar(); ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("River")) { + show_river_table_bar(); + ImGui::EndTabItem(); + } if (ImGui::BeginTabItem("Biome")) { show_biome_table_bar(); ImGui::EndTabItem(); diff --git a/src/gameplay/builders/biome_builder.cpp b/src/gameplay/builders/biome_builder.cpp index 6582982..080e546 100644 --- a/src/gameplay/builders/biome_builder.cpp +++ b/src/gameplay/builders/biome_builder.cpp @@ -15,27 +15,5 @@ void BiomeBuilder::build_bottom() { } } } -void BiomeBuilder::fill_water() { - ChunkGenerator& chunk_generator = get_chunk_generator(); - Chunk& chunk = chunk_generator.chunk(); - auto& m_blocks = chunk.blocks(); - auto& neighbor = chunk_generator.neighbor_biome(); - auto& heightmap = chunk.heightmap(); - for (int i = 0; i < 8; i++) { - if (neighbor[i] == BiomeType::RIVER) { - for (int x = 0; x < SIZE_X; x++) { - for (int z = 0; z < SIZE_Z; z++) { - if (heightmap[x][z] >= SEA_LEVEL) { - continue; - } - int height = heightmap[x][z]; - for (int y = height; y < SEA_LEVEL; y++) { - m_blocks[Chunk::index(x, y, z)] = 7; - } - } - } - return; - } - } -} + } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/builders/desert_builder.cpp b/src/gameplay/builders/desert_builder.cpp index 25aefe0..2b60cfa 100644 --- a/src/gameplay/builders/desert_builder.cpp +++ b/src/gameplay/builders/desert_builder.cpp @@ -29,7 +29,7 @@ void DesertBuilder::build_blocks() { } } -void DesertBuilder::build_vegetation() { fill_water(); } +void DesertBuilder::build_vegetation() {} ChunkGenerator& DesertBuilder::get_chunk_generator() { return m_chunk_generator; diff --git a/src/gameplay/builders/forest_builder.cpp b/src/gameplay/builders/forest_builder.cpp index 34eda3f..e9fb9c1 100644 --- a/src/gameplay/builders/forest_builder.cpp +++ b/src/gameplay/builders/forest_builder.cpp @@ -52,7 +52,6 @@ void ForestBuilder::build_vegetation() { } } } - fill_water(); } ChunkGenerator& ForestBuilder::get_chunk_generator() { diff --git a/src/gameplay/builders/mountain_builder.cpp b/src/gameplay/builders/mountain_builder.cpp index 1af20c5..0c8c379 100644 --- a/src/gameplay/builders/mountain_builder.cpp +++ b/src/gameplay/builders/mountain_builder.cpp @@ -25,7 +25,7 @@ void MountainBuilder::build_blocks() { } } -void MountainBuilder::build_vegetation() { fill_water(); } +void MountainBuilder::build_vegetation() {} ChunkGenerator& MountainBuilder::get_chunk_generator() { return m_chunk_generator; diff --git a/src/gameplay/builders/plain_builder.cpp b/src/gameplay/builders/plain_builder.cpp index 6c20fbd..b316795 100644 --- a/src/gameplay/builders/plain_builder.cpp +++ b/src/gameplay/builders/plain_builder.cpp @@ -29,7 +29,7 @@ void PlainBuilder::build_blocks() { } } -void PlainBuilder::build_vegetation() { fill_water(); } +void PlainBuilder::build_vegetation() {} ChunkGenerator& PlainBuilder::get_chunk_generator() { return m_chunk_generator; diff --git a/src/gameplay/builders/river_builder.cpp b/src/gameplay/builders/river_builder.cpp index 3e7fa6e..417c72b 100644 --- a/src/gameplay/builders/river_builder.cpp +++ b/src/gameplay/builders/river_builder.cpp @@ -1,6 +1,5 @@ #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) @@ -12,6 +11,7 @@ void RiverBuilder::build_biome() { }; void RiverBuilder::build_blocks() { + /* auto& m_chunk = m_chunk_generator.chunk(); auto& m_blocks = m_chunk.blocks(); auto& m_heightmap = m_chunk.heightmap(); @@ -33,9 +33,11 @@ void RiverBuilder::build_blocks() { } } } + */ } void RiverBuilder::build_vegetation() { + /* auto& m_chunk = m_chunk_generator.chunk(); auto& m_blocks = m_chunk.blocks(); auto& m_heightmap = m_chunk.heightmap(); @@ -50,6 +52,7 @@ void RiverBuilder::build_vegetation() { } } } + */ } ChunkGenerator& RiverBuilder::get_chunk_generator() { diff --git a/src/gameplay/builders/snowy_plain_builder.cpp b/src/gameplay/builders/snowy_plain_builder.cpp index 9a46298..189ce46 100644 --- a/src/gameplay/builders/snowy_plain_builder.cpp +++ b/src/gameplay/builders/snowy_plain_builder.cpp @@ -29,7 +29,7 @@ void SnowyPlainBuilder::build_blocks() { } } -void SnowyPlainBuilder::build_vegetation() { fill_water(); } +void SnowyPlainBuilder::build_vegetation() {} ChunkGenerator& SnowyPlainBuilder::get_chunk_generator() { return m_chunk_generator; diff --git a/src/gameplay/cave_carver.cpp b/src/gameplay/cave_carver.cpp index 34135d1..83052e6 100644 --- a/src/gameplay/cave_carver.cpp +++ b/src/gameplay/cave_carver.cpp @@ -5,7 +5,7 @@ namespace Cubed { CaveCarver::CaveCarver() {} -std::unordered_map& CaveCarver::paths() { return m_paths; } +std::unordered_map& CaveCarver::paths() { return m_paths; } void CaveCarver::init(unsigned world_seed) { m_seed = world_seed; @@ -19,13 +19,17 @@ void CaveCarver::reload(unsigned world_seed) { init(world_seed); } -void CaveCarver::add_path(const glm::vec3& pos) { - m_paths.emplace(m_sum, CavePath{m_seed, m_sum, pos}); +void CaveCarver::add_path(const glm::vec3& pos, unsigned chunk_seed) { + m_paths.emplace(chunk_seed, CavePath{m_seed, m_sum, pos}); m_sum++; } void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos, unsigned chunk_seed) { + auto it = m_paths.find(chunk_seed); + if (it != m_paths.end()) { + return; + } Random random{chunk_seed}; if (random.random_bool(static_cast(m_cave_probability))) { const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE; @@ -38,7 +42,7 @@ void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos, int x = random.random_int(CHUNK_MIN_X, CHUNK_MAX_X); int y = random.random_int(CHUNK_MIN_Y + 1, max_y); int z = random.random_int(CHUNK_MIN_Z, CHUNK_MAX_Z); - add_path(glm::vec3{x, y, z}); + add_path(glm::vec3{x, y, z}, chunk_seed); } } diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index bc3bd0c..da68909 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -151,7 +151,7 @@ void Chunk::gen_vertex_data( auto is_cull = [&](const std::vector* chunk_blocks) { if (chunk_blocks == nullptr) { - return false; + return true; } int x, y, z; y = world_ny; @@ -294,6 +294,7 @@ void Chunk::gen_phase_six( } m_generator->blend_surface_blocks_borders(neighbor_block); m_generator->generate_cave(); + m_generator->generate_river(); } void Chunk::gen_phase_seven() { diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index 1132fc7..0117f33 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -539,11 +539,17 @@ 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() == BiomeType::RIVER && final_type == 1) { - final_type = 2; + if (final_type == 7 && top_y > SEA_LEVEL) { + if (type_self == 7) { + m_blocks[Chunk::index(x, top_y, z)] = 0; + } else { + m_blocks[Chunk::index(x, top_y, z)] = type_self; + } + + } else { + m_blocks[Chunk::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) { @@ -552,7 +558,11 @@ void ChunkGenerator::blend_surface_blocks_borders( fill_type = 4; } for (int y = top_y - 5; y < top_y; y++) { - m_blocks[Chunk::index(x, y, z)] = fill_type; + if (fill_type == 7 && y > SEA_LEVEL) { + m_blocks[Chunk::index(x, y, z)] = 0; + } else { + m_blocks[Chunk::index(x, y, z)] = fill_type; + } } } } @@ -650,6 +660,76 @@ void ChunkGenerator::generate_cave() { } } +void ChunkGenerator::generate_river() { + + auto& river_worm = m_chunk.world().river_worm(); + auto& paths = river_worm.paths(); + const auto& chunk_pos = m_chunk.chunk_pos(); + auto& blocks = m_chunk.blocks(); + const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE; + const int CHUNK_MIN_Z = chunk_pos.z * CHUNK_SIZE; + const int CHUNK_MAX_X = CHUNK_MIN_X + SIZE_X - 1; + const int CHUNK_MAX_Z = CHUNK_MIN_Z + SIZE_Z - 1; + const int CHUNK_MIN_Y = 0; + const int CHUNK_MAX_Y = SIZE_Y - 1; + + bool is_river = false; + + for (auto& [id, path] : paths) { + for (const auto& point : path.points()) { + if (m_chunk.biome() == BiomeType::DESERT) { + path.clear_chunk(chunk_pos); + continue; + } + const glm::vec3& center = point.pos; + float rad_xz = point.rad_xz; + float rad_y = point.rad_y; + + int min_x = static_cast(std::floor(center.x - rad_xz)); + int max_x = static_cast(std::floor(center.x + rad_xz)); + int min_z = static_cast(std::floor(center.z - rad_xz)); + int max_z = static_cast(std::floor(center.z + rad_xz)); + int min_y = static_cast(std::floor(center.y - rad_y)); + int max_y = static_cast(std::floor(center.y + rad_y)); + + min_x = std::max(min_x, CHUNK_MIN_X); + max_x = std::min(max_x, CHUNK_MAX_X); + min_z = std::max(min_z, CHUNK_MIN_Z); + max_z = std::min(max_z, CHUNK_MAX_Z); + min_y = std::max(min_y, CHUNK_MIN_Y); + max_y = std::min(max_y, CHUNK_MAX_Y); + + for (int wx = min_x; wx <= max_x; ++wx) { + int x = wx - CHUNK_MIN_X; + for (int wz = min_z; wz <= max_z; ++wz) { + int z = wz - CHUNK_MIN_Z; + for (int wy = min_y; wy <= max_y; ++wy) { + int y = wy; + glm::vec3 pos(static_cast(wx), + static_cast(wy), + static_cast(wz)); + if (point.contains(pos)) { + if (y > SEA_LEVEL) { + blocks[Chunk::index(x, y, z)] = 0; + continue; + } + is_river = true; + if (blocks[Chunk::index(x, y, z)] == 0) { + continue; + } + blocks[Chunk::index(x, y, z)] = 7; + } + } + } + } + } + path.clear_chunk(chunk_pos); + } + if (is_river) { + m_chunk.biome(RIVER); + } +} + Chunk& ChunkGenerator::chunk() { return m_chunk; } Random& ChunkGenerator::random() { return m_random; } diff --git a/src/gameplay/river_path.cpp b/src/gameplay/river_path.cpp new file mode 100644 index 0000000..71d12a7 --- /dev/null +++ b/src/gameplay/river_path.cpp @@ -0,0 +1,93 @@ +#include "Cubed/constants.hpp" +#include "Cubed/gameplay/river.path.hpp" +#include "Cubed/tools/cubed_hash.hpp" +#include "Cubed/tools/math_tools.hpp" + +#include +namespace Cubed { +RiverPath::RiverPath(unsigned int world_seed, int path_id, + const glm::vec3& start_pos) { + m_path_id = path_id; + m_seed = HASH::combine_32(world_seed, path_id); + m_random.init(m_seed); + m_yaw = m_random.random_float(0.0f, 360.0f); + m_pitch = 0.0f; + m_start_path_point.pos = start_pos; + m_start_path_point.rad_xz = + m_random.random_float(m_radius_xz_min, m_radius_xz_max); + m_start_path_point.rad_y = + m_random.random_float(m_radius_y_min, m_radius_y_max); + m_step = m_random.random_int(m_step_min, m_step_max); + m_points.reserve(m_step + 1); + m_points.push_back(m_start_path_point); + collect_path_points(); + precompute_chunk_coverage(); +} + +void RiverPath::collect_path_points() { + for (int i = 0; i < m_step; i++) { + + m_yaw = std::fmod(m_yaw, 360.0f); + if (m_yaw < 0.0f) + m_yaw += 360.0f; + + float dx = std::cos(glm::radians(m_pitch)) * + std::sin(glm::radians(m_yaw)) * m_step_len; + float dy = std::sin(glm::radians(m_pitch)) * m_step_len; + float dz = std::cos(glm::radians(m_pitch)) * + std::cos(glm::radians(m_yaw)) * m_step_len; + + m_points[i].tangent = glm::normalize(glm::vec3{dx, dy, dz}); + + float t = Math::smootherstep(0, m_step - 1, i); + + float drad_xz = m_start_path_point.rad_xz * (1.0f - t); + float drad_y = m_start_path_point.rad_y * (1.0f - t); + drad_xz = std::max(drad_xz, 4.0f); + drad_y = std::max(drad_y, 4.0f); + m_points.emplace_back(m_points[i].pos + glm::vec3{dx, dy, dz}, drad_xz, + drad_y); + + m_yaw += m_random.random_float(m_delta_angle_min, m_delta_angle_max); + } + auto n = m_points.size(); + if (n >= 2) { + m_points[n - 1].tangent = m_points[n - 2].tangent; + } +} + +void RiverPath::precompute_chunk_coverage() { + for (const auto& point : m_points) { + float rad = point.rad_xz; + const glm::vec3& center = point.pos; + + int min_cx = + static_cast(std::floor((center.x - rad) / CHUNK_SIZE)); + int max_cx = + static_cast(std::floor((center.x + rad) / CHUNK_SIZE)); + int min_cz = + static_cast(std::floor((center.z - rad) / CHUNK_SIZE)); + int max_cz = + static_cast(std::floor((center.z + rad) / CHUNK_SIZE)); + + for (int cx = min_cx; cx <= max_cx; ++cx) + for (int cz = min_cz; cz <= max_cz; ++cz) + m_pending_chunks.insert({cx, cz}); + } +} + +void RiverPath::clear_chunk(const ChunkPos& pos) { + m_pending_chunks.erase(pos); +} +const std::vector& RiverPath::points() const { return m_points; } +bool RiverPath::is_finished() const { return m_pending_chunks.empty(); } + +float& RiverPath::radius_xz_min() { return m_radius_xz_min; } +float& RiverPath::radius_xz_max() { return m_radius_xz_max; } +float& RiverPath::radius_y_min() { return m_radius_y_min; } +float& RiverPath::radius_y_max() { return m_radius_y_max; } +float& RiverPath::delta_angle_min() { return m_delta_angle_min; } +float& RiverPath::delta_angle_max() { return m_delta_angle_max; } +int& RiverPath::step_min() { return m_step_min; } +int& RiverPath::step_max() { return m_step_max; } +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/river_worm.cpp b/src/gameplay/river_worm.cpp new file mode 100644 index 0000000..0d98830 --- /dev/null +++ b/src/gameplay/river_worm.cpp @@ -0,0 +1,53 @@ +#include "Cubed/gameplay/river_worm.hpp" + +#include "Cubed/constants.hpp" + +namespace Cubed { +RiverWorm::RiverWorm() {} + +std::unordered_map& RiverWorm::paths() { return m_paths; } + +void RiverWorm::init(unsigned world_seed) { + m_seed = world_seed; + m_sum = 0; + m_random.init(m_seed); +} + +void RiverWorm::reload(unsigned world_seed) { + m_seed = world_seed; + m_paths.clear(); + init(world_seed); +} + +void RiverWorm::add_path(const glm::vec3& pos, unsigned chunk_seed) { + m_paths.emplace(chunk_seed, RiverPath{m_seed, m_sum, pos}); + m_sum++; +} + +void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos, + unsigned chunk_seed) { + auto it = m_paths.find(chunk_seed); + if (it != m_paths.end()) { + return; + } + Random random{chunk_seed}; + if (random.random_bool(static_cast(m_probability))) { + const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE; + const int CHUNK_MIN_Z = chunk_pos.z * CHUNK_SIZE; + const int CHUNK_MAX_X = CHUNK_MIN_X + SIZE_X - 1; + const int CHUNK_MAX_Z = CHUNK_MIN_Z + SIZE_Z - 1; + int x = random.random_int(CHUNK_MIN_X, CHUNK_MAX_X); + int y = SEA_LEVEL + 2; + int z = random.random_int(CHUNK_MIN_Z, CHUNK_MAX_Z); + add_path(glm::vec3{x, y, z}, chunk_seed); + } +} + +void RiverWorm::cleanup_finished_rivers() { + std::erase_if(m_paths, + [](const auto& kv) { return kv.second.is_finished(); }); +} + +int RiverWorm::river_sum() const { return m_sum; } +float& RiverWorm::river_probability() { return m_probability; } +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index 7510248..0401c6b 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -64,6 +64,7 @@ Player& World::get_player(const std::string& name) { void World::init_world() { m_cave_carcer.init(ChunkGenerator::seed()); + m_river_worm.init(ChunkGenerator::seed()); m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4); auto t1 = std::chrono::system_clock::now(); @@ -339,15 +340,17 @@ void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.0f; m_chunk_gen_finished = false; ChunkPosSet required_chunks; - compute_required_chunks(required_chunks); + ChunkHashMap temp_neighbor; + compute_required_chunks(required_chunks, temp_neighbor); ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!"); std::vector need_gen_chunks_pos; + sync_and_collect_missing_chunks(need_gen_chunks_pos, required_chunks); Logger::info("New Gen Chunks Sum: {}", need_gen_chunks_pos.size()); - + Logger::info("Temp Chunks sum {}", temp_neighbor.size()); if (need_gen_chunks_pos.empty()) { m_could_gen = true; m_chunk_gen_fraction = 1.0f; @@ -364,26 +367,27 @@ void World::gen_chunks_internal() { ConstChunkMap new_chunks_neighbor; // affected neighbor ChunkPtrUpdateList affected_neighbor; - ChunkHashMap temp_neighbor; - build_neighbor_context_for_new_chunks( - new_chunks_neighbor, affected_neighbor, new_chunks, temp_neighbor); + build_neighbor_context_for_new_chunks(new_chunks_neighbor, + affected_neighbor, new_chunks); - // 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()); + m_river_worm.try_to_add_path(pos, chunk.seed()); + } + // precompute path to ensure the continuity of the path + for (auto& [pos, chunk] : temp_neighbor) { + chunk.gen_phase_one(); + m_cave_carcer.try_to_add_path(pos, chunk.seed()); + m_river_worm.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++) { @@ -391,13 +395,14 @@ void World::gen_chunks_internal() { auto it = new_chunks_neighbor.find(neighbor_pos); if (it == new_chunks_neighbor.end()) { neighbor_chunks[i] = nullptr; - ASSERT_MSG(false, "Cant Find Neighbot"); + // ASSERT_MSG(false, "Cant Find Neighbot"); continue; } neighbor_chunks[i] = it->second; } chunks.gen_phase_two(neighbor_chunks); } + */ /* for (auto& [pos, chunks] : temp_neighbor) { @@ -545,6 +550,7 @@ void World::gen_chunks_internal() { } } m_cave_carcer.cleanup_finished_caves(); + m_river_worm.cleanup_finished_rivers(); m_chunk_gen_fraction = 1.0f; m_chunk_gen_finished = true; } @@ -554,7 +560,8 @@ void World::sync_player_pos(glm::vec3& player_pos) { player_pos = m_gen_player_pos; } -void World::compute_required_chunks(ChunkPosSet& required_chunks) { +void World::compute_required_chunks(ChunkPosSet& required_chunks, + ChunkHashMap& temp_neighbor) { glm::vec3 player_pos; sync_player_pos(player_pos); @@ -569,6 +576,18 @@ void World::compute_required_chunks(ChunkPosSet& required_chunks) { required_chunks.emplace(u, v); } } + int max_path_len = std::max(CavePath::step_max(), RiverPath::step_max()); + half = std::ceil(static_cast(max_path_len) / CHUNK_SIZE) * 2; + for (int u = chunk_x - half; u <= chunk_x + half; ++u) { + for (int v = chunk_z - half; v <= chunk_z + half; ++v) { + ChunkPos pos{u, v}; + auto it = required_chunks.find(pos); + if (it != required_chunks.end()) { + continue; + } + temp_neighbor.emplace(pos, Chunk(*this, pos)); + } + } } void World::sync_and_collect_missing_chunks( @@ -593,7 +612,7 @@ void World::sync_and_collect_missing_chunks( void World::build_neighbor_context_for_new_chunks( ConstChunkMap& new_chunks_neighbor, ChunkPtrUpdateList& affected_neighbor, - const ChunkUpdateList& new_chunks, ChunkHashMap& temp_neighbor) { + const ChunkUpdateList& new_chunks) { { std::lock_guard lk(m_chunks_mutex); for (auto& [pos, chunk] : new_chunks) { @@ -602,8 +621,6 @@ void World::build_neighbor_context_for_new_chunks( if (it != m_chunks.end()) { new_chunks_neighbor.insert({it->first, &(it->second)}); affected_neighbor.push_back({it->first, &(it->second)}); - } else { - temp_neighbor.emplace(pos + dir, Chunk(*this, pos + dir)); } } } @@ -611,9 +628,6 @@ void World::build_neighbor_context_for_new_chunks( for (auto& [pos, chunk] : new_chunks) { new_chunks_neighbor.insert({pos, &chunk}); } - for (auto& [pos, chunk] : temp_neighbor) { - new_chunks_neighbor.insert({pos, &chunk}); - } } void World::build_neighbor_context_for_affected_neighbors( @@ -860,6 +874,7 @@ void World::rebuild_world() { m_is_rebuilding = true; stop_gen_thread(); m_cave_carcer.reload(ChunkGenerator::seed()); + m_river_worm.reload(ChunkGenerator::seed()); { std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex); m_chunks.clear(); @@ -882,5 +897,5 @@ void World::rendering_distance(int rendering_distance) { } CaveCarver& World::cave_carcer() { return m_cave_carcer; } - +RiverWorm& World::river_worm() { return m_river_worm; } } // namespace Cubed \ No newline at end of file