diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b95893..1f70335 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,21 @@ if (WIN32) if(TARGET freetype) add_library(Freetype::Freetype ALIAS freetype) endif() + set(_BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS ON) + + FetchContent_Declare( + onetbb + GIT_REPOSITORY https://github.com/uxlfoundation/oneTBB.git + GIT_TAG v2023.0.0 + ) + set(BUILD_TESTING OFF CACHE BOOL "Build tests" FORCE) + set(TBB_TEST OFF CACHE BOOL "Build TBB tests" FORCE) + FetchContent_MakeAvailable(onetbb) + + set(BUILD_SHARED_LIBS ${_BUILD_SHARED_LIBS_SAVED}) + unset(_BUILD_SHARED_LIBS_SAVED) + endif() FetchContent_Declare( @@ -83,7 +98,6 @@ FetchContent_MakeAvailable(tomlplusplus) add_subdirectory(third_party/imgui) - set(INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) add_executable(${PROJECT_NAME} @@ -164,10 +178,11 @@ target_link_libraries(${PROJECT_NAME} Freetype::Freetype tomlplusplus::tomlplusplus imgui + tbb ) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_link_libraries(${PROJECT_NAME} PRIVATE tbb) +# target_link_libraries(${PROJECT_NAME} PRIVATE tbb) endif() if (UNIX AND NOT APPLE) @@ -187,3 +202,19 @@ if (UNIX AND NOT APPLE) target_compile_options(${PROJECT_NAME} PRIVATE ${EGL_CFLAGS_OTHER} ${Wayland_CFLAGS_OTHER}) endif() + +if (WIN32) + foreach(TBB_LIB IN ITEMS tbb tbbmalloc tbbmalloc_proxy) + if(TARGET ${TBB_LIB}) + add_custom_command( + TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + COMMENT "Copying ${TBB_LIB}.dll" + ) + else() + message(STATUS "Target ${TBB_LIB} not found, skipping copy") + endif() + endforeach() +endif() \ No newline at end of file diff --git a/include/Cubed/constants.hpp b/include/Cubed/constants.hpp index fc741b7..ccfd053 100644 --- a/include/Cubed/constants.hpp +++ b/include/Cubed/constants.hpp @@ -6,7 +6,7 @@ namespace Cubed { constexpr int WORLD_SIZE_Y = 256; constexpr int CHUNK_SIZE = 16; -constexpr int SEA_LEVEL = 64; +constexpr int SEA_LEVEL = 63; constexpr int MAX_UI_NUM = 1; constexpr int MAX_BLOCK_STATUS = 1; diff --git a/include/Cubed/gameplay/cave_carver.hpp b/include/Cubed/gameplay/cave_carver.hpp index 76b397e..cbfea66 100644 --- a/include/Cubed/gameplay/cave_carver.hpp +++ b/include/Cubed/gameplay/cave_carver.hpp @@ -1,10 +1,14 @@ #pragma once #include "Cubed/gameplay/cave_path.hpp" + +#include namespace Cubed { class CaveCarver { + using CaveHashMap = tbb::concurrent_hash_map; + public: CaveCarver(); - std::unordered_map& paths(); + CaveHashMap& paths(); void init(unsigned world_seed); void reload(unsigned world_seed); void add_path(const glm::vec3& pos, unsigned chunk_seed); @@ -15,9 +19,8 @@ public: float& cave_probability(); private: - std::unordered_map m_paths; + CaveHashMap m_paths; unsigned m_seed = 0; - int m_sum = 0; Random m_random; float m_cave_probability = 0.035f; }; diff --git a/include/Cubed/gameplay/cave_path.hpp b/include/Cubed/gameplay/cave_path.hpp index 85ad01a..60a32dd 100644 --- a/include/Cubed/gameplay/cave_path.hpp +++ b/include/Cubed/gameplay/cave_path.hpp @@ -5,12 +5,16 @@ #include "Cubed/tools/cubed_random.hpp" #include -#include +#include namespace Cubed { class CavePath { + using ChunkPosSet = + tbb::concurrent_hash_map; + public: - CavePath(unsigned int world_seed, int path_id, const glm::vec3& start_pos); + CavePath(unsigned int chunk_seed, unsigned world_seed, + const glm::vec3& start_pos); const std::vector& points() const; void clear_chunk(const ChunkPos& pos); bool is_finished() const; @@ -34,7 +38,6 @@ private: 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; @@ -44,7 +47,7 @@ private: Random m_random; std::vector m_points; - std::unordered_set m_pending_chunks; + ChunkPosSet m_pending_chunks; void collect_path_points(); void precompute_chunk_coverage(); }; diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp index ef43c44..9efcbf3 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -14,6 +14,8 @@ class World; // if want to use, do init_chunk(), gen_vertex_data() and class Chunk { private: + using OptionalBlockVectorArray = + std::array>, 4>; static constexpr int SIZE_X = CHUNK_SIZE; static constexpr int SIZE_Y = WORLD_SIZE_Y; static constexpr int SIZE_Z = CHUNK_SIZE; @@ -46,8 +48,7 @@ private: BiomeConditions m_conditions; void clear_dirty(); - void gen_vertices( - const std::array*, 4>& neighbor_block); + void gen_vertices(const OptionalBlockVectorArray& neighbor_block); void gen_cross_plane_vertices(int world_x, int world_y, int world_z, BlockType id); @@ -97,8 +98,7 @@ public: // 1 : (-1, 0) // 2 : (0, 1) // 3 : (0, -1) - void gen_vertex_data( - const std::array*, 4>& neighbor_block); + void gen_vertex_data(const OptionalBlockVectorArray& neighbor_block); void upload_to_gpu(); GLuint get_normal_vao() const; diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index 2cf5d04..54ccd57 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -4,6 +4,7 @@ #include "Cubed/gameplay/biome.hpp" #include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/builders/biome_builder.hpp" +#include "Cubed/gameplay/path_point.hpp" #include "Cubed/tools/cubed_random.hpp" #include @@ -61,6 +62,9 @@ private: unsigned m_chunk_seed = 0; void make_biome_builder(); + void + carve_worm(const std::vector& points, const ChunkPos& chunk_pos, + std::function on_hit); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/chunk_pos.hpp b/include/Cubed/gameplay/chunk_pos.hpp index f1db40e..b89026f 100644 --- a/include/Cubed/gameplay/chunk_pos.hpp +++ b/include/Cubed/gameplay/chunk_pos.hpp @@ -16,7 +16,14 @@ struct ChunkPos { return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); } }; - + struct TBBHash { + std::size_t hash(const ChunkPos& p) const { + return ChunkPos::Hash{}(p); + } + bool equal(const ChunkPos& a, const ChunkPos& b) const { + return a == b; + } + }; ChunkPos operator+(const ChunkPos& pos) const { return ChunkPos{x + pos.x, z + pos.z}; } diff --git a/include/Cubed/gameplay/river.path.hpp b/include/Cubed/gameplay/river.path.hpp index 89837eb..cd3c215 100644 --- a/include/Cubed/gameplay/river.path.hpp +++ b/include/Cubed/gameplay/river.path.hpp @@ -5,13 +5,16 @@ #include "Cubed/tools/cubed_random.hpp" #include -#include +#include namespace Cubed { class RiverPath { + using ChunkPosSet = + tbb::concurrent_hash_map; public: - RiverPath(unsigned int world_seed, int path_id, const glm::vec3& start_pos); + RiverPath(unsigned int chunk_seed, unsigned world_seed, + const glm::vec3& start_pos); const std::vector& points() const; void clear_chunk(const ChunkPos& pos); bool is_finished() const; @@ -32,12 +35,12 @@ private: 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_min = 200; static inline int m_step_max = 400; - int m_path_id = 0; unsigned int m_seed = 0; float m_yaw = 0.0f; + float m_initial_yaw = 0.0f; float m_pitch = 0.0f; int m_step = 0; float m_step_len = 1.0f; @@ -45,7 +48,7 @@ private: Random m_random; std::vector m_points; - std::unordered_set m_pending_chunks; + ChunkPosSet m_pending_chunks; void collect_path_points(); void precompute_chunk_coverage(); }; diff --git a/include/Cubed/gameplay/river_worm.hpp b/include/Cubed/gameplay/river_worm.hpp index 281cc32..c2c4fa2 100644 --- a/include/Cubed/gameplay/river_worm.hpp +++ b/include/Cubed/gameplay/river_worm.hpp @@ -4,13 +4,15 @@ #include "Cubed/tools/cubed_random.hpp" #include -#include +#include namespace Cubed { class RiverWorm { + using RiverHashMap = tbb::concurrent_hash_map; + public: RiverWorm(); - std::unordered_map& paths(); + RiverHashMap& paths(); void init(unsigned world_seed); void reload(unsigned world_seed); void add_path(const glm::vec3& pos, unsigned chunk_seed); @@ -21,9 +23,8 @@ public: float& river_probability(); private: - std::unordered_map m_paths; + RiverHashMap m_paths; unsigned m_seed = 0; - int m_sum = 0; Random m_random; float m_probability = 0.01f; }; diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index c6ed49f..fec1ad5 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -31,8 +31,10 @@ class Player; class TextureManager; class World { private: + using OptionalBlockVectorArray = + std::array>, 4>; using ChunkPtrUpdateList = std::vector>; - using ChunkUpdateList = std::vector>; + using ChunkPairVector = std::vector>; using ConstChunkMap = std::unordered_map; using ChunkPosSet = std::unordered_set; @@ -72,14 +74,14 @@ private: void sync_player_pos(glm::vec3& player_pos); void compute_required_chunks(ChunkPosSet& required_chunks, - ChunkHashMap& temp_neighbor, + ChunkPairVector& temp_neighbor, std::vector& need_gen_temp_chunks_pos); 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); + const ChunkPairVector& new_chunks); void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&, ConstChunkMap&); diff --git a/include/Cubed/tools/cubed_hash.hpp b/include/Cubed/tools/cubed_hash.hpp index 4981b8a..c7ef96c 100644 --- a/include/Cubed/tools/cubed_hash.hpp +++ b/include/Cubed/tools/cubed_hash.hpp @@ -7,7 +7,17 @@ namespace HASH { inline std::size_t str(std::string_view value) { return std::hash{}(value); } -inline uint32_t mix_hash(int32_t a, int32_t b, uint32_t fixed_seed) { +inline uint32_t combine_32(uint32_t seed, uint32_t v) { + seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; +} +inline uint32_t chunk_seed_hash(int32_t a, int32_t b, uint32_t fixed_seed) { + uint32_t seed = + combine_32(combine_32(fixed_seed, (uint32_t)a), (uint32_t)b); + return seed; +} +/* +inline uint32_t chunk_seed_hash(int32_t a, int32_t b, uint32_t fixed_seed) { uint32_t h = fixed_seed; h ^= (uint32_t)a * 0xcc9e2d51u; @@ -27,10 +37,8 @@ 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/src/gameplay/cave_carver.cpp b/src/gameplay/cave_carver.cpp index 83052e6..2170b2f 100644 --- a/src/gameplay/cave_carver.cpp +++ b/src/gameplay/cave_carver.cpp @@ -5,11 +5,10 @@ namespace Cubed { CaveCarver::CaveCarver() {} -std::unordered_map& CaveCarver::paths() { return m_paths; } +CaveCarver::CaveHashMap& CaveCarver::paths() { return m_paths; } void CaveCarver::init(unsigned world_seed) { m_seed = world_seed; - m_sum = 0; m_random.init(m_seed); } @@ -20,16 +19,18 @@ void CaveCarver::reload(unsigned world_seed) { } void CaveCarver::add_path(const glm::vec3& pos, unsigned chunk_seed) { - m_paths.emplace(chunk_seed, CavePath{m_seed, m_sum, pos}); - m_sum++; + m_paths.emplace(chunk_seed, CavePath{chunk_seed, m_seed, pos}); } 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; + { + CaveHashMap::const_accessor acc; + if (m_paths.find(acc, chunk_seed)) { + return; + } } + Random random{chunk_seed}; if (random.random_bool(static_cast(m_cave_probability))) { const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE; @@ -47,10 +48,17 @@ void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos, } void CaveCarver::cleanup_finished_caves() { - std::erase_if(m_paths, - [](const auto& kv) { return kv.second.is_finished(); }); + std::vector finished_keys; + for (const auto& pair : m_paths) { + if (pair.second.is_finished()) { + finished_keys.push_back(pair.first); + } + } + for (const auto& key : finished_keys) { + m_paths.erase(key); + } } -int CaveCarver::cave_sum() const { return m_sum; } +int CaveCarver::cave_sum() const { return m_paths.size(); } 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 index 4987d31..5792f9a 100644 --- a/src/gameplay/cave_path.cpp +++ b/src/gameplay/cave_path.cpp @@ -6,10 +6,9 @@ #include namespace Cubed { -CavePath::CavePath(unsigned int world_seed, int path_id, +CavePath::CavePath(unsigned int chunk_seed, unsigned world_seed, const glm::vec3& start_pos) { - m_path_id = path_id; - m_seed = HASH::combine_32(world_seed, path_id); + m_seed = HASH::combine_32(chunk_seed, world_seed); 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); @@ -75,7 +74,8 @@ void CavePath::precompute_chunk_coverage() { for (int cx = min_cx; cx <= max_cx; ++cx) for (int cz = min_cz; cz <= max_cz; ++cz) - m_pending_chunks.insert({cx, cz}); + m_pending_chunks.insert( + std::make_pair(ChunkPos{cx, cz}, false)); } } diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index 60df97f..2fc9ae9 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -103,8 +103,7 @@ 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) { +void Chunk::gen_vertex_data(const OptionalBlockVectorArray& neighbor_block) { if (m_is_on_gen_vertex_data) { return; } @@ -265,8 +264,7 @@ unsigned Chunk::seed() const { BiomeConditions& Chunk::conditions() { return m_conditions; } -void Chunk::gen_vertices( - const std::array*, 4>& neighbor_block) { +void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) { static const glm::ivec3 DIR[6] = {{0, 0, 1}, {1, 0, 0}, {0, 0, -1}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}}; @@ -299,8 +297,9 @@ void Chunk::gen_vertices( World::chunk_pos(world_nx, world_nz); auto is_culled = - [&](const std::vector* chunk_blocks) { - if (chunk_blocks == nullptr) { + [&](const std::optional>& + chunk_blocks) { + if (chunk_blocks == std::nullopt) { return true; } int x, y, z; diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index c07b471..76f58a1 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -22,7 +22,7 @@ constexpr int BLEND_RADIUS = 8; ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) { ASSERT_MSG(is_init, "ChunksGenerator is not init"); ChunkPos pos = m_chunk.get_chunk_pos(); - unsigned seed = HASH::mix_hash(pos.x, pos.z, m_generator_seed); + unsigned seed = HASH::chunk_seed_hash(pos.x, pos.z, m_generator_seed); m_random.init(seed); m_chunk_seed = seed; } @@ -642,69 +642,98 @@ void ChunkGenerator::make_biome_builder() { void ChunkGenerator::ocean_build() { m_biome_builder->ocean_water_build(); } -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(); +void ChunkGenerator::carve_worm( + const std::vector& points, const ChunkPos& chunk_pos, + std::function on_hit) { 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 (const auto& point : points) { - for (auto& [id, path] : paths) { - for (const auto& point : path.points()) { - if ((m_chunk.biome() == BiomeType::RIVER) || - (m_chunk.biome() == BiomeType::OCEAN)) { - path.clear_chunk(chunk_pos); + const glm::vec3& center = point.pos; + float rad_xz = point.rad_xz; + float rad_y = point.rad_y; + + if (center.x + rad_xz < CHUNK_MIN_X || + center.x - rad_xz > CHUNK_MAX_X || + center.z + rad_xz < CHUNK_MIN_Z || + center.z - rad_xz > CHUNK_MAX_Z || center.y + rad_y < CHUNK_MIN_Y || + center.y - rad_y > CHUNK_MAX_Y) { + continue; + } + + 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); + + glm::vec3 right_raw = + glm::cross(point.tangent, glm::vec3(0.0f, 1.0f, 0.0f)); + if (glm::dot(right_raw, right_raw) < 1e-6f) + right_raw = glm::cross(point.tangent, glm::vec3(1.0f, 0.0f, 0.0f)); + glm::vec3 right = glm::normalize(right_raw); + glm::vec3 up = glm::normalize(glm::cross(point.tangent, right)); + + float inv_a2 = 1.0f / (point.rad_xz * point.rad_xz); + float inv_b2 = 1.0f / (point.rad_y * point.rad_y); + + for (int wy = min_y; wy <= max_y; ++wy) { + if (wy == 0) continue; - } - const glm::vec3& center = point.pos; - float rad_xz = point.rad_xz; - float rad_y = point.rad_y; + float dy = static_cast(wy) - point.pos.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); + float vy_contrib = dy * up.y; + float vy2 = vy_contrib * vy_contrib * inv_b2; + if (vy2 >= 1.0f) + continue; for (int wx = min_x; wx <= max_x; ++wx) { - int x = wx - CHUNK_MIN_X; + float dx = static_cast(wx) - point.pos.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; - } - if (blocks[Chunk::index(x, y, z)] == 7) { - continue; - } - if (y < WORLD_SIZE_Y - 1 && - blocks[Chunk::index(x, y + 1, z)] == 7) { - continue; - } - blocks[Chunk::index(x, y, z)] = 0; - } - } + float dz = static_cast(wz) - point.pos.z; + glm::vec3 to_point(dx, dy, dz); + + float h = glm::dot(to_point, right); + float v = glm::dot(to_point, up); + + if (h * h * inv_a2 + v * v * inv_b2 > 1.0f) + continue; + int x = wx - CHUNK_MIN_X; + on_hit(x, wy, wz - CHUNK_MIN_Z); } } } + } +} + +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(); + + for (auto& [id, path] : paths) { + + carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void { + int idx = Chunk::index(x, y, z); + if (blocks[idx] == 7) + return; + if (y < WORLD_SIZE_Y - 1 && blocks[Chunk::index(x, y + 1, z)] == 7) + return; + blocks[idx] = 0; + }); path.clear_chunk(chunk_pos); } } @@ -715,64 +744,27 @@ void ChunkGenerator::generate_river() { 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) || - (m_chunk.biome() == BiomeType::OCEAN)) { - 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; - } - } - } - } + if ((m_chunk.biome() == BiomeType::DESERT) || + (m_chunk.biome() == BiomeType::OCEAN)) { + path.clear_chunk(chunk_pos); + continue; } + carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void { + int idx = Chunk::index(x, y, z); + if (y > SEA_LEVEL) { + blocks[idx] = 0; + return; + } + is_river = true; + if (blocks[idx] == 0) { + return; + } + blocks[idx] = 7; + }); path.clear_chunk(chunk_pos); } if (is_river) { diff --git a/src/gameplay/river_path.cpp b/src/gameplay/river_path.cpp index 71d12a7..1d797e3 100644 --- a/src/gameplay/river_path.cpp +++ b/src/gameplay/river_path.cpp @@ -5,12 +5,15 @@ #include namespace Cubed { -RiverPath::RiverPath(unsigned int world_seed, int path_id, +RiverPath::RiverPath(unsigned int chunk_seed, unsigned world_seed, const glm::vec3& start_pos) { - m_path_id = path_id; - m_seed = HASH::combine_32(world_seed, path_id); + + m_seed = HASH::combine_32(chunk_seed, world_seed); m_random.init(m_seed); + m_yaw = m_random.random_float(0.0f, 360.0f); + + m_initial_yaw = m_yaw; m_pitch = 0.0f; m_start_path_point.pos = start_pos; m_start_path_point.rad_xz = @@ -41,14 +44,15 @@ void RiverPath::collect_path_points() { 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); + float drad_xz = m_start_path_point.rad_xz * t; + float drad_y = m_start_path_point.rad_y * 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_yaw = std::clamp(m_yaw, m_initial_yaw - 10.0f, m_initial_yaw + 10.0f); } auto n = m_points.size(); if (n >= 2) { @@ -72,7 +76,8 @@ void RiverPath::precompute_chunk_coverage() { for (int cx = min_cx; cx <= max_cx; ++cx) for (int cz = min_cz; cz <= max_cz; ++cz) - m_pending_chunks.insert({cx, cz}); + m_pending_chunks.insert( + std::make_pair(ChunkPos{cx, cz}, false)); } } diff --git a/src/gameplay/river_worm.cpp b/src/gameplay/river_worm.cpp index 0d98830..41c4ca4 100644 --- a/src/gameplay/river_worm.cpp +++ b/src/gameplay/river_worm.cpp @@ -5,11 +5,11 @@ namespace Cubed { RiverWorm::RiverWorm() {} -std::unordered_map& RiverWorm::paths() { return m_paths; } +RiverWorm::RiverHashMap& RiverWorm::paths() { return m_paths; } void RiverWorm::init(unsigned world_seed) { m_seed = world_seed; - m_sum = 0; + m_random.init(m_seed); } @@ -20,15 +20,16 @@ void RiverWorm::reload(unsigned 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++; + m_paths.emplace(chunk_seed, RiverPath{chunk_seed, m_seed, pos}); } 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; + { + RiverHashMap::const_accessor acc; + if (m_paths.find(acc, chunk_seed)) { + return; + } } Random random{chunk_seed}; if (random.random_bool(static_cast(m_probability))) { @@ -44,10 +45,17 @@ void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos, } void RiverWorm::cleanup_finished_rivers() { - std::erase_if(m_paths, - [](const auto& kv) { return kv.second.is_finished(); }); + std::vector finished_keys; + for (const auto& pair : m_paths) { + if (pair.second.is_finished()) { + finished_keys.push_back(pair.first); + } + } + for (const auto& key : finished_keys) { + m_paths.erase(key); + } } -int RiverWorm::river_sum() const { return m_sum; } +int RiverWorm::river_sum() const { return m_paths.size(); } 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 c2c7c37..21b337e 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -5,6 +5,8 @@ #include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/cubed_hash.hpp" +#include + namespace Cubed { struct ChunkRenderData { @@ -89,6 +91,7 @@ void World::init_world() { void World::init_chunks() { hot_reload(); while (!m_chunk_gen_finished) { + // Logger::info("World Spawn: {:.2f}%", m_chunk_gen_fraction.load()); std::this_thread::sleep_for(std::chrono::microseconds(200)); } } @@ -319,7 +322,7 @@ void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.0f; m_chunk_gen_finished = false; ChunkPosSet required_chunks; - ChunkHashMap temp_neighbor; + ChunkPairVector temp_neighbor; std::vector need_gen_temp_chunks_pos; compute_required_chunks(required_chunks, temp_neighbor, need_gen_temp_chunks_pos); @@ -340,7 +343,7 @@ void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.1f; - ChunkUpdateList new_chunks; + ChunkPairVector new_chunks; ChunkHashMap new_temp_chunks; for (auto& pos : need_gen_chunks_pos) { new_chunks.push_back({pos, Chunk(*this, pos)}); @@ -357,20 +360,29 @@ void World::gen_chunks_internal() { // 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()); - } - for (auto& [pos, chunk] : new_temp_chunks) { - chunk.gen_phase_one(); - } + std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(), + [this](std::pair& new_chunk) { + auto& [pos, chunk] = new_chunk; + chunk.gen_phase_one(); + m_cave_carcer.try_to_add_path(pos, chunk.seed()); + m_river_worm.try_to_add_path(pos, chunk.seed()); + }); + + std::for_each(new_temp_chunks.begin(), new_temp_chunks.end(), + [](std::pair& new_chunk) { + auto& [pos, chunk] = new_chunk; + chunk.gen_phase_one(); + }); // 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()); - } + std::for_each(std::execution::par, temp_neighbor.begin(), + temp_neighbor.end(), + [this](std::pair& new_chunk) { + auto& [pos, chunk] = new_chunk; + chunk.gen_phase_one(); + m_cave_carcer.try_to_add_path(pos, chunk.seed()); + m_river_worm.try_to_add_path(pos, chunk.seed()); + }); + m_chunk_gen_fraction = 0.2f; /* @@ -407,9 +419,12 @@ void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.3f; - for (auto& [pos, chunks] : new_chunks) { - chunks.gen_phase_three(); - } + std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(), + [](std::pair& pair) { + auto& [pos, chunks] = pair; + chunks.gen_phase_three(); + }); + for (auto& [pos, chunk] : new_temp_chunks) { chunk.gen_phase_three(); } @@ -417,8 +432,6 @@ void World::gen_chunks_internal() { // chunks.gen_phase_three(); // } - m_chunk_gen_fraction = 0.4f; - /* for (int i = 0; i < 4; i++) { for (auto& [pos, chunks] : temp_neighbor) { @@ -462,22 +475,27 @@ void World::gen_chunks_internal() { } } */ + m_chunk_gen_fraction = 0.4f; - m_chunk_gen_fraction = 0.5f; for (auto& [pos, chunks] : new_chunks) { chunks.gen_phase_five(); } + m_chunk_gen_fraction = 0.45f; for (auto& [pos, chunk] : new_temp_chunks) { chunk.gen_phase_five(); } + m_chunk_gen_fraction = 0.5f; /* for (auto& [pos, chunks] : temp_neighbor) { chunks.gen_phase_five(); } */ - std::array>, 4> neighbor_blocks_data; - for (auto& [pos, chunks] : new_chunks) { + std::vector> + new_chunks_surface_blend_data(new_chunks.size()); + for (size_t idx = 0; idx < new_chunks.size(); idx++) { + auto& [pos, chunk] = new_chunks[idx]; + new_chunks_surface_blend_data[idx].first = &chunk; { // std::lock_guard lk(m_chunks_mutex); for (int i = 0; i < 4; i++) { @@ -486,53 +504,77 @@ void World::gen_chunks_internal() { if (it == new_chunks_neighbor.end()) { auto it = new_temp_chunks.find(neighbor_pos); if (it == new_temp_chunks.end()) { - neighbor_blocks_data[i] = std::nullopt; + new_chunks_surface_blend_data[idx].second[i] = + std::nullopt; Logger::warn( "Can't find neighbor for chunk surface blend"); continue; } - neighbor_blocks_data[i] = it->second.get_chunk_blocks(); + new_chunks_surface_blend_data[idx].second[i] = + it->second.get_chunk_blocks(); continue; } - neighbor_blocks_data[i] = it->second->get_chunk_blocks(); + new_chunks_surface_blend_data[idx].second[i] = + it->second->get_chunk_blocks(); } } - chunks.gen_phase_six(neighbor_blocks_data); } - for (auto& [pos, chunks] : new_chunks) { - chunks.gen_phase_seven(); - } + std::for_each( + std::execution::par, new_chunks_surface_blend_data.begin(), + new_chunks_surface_blend_data.end(), + [](std::pair& new_chunk_data) { + auto& [chunk, neighbor_data] = new_chunk_data; + chunk->gen_phase_six(neighbor_data); + }); + + m_chunk_gen_fraction = 0.55f; + std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(), + [](std::pair& new_chunk) { + auto& [pos, chunk] = new_chunk; + chunk.gen_phase_seven(); + }); m_chunk_gen_fraction = 0.6f; - std::array*, 4> neighbor_block; - for (auto& [pos, chunk] : new_chunks) { + std::vector> + new_chunk_vertices_data(new_chunks.size()); + for (size_t idx = 0; idx < new_chunks.size(); idx++) { + auto& [pos, chunk] = new_chunks[idx]; + new_chunk_vertices_data[idx].first = &chunk; for (int i = 0; i < 4; i++) { auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); if (it != new_chunks_neighbor.end()) { - neighbor_block[i] = &(it->second->get_chunk_blocks()); + new_chunk_vertices_data[idx].second[i] = + (it->second->get_chunk_blocks()); } else { - neighbor_block[i] = nullptr; + new_chunk_vertices_data[idx].second[i] = std::nullopt; } } - chunk.gen_vertex_data(neighbor_block); } + std::for_each( + std::execution::par, new_chunk_vertices_data.begin(), + new_chunk_vertices_data.end(), + [](std::pair& new_chunk_data) { + auto& [chunk, neighbor_data] = new_chunk_data; + chunk->gen_vertex_data(neighbor_data); + }); + m_chunk_gen_fraction = 0.7f; build_neighbor_context_for_affected_neighbors(affected_neighbor, new_chunks_neighbor); m_chunk_gen_fraction = 0.8f; - + OptionalBlockVectorArray neighbor_block; for (auto& [pos, chunk] : affected_neighbor) { for (int i = 0; i < 4; i++) { auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); if (it != new_chunks_neighbor.end()) { - neighbor_block[i] = &(it->second->get_chunk_blocks()); + neighbor_block[i] = (it->second->get_chunk_blocks()); } else { - neighbor_block[i] = nullptr; + neighbor_block[i] = std::nullopt; } } chunk->gen_vertex_data(neighbor_block); @@ -559,7 +601,7 @@ void World::sync_player_pos(glm::vec3& player_pos) { } void World::compute_required_chunks( - ChunkPosSet& required_chunks, ChunkHashMap& temp_neighbor, + ChunkPosSet& required_chunks, ChunkPairVector& temp_neighbor, std::vector& need_gen_temp_chunks_pos) { glm::vec3 player_pos; sync_player_pos(player_pos); @@ -567,33 +609,44 @@ void World::compute_required_chunks( int x = std::floor(player_pos.x); int z = std::floor(player_pos.z); auto [chunk_x, chunk_z] = chunk_pos(x, z); + int radius = m_rendering_distance; + int r2 = radius * radius; + required_chunks.reserve(radius * radius); - required_chunks.reserve(m_rendering_distance * m_rendering_distance); - int half = m_rendering_distance / 2; - for (int u = chunk_x - half; u <= chunk_x + half; ++u) { - for (int v = chunk_z - half; v <= chunk_z + half; ++v) { - required_chunks.emplace(u, v); + for (int dx = -radius; dx <= radius; ++dx) { + for (int dz = -radius; dz <= radius; ++dz) { + if (dx * dx + dz * dz <= r2) { + required_chunks.emplace(chunk_x + dx, chunk_z + dz); + } } } - int new_half = half + 1; - for (int u = chunk_x - new_half; u <= chunk_x + new_half; ++u) { - for (int v = chunk_z - new_half; v <= chunk_z + new_half; ++v) { - auto it = required_chunks.find({u, v}); - if (it == required_chunks.end()) { - need_gen_temp_chunks_pos.push_back({u, v}); + int new_radius = radius + 1; + int new_r2 = new_radius * new_radius; + for (int dx = -new_radius; dx <= new_radius; ++dx) { + for (int dz = -new_radius; dz <= new_radius; ++dz) { + if (dx * dx + dz * dz <= new_r2) { + int nx = chunk_x + dx; + int nz = chunk_z + dz; + auto it = required_chunks.find({nx, nz}); + if (it == required_chunks.end()) { + need_gen_temp_chunks_pos.push_back({nx, nz}); + } } } } 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; + radius = max_path_len / 2; + r2 = radius * radius; + for (int dx = -radius; dx <= radius; ++dx) { + for (int dz = -radius; dz <= radius; ++dz) { + if (dx * dx + dz * dz <= r2) { + ChunkPos pos{chunk_x + dx, chunk_z + dz}; + auto it = required_chunks.find(pos); + if (it != required_chunks.end()) { + continue; + } + temp_neighbor.emplace_back(pos, Chunk(*this, pos)); } - temp_neighbor.emplace(pos, Chunk(*this, pos)); } } } @@ -620,7 +673,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) { + const ChunkPairVector& new_chunks) { { std::lock_guard lk(m_chunks_mutex); for (auto& [pos, chunk] : new_chunks) { @@ -865,13 +918,13 @@ 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; + OptionalBlockVectorArray neighbor_block; for (int i = 0; i < 4; i++) { auto it = m_chunks.find(pos + CHUNK_DIR[i]); if (it != m_chunks.end()) { - neighbor_block[i] = &(it->second.get_chunk_blocks()); + neighbor_block[i] = (it->second.get_chunk_blocks()); } else { - neighbor_block[i] = nullptr; + neighbor_block[i] = std::nullopt; } } chunk.gen_vertex_data(neighbor_block); diff --git a/src/tools/cubed_random.cpp b/src/tools/cubed_random.cpp index 6e94f9c..701a521 100644 --- a/src/tools/cubed_random.cpp +++ b/src/tools/cubed_random.cpp @@ -5,8 +5,15 @@ 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); + if (probability <= 0.0) + return false; + if (probability >= 1.0) + return true; + + const double MAX_VAL = 4294967295.0; + unsigned threshold = static_cast(probability * MAX_VAL); + + return m_engine() <= threshold; } std::mt19937& Random::engine() { return m_engine; } @@ -18,12 +25,23 @@ void Random::init(unsigned seed) { m_engine.seed(seed); } int Random::random_int(int min, int max) { - std::uniform_int_distribution dist(min, max); - return dist(m_engine); + unsigned range = static_cast(max - min) + 1; + + const unsigned LIMIT = + (std::numeric_limits::max() / range) * range; + unsigned r; + do { + r = m_engine(); + } while (r >= LIMIT); + + return min + static_cast(r % range); } float Random::random_float(float min, float max) { - std::uniform_real_distribution dist(min, max); - return dist(m_engine); -} + unsigned r = m_engine() >> 8; + float t = static_cast(r) * (1.0f / 16777216.0f); + float result = min + t * (max - min); + + return result; +} } // namespace Cubed \ No newline at end of file