From 1a26474a05b80e260ffb40ad7ea7626e8b0b558e Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Sat, 9 May 2026 20:13:55 +0800 Subject: [PATCH] feat: add cave (#8) * feat: add cave generate * fix: incorrect blocks on cave surface * fix: non-deterministic cave generator * refactor: move all chunk generation to dedicated generation thread * refactor: remove inital cave * feat: add cave parameter adjustment * refactor: adjust cave probability --- CMakeLists.txt | 2 + include/Cubed/dev_panel.hpp | 1 + include/Cubed/gameplay/cave_carver.hpp | 24 ++++++ include/Cubed/gameplay/cave_path.hpp | 85 +++++++++++++++++++ include/Cubed/gameplay/chunk.hpp | 4 +- include/Cubed/gameplay/chunk_generator.hpp | 5 +- include/Cubed/gameplay/world.hpp | 6 ++ include/Cubed/tools/cubed_hash.hpp | 4 + include/Cubed/tools/cubed_random.hpp | 4 +- include/Cubed/tools/math_tools.hpp | 5 +- src/dev_panel.cpp | 52 +++++++++++- src/gameplay/cave_carver.cpp | 52 ++++++++++++ src/gameplay/cave_path.cpp | 95 ++++++++++++++++++++++ src/gameplay/chunk.cpp | 13 ++- src/gameplay/chunk_generator.cpp | 66 ++++++++++++++- src/gameplay/player.cpp | 5 +- src/gameplay/world.cpp | 44 ++++++---- src/tools/cubed_random.cpp | 12 ++- src/tools/math_tools.cpp | 10 ++- 19 files changed, 456 insertions(+), 33 deletions(-) create mode 100644 include/Cubed/gameplay/cave_carver.hpp create mode 100644 include/Cubed/gameplay/cave_path.hpp create mode 100644 src/gameplay/cave_carver.cpp create mode 100644 src/gameplay/cave_path.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b7ab7d..a6fdcfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,8 @@ add_executable(${PROJECT_NAME} src/gameplay/builders/river_builder.cpp src/gameplay/builders/desert_builder.cpp src/gameplay/builders/forest_builder.cpp + src/gameplay/cave_carver.cpp + src/gameplay/cave_path.cpp ) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/include/Cubed/dev_panel.hpp b/include/Cubed/dev_panel.hpp index 6e0f56e..dfa484d 100644 --- a/include/Cubed/dev_panel.hpp +++ b/include/Cubed/dev_panel.hpp @@ -46,6 +46,7 @@ private: int m_theme = 0; void show_about_table_bar(); void show_biome_table_bar(); + void show_cave_table_bar(); void show_settings_tab_item(); void show_world_tab_item(); void show_player_tab_item(); diff --git a/include/Cubed/gameplay/cave_carver.hpp b/include/Cubed/gameplay/cave_carver.hpp new file mode 100644 index 0000000..c340660 --- /dev/null +++ b/include/Cubed/gameplay/cave_carver.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "Cubed/gameplay/cave_path.hpp" +namespace Cubed { +class CaveCarver { +public: + CaveCarver(); + std::unordered_map& paths(); + void init(unsigned world_seed); + void reload(unsigned world_seed); + void add_path(const glm::vec3& pos); + void try_to_add_path(const ChunkPos& pos, unsigned chunk_seed); + void cleanup_finished_caves(); + + int cave_sum() const; + float& cave_probability(); + +private: + std::unordered_map m_paths; + unsigned m_seed = 0; + int m_sum = 0; + Random m_random; + float m_cave_probability = 0.035f; +}; +} // namespace Cubed diff --git a/include/Cubed/gameplay/cave_path.hpp b/include/Cubed/gameplay/cave_path.hpp new file mode 100644 index 0000000..269c760 --- /dev/null +++ b/include/Cubed/gameplay/cave_path.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "Cubed/gameplay/chunk_pos.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); + 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 = 15.0f; + static inline float m_radius_y_min = 4.0f; + static inline float m_radius_y_max = 10.0f; + static inline float m_delta_angle_min = -5.0f; + static inline float m_delta_angle_max = 5.0f; + static inline int m_step_min = 10; + 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 \ No newline at end of file diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp index 9bd93d0..3557fea 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -38,7 +38,7 @@ private: float frequency = 0.01f; float height = 80; - + unsigned m_seed = 0; void clear_dirty(); public: @@ -98,6 +98,8 @@ public: void biome(BiomeType b); HeightMapArray& heightmap(); std::vector& blocks(); + World& world(); + unsigned seed() const; }; } // 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 0cfc93d..391a25e 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -20,7 +20,7 @@ public: static void reload(); static const unsigned& seed(); static void seed(unsigned s); - + unsigned chunk_seed() const; // Generate Biome void assign_chunk_biome(); // Adjust Biome @@ -54,7 +54,10 @@ private: std::unique_ptr m_biome_builder{nullptr}; bool is_cur_chunk_ins = false; std::array m_neighbor_biome; + 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/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index b338389..adea6a2 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -1,5 +1,6 @@ #pragma once #include "Cubed/AABB.hpp" +#include "Cubed/gameplay/cave_carver.hpp" #include "Cubed/gameplay/chunk.hpp" #include @@ -45,6 +46,7 @@ private: std::atomic m_gen_running{false}; std::atomic m_need_gen_chunk{false}; std::atomic m_is_rebuilding{false}; + std::atomic m_chunk_gen_finished{false}; std::atomic m_could_gen{true}; std::atomic m_rendering_distance{24}; std::atomic m_chunk_gen_fraction{0.0f}; @@ -53,6 +55,8 @@ private: std::vector> m_new_chunk; std::vector> m_new_chunk_queue; + CaveCarver m_cave_carcer; + void init_chunks(); void gen_chunks_internal(); @@ -106,6 +110,8 @@ public: void rendering_distance(int rendering_distance); void start_gen_thread(); void stop_gen_thread(); + + CaveCarver& cave_carcer(); }; } // namespace Cubed diff --git a/include/Cubed/tools/cubed_hash.hpp b/include/Cubed/tools/cubed_hash.hpp index b45061a..4981b8a 100644 --- a/include/Cubed/tools/cubed_hash.hpp +++ b/include/Cubed/tools/cubed_hash.hpp @@ -27,6 +27,10 @@ inline uint32_t mix_hash(int32_t a, int32_t b, uint32_t fixed_seed) { return h; } +inline uint32_t combine_32(uint32_t seed, uint32_t v) { + seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; +} } // namespace HASH } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/cubed_random.hpp b/include/Cubed/tools/cubed_random.hpp index de1b83f..8e13065 100644 --- a/include/Cubed/tools/cubed_random.hpp +++ b/include/Cubed/tools/cubed_random.hpp @@ -5,12 +5,14 @@ namespace Cubed { class Random { public: Random(); - + Random(unsigned seed); bool random_bool(double probability); std::mt19937& engine(); unsigned seed(); void init(unsigned seed); + int random_int(int min, int max); + float random_float(float min, float max); private: unsigned int m_seed = 0; diff --git a/include/Cubed/tools/math_tools.hpp b/include/Cubed/tools/math_tools.hpp index b367560..61f20d5 100644 --- a/include/Cubed/tools/math_tools.hpp +++ b/include/Cubed/tools/math_tools.hpp @@ -4,8 +4,11 @@ namespace Cubed { namespace Math { + void extract_frustum_planes(const glm::mat4& mvp_matrix, std::vector& planes); -} + +float smootherstep(float edge0, float edge1, float x); +} // namespace Math } // namespace Cubed \ No newline at end of file diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index ed6283b..0375378 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -34,6 +34,17 @@ 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 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; + static int filter_unsigned(ImGuiInputTextCallbackData* data) { if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) { char c = data->EventChar; @@ -108,7 +119,7 @@ void DevPanel::show_about_table_bar() { } void DevPanel::show_biome_table_bar() { - ImGui::Text("Biome"); + if (ImGui::BeginTabBar("Biome")) { if (ImGui::BeginTabItem("Plain")) { ImGui::SliderFloat("MinTemp##plain", &plain_params().temp.first, @@ -253,6 +264,30 @@ void DevPanel::show_biome_table_bar() { } } +void DevPanel::show_cave_table_bar() { + auto& cave_carcer = m_app.world().cave_carcer(); + + ImGui::Text("Total Cave Sum %d", cave_carcer.cave_sum()); + ImGui::SliderFloat("Cave Probability", &cave_carcer.cave_probability(), + CAVE_PROBABILITY_MIN, CAVE_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(), + RADIUS_XZ_MIN, RADIUS_XZ_MAX); + ImGui::SliderFloat("Radius Y Min", &CavePath::radius_y_min(), RADIUS_Y_MIN, + RADIUS_Y_MAX); + ImGui::SliderFloat("Radius Y Max", &CavePath::radius_y_max(), RADIUS_Y_MIN, + RADIUS_Y_MAX); + ImGui::SliderFloat("Delta Angle Min", &CavePath::delta_angle_min(), + 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); +} + void DevPanel::show_settings_tab_item() { if (ImGui::BeginTabItem("settings")) { if (ImGui::SliderFloat("FOV", &m_config.fov, 1.0f, 140.0f)) { @@ -354,7 +389,8 @@ void DevPanel::show_settings_tab_item() { void DevPanel::show_world_tab_item() { if (ImGui::BeginTabItem("world")) { if (m_text_editing.perlin_seed) { - if (ImGui::InputText("Perlin Noise Seed", perlin_noise_input_buffer, + if (ImGui::InputText("ChunkGenerator Seed", + perlin_noise_input_buffer, sizeof(perlin_noise_input_buffer), ImGuiInputTextFlags_CallbackCharFilter | ImGuiInputTextFlags_EnterReturnsTrue, @@ -397,7 +433,17 @@ void DevPanel::show_world_tab_item() { } ImGui::Text("Chunk Build Progress\n"); ImGui::ProgressBar(m_app.world().chunk_gen_fraction()); - show_biome_table_bar(); + if (ImGui::BeginTabBar("World Settings")) { + if (ImGui::BeginTabItem("Cave")) { + show_cave_table_bar(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Biome")) { + show_biome_table_bar(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } ImGui::EndTabItem(); } } diff --git a/src/gameplay/cave_carver.cpp b/src/gameplay/cave_carver.cpp new file mode 100644 index 0000000..34135d1 --- /dev/null +++ b/src/gameplay/cave_carver.cpp @@ -0,0 +1,52 @@ +#include "Cubed/gameplay/cave_carver.hpp" + +#include "Cubed/constants.hpp" + +namespace Cubed { +CaveCarver::CaveCarver() {} + +std::unordered_map& CaveCarver::paths() { return m_paths; } + +void CaveCarver::init(unsigned world_seed) { + m_seed = world_seed; + m_sum = 0; + m_random.init(m_seed); +} + +void CaveCarver::reload(unsigned world_seed) { + m_seed = world_seed; + m_paths.clear(); + init(world_seed); +} + +void CaveCarver::add_path(const glm::vec3& pos) { + m_paths.emplace(m_sum, CavePath{m_seed, m_sum, pos}); + m_sum++; +} + +void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos, + unsigned chunk_seed) { + Random random{chunk_seed}; + if (random.random_bool(static_cast(m_cave_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; + const int CHUNK_MIN_Y = 0; + const int CHUNK_MAX_Y = SIZE_Y - 1; + int max_y = std::min(CHUNK_MAX_Y, 40); + 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}); + } +} + +void CaveCarver::cleanup_finished_caves() { + std::erase_if(m_paths, + [](const auto& kv) { return kv.second.is_finished(); }); +} + +int CaveCarver::cave_sum() const { return m_sum; } +float& CaveCarver::cave_probability() { return m_cave_probability; } +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/cave_path.cpp b/src/gameplay/cave_path.cpp new file mode 100644 index 0000000..4987d31 --- /dev/null +++ b/src/gameplay/cave_path.cpp @@ -0,0 +1,95 @@ +#include "Cubed/gameplay/cave_path.hpp" + +#include "Cubed/constants.hpp" +#include "Cubed/tools/cubed_hash.hpp" +#include "Cubed/tools/math_tools.hpp" + +#include +namespace Cubed { +CavePath::CavePath(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 = m_random.random_float(-10.0f, 10.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 CavePath::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; + m_pitch = std::clamp(m_pitch, -90.0f, 90.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); + m_pitch += 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 CavePath::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 CavePath::clear_chunk(const ChunkPos& pos) { m_pending_chunks.erase(pos); } +const std::vector& CavePath::points() const { return m_points; } +bool CavePath::is_finished() const { return m_pending_chunks.empty(); } + +float& CavePath::radius_xz_min() { return m_radius_xz_min; } +float& CavePath::radius_xz_max() { return m_radius_xz_max; } +float& CavePath::radius_y_min() { return m_radius_y_min; } +float& CavePath::radius_y_max() { return m_radius_y_max; } +float& CavePath::delta_angle_min() { return m_delta_angle_min; } +float& CavePath::delta_angle_max() { return m_delta_angle_max; } +int& CavePath::step_min() { return m_step_min; } +int& CavePath::step_max() { return m_step_max; } + +} // namespace Cubed diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index 0fdfd0d..10ffd6b 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -24,7 +24,7 @@ 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_vertexs_data(std::move(other.m_vertexs_data)), m_seed(other.m_seed) { other.m_vbo = 0; } @@ -43,6 +43,7 @@ Chunk& Chunk::operator=(Chunk&& other) noexcept { m_is_on_gen_vertex_data = other.m_is_on_gen_vertex_data.load(); m_need_upload = other.m_need_upload.load(); m_vertex_sum = other.m_vertex_sum.load(); + m_seed = other.m_seed; return *this; } @@ -213,6 +214,7 @@ void Chunk::gen_phase_one() { return; } m_generator->assign_chunk_biome(); + m_seed = m_generator->chunk_seed(); } void Chunk::gen_phase_two(const std::array& adj_chunks) { @@ -255,7 +257,7 @@ void Chunk::gen_phase_six( Logger::error("ChunkGenerator is Nullptr"); return; } - m_generator->blend_surface_blocks_borders(neighbor_block); + // m_generator->blend_surface_blocks_borders(neighbor_block); } void Chunk::gen_phase_seven() { @@ -307,4 +309,11 @@ void Chunk::biome(BiomeType b) { m_biome = b; } HeightMapArray& Chunk::heightmap() { return m_heightmap; } std::vector& Chunk::blocks() { return m_blocks; } +World& Chunk::world() { return m_world; } +unsigned Chunk::seed() const { + if (m_seed == 0) { + Logger::warn("Seed Not Generator"); + } + return m_seed; +} } // namespace Cubed diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index 855207e..f8a67ad 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -7,9 +7,9 @@ #include "Cubed/gameplay/builders/river_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/perlin_noise.hpp" - namespace Cubed { using enum BiomeType; @@ -20,6 +20,7 @@ ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) { ChunkPos pos = m_chunk.get_chunk_pos(); unsigned seed = HASH::mix_hash(pos.x, pos.z, m_generator_seed); m_random.init(seed); + m_chunk_seed = seed; } void ChunkGenerator::init() { @@ -43,7 +44,12 @@ void ChunkGenerator::seed(unsigned s) { is_seed_change = true; m_generator_seed = s; } - +unsigned ChunkGenerator::chunk_seed() const { + if (m_chunk_seed == 0) { + Logger::warn("Chunk Seed Generator Fail"); + } + return m_chunk_seed; +} void ChunkGenerator::assign_chunk_biome() { auto m_chunk_pos = m_chunk.chunk_pos(); float x = static_cast(m_chunk_pos.x); @@ -356,6 +362,7 @@ 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( @@ -511,6 +518,61 @@ void ChunkGenerator::make_biome_builder() { } } +void ChunkGenerator::generate_cave() { + auto& cave_carver = m_chunk.world().cave_carcer(); + auto& paths = cave_carver.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; + for (auto& [id, path] : paths) { + for (const auto& point : path.points()) { + + 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 == 0) { + continue; + } + blocks[Chunk::get_index(x, y, z)] = 0; + } + } + } + } + } + path.clear_chunk(chunk_pos); + } +} + Chunk& ChunkGenerator::chunk() { return m_chunk; } Random& ChunkGenerator::random() { return m_random; } diff --git a/src/gameplay/player.cpp b/src/gameplay/player.cpp index ab5dd5c..483d59f 100644 --- a/src/gameplay/player.cpp +++ b/src/gameplay/player.cpp @@ -237,10 +237,7 @@ void Player::update_front_vec(float offset_x, float offset_y) { m_yaw = std::fmod(m_yaw, 360.0); - if (m_pitch > 89.0f) - m_pitch = 89.0f; - if (m_pitch < -89.0f) - m_pitch = -89.0f; + m_pitch = std::clamp(m_pitch, -89.0f, 89.0f); m_front.x = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); m_front.y = sin(glm::radians(m_pitch)); diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index e40fa2b..a0b63a3 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -7,8 +7,6 @@ #include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/math_tools.hpp" -#include - namespace Cubed { struct ChunkRenderData { @@ -65,23 +63,30 @@ Player& World::get_player(const std::string& name) { } void World::init_world() { + m_cave_carcer.init(ChunkGenerator::seed()); m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4); auto t1 = std::chrono::system_clock::now(); Logger::info("Max Support Thread is {}", std::thread::hardware_concurrency()); + // init players + m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer")); + + start_gen_thread(); init_chunks(); auto t2 = std::chrono::system_clock::now(); auto d = std::chrono::duration_cast(t2 - t1); Logger::info("Chunk Block Init Finish, Time Consuming: {}", d); - // init players - m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer")); + Logger::info("TestPlayer Create Finish"); - - start_gen_thread(); - hot_reload(); } - +void World::init_chunks() { + hot_reload(); + while (!m_chunk_gen_finished) { + std::this_thread::sleep_for(std::chrono::microseconds(200)); + } +} +/* void World::init_chunks() { int dis_x = PRE_LOAD_DISTANCE; @@ -112,11 +117,13 @@ void World::init_chunks() { } } } - for (auto& [pos, chunks] : m_chunks) { - chunks.gen_phase_one(); + for (auto& [pos, chunk] : m_chunks) { + chunk.gen_phase_one(); + m_cave_carcer.try_to_add_path(pos, chunk.seed()); } - for (auto& [pos, chunks] : temp_neighbor) { - chunks.gen_phase_one(); + for (auto& [pos, chunk] : temp_neighbor) { + chunk.gen_phase_one(); + m_cave_carcer.try_to_add_path(pos, chunk.seed()); } std::array neighbor_chunks; @@ -251,6 +258,8 @@ void World::init_chunks() { sync.store(1, std::memory_order_release); sync.load(std::memory_order_acquire); + m_cave_carcer.cleanup_finished_caves(); + std::vector pending_gen_data; pending_gen_data.reserve(m_chunks.size()); for (auto& [pos, chunk] : m_chunks) { @@ -278,7 +287,7 @@ void World::init_chunks() { chunk.upload_to_gpu(); } } - +*/ void World::render(const glm::mat4& mvp_matrix) { Math::extract_frustum_planes(mvp_matrix, m_planes); int rendered_sum = 0; @@ -325,6 +334,7 @@ ChunkPos World::chunk_pos(int world_x, int world_z) { void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.0f; + m_chunk_gen_finished = false; ChunkPosSet required_chunks; compute_required_chunks(required_chunks); @@ -356,9 +366,11 @@ void World::gen_chunks_internal() { 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()); } m_chunk_gen_fraction = 0.2f; std::array neighbor_chunks; @@ -500,7 +512,9 @@ void World::gen_chunks_internal() { m_new_chunk_queue.emplace_back(std::move(x)); } } + m_cave_carcer.cleanup_finished_caves(); m_chunk_gen_fraction = 1.0f; + m_chunk_gen_finished = true; } void World::sync_player_pos(glm::vec3& player_pos) { @@ -819,7 +833,7 @@ void World::rebuild_world() { } m_is_rebuilding = true; stop_gen_thread(); - + m_cave_carcer.reload(ChunkGenerator::seed()); { std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex); m_chunks.clear(); @@ -841,4 +855,6 @@ void World::rendering_distance(int rendering_distance) { m_rendering_distance = rendering_distance; } +CaveCarver& World::cave_carcer() { return m_cave_carcer; } + } // namespace Cubed \ No newline at end of file diff --git a/src/tools/cubed_random.cpp b/src/tools/cubed_random.cpp index ae2b017..6e94f9c 100644 --- a/src/tools/cubed_random.cpp +++ b/src/tools/cubed_random.cpp @@ -1,11 +1,9 @@ #include "Cubed/tools/cubed_random.hpp" -#include "Cubed/tools/log.hpp" - namespace Cubed { Random::Random() {} - +Random::Random(unsigned seed) { init(seed); } bool Random::random_bool(double probability) { std::bernoulli_distribution dist(probability); return dist(m_engine); @@ -19,5 +17,13 @@ void Random::init(unsigned seed) { m_seed = seed; m_engine.seed(seed); } +int Random::random_int(int min, int max) { + std::uniform_int_distribution dist(min, max); + return dist(m_engine); +} +float Random::random_float(float min, float max) { + std::uniform_real_distribution dist(min, max); + return dist(m_engine); +} } // namespace Cubed \ No newline at end of file diff --git a/src/tools/math_tools.cpp b/src/tools/math_tools.cpp index 0d9f29b..2533c44 100644 --- a/src/tools/math_tools.cpp +++ b/src/tools/math_tools.cpp @@ -1,10 +1,11 @@ #include "Cubed/tools/math_tools.hpp" +#include #include - namespace Cubed { namespace Math { + void extract_frustum_planes(const glm::mat4& mvp_matrix, std::vector& planes) { if (planes.size() != 6) { @@ -37,6 +38,13 @@ void extract_frustum_planes(const glm::mat4& mvp_matrix, } } +float smootherstep(float edge0, float edge1, float x) { + + x = std::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); + + return x * x * x * (x * (6.0f * x - 15.0f) + 10.0f); +} + } // namespace Math } // namespace Cubed \ No newline at end of file