mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-06-17 16:17:02 +08:00
feat: ocean (#16)
* feat(gameplay): add Ocean biome with water generation and heightmap adjustments - Introduce Ocean biome enum, builder, and detection logic. - Add ocean water building to all existing biomes and modify heightmap thresholds for low mountainous areas. - Skip cave and river generation in Ocean (and River) biomes; avoid carving water blocks. - Comment out border blending call and update block fill logic. * fix(gameplay): re-enable border blending and protect water in cave gen * refactor(generation): move ocean water build to later phase * feat(block): add is_transitional property and refine border blending * fix(block): set stone block as transitional * fix(world): generate temporary chunks for surface blend neighbor data * fix(gameplay): simplify block fill logic in blend_surface_blocks_borders * refactor(tree): remove debug logging and unused include
This commit is contained in:
@@ -124,6 +124,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
src/gameplay/river_path.cpp
|
src/gameplay/river_path.cpp
|
||||||
src/block.cpp
|
src/block.cpp
|
||||||
src/gameplay/vertex_data.cpp
|
src/gameplay/vertex_data.cpp
|
||||||
|
src/gameplay/builders/ocean_builder.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ is_discard = true
|
|||||||
is_gas = true
|
is_gas = true
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = true
|
is_passable = true
|
||||||
|
is_transitional = false
|
||||||
is_transparent = true
|
is_transparent = true
|
||||||
name = 'air'
|
name = 'air'
|
||||||
@@ -5,5 +5,6 @@ is_discard = false
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = false
|
is_passable = false
|
||||||
|
is_transitional = true
|
||||||
is_transparent = false
|
is_transparent = false
|
||||||
name = 'dirt'
|
name = 'dirt'
|
||||||
@@ -5,5 +5,6 @@ is_discard = true
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = true
|
is_passable = true
|
||||||
|
is_transitional = false
|
||||||
is_transparent = true
|
is_transparent = true
|
||||||
name = 'grass'
|
name = 'grass'
|
||||||
@@ -5,5 +5,6 @@ is_discard = false
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = false
|
is_passable = false
|
||||||
|
is_transitional = true
|
||||||
is_transparent = false
|
is_transparent = false
|
||||||
name = 'grass_block'
|
name = 'grass_block'
|
||||||
@@ -5,5 +5,6 @@ is_discard = true
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = false
|
is_passable = false
|
||||||
|
is_transitional = false
|
||||||
is_transparent = true
|
is_transparent = true
|
||||||
name = 'leaf'
|
name = 'leaf'
|
||||||
@@ -5,5 +5,6 @@ is_discard = false
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = false
|
is_passable = false
|
||||||
|
is_transitional = false
|
||||||
is_transparent = false
|
is_transparent = false
|
||||||
name = 'log'
|
name = 'log'
|
||||||
@@ -5,5 +5,6 @@ is_discard = false
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = false
|
is_passable = false
|
||||||
|
is_transitional = true
|
||||||
is_transparent = false
|
is_transparent = false
|
||||||
name = 'sand'
|
name = 'sand'
|
||||||
@@ -5,5 +5,6 @@ is_discard = false
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = false
|
is_passable = false
|
||||||
|
is_transitional = true
|
||||||
is_transparent = false
|
is_transparent = false
|
||||||
name = 'snowy_grass_block'
|
name = 'snowy_grass_block'
|
||||||
@@ -5,5 +5,6 @@ is_discard = false
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = false
|
is_liquid = false
|
||||||
is_passable = false
|
is_passable = false
|
||||||
|
is_transitional = true
|
||||||
is_transparent = false
|
is_transparent = false
|
||||||
name = 'stone'
|
name = 'stone'
|
||||||
@@ -7,3 +7,4 @@ is_cross_plane = false
|
|||||||
is_transparent = false
|
is_transparent = false
|
||||||
is_discard = false
|
is_discard = false
|
||||||
is_blend = false
|
is_blend = false
|
||||||
|
is_transitional = false
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ is_discard = false
|
|||||||
is_gas = false
|
is_gas = false
|
||||||
is_liquid = true
|
is_liquid = true
|
||||||
is_passable = true
|
is_passable = true
|
||||||
|
is_transitional = false
|
||||||
is_transparent = true
|
is_transparent = true
|
||||||
name = 'water'
|
name = 'water'
|
||||||
@@ -15,6 +15,7 @@ enum class BiomeType {
|
|||||||
MOUNTAIN,
|
MOUNTAIN,
|
||||||
RIVER,
|
RIVER,
|
||||||
SNOWY_PLAIN,
|
SNOWY_PLAIN,
|
||||||
|
OCEAN,
|
||||||
NONE
|
NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -50,13 +50,14 @@ struct BlockData {
|
|||||||
|
|
||||||
bool is_discard = false;
|
bool is_discard = false;
|
||||||
bool is_blend = false;
|
bool is_blend = false;
|
||||||
|
bool is_transitional = false;
|
||||||
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
|
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
|
||||||
bool passable, bool cross_plane, bool transparent, bool gas,
|
bool passable, bool cross_plane, bool transparent, bool gas,
|
||||||
bool discard, bool blend)
|
bool discard, bool blend, bool transitional)
|
||||||
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
|
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
|
||||||
is_passable(passable), is_cross_plane(cross_plane),
|
is_passable(passable), is_cross_plane(cross_plane),
|
||||||
is_transparent(transparent), is_discard(discard), is_blend(blend) {}
|
is_transparent(transparent), is_discard(discard), is_blend(blend),
|
||||||
|
is_transitional(transitional) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlockManager {
|
class BlockManager {
|
||||||
@@ -77,7 +78,7 @@ public:
|
|||||||
|
|
||||||
static bool is_discard(BlockType id);
|
static bool is_discard(BlockType id);
|
||||||
static bool is_blend(BlockType id);
|
static bool is_blend(BlockType id);
|
||||||
|
static bool is_transitional(BlockType id);
|
||||||
static BlockType cross_plane_index(BlockType id);
|
static BlockType cross_plane_index(BlockType id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public:
|
|||||||
virtual ChunkGenerator& get_chunk_generator() = 0;
|
virtual ChunkGenerator& get_chunk_generator() = 0;
|
||||||
virtual void build_biome() = 0;
|
virtual void build_biome() = 0;
|
||||||
virtual void build_vegetation() = 0;
|
virtual void build_vegetation() = 0;
|
||||||
|
void ocean_water_build();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void build_bottom();
|
void build_bottom();
|
||||||
|
|||||||
23
include/Cubed/gameplay/builders/ocean_builder.hpp
Normal file
23
include/Cubed/gameplay/builders/ocean_builder.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Cubed/gameplay/builders/biome_builder.hpp"
|
||||||
|
namespace Cubed {
|
||||||
|
|
||||||
|
class ChunkGenerator;
|
||||||
|
|
||||||
|
class OceanBuilder : public BiomeBuilder {
|
||||||
|
public:
|
||||||
|
OceanBuilder(ChunkGenerator& chunk_generator);
|
||||||
|
void build_biome() override;
|
||||||
|
ChunkGenerator& get_chunk_generator() override;
|
||||||
|
void build_vegetation() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChunkGenerator& m_chunk_generator;
|
||||||
|
|
||||||
|
void build_blocks();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
@@ -45,6 +45,7 @@ public:
|
|||||||
Chunk& chunk();
|
Chunk& chunk();
|
||||||
Random& random();
|
Random& random();
|
||||||
const std::array<BiomeType, 8>& neighbor_biome() const;
|
const std::array<BiomeType, 8>& neighbor_biome() const;
|
||||||
|
void ocean_build();
|
||||||
void generate_cave();
|
void generate_cave();
|
||||||
void generate_river();
|
void generate_river();
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,10 @@ private:
|
|||||||
|
|
||||||
void gen_chunks_internal();
|
void gen_chunks_internal();
|
||||||
void sync_player_pos(glm::vec3& player_pos);
|
void sync_player_pos(glm::vec3& player_pos);
|
||||||
void compute_required_chunks(ChunkPosSet& required_chunks,
|
void
|
||||||
ChunkHashMap& temp_neighbor);
|
compute_required_chunks(ChunkPosSet& required_chunks,
|
||||||
|
ChunkHashMap& temp_neighbor,
|
||||||
|
std::vector<ChunkPos>& need_gen_temp_chunks_pos);
|
||||||
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
|
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
|
||||||
const ChunkPosSet&);
|
const ChunkPosSet&);
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ void extract_frustum_planes(const glm::mat4& mvp_matrix,
|
|||||||
float smootherstep(float edge0, float edge1, float x);
|
float smootherstep(float edge0, float edge1, float x);
|
||||||
bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents,
|
bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents,
|
||||||
const std::vector<glm::vec4>& planes);
|
const std::vector<glm::vec4>& planes);
|
||||||
|
float deterministic_random(int x, int z, uint64_t seed);
|
||||||
} // namespace Math
|
} // namespace Math
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -104,7 +104,13 @@ bool BlockManager::is_blend(BlockType id) {
|
|||||||
}
|
}
|
||||||
return m_datas[id].is_blend;
|
return m_datas[id].is_blend;
|
||||||
}
|
}
|
||||||
|
bool BlockManager::is_transitional(BlockType id) {
|
||||||
|
if (id >= sums()) {
|
||||||
|
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||||
|
return m_datas[0].is_transitional;
|
||||||
|
}
|
||||||
|
return m_datas[id].is_transitional;
|
||||||
|
}
|
||||||
void BlockManager::init() {
|
void BlockManager::init() {
|
||||||
fs::path data_path{block_data_dir};
|
fs::path data_path{block_data_dir};
|
||||||
|
|
||||||
@@ -142,9 +148,10 @@ void BlockManager::init() {
|
|||||||
auto is_gas = safe_get_value(block, "is_gas", false);
|
auto is_gas = safe_get_value(block, "is_gas", false);
|
||||||
auto is_discard = safe_get_value(block, "is_discard", false);
|
auto is_discard = safe_get_value(block, "is_discard", false);
|
||||||
auto is_blend = safe_get_value(block, "is_blend", false);
|
auto is_blend = safe_get_value(block, "is_blend", false);
|
||||||
|
auto is_transitional = safe_get_value(block, "is_transitional", false);
|
||||||
m_datas.emplace_back(*id, *name, *is_liquid, *is_passable,
|
m_datas.emplace_back(*id, *name, *is_liquid, *is_passable,
|
||||||
*is_cross_plane, *is_transparent, *is_gas,
|
*is_cross_plane, *is_transparent, *is_gas,
|
||||||
*is_discard, *is_blend);
|
*is_discard, *is_blend, *is_transitional);
|
||||||
}
|
}
|
||||||
std::sort(
|
std::sort(
|
||||||
m_datas.begin(), m_datas.end(),
|
m_datas.begin(), m_datas.end(),
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ std::string get_biome_str(BiomeType biome) {
|
|||||||
break;
|
break;
|
||||||
case SNOWY_PLAIN:
|
case SNOWY_PLAIN:
|
||||||
str = "Snowy Plain";
|
str = "Snowy Plain";
|
||||||
|
break;
|
||||||
|
case OCEAN:
|
||||||
|
str = "Ocean";
|
||||||
|
break;
|
||||||
case NONE:
|
case NONE:
|
||||||
str = "Unknown";
|
str = "Unknown";
|
||||||
break;
|
break;
|
||||||
@@ -190,6 +194,9 @@ BiomeType determine_biome(const BiomeConditions& conditions) {
|
|||||||
if (conditions.mountainous > 0.75) {
|
if (conditions.mountainous > 0.75) {
|
||||||
return MOUNTAIN;
|
return MOUNTAIN;
|
||||||
}
|
}
|
||||||
|
if (conditions.mountainous < 0.25) {
|
||||||
|
return OCEAN;
|
||||||
|
}
|
||||||
auto temp = conditions.temp;
|
auto temp = conditions.temp;
|
||||||
auto humid = conditions.humid;
|
auto humid = conditions.humid;
|
||||||
if (temp < 0.5) {
|
if (temp < 0.5) {
|
||||||
|
|||||||
@@ -39,4 +39,23 @@ void BiomeBuilder::place_grass() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BiomeBuilder::ocean_water_build() {
|
||||||
|
ChunkGenerator& chunk_generator = get_chunk_generator();
|
||||||
|
Chunk& chunk = chunk_generator.chunk();
|
||||||
|
auto& blocks = chunk.blocks();
|
||||||
|
const auto& heightmap = chunk.get_heightmap();
|
||||||
|
|
||||||
|
for (int x = 0; x < SIZE_X; ++x) {
|
||||||
|
for (int z = 0; z < SIZE_Z; ++z) {
|
||||||
|
int height = heightmap[x][z];
|
||||||
|
if (height <= SEA_LEVEL) {
|
||||||
|
for (int y = height; y <= SEA_LEVEL; y++) {
|
||||||
|
blocks[Chunk::index(x, y, z)] = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
34
src/gameplay/builders/ocean_builder.cpp
Normal file
34
src/gameplay/builders/ocean_builder.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include "Cubed/gameplay/builders/ocean_builder.hpp"
|
||||||
|
|
||||||
|
#include "Cubed/gameplay/chunk.hpp"
|
||||||
|
#include "Cubed/gameplay/chunk_generator.hpp"
|
||||||
|
namespace Cubed {
|
||||||
|
OceanBuilder::OceanBuilder(ChunkGenerator& chunk_generator)
|
||||||
|
: m_chunk_generator(chunk_generator) {}
|
||||||
|
|
||||||
|
void OceanBuilder::build_biome() {
|
||||||
|
BiomeBuilder::build_bottom();
|
||||||
|
build_blocks();
|
||||||
|
};
|
||||||
|
|
||||||
|
void OceanBuilder::build_blocks() {
|
||||||
|
auto& m_chunk = m_chunk_generator.chunk();
|
||||||
|
auto& m_blocks = m_chunk.blocks();
|
||||||
|
auto& m_heightmap = m_chunk.heightmap();
|
||||||
|
for (int x = 0; x < CHUNK_SIZE; x++) {
|
||||||
|
for (int z = 0; z < CHUNK_SIZE; z++) {
|
||||||
|
int height = static_cast<int>(m_heightmap[x][z]);
|
||||||
|
for (int y = 5; y <= height; y++) {
|
||||||
|
m_blocks[Chunk::index(x, y, z)] = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OceanBuilder::build_vegetation() {}
|
||||||
|
|
||||||
|
ChunkGenerator& OceanBuilder::get_chunk_generator() {
|
||||||
|
return m_chunk_generator;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
@@ -199,9 +199,8 @@ void Chunk::gen_phase_six(
|
|||||||
Logger::error("ChunkGenerator is Nullptr");
|
Logger::error("ChunkGenerator is Nullptr");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// This must be fully completed before any other operations can proceed!
|
||||||
m_generator->blend_surface_blocks_borders(neighbor_block);
|
m_generator->blend_surface_blocks_borders(neighbor_block);
|
||||||
m_generator->generate_cave();
|
|
||||||
m_generator->generate_river();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunk::gen_phase_seven() {
|
void Chunk::gen_phase_seven() {
|
||||||
@@ -209,6 +208,10 @@ void Chunk::gen_phase_seven() {
|
|||||||
Logger::error("ChunkGenerator is Nullptr");
|
Logger::error("ChunkGenerator is Nullptr");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_generator->ocean_build();
|
||||||
|
m_generator->generate_river();
|
||||||
|
m_generator->generate_cave();
|
||||||
|
|
||||||
m_generator->generate_vegetation();
|
m_generator->generate_vegetation();
|
||||||
mark_dirty();
|
mark_dirty();
|
||||||
m_generator = nullptr;
|
m_generator = nullptr;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "Cubed/gameplay/builders/desert_builder.hpp"
|
#include "Cubed/gameplay/builders/desert_builder.hpp"
|
||||||
#include "Cubed/gameplay/builders/forest_builder.hpp"
|
#include "Cubed/gameplay/builders/forest_builder.hpp"
|
||||||
#include "Cubed/gameplay/builders/mountain_builder.hpp"
|
#include "Cubed/gameplay/builders/mountain_builder.hpp"
|
||||||
|
#include "Cubed/gameplay/builders/ocean_builder.hpp"
|
||||||
#include "Cubed/gameplay/builders/plain_builder.hpp"
|
#include "Cubed/gameplay/builders/plain_builder.hpp"
|
||||||
#include "Cubed/gameplay/builders/river_builder.hpp"
|
#include "Cubed/gameplay/builders/river_builder.hpp"
|
||||||
#include "Cubed/gameplay/builders/snowy_plain_builder.hpp"
|
#include "Cubed/gameplay/builders/snowy_plain_builder.hpp"
|
||||||
@@ -17,7 +18,7 @@ namespace Cubed {
|
|||||||
|
|
||||||
using enum BiomeType;
|
using enum BiomeType;
|
||||||
|
|
||||||
constexpr int BLEND_RADIUS = 12;
|
constexpr int BLEND_RADIUS = 8;
|
||||||
ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) {
|
ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) {
|
||||||
ASSERT_MSG(is_init, "ChunksGenerator is not init");
|
ASSERT_MSG(is_init, "ChunksGenerator is not init");
|
||||||
ChunkPos pos = m_chunk.get_chunk_pos();
|
ChunkPos pos = m_chunk.get_chunk_pos();
|
||||||
@@ -166,18 +167,43 @@ void ChunkGenerator::generate_heightmap() {
|
|||||||
amplitude = std::lerp(10, 40, t);
|
amplitude = std::lerp(10, 40, t);
|
||||||
*/
|
*/
|
||||||
float t;
|
float t;
|
||||||
if (mountainous >= 0.7f) {
|
if (mountainous >= 0.95f) {
|
||||||
t = Math::smootherstep(0.7f, 0.75, mountainous);
|
t = Math::smootherstep(0.95f, 1.0f, mountainous);
|
||||||
base_y = std::lerp(70, 88, t);
|
base_y = std::lerp(130, 140, t);
|
||||||
amplitude = std::lerp(28, 48, t);
|
amplitude = std::lerp(38, 48, t);
|
||||||
} else if (mountainous >= 0.65f) {
|
} else if (mountainous >= 0.85f) {
|
||||||
t = Math::smootherstep(0.65f, 0.7f, mountainous);
|
t = Math::smootherstep(0.85f, 0.95f, mountainous);
|
||||||
base_y = std::lerp(66, 70, t);
|
base_y = std::lerp(100, 130, t);
|
||||||
|
amplitude = std::lerp(28, 38, t);
|
||||||
|
} else if (mountainous >= 0.8) {
|
||||||
|
t = Math::smootherstep(0.8f, 0.85f, mountainous);
|
||||||
|
base_y = std::lerp(85, 100, t);
|
||||||
amplitude = std::lerp(18, 28, t);
|
amplitude = std::lerp(18, 28, t);
|
||||||
|
} else if (mountainous >= 0.75f) {
|
||||||
|
t = Math::smootherstep(0.75f, 0.8f, mountainous);
|
||||||
|
base_y = std::lerp(70, 85, t);
|
||||||
|
amplitude = std::lerp(6, 18, t);
|
||||||
|
} else if (mountainous >= 0.7) {
|
||||||
|
t = Math::smootherstep(0.7f, 0.75f, mountainous);
|
||||||
|
base_y = std::lerp(66, 70, t);
|
||||||
|
amplitude = std::lerp(6, 6, t);
|
||||||
|
|
||||||
|
} else if (mountainous >= 0.45f) {
|
||||||
|
t = Math::smootherstep(0.45f, 0.7f, mountainous);
|
||||||
|
base_y = std::lerp(64, 66, t);
|
||||||
|
amplitude = std::lerp(6, 6, t);
|
||||||
|
} else if (mountainous >= 0.3f) {
|
||||||
|
t = Math::smootherstep(0.3f, 0.45f, mountainous);
|
||||||
|
base_y = std::lerp(60, 64, t);
|
||||||
|
amplitude = std::lerp(6, 6, t);
|
||||||
|
} else if (mountainous >= 0.25f) {
|
||||||
|
t = Math::smootherstep(0.25f, 0.3f, mountainous);
|
||||||
|
base_y = std::lerp(44, 60, t);
|
||||||
|
amplitude = std::lerp(6, 6, t);
|
||||||
} else {
|
} else {
|
||||||
t = Math::smootherstep(0.55, 0.65, mountainous);
|
t = Math::smootherstep(0.0f, 0.25f, mountainous);
|
||||||
base_y = std::lerp(58, 66, t);
|
base_y = std::lerp(35, 44, t);
|
||||||
amplitude = std::lerp(8, 18, t);
|
amplitude = std::lerp(3, 6, t);
|
||||||
}
|
}
|
||||||
heightmap[x][z] =
|
heightmap[x][z] =
|
||||||
base_y + fbm_height(world_x, world_z, octaves, lacunarity, gain,
|
base_y + fbm_height(world_x, world_z, octaves, lacunarity, gain,
|
||||||
@@ -451,9 +477,11 @@ void ChunkGenerator::blend_surface_blocks_borders(
|
|||||||
for (int y = WORLD_HEIGHT - 1; y >= 0; --y) {
|
for (int y = WORLD_HEIGHT - 1; y >= 0; --y) {
|
||||||
int idx = Chunk::index(nx, y,
|
int idx = Chunk::index(nx, y,
|
||||||
nz); // linear index: y * area + z * size + x
|
nz); // linear index: y * area + z * size + x
|
||||||
if (idx >= 0 && idx < static_cast<int>(blocks.size()) &&
|
if (idx >= 0 && idx < static_cast<int>(blocks.size())) {
|
||||||
blocks[idx] != 0) {
|
BlockType neighbor_type = blocks[idx];
|
||||||
return blocks[idx];
|
if (BlockManager::is_transitional(neighbor_type)) {
|
||||||
|
return neighbor_type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0; // fallback, should not happen for valid chunks
|
return 0; // fallback, should not happen for valid chunks
|
||||||
@@ -473,8 +501,8 @@ void ChunkGenerator::blend_surface_blocks_borders(
|
|||||||
|
|
||||||
// Weight map: type -> total weight
|
// Weight map: type -> total weight
|
||||||
std::unordered_map<BlockType, float> weights;
|
std::unordered_map<BlockType, float> weights;
|
||||||
weights[type_self] = 1.0f; // self weight
|
float self_weight = 1.0f;
|
||||||
|
weights[type_self] = self_weight;
|
||||||
// --- Right neighbor (index 0) ---
|
// --- Right neighbor (index 0) ---
|
||||||
if (neighbor_block[0] && x >= CHUNK_SIZE - BLEND_RADIUS) {
|
if (neighbor_block[0] && x >= CHUNK_SIZE - BLEND_RADIUS) {
|
||||||
int dist = (CHUNK_SIZE - 1) - x;
|
int dist = (CHUNK_SIZE - 1) - x;
|
||||||
@@ -523,47 +551,50 @@ void ChunkGenerator::blend_surface_blocks_borders(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (weights.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Find type with maximum total weight
|
// Find type with maximum total weight
|
||||||
BlockType final_type = type_self;
|
BlockType final_type = type_self;
|
||||||
float max_weight = weights[type_self];
|
/*float max_weight = weights[type_self];
|
||||||
for (const auto& [type, w] : weights) {
|
for (const auto& [type, w] : weights) {
|
||||||
if (w > max_weight) {
|
if (w > max_weight) {
|
||||||
max_weight = w;
|
max_weight = w;
|
||||||
final_type = type;
|
final_type = type;
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
float sum = 0.0f;
|
||||||
|
for (auto& kv : weights) {
|
||||||
|
sum += kv.second;
|
||||||
|
}
|
||||||
|
float rnd = m_random.random_float(0.0f, 1.0f);
|
||||||
|
float accum = 0.0f;
|
||||||
|
for (auto [t, w] : weights) {
|
||||||
|
accum += w / sum;
|
||||||
|
if (rnd < accum) {
|
||||||
|
final_type = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (final_type == 0) {
|
if (!BlockManager::is_transitional(final_type)) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the top block if the type changed
|
// Update the top block if the type changed
|
||||||
if (final_type != type_self) {
|
if (final_type != type_self) {
|
||||||
// top block
|
// top block
|
||||||
if (final_type == 7 && top_y > SEA_LEVEL) {
|
BlockType new_surface = final_type;
|
||||||
if (type_self == 7) {
|
m_blocks[Chunk::index(x, top_y, z)] = new_surface;
|
||||||
m_blocks[Chunk::index(x, top_y, z)] = 0;
|
|
||||||
} else {
|
|
||||||
m_blocks[Chunk::index(x, top_y, z)] = type_self;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
m_blocks[Chunk::index(x, top_y, z)] = final_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bottom block
|
// bottom block
|
||||||
unsigned fill_type = 2;
|
unsigned fill_type = 2;
|
||||||
if (final_type == 1) {
|
if (final_type == 1 || final_type == 8) {
|
||||||
fill_type = 2;
|
fill_type = 2;
|
||||||
} else if (final_type == 4) {
|
} else {
|
||||||
fill_type = 4;
|
fill_type = final_type;
|
||||||
}
|
}
|
||||||
for (int y = top_y - 5; y < top_y; y++) {
|
for (int y = std::max(0, top_y - 5); y < top_y; y++) {
|
||||||
if (fill_type == 7 && y > SEA_LEVEL) {
|
m_blocks[Chunk::index(x, y, z)] = fill_type;
|
||||||
m_blocks[Chunk::index(x, y, z)] = 0;
|
|
||||||
} else {
|
|
||||||
m_blocks[Chunk::index(x, y, z)] = fill_type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -600,12 +631,17 @@ void ChunkGenerator::make_biome_builder() {
|
|||||||
case SNOWY_PLAIN:
|
case SNOWY_PLAIN:
|
||||||
m_biome_builder = std::make_unique<SnowyPlainBuilder>(*this);
|
m_biome_builder = std::make_unique<SnowyPlainBuilder>(*this);
|
||||||
break;
|
break;
|
||||||
|
case OCEAN:
|
||||||
|
m_biome_builder = std::make_unique<OceanBuilder>(*this);
|
||||||
|
break;
|
||||||
case NONE:
|
case NONE:
|
||||||
m_biome_builder = nullptr;
|
m_biome_builder = nullptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChunkGenerator::ocean_build() { m_biome_builder->ocean_water_build(); }
|
||||||
|
|
||||||
void ChunkGenerator::generate_cave() {
|
void ChunkGenerator::generate_cave() {
|
||||||
auto& cave_carver = m_chunk.world().cave_carcer();
|
auto& cave_carver = m_chunk.world().cave_carcer();
|
||||||
auto& paths = cave_carver.paths();
|
auto& paths = cave_carver.paths();
|
||||||
@@ -617,9 +653,14 @@ void ChunkGenerator::generate_cave() {
|
|||||||
const int CHUNK_MAX_Z = CHUNK_MIN_Z + SIZE_Z - 1;
|
const int CHUNK_MAX_Z = CHUNK_MIN_Z + SIZE_Z - 1;
|
||||||
const int CHUNK_MIN_Y = 0;
|
const int CHUNK_MIN_Y = 0;
|
||||||
const int CHUNK_MAX_Y = SIZE_Y - 1;
|
const int CHUNK_MAX_Y = SIZE_Y - 1;
|
||||||
|
|
||||||
for (auto& [id, path] : paths) {
|
for (auto& [id, path] : paths) {
|
||||||
for (const auto& point : path.points()) {
|
for (const auto& point : path.points()) {
|
||||||
|
if ((m_chunk.biome() == BiomeType::RIVER) ||
|
||||||
|
(m_chunk.biome() == BiomeType::OCEAN)) {
|
||||||
|
path.clear_chunk(chunk_pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const glm::vec3& center = point.pos;
|
const glm::vec3& center = point.pos;
|
||||||
float rad_xz = point.rad_xz;
|
float rad_xz = point.rad_xz;
|
||||||
float rad_y = point.rad_y;
|
float rad_y = point.rad_y;
|
||||||
@@ -651,6 +692,13 @@ void ChunkGenerator::generate_cave() {
|
|||||||
if (y == 0) {
|
if (y == 0) {
|
||||||
continue;
|
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;
|
blocks[Chunk::index(x, y, z)] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -678,7 +726,8 @@ void ChunkGenerator::generate_river() {
|
|||||||
|
|
||||||
for (auto& [id, path] : paths) {
|
for (auto& [id, path] : paths) {
|
||||||
for (const auto& point : path.points()) {
|
for (const auto& point : path.points()) {
|
||||||
if (m_chunk.biome() == BiomeType::DESERT) {
|
if ((m_chunk.biome() == BiomeType::DESERT) ||
|
||||||
|
(m_chunk.biome() == BiomeType::OCEAN)) {
|
||||||
path.clear_chunk(chunk_pos);
|
path.clear_chunk(chunk_pos);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "Cubed/gameplay/tree.hpp"
|
#include "Cubed/gameplay/tree.hpp"
|
||||||
|
|
||||||
#include "Cubed/gameplay/chunk.hpp"
|
#include "Cubed/gameplay/chunk.hpp"
|
||||||
#include "Cubed/tools/log.hpp"
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
@@ -32,7 +31,6 @@ bool build_tree(Chunk& chunk, const glm::ivec3& pos) {
|
|||||||
auto& block = chunk.get_chunk_blocks();
|
auto& block = chunk.get_chunk_blocks();
|
||||||
|
|
||||||
if (block[Chunk::index(pos)] != 1) {
|
if (block[Chunk::index(pos)] != 1) {
|
||||||
Logger::info("Root is not Grass Block");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const auto& d : TREE) {
|
for (const auto& d : TREE) {
|
||||||
|
|||||||
@@ -320,7 +320,9 @@ void World::gen_chunks_internal() {
|
|||||||
m_chunk_gen_finished = false;
|
m_chunk_gen_finished = false;
|
||||||
ChunkPosSet required_chunks;
|
ChunkPosSet required_chunks;
|
||||||
ChunkHashMap temp_neighbor;
|
ChunkHashMap temp_neighbor;
|
||||||
compute_required_chunks(required_chunks, temp_neighbor);
|
std::vector<ChunkPos> need_gen_temp_chunks_pos;
|
||||||
|
compute_required_chunks(required_chunks, temp_neighbor,
|
||||||
|
need_gen_temp_chunks_pos);
|
||||||
|
|
||||||
ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!");
|
ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!");
|
||||||
|
|
||||||
@@ -339,10 +341,13 @@ void World::gen_chunks_internal() {
|
|||||||
m_chunk_gen_fraction = 0.1f;
|
m_chunk_gen_fraction = 0.1f;
|
||||||
|
|
||||||
ChunkUpdateList new_chunks;
|
ChunkUpdateList new_chunks;
|
||||||
|
ChunkHashMap new_temp_chunks;
|
||||||
for (auto& pos : need_gen_chunks_pos) {
|
for (auto& pos : need_gen_chunks_pos) {
|
||||||
new_chunks.push_back({pos, Chunk(*this, pos)});
|
new_chunks.push_back({pos, Chunk(*this, pos)});
|
||||||
}
|
}
|
||||||
|
for (auto& pos : need_gen_temp_chunks_pos) {
|
||||||
|
new_temp_chunks.emplace(pos, Chunk(*this, pos));
|
||||||
|
}
|
||||||
ConstChunkMap new_chunks_neighbor;
|
ConstChunkMap new_chunks_neighbor;
|
||||||
// affected neighbor
|
// affected neighbor
|
||||||
ChunkPtrUpdateList affected_neighbor;
|
ChunkPtrUpdateList affected_neighbor;
|
||||||
@@ -357,13 +362,15 @@ void World::gen_chunks_internal() {
|
|||||||
m_cave_carcer.try_to_add_path(pos, chunk.seed());
|
m_cave_carcer.try_to_add_path(pos, chunk.seed());
|
||||||
m_river_worm.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();
|
||||||
|
}
|
||||||
// precompute path to ensure the continuity of the path
|
// precompute path to ensure the continuity of the path
|
||||||
for (auto& [pos, chunk] : temp_neighbor) {
|
for (auto& [pos, chunk] : temp_neighbor) {
|
||||||
chunk.gen_phase_one();
|
chunk.gen_phase_one();
|
||||||
m_cave_carcer.try_to_add_path(pos, chunk.seed());
|
m_cave_carcer.try_to_add_path(pos, chunk.seed());
|
||||||
m_river_worm.try_to_add_path(pos, chunk.seed());
|
m_river_worm.try_to_add_path(pos, chunk.seed());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_chunk_gen_fraction = 0.2f;
|
m_chunk_gen_fraction = 0.2f;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -403,6 +410,9 @@ void World::gen_chunks_internal() {
|
|||||||
for (auto& [pos, chunks] : new_chunks) {
|
for (auto& [pos, chunks] : new_chunks) {
|
||||||
chunks.gen_phase_three();
|
chunks.gen_phase_three();
|
||||||
}
|
}
|
||||||
|
for (auto& [pos, chunk] : new_temp_chunks) {
|
||||||
|
chunk.gen_phase_three();
|
||||||
|
}
|
||||||
// for (auto& [pos, chunks] : temp_neighbor) {
|
// for (auto& [pos, chunks] : temp_neighbor) {
|
||||||
// chunks.gen_phase_three();
|
// chunks.gen_phase_three();
|
||||||
// }
|
// }
|
||||||
@@ -457,7 +467,9 @@ void World::gen_chunks_internal() {
|
|||||||
for (auto& [pos, chunks] : new_chunks) {
|
for (auto& [pos, chunks] : new_chunks) {
|
||||||
chunks.gen_phase_five();
|
chunks.gen_phase_five();
|
||||||
}
|
}
|
||||||
|
for (auto& [pos, chunk] : new_temp_chunks) {
|
||||||
|
chunk.gen_phase_five();
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
for (auto& [pos, chunks] : temp_neighbor) {
|
for (auto& [pos, chunks] : temp_neighbor) {
|
||||||
chunks.gen_phase_five();
|
chunks.gen_phase_five();
|
||||||
@@ -472,7 +484,14 @@ void World::gen_chunks_internal() {
|
|||||||
auto neighbor_pos = pos + CHUNK_DIR[i];
|
auto neighbor_pos = pos + CHUNK_DIR[i];
|
||||||
auto it = new_chunks_neighbor.find(neighbor_pos);
|
auto it = new_chunks_neighbor.find(neighbor_pos);
|
||||||
if (it == new_chunks_neighbor.end()) {
|
if (it == new_chunks_neighbor.end()) {
|
||||||
neighbor_blocks_data[i] = std::nullopt;
|
auto it = new_temp_chunks.find(neighbor_pos);
|
||||||
|
if (it == new_temp_chunks.end()) {
|
||||||
|
neighbor_blocks_data[i] = std::nullopt;
|
||||||
|
Logger::warn(
|
||||||
|
"Can't find neighbor for chunk surface blend");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
neighbor_blocks_data[i] = it->second.get_chunk_blocks();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
neighbor_blocks_data[i] = it->second->get_chunk_blocks();
|
neighbor_blocks_data[i] = it->second->get_chunk_blocks();
|
||||||
@@ -539,8 +558,9 @@ void World::sync_player_pos(glm::vec3& player_pos) {
|
|||||||
player_pos = m_gen_player_pos;
|
player_pos = m_gen_player_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::compute_required_chunks(ChunkPosSet& required_chunks,
|
void World::compute_required_chunks(
|
||||||
ChunkHashMap& temp_neighbor) {
|
ChunkPosSet& required_chunks, ChunkHashMap& temp_neighbor,
|
||||||
|
std::vector<ChunkPos>& need_gen_temp_chunks_pos) {
|
||||||
glm::vec3 player_pos;
|
glm::vec3 player_pos;
|
||||||
sync_player_pos(player_pos);
|
sync_player_pos(player_pos);
|
||||||
|
|
||||||
@@ -555,6 +575,15 @@ void World::compute_required_chunks(ChunkPosSet& required_chunks,
|
|||||||
required_chunks.emplace(u, v);
|
required_chunks.emplace(u, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 max_path_len = std::max(CavePath::step_max(), RiverPath::step_max());
|
int max_path_len = std::max(CavePath::step_max(), RiverPath::step_max());
|
||||||
half = std::ceil(static_cast<float>(max_path_len) / CHUNK_SIZE) * 2;
|
half = std::ceil(static_cast<float>(max_path_len) / CHUNK_SIZE) * 2;
|
||||||
for (int u = chunk_x - half; u <= chunk_x + half; ++u) {
|
for (int u = chunk_x - half; u <= chunk_x + half; ++u) {
|
||||||
|
|||||||
@@ -59,7 +59,12 @@ bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
float deterministic_random(int x, int z, uint64_t seed) {
|
||||||
|
uint64_t h = seed;
|
||||||
|
h = h * 6364136223846793005ULL + (uint64_t)x;
|
||||||
|
h = h * 6364136223846793005ULL + (uint64_t)z;
|
||||||
|
return (float)(h >> 40) / (float)(1 << 24);
|
||||||
|
}
|
||||||
} // namespace Math
|
} // namespace Math
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
Reference in New Issue
Block a user