diff options
Diffstat (limited to 'game')
| -rw-r--r-- | game/server/overworld.cc | 361 | ||||
| -rw-r--r-- | game/server/overworld.hh | 30 | ||||
| -rw-r--r-- | game/shared/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | game/shared/chunk.cc | 11 | ||||
| -rw-r--r-- | game/shared/chunk.hh | 6 | ||||
| -rw-r--r-- | game/shared/dimension.cc | 22 | ||||
| -rw-r--r-- | game/shared/dimension.hh | 6 | ||||
| -rw-r--r-- | game/shared/feature.cc | 51 | ||||
| -rw-r--r-- | game/shared/feature.hh | 18 | ||||
| -rw-r--r-- | game/shared/types.hh | 10 |
10 files changed, 352 insertions, 165 deletions
diff --git a/game/server/overworld.cc b/game/server/overworld.cc index 87d8dd6..e66addf 100644 --- a/game/server/overworld.cc +++ b/game/server/overworld.cc @@ -5,57 +5,107 @@ #include "shared/game_voxels.hh" #include "shared/voxel_storage.hh" -Overworld::Overworld(const char *name) : Dimension(name, -30.0f) +// FIXME: load these from a file +static void compute_tree_feature(unsigned int height, Feature &feature, voxel_id log_voxel, voxel_id leaves_voxel) { + // Ensure the tree height is too small + height = cxpr::max<unsigned int>(height, 4U); + + // Generate tree stem + for(unsigned int i = 0; i < height; ++i) { + feature.push_back({ voxel_pos(0, i, 0), log_voxel }); + } + + auto leaves_start = height - 3U; + auto leaves_thick_end = height - 2U; + auto leaves_thin_end = height - 1U; + + // Generate the thin 3x3 layer of leaves that + // starts from leaves_start and ends at leaves_thin_end + for(unsigned int i = leaves_start; i <= leaves_thin_end; ++i) { + feature.push_back({ local_pos(-1, i, -1), leaves_voxel }); + feature.push_back({ local_pos(-1, i, +0), leaves_voxel }); + feature.push_back({ local_pos(-1, i, +1), leaves_voxel }); + feature.push_back({ local_pos(+0, i, -1), leaves_voxel }); + feature.push_back({ local_pos(+0, i, +1), leaves_voxel }); + feature.push_back({ local_pos(+1, i, -1), leaves_voxel }); + feature.push_back({ local_pos(+1, i, +0), leaves_voxel }); + feature.push_back({ local_pos(+1, i, +1), leaves_voxel }); + } + + // Generate the tree cap; a 3x3 patch of leaves + // that is slapped right on top of the thin 3x3 layer + feature.push_back({ local_pos(-1, height, +0), leaves_voxel }); + feature.push_back({ local_pos(+0, height, -1), leaves_voxel }); + feature.push_back({ local_pos(+0, height, +0), leaves_voxel }); + feature.push_back({ local_pos(+0, height, +1), leaves_voxel }); + feature.push_back({ local_pos(+1, height, +0), leaves_voxel }); + + // Generate the thin 5x5 layer of leaves that + // starts from leaves_start and ends at leaves_thin_end + for(unsigned int i = leaves_start; i <= leaves_thick_end; ++i) { + feature.push_back({ local_pos(-1, i, -2), leaves_voxel }); + feature.push_back({ local_pos(-1, i, +2), leaves_voxel }); + feature.push_back({ local_pos(-2, i, -1), leaves_voxel }); + feature.push_back({ local_pos(-2, i, -2), leaves_voxel }); + feature.push_back({ local_pos(-2, i, +0), leaves_voxel }); + feature.push_back({ local_pos(-2, i, +1), leaves_voxel }); + feature.push_back({ local_pos(-2, i, +2), leaves_voxel }); + feature.push_back({ local_pos(+0, i, -2), leaves_voxel }); + feature.push_back({ local_pos(+0, i, +2), leaves_voxel }); + feature.push_back({ local_pos(+1, i, -2), leaves_voxel }); + feature.push_back({ local_pos(+1, i, +2), leaves_voxel }); + feature.push_back({ local_pos(+2, i, -1), leaves_voxel }); + feature.push_back({ local_pos(+2, i, -2), leaves_voxel }); + feature.push_back({ local_pos(+2, i, +0), leaves_voxel }); + feature.push_back({ local_pos(+2, i, +1), leaves_voxel }); + feature.push_back({ local_pos(+2, i, +2), leaves_voxel }); + } +} +Overworld::Overworld(const char *name) : Dimension(name, -30.0f) +{ + m_bottommost_chunk.set_limits(-64, -4); + m_terrain_variation.set_limits(16, 256); + compute_tree_feature(5U, m_feat_tree, game_voxels::oak_log, game_voxels::oak_leaves); } void Overworld::init(Config &config) { m_terrain_variation.set_value(64); m_bottommost_chunk.set_value(-4); - m_enable_surface.set_value(true); - m_enable_carvers.set_value(true); - m_enable_features.set_value(true); config.add_value("overworld.terrain_variation", m_terrain_variation); config.add_value("overworld.bottommost_chunk", m_bottommost_chunk); - config.add_value("overworld.enable_surface", m_enable_surface); - config.add_value("overworld.enable_carvers", m_enable_carvers); - config.add_value("overworld.enable_features", m_enable_features); } void Overworld::init_late(std::uint64_t global_seed) { - m_twister.seed(global_seed); + std::mt19937 twister(global_seed); m_fnl_terrain = fnlCreateState(); - m_fnl_terrain.seed = static_cast<int>(m_twister()); + m_fnl_terrain.seed = static_cast<int>(twister()); m_fnl_terrain.noise_type = FNL_NOISE_OPENSIMPLEX2S; m_fnl_terrain.fractal_type = FNL_FRACTAL_FBM; m_fnl_terrain.frequency = 0.005f; m_fnl_terrain.octaves = 4; m_fnl_caves_a = fnlCreateState(); - m_fnl_caves_a.seed = static_cast<int>(m_twister()); + m_fnl_caves_a.seed = static_cast<int>(twister()); m_fnl_caves_a.noise_type = FNL_NOISE_PERLIN; m_fnl_caves_a.frequency = 0.0075f; m_fnl_caves_b = fnlCreateState(); - m_fnl_caves_b.seed = static_cast<int>(m_twister()); + m_fnl_caves_b.seed = static_cast<int>(twister()); m_fnl_caves_b.noise_type = FNL_NOISE_PERLIN; m_fnl_caves_b.frequency = 0.0075f; - // This ensures the metadata is cleaned - // between different world loads that happen - // on singleplayer; this should fix retained - // entropy bug we've just found out this morning - m_metadata.clear(); + m_metamap.clear(); } bool Overworld::generate(const chunk_pos &cpos, VoxelStorage &voxels) { - if(cpos.y < m_bottommost_chunk.get_value()) { + if(cpos.y <= m_bottommost_chunk.get_value()) { // If the player asks the generator // to generate a lot of stuff below // the surface, it will happily chew @@ -69,77 +119,120 @@ bool Overworld::generate(const chunk_pos &cpos, VoxelStorage &voxels) generate_terrain(cpos, voxels); m_mutex.unlock(); - if(m_enable_surface.get_value()) { - m_mutex.lock(); - generate_surface(cpos, voxels); - m_mutex.unlock(); - } + m_mutex.lock(); + generate_surface(cpos, voxels); + m_mutex.unlock(); - if(m_enable_carvers.get_value()) { - m_mutex.lock(); - generate_carvers(cpos, voxels); - m_mutex.unlock(); - } + m_mutex.lock(); + generate_caves(cpos, voxels); + m_mutex.unlock(); - if(m_enable_features.get_value()) { - m_mutex.lock(); - generate_features(cpos, voxels); - m_mutex.unlock(); - } + m_mutex.lock(); + generate_features(cpos, voxels); + m_mutex.unlock(); return true; } -float Overworld::get_noise(const voxel_pos &vpos, std::int64_t variation) +bool Overworld::is_inside_cave(const voxel_pos &vpos) +{ + auto noise_a = fnlGetNoise3D(&m_fnl_caves_a, vpos.x, 1.5f * vpos.y, vpos.z); + auto noise_b = fnlGetNoise3D(&m_fnl_caves_b, vpos.x, 1.5f * vpos.y, vpos.z); + auto noise_combined = noise_a * noise_a + noise_b * noise_b; + return noise_combined < (1.0f / 1024.0f); +} + +bool Overworld::is_inside_terrain(const voxel_pos &vpos) { - // Terrain noise is also sampled when we're placing - // surface voxels; this is needed becuase chunks don't - // know if they have generated neighbours or not. - return variation * fnlGetNoise3D(&m_fnl_terrain, vpos.x, vpos.y, vpos.z) - vpos.y; + auto variation = m_terrain_variation.get_value(); + auto noise = variation * fnlGetNoise3D(&m_fnl_terrain, vpos.x, vpos.y, vpos.z) - vpos.y; + return noise > 0.0f; } -Metadata_2501 &Overworld::get_metadata(const worldgen_chunk_pos &cpos) +const Overworld_Metadata &Overworld::get_or_create_metadata(const chunk_pos_xz &cpos) { - const auto it = m_metadata.find(cpos); + auto it = m_metamap.find(cpos); + + if(it != m_metamap.cend()) { + // Metadata is present + return it->second; + } + + auto &metadata = m_metamap.insert_or_assign(cpos, Overworld_Metadata()).first->second; + metadata.entropy.fill(std::numeric_limits<std::uint64_t>::max()); + metadata.heightmap.fill(std::numeric_limits<voxel_pos::value_type>::min()); + + auto twister = std::mt19937_64(std::hash<chunk_pos_xz>()(cpos)); + auto variation = m_terrain_variation.get_value(); + + // Generator might need some randomness + // that depends on 2D coordinates, so we + // generate this entropy ahead of time + for(int i = 0; i < CHUNK_AREA; ++i) { + metadata.entropy[i] = twister(); + } + + // Generate speculative heightmap; + // Cave generation might have issues with placing + // surface features such as trees but I genuinely don't give a shit + for(int lx = 0; lx < CHUNK_SIZE; lx += 1) { + for(int lz = 0; lz < CHUNK_SIZE; lz += 1) { + auto hdx = static_cast<std::size_t>(lx + lz * CHUNK_SIZE); + auto vpos = coord::to_voxel(chunk_pos(cpos.x, 0, cpos.y), local_pos(lx, 0, lz)); + + for(vpos.y = variation; vpos.y >= -variation; vpos.y -= 1) { + if(is_inside_terrain(vpos)) { + metadata.heightmap[hdx] = vpos.y; + break; + } + } + } + } + + // FIXME: make this into a configuration value + constexpr static unsigned int TREE_DENSITY = 4U; - if(it == m_metadata.cend()) { + // Generate tree locations for this chunk + while(metadata.trees.size() < TREE_DENSITY) { + auto lpos = local_pos_xz((twister() % CHUNK_SIZE), (twister() % CHUNK_SIZE)); + auto is_unique = true; - auto &metadata = m_metadata.insert_or_assign(cpos, Metadata_2501()).first->second; - for(std::size_t i = 0; i < CHUNK_AREA; metadata.entropy[i++] = m_twister()); - metadata.heightmap.fill(INT64_MIN); + for(const auto &check_lpos : metadata.trees) { + if(check_lpos == lpos) { + is_unique = false; + break; + } + } - return metadata; + if(is_unique) { + metadata.trees.push_back(lpos); + } } - return it->second; + return metadata; } void Overworld::generate_terrain(const chunk_pos &cpos, VoxelStorage &voxels) { - auto &metadata = get_metadata(worldgen_chunk_pos(cpos.x, cpos.z)); + auto &metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); + auto variation = m_terrain_variation.get_value(); - for(std::size_t index = 0; index < CHUNK_VOLUME; index += 1) { - auto lpos = coord::to_local(index); + for(unsigned long i = 0; i < CHUNK_VOLUME; ++i) { + auto lpos = coord::to_local(i); auto vpos = coord::to_voxel(cpos, lpos); - auto hdx = static_cast<std::size_t>(lpos.x + lpos.z * CHUNK_SIZE); - // Sampling 3D noise like that is expensive; to - // avoid unnecessary noise sampling we can speculate - // where the terrain would be guaranteed to be solid or air - if(cxpr::abs(vpos.y) >= (m_terrain_variation.get_value() + 1)) { - if(vpos.y < INT64_C(0)) { - if(vpos.y > metadata.heightmap[hdx]) - metadata.heightmap[hdx] = vpos.y; - voxels[index] = game_voxels::stone; - } + if(vpos.y > variation) { + voxels[i] = NULL_VOXEL_ID; + continue; + } + if(vpos.y < -variation) { + voxels[i] = game_voxels::stone; continue; } - if(get_noise(vpos, m_terrain_variation.get_value()) > 0.0f) { - if(vpos.y > metadata.heightmap[hdx]) - metadata.heightmap[hdx] = vpos.y; - voxels[index] = game_voxels::stone; + if(is_inside_terrain(vpos)) { + voxels[i] = game_voxels::stone; continue; } } @@ -147,76 +240,70 @@ void Overworld::generate_terrain(const chunk_pos &cpos, VoxelStorage &voxels) void Overworld::generate_surface(const chunk_pos &cpos, VoxelStorage &voxels) { - auto &metadata = get_metadata(worldgen_chunk_pos(cpos.x, cpos.z)); + auto &metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); + auto variation = m_terrain_variation.get_value(); - for(std::size_t index = 0; index < CHUNK_VOLUME; index += 1) { - auto lpos = coord::to_local(index); + for(unsigned long i = 0; i < CHUNK_VOLUME; ++i) { + auto lpos = coord::to_local(i); auto vpos = coord::to_voxel(cpos, lpos); + auto hdx = static_cast<std::size_t>(lpos.x + lpos.z * CHUNK_SIZE); - // Same speculation check applies here albeit - // a little differently - there's no surface to - // place voxels on above variation range - if(cxpr::abs(vpos.y) >= (m_terrain_variation.get_value() + 1)) { + if((vpos.y > variation) || (vpos.y < -variation)) { + // Speculative optimization continue; } - // Surface voxel checks only apply for solid voxels; - // it's kind of obvious you can't replace air with grass - if(voxels[index] == NULL_VOXEL_ID) { + if(voxels[i] == NULL_VOXEL_ID) { + // Surface voxel checks only apply for solid voxels; + // it's kind of obvious you can't replace air with grass continue; } - std::size_t depth = 0; + unsigned int depth = 0U; - for(local_pos::value_type dy = 0; dy < 5; dy += 1) { - auto dlpos = local_pos(lpos.x, lpos.y + dy + 1, lpos.z); - auto dvpos = coord::to_voxel(cpos, dlpos); - auto didx = coord::to_index(dlpos); + for(unsigned int dy = 0U; dy < 5U; dy += 1U) { + auto d_lpos = local_pos(lpos.x, lpos.y + dy + 1, lpos.z); + auto d_vpos = coord::to_voxel(cpos, d_lpos); + auto d_index = coord::to_index(d_lpos); - if(dlpos.y >= CHUNK_SIZE) { - if(get_noise(dvpos, m_terrain_variation.get_value()) <= 0.0f) + if(d_lpos.y >= CHUNK_SIZE) { + if(!is_inside_terrain(d_vpos)) break; - depth += 1; + depth += 1U; } else { - if(voxels[didx] == NULL_VOXEL_ID) + if(voxels[d_index] == NULL_VOXEL_ID) break; - depth += 1; + depth += 1U; } } - if(depth < 5) { - if(depth == 0) - voxels[index] = game_voxels::grass; - else voxels[index] = game_voxels::dirt; + if(depth < (2U + (metadata.entropy[hdx] % 5U))) { + if(depth == 0U) + voxels[i] = game_voxels::grass; + else voxels[i] = game_voxels::dirt; } } + } -void Overworld::generate_carvers(const chunk_pos &cpos, VoxelStorage &voxels) +void Overworld::generate_caves(const chunk_pos &cpos, VoxelStorage &voxels) { - auto &metadata = get_metadata(worldgen_chunk_pos(cpos.x, cpos.z)); - - for(std::size_t index = 0; index < CHUNK_VOLUME; index += 1) { - auto lpos = coord::to_local(index); + auto &metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); + auto variation = m_terrain_variation.get_value(); + + for(unsigned long i = 0U; i < CHUNK_VOLUME; ++i) { + auto lpos = coord::to_local(i); auto vpos = coord::to_voxel(cpos, lpos); - auto hdx = static_cast<std::size_t>(lpos.x + lpos.z * CHUNK_SIZE); - // Speculative optimization - there's no solid - // terrain above variation to carve caves out from - if(vpos[1] > (m_terrain_variation.get_value() + 1)) { + if(vpos.y > variation) { + // Speculative optimization - there's no solid + // terrain above variation to carve caves out from continue; } - const float na = fnlGetNoise3D(&m_fnl_caves_a, vpos.x, 1.5f * vpos.y, vpos.z); - const float nb = fnlGetNoise3D(&m_fnl_caves_b, vpos.x, 1.5f * vpos.y, vpos.z); - - if((na * na + nb * nb) <= (1.0f / 1024.0f)) { - if(vpos[1] == metadata.heightmap[hdx]) { - metadata.heightmap[hdx] = INT64_MIN; - } - - voxels[index] = NULL_VOXEL_ID; + if(is_inside_cave(vpos)) { + voxels[i] = NULL_VOXEL_ID; continue; } } @@ -224,43 +311,43 @@ void Overworld::generate_carvers(const chunk_pos &cpos, VoxelStorage &voxels) void Overworld::generate_features(const chunk_pos &cpos, VoxelStorage &voxels) { - auto &metadata = get_metadata(worldgen_chunk_pos(cpos.x, cpos.z)); - -#if 1 - constexpr static std::size_t COUNT = 5; - std::array<std::int16_t, COUNT> lxa = {}; - std::array<std::int16_t, COUNT> lza = {}; - std::array<std::int64_t, COUNT> heights = {}; - - for(std::size_t tc = 0; tc < COUNT; tc += 1) { - lxa[tc] = static_cast<std::int16_t>(metadata.entropy[tc * 3 + 0] % CHUNK_SIZE); - lza[tc] = static_cast<std::int16_t>(metadata.entropy[tc * 3 + 1] % CHUNK_SIZE); - heights[tc] = 3 + static_cast<std::int64_t>(metadata.entropy[tc * 3 + 2] % 4); - } + const chunk_pos_xz tree_chunks[] = { + chunk_pos_xz(cpos.x - 0, cpos.z - 1), + chunk_pos_xz(cpos.x - 1, cpos.z - 1), + chunk_pos_xz(cpos.x - 1, cpos.z + 0), + chunk_pos_xz(cpos.x - 1, cpos.z + 1), + chunk_pos_xz(cpos.x + 0, cpos.z + 0), + chunk_pos_xz(cpos.x + 0, cpos.z + 1), + chunk_pos_xz(cpos.x + 1, cpos.z - 1), + chunk_pos_xz(cpos.x + 1, cpos.z + 0), + chunk_pos_xz(cpos.x + 1, cpos.z + 1), + }; + + for(unsigned int i = 0U; i < cxpr::array_size(tree_chunks); ++i) { + const auto &cpos_xz = tree_chunks[i]; + const auto &metadata = get_or_create_metadata(cpos_xz); + + for(const auto &lpos_xz : metadata.trees) { + auto hdx = static_cast<std::size_t>(lpos_xz.x + lpos_xz.y * CHUNK_SIZE); + auto height = metadata.heightmap[hdx]; + + if(height == std::numeric_limits<voxel_pos::value_type>::min()) { + // What happened? Cave happened + continue; + } - for(std::size_t index = 0; index < CHUNK_VOLUME; index += 1) { - auto lpos = coord::to_local(index); - auto vpos = coord::to_voxel(cpos, lpos); - auto hdx = static_cast<std::size_t>(lpos.x + lpos.z * CHUNK_SIZE); + auto cpos_xyz = chunk_pos(cpos_xz.x, 0, cpos_xz.y); + auto lpos_xyz = local_pos(lpos_xz.x, 0, lpos_xz.y); + auto vpos = coord::to_voxel(cpos_xyz, lpos_xyz); - for(std::size_t tc = 0; tc < COUNT; tc += 1) { - if((lpos.x == lxa[tc]) && (lpos.z == lza[tc])) { - if(cxpr::range<std::int64_t>(vpos.y - metadata.heightmap[hdx], 1, heights[tc])) - voxels[index] = game_voxels::cobblestone; - break; + if(is_inside_cave(vpos)) { + // Cave is in the way + continue; } - } - } -#else - for(std::size_t index = 0; index < CHUNK_VOLUME; index += 1) { - auto lpos = coord::to_local(index); - auto vpos = coord::to_voxel(cpos, lpos); - auto hdx = static_cast<std::size_t>(lpos.x + lpos.z * CHUNK_SIZE); - if(vpos.y == (metadata.heightmap[hdx] + 1)) { - voxels[index] = game_voxels::vtest; - continue; + m_feat_tree.place(vpos + DIR_UP<voxel_pos::value_type>, cpos, voxels); } + } -#endif + } diff --git a/game/server/overworld.hh b/game/server/overworld.hh index dbe66d0..78844ce 100644 --- a/game/server/overworld.hh +++ b/game/server/overworld.hh @@ -6,10 +6,12 @@ #include "shared/const.hh" #include "shared/dimension.hh" +#include "shared/feature.hh" -struct Metadata_2501 final { - std::array<std::uint64_t, CHUNK_AREA> entropy; - std::array<voxel_pos::value_type, CHUNK_AREA> heightmap; +struct Overworld_Metadata final { + dimension_entropy_map entropy; + dimension_height_map heightmap; + std::vector<local_pos_xz> trees; }; class Overworld final : public Dimension { @@ -23,28 +25,34 @@ public: virtual bool generate(const chunk_pos &cpos, VoxelStorage &voxels) override; private: - float get_noise(const voxel_pos &vpos, std::int64_t variation); - Metadata_2501 &get_metadata(const worldgen_chunk_pos &cpos); + bool is_inside_cave(const voxel_pos &vpos); + bool is_inside_terrain(const voxel_pos &vpos); + +private: + const Overworld_Metadata &get_or_create_metadata(const chunk_pos_xz &cpos); + +private: void generate_terrain(const chunk_pos &cpos, VoxelStorage &voxels); void generate_surface(const chunk_pos &cpos, VoxelStorage &voxels); - void generate_carvers(const chunk_pos &cpos, VoxelStorage &voxels); + void generate_caves(const chunk_pos &cpos, VoxelStorage &voxels); void generate_features(const chunk_pos &cpos, VoxelStorage &voxels); private: ConfigInt m_terrain_variation; ConfigInt m_bottommost_chunk; - ConfigBoolean m_enable_surface; - ConfigBoolean m_enable_carvers; - ConfigBoolean m_enable_features; private: - emhash8::HashMap<worldgen_chunk_pos, Metadata_2501> m_metadata; - std::mt19937_64 m_twister; + emhash8::HashMap<chunk_pos_xz, Overworld_Metadata> m_metamap; + +private: fnl_state m_fnl_terrain; fnl_state m_fnl_caves_a; fnl_state m_fnl_caves_b; private: + Feature m_feat_tree; + +private: std::mutex m_mutex; }; diff --git a/game/shared/CMakeLists.txt b/game/shared/CMakeLists.txt index cd3eed4..b0cd086 100644 --- a/game/shared/CMakeLists.txt +++ b/game/shared/CMakeLists.txt @@ -12,6 +12,8 @@ add_library(shared STATIC "${CMAKE_CURRENT_LIST_DIR}/dimension.hh" "${CMAKE_CURRENT_LIST_DIR}/factory.cc" "${CMAKE_CURRENT_LIST_DIR}/factory.hh" + "${CMAKE_CURRENT_LIST_DIR}/feature.cc" + "${CMAKE_CURRENT_LIST_DIR}/feature.hh" "${CMAKE_CURRENT_LIST_DIR}/game_items.cc" "${CMAKE_CURRENT_LIST_DIR}/game_items.hh" "${CMAKE_CURRENT_LIST_DIR}/game_voxels.cc" diff --git a/game/shared/chunk.cc b/game/shared/chunk.cc index c996973..17fdcc1 100644 --- a/game/shared/chunk.cc +++ b/game/shared/chunk.cc @@ -8,6 +8,7 @@ Chunk::Chunk(entt::entity entity, Dimension *dimension) m_entity = entity; m_dimension = dimension; m_voxels.fill(NULL_VOXEL_ID); + m_biome = BIOME_VOID; } voxel_id Chunk::get_voxel(const local_pos &lpos) const @@ -44,6 +45,16 @@ void Chunk::set_voxels(const VoxelStorage &voxels) m_voxels = voxels; } +unsigned int Chunk::get_biome(void) const +{ + return m_biome; +} + +void Chunk::set_biome(unsigned int biome) +{ + m_biome = biome; +} + entt::entity Chunk::get_entity(void) const { return m_entity; diff --git a/game/shared/chunk.hh b/game/shared/chunk.hh index 506d867..560d3a7 100644 --- a/game/shared/chunk.hh +++ b/game/shared/chunk.hh @@ -5,6 +5,8 @@ #include "shared/types.hh" #include "shared/voxel_storage.hh" +constexpr static unsigned int BIOME_VOID = 0U; + class Dimension; class Chunk final { @@ -21,6 +23,9 @@ public: const VoxelStorage &get_voxels(void) const; void set_voxels(const VoxelStorage &voxels); + unsigned int get_biome(void) const; + void set_biome(unsigned int biome); + entt::entity get_entity(void) const; Dimension *get_dimension(void) const; @@ -28,6 +33,7 @@ private: entt::entity m_entity; Dimension *m_dimension; VoxelStorage m_voxels; + unsigned int m_biome; }; #endif /* SHARED_CHUNK_HH */ diff --git a/game/shared/dimension.cc b/game/shared/dimension.cc index 378779c..2377214 100644 --- a/game/shared/dimension.cc +++ b/game/shared/dimension.cc @@ -13,7 +13,7 @@ Dimension::Dimension(const char *name, float gravity) Dimension::~Dimension(void) { - for(const auto it : m_hashmap) + for(const auto it : m_chunkmap) delete it.second; entities.clear(); chunks.clear(); @@ -31,9 +31,9 @@ float Dimension::get_gravity(void) const Chunk *Dimension::create_chunk(const chunk_pos &cpos) { - auto it = m_hashmap.find(cpos); + auto it = m_chunkmap.find(cpos); - if(it != m_hashmap.cend()) { + if(it != m_chunkmap.cend()) { // Chunk already exists return it->second; } @@ -52,7 +52,7 @@ Chunk *Dimension::create_chunk(const chunk_pos &cpos) globals::dispatcher.trigger(event); - return m_hashmap.insert_or_assign(cpos, std::move(chunk)).first->second; + return m_chunkmap.insert_or_assign(cpos, std::move(chunk)).first->second; } Chunk *Dimension::find_chunk(entt::entity entity) const @@ -64,8 +64,8 @@ Chunk *Dimension::find_chunk(entt::entity entity) const Chunk *Dimension::find_chunk(const chunk_pos &cpos) const { - auto it = m_hashmap.find(cpos); - if(it != m_hashmap.cend()) + auto it = m_chunkmap.find(cpos); + if(it != m_chunkmap.cend()) return it->second; return nullptr; } @@ -74,18 +74,18 @@ void Dimension::remove_chunk(entt::entity entity) { if(chunks.valid(entity)) { auto &component = chunks.get<ChunkComponent>(entity); - m_hashmap.erase(component.cpos); + m_chunkmap.erase(component.cpos); chunks.destroy(entity); } } void Dimension::remove_chunk(const chunk_pos &cpos) { - auto it = m_hashmap.find(cpos); + auto it = m_chunkmap.find(cpos); - if(it != m_hashmap.cend()) { + if(it != m_chunkmap.cend()) { chunks.destroy(it->second->get_entity()); - m_hashmap.erase(it); + m_chunkmap.erase(it); } } @@ -93,7 +93,7 @@ void Dimension::remove_chunk(Chunk *chunk) { if(chunk) { const auto &component = chunks.get<ChunkComponent>(chunk->get_entity()); - m_hashmap.erase(component.cpos); + m_chunkmap.erase(component.cpos); chunks.destroy(chunk->get_entity()); } } diff --git a/game/shared/dimension.hh b/game/shared/dimension.hh index eb6f896..8806115 100644 --- a/game/shared/dimension.hh +++ b/game/shared/dimension.hh @@ -2,12 +2,16 @@ #define SHARED_DIMENSION_HH 1 #pragma once +#include "shared/const.hh" #include "shared/types.hh" class Chunk; class Config; class VoxelStorage; +using dimension_entropy_map = std::array<std::uint64_t, CHUNK_AREA>; +using dimension_height_map = std::array<voxel_pos::value_type, CHUNK_AREA>; + class Dimension { public: explicit Dimension(const char *name, float gravity); @@ -43,7 +47,7 @@ public: private: std::string m_name; - emhash8::HashMap<chunk_pos, Chunk *> m_hashmap; + emhash8::HashMap<chunk_pos, Chunk *> m_chunkmap; float m_gravity; }; diff --git a/game/shared/feature.cc b/game/shared/feature.cc new file mode 100644 index 0000000..6f884b2 --- /dev/null +++ b/game/shared/feature.cc @@ -0,0 +1,51 @@ +#include "shared/pch.hh" +#include "shared/feature.hh" + +#include "shared/chunk.hh" +#include "shared/coord.hh" +#include "shared/dimension.hh" +#include "shared/voxel_storage.hh" + +void Feature::place(const voxel_pos &vpos, Dimension *dimension, bool overwrite) const +{ + for(const auto &it : (*this)) { + auto it_vpos = vpos + it.first; + auto it_cpos = coord::to_chunk(it_vpos); + + if(auto chunk = dimension->create_chunk(it_cpos)) { + auto it_lpos = coord::to_local(it_vpos); + auto it_index = coord::to_index(it_lpos); + + if(chunk->get_voxel(it_index) && !overwrite) { + // There is something in the way + // and the called intentionally requested + // we do not force feature to overwrite voxels + continue; + } + + chunk->set_voxel(it.second, it_index); + } + } +} + +void Feature::place(const voxel_pos &vpos, const chunk_pos &cpos, VoxelStorage &voxels, bool overwrite) const +{ + for(const auto &it : (*this)) { + auto it_vpos = vpos + it.first; + auto it_cpos = coord::to_chunk(it_vpos); + + if(it_cpos == cpos) { + auto it_lpos = coord::to_local(it_vpos); + auto it_index = coord::to_index(it_lpos); + + if(voxels[it_index] && !overwrite) { + // There is something in the way + // and the called intentionally requested + // we do not force feature to overwrite voxels + continue; + } + + voxels[it_index] = it.second; + } + } +} diff --git a/game/shared/feature.hh b/game/shared/feature.hh new file mode 100644 index 0000000..eb7cf2a --- /dev/null +++ b/game/shared/feature.hh @@ -0,0 +1,18 @@ +#ifndef SHARED_FEATURE_HH +#define SHARED_FEATURE_HH 1 +#pragma once + +#include "shared/types.hh" + +class Dimension; +class VoxelStorage; + +class Feature final : public std::vector<std::pair<voxel_pos, voxel_id>> { +public: + explicit Feature(void) = default; + virtual ~Feature(void) = default; + void place(const voxel_pos &vpos, Dimension *dimension, bool overwrite = false) const; + void place(const voxel_pos &vpos, const chunk_pos &cpos, VoxelStorage &voxels, bool overwrite = false) const; +}; + +#endif /* SHARED_FEATURE_HH */ diff --git a/game/shared/types.hh b/game/shared/types.hh index dace85e..a107e54 100644 --- a/game/shared/types.hh +++ b/game/shared/types.hh @@ -14,9 +14,9 @@ using chunk_pos = glm::vec<3, std::int32_t>; using local_pos = glm::vec<3, std::int16_t>; using voxel_pos = glm::vec<3, std::int64_t>; -// A special 2D chunk coordinate used by world generation code -// to cache things like 2D noise and terrain heightmap for performance reasons -using worldgen_chunk_pos = glm::vec<2, chunk_pos::value_type>; +using chunk_pos_xz = glm::vec<2, chunk_pos::value_type>; +using local_pos_xz = glm::vec<2, local_pos::value_type>; +using voxel_pos_xz = glm::vec<2, local_pos::value_type>; template<> struct std::hash<chunk_pos> final { @@ -31,8 +31,8 @@ struct std::hash<chunk_pos> final { }; template<> -struct std::hash<worldgen_chunk_pos> final { - constexpr inline std::size_t operator()(const worldgen_chunk_pos &cwpos) const +struct std::hash<chunk_pos_xz> final { + constexpr inline std::size_t operator()(const chunk_pos_xz &cwpos) const { std::size_t value = 0; value ^= cwpos.x * 73856093; |
