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
This commit is contained in:
zhenyan121
2026-05-09 20:13:55 +08:00
committed by GitHub
parent d986e03f9c
commit 1a26474a05
19 changed files with 456 additions and 33 deletions

View File

@@ -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();

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Cubed/gameplay/cave_path.hpp"
namespace Cubed {
class CaveCarver {
public:
CaveCarver();
std::unordered_map<int, CavePath>& 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<int, CavePath> m_paths;
unsigned m_seed = 0;
int m_sum = 0;
Random m_random;
float m_cave_probability = 0.035f;
};
} // namespace Cubed

View File

@@ -0,0 +1,85 @@
#pragma once
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/tools/cubed_random.hpp"
#include <glm/glm.hpp>
#include <unordered_set>
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<PathPoint>& 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<PathPoint> m_points;
std::unordered_set<ChunkPos, ChunkPos::Hash> m_pending_chunks;
void collect_path_points();
void precompute_chunk_coverage();
};
} // namespace Cubed

View File

@@ -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<uint8_t>& blocks();
World& world();
unsigned seed() const;
};
} // namespace Cubed

View File

@@ -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<BiomeBuilder> m_biome_builder{nullptr};
bool is_cur_chunk_ins = false;
std::array<BiomeType, 8> m_neighbor_biome;
unsigned m_chunk_seed = 0;
void make_biome_builder();
void generate_cave();
};
} // namespace Cubed

View File

@@ -1,5 +1,6 @@
#pragma once
#include "Cubed/AABB.hpp"
#include "Cubed/gameplay/cave_carver.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include <atomic>
@@ -45,6 +46,7 @@ private:
std::atomic<bool> m_gen_running{false};
std::atomic<bool> m_need_gen_chunk{false};
std::atomic<bool> m_is_rebuilding{false};
std::atomic<bool> m_chunk_gen_finished{false};
std::atomic<bool> m_could_gen{true};
std::atomic<int> m_rendering_distance{24};
std::atomic<float> m_chunk_gen_fraction{0.0f};
@@ -53,6 +55,8 @@ private:
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk;
std::vector<std::pair<ChunkPos, Chunk>> 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

View File

@@ -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

View File

@@ -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;

View File

@@ -4,8 +4,11 @@
namespace Cubed {
namespace Math {
void extract_frustum_planes(const glm::mat4& mvp_matrix,
std::vector<glm::vec4>& planes);
}
float smootherstep(float edge0, float edge1, float x);
} // namespace Math
} // namespace Cubed