feat: improve mountain realism

This commit is contained in:
2026-05-22 20:33:38 +08:00
parent 17c7ad989d
commit ca4d767a52
6 changed files with 85 additions and 39 deletions

View File

@@ -7,8 +7,15 @@ namespace Cubed {
constexpr float BIOME_NOISE_FREQUENCY = 0.03f; constexpr float BIOME_NOISE_FREQUENCY = 0.03f;
constexpr float HEIGHTMAP_NOISE_FREQUENCY = 0.001f; constexpr float HEIGHTMAP_NOISE_FREQUENCY = 0.001f;
constexpr float MOUNTAINOUS_NOISE_FREQUENCY = 0.003f;
enum class BiomeType { PLAIN = 0, FOREST, DESERT, MOUNTAIN, RIVER, NONE }; enum class BiomeType { PLAIN = 0, FOREST, DESERT, MOUNTAIN, RIVER, NONE };
struct BiomeConditions {
float temp = 0.0f;
float humid = 0.0f;
float mountainous = 0.0f;
};
struct BiomeHeightRange { struct BiomeHeightRange {
int base_y; int base_y;
int amplitude; int amplitude;
@@ -47,13 +54,14 @@ struct MountainParams : public BaseBiomeParams {};
struct RiverParams : public BaseBiomeParams {}; struct RiverParams : public BaseBiomeParams {};
std::string get_biome_str(BiomeType biome); std::string get_biome_str(BiomeType biome);
BiomeType get_biome_from_noise(float temp, float humid);
std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome); std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome);
BiomeHeightRange get_biome_height_range(BiomeType biome); BiomeHeightRange get_biome_height_range(BiomeType biome);
BiomeType safe_int_to_biome(int x); BiomeType safe_int_to_biome(int x);
int get_interpolated_height(float world_x, float world_z, float temp, int get_interpolated_height(float world_x, float world_z, float temp,
float humid); float humid);
BiomeType determine_biome(const BiomeConditions& conditions);
PlainParams& plain_params(); PlainParams& plain_params();
ForestParams& forest_params(); ForestParams& forest_params();
DesertParams& desert_params(); DesertParams& desert_params();

View File

@@ -38,6 +38,9 @@ private:
float frequency = 0.01f; float frequency = 0.01f;
float height = 80; float height = 80;
unsigned m_seed = 0; unsigned m_seed = 0;
BiomeConditions m_conditions;
void clear_dirty(); void clear_dirty();
public: public:
@@ -108,6 +111,7 @@ public:
std::vector<BlockType>& blocks(); std::vector<BlockType>& blocks();
World& world(); World& world();
unsigned seed() const; unsigned seed() const;
BiomeConditions& conditions();
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -7,6 +7,8 @@
namespace Cubed { namespace Cubed {
using enum BiomeType;
static PlainParams plain{{BiomeType::PLAIN, static PlainParams plain{{BiomeType::PLAIN,
{0.0f, 0.5f}, {0.0f, 0.5f},
{0.0f, 0.5f}, {0.0f, 0.5f},
@@ -84,27 +86,7 @@ Biome get_biome_from_noise(float temp, float humid) {
return Biome::FOREST; return Biome::FOREST;
} }
*/ */
BiomeType get_biome_from_noise(float temp, float humid) {
using enum BiomeType;
if (plain.temp.first <= temp && temp < plain.temp.second &&
plain.humid.first <= humid && humid < plain.humid.second) {
return PLAIN;
}
if (forest.temp.first <= temp && temp < forest.temp.second &&
forest.humid.first <= humid && humid < forest.humid.second) {
return FOREST;
}
if (desert.temp.first <= temp && temp < desert.temp.second &&
desert.humid.first <= humid && humid < desert.humid.second) {
return DESERT;
}
if (mountain.temp.first <= temp && temp <= mountain.temp.second &&
mountain.humid.first <= humid && humid <= mountain.humid.second) {
return MOUNTAIN;
}
Logger::warn("Invail Temp {} or Humid {}", temp, humid);
return PLAIN;
}
std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome) { std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome) {
using enum BiomeType; using enum BiomeType;
switch (biome) { switch (biome) {
@@ -201,6 +183,29 @@ int get_interpolated_height(float world_x, float world_z, float temp,
return static_cast<int>(h); return static_cast<int>(h);
} }
*/ */
BiomeType determine_biome(const BiomeConditions& conditions) {
if (conditions.mountainous > 0.85) {
return MOUNTAIN;
}
auto temp = conditions.temp;
auto humid = conditions.humid;
if (plain.temp.first <= temp && temp < plain.temp.second &&
plain.humid.first <= humid && humid < plain.humid.second) {
return PLAIN;
}
if (forest.temp.first <= temp && temp < forest.temp.second &&
forest.humid.first <= humid && humid < forest.humid.second) {
return FOREST;
}
if (desert.temp.first <= temp && temp < desert.temp.second &&
desert.humid.first <= humid && humid < desert.humid.second) {
return DESERT;
}
return PLAIN;
}
PlainParams& plain_params() { return plain; } PlainParams& plain_params() { return plain; }
ForestParams& forest_params() { return forest; } ForestParams& forest_params() { return forest; }
DesertParams& desert_params() { return desert; } DesertParams& desert_params() { return desert; }

View File

@@ -18,21 +18,9 @@ void MountainBuilder::build_blocks() {
for (int x = 0; x < CHUNK_SIZE; x++) { for (int x = 0; x < CHUNK_SIZE; x++) {
for (int z = 0; z < CHUNK_SIZE; z++) { for (int z = 0; z < CHUNK_SIZE; z++) {
int height = static_cast<int>(m_heightmap[x][z]); int height = static_cast<int>(m_heightmap[x][z]);
for (int y = 5; y < height - 5; y++) { for (int y = 5; y <= height; y++) {
m_blocks[Chunk::index(x, y, z)] = 3; m_blocks[Chunk::index(x, y, z)] = 3;
} }
for (int y = height - 5; y <= height - 1; y++) {
if (y > 110) {
m_blocks[Chunk::index(x, y, z)] = 3;
} else {
m_blocks[Chunk::index(x, y, z)] = 2;
}
}
if (height > 110) {
m_blocks[Chunk::index(x, height, z)] = 3;
} else {
m_blocks[Chunk::index(x, height, z)] = 1;
}
} }
} }
} }

View File

@@ -24,7 +24,8 @@ Chunk::Chunk(Chunk&& other) noexcept
m_chunk_pos(std::move(other.m_chunk_pos)), m_world(other.m_world), m_chunk_pos(std::move(other.m_chunk_pos)), m_world(other.m_world),
m_heightmap(std::move(other.m_heightmap)), m_heightmap(std::move(other.m_heightmap)),
m_blocks(std::move(other.m_blocks)), m_vbo(other.m_vbo), m_blocks(std::move(other.m_blocks)), m_vbo(other.m_vbo),
m_vertexs_data(std::move(other.m_vertexs_data)), m_seed(other.m_seed) { m_vertexs_data(std::move(other.m_vertexs_data)), m_seed(other.m_seed),
m_conditions(other.m_conditions) {
other.m_vbo = 0; other.m_vbo = 0;
} }
@@ -44,6 +45,7 @@ Chunk& Chunk::operator=(Chunk&& other) noexcept {
m_need_upload = other.m_need_upload.load(); m_need_upload = other.m_need_upload.load();
m_vertex_sum = other.m_vertex_sum.load(); m_vertex_sum = other.m_vertex_sum.load();
m_seed = other.m_seed; m_seed = other.m_seed;
m_conditions = other.m_conditions;
return *this; return *this;
} }
@@ -349,4 +351,7 @@ unsigned Chunk::seed() const {
} }
return m_seed; return m_seed;
} }
BiomeConditions& Chunk::conditions() { return m_conditions; }
} // namespace Cubed } // namespace Cubed

View File

@@ -9,6 +9,7 @@
#include "Cubed/gameplay/tree.hpp" #include "Cubed/gameplay/tree.hpp"
#include "Cubed/gameplay/world.hpp" #include "Cubed/gameplay/world.hpp"
#include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/cubed_hash.hpp"
#include "Cubed/tools/math_tools.hpp"
#include "Cubed/tools/perlin_noise.hpp" #include "Cubed/tools/perlin_noise.hpp"
namespace Cubed { namespace Cubed {
@@ -59,7 +60,16 @@ void ChunkGenerator::assign_chunk_biome() {
z * BIOME_NOISE_FREQUENCY); z * BIOME_NOISE_FREQUENCY);
float humid = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 1.0f, float humid = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 1.0f,
z * BIOME_NOISE_FREQUENCY); z * BIOME_NOISE_FREQUENCY);
auto biome = get_biome_from_noise(temp, humid); float center_x = static_cast<float>(SIZE_X / 2) + x * CHUNK_SIZE + 0.5f;
float center_z = static_cast<float>(SIZE_Z / 2) + z * CHUNK_SIZE + 0.5f;
float mountainous =
PerlinNoise2D::noise(center_x * MOUNTAINOUS_NOISE_FREQUENCY,
center_z * MOUNTAINOUS_NOISE_FREQUENCY);
auto& conditions = m_chunk.conditions();
conditions.mountainous = mountainous;
conditions.humid = humid;
conditions.temp = temp;
auto biome = determine_biome(conditions);
m_chunk.biome(biome); m_chunk.biome(biome);
} }
@@ -139,11 +149,37 @@ void ChunkGenerator::generate_heightmap() {
} }
return value; return value;
}; };
int octaves = 4; int octaves = 4;
float lacunarity = 2.0f; float lacunarity = 2.0f;
float gain = 0.5f; float gain = 0.5f;
heightmap[x][z] = 64 + fbm_height(world_x, world_z, octaves, float base_y = 64;
lacunarity, gain, 40, 0.005f); float amplitude = 40.0f;
float mountainous =
PerlinNoise2D::noise(world_x * MOUNTAINOUS_NOISE_FREQUENCY,
world_z * MOUNTAINOUS_NOISE_FREQUENCY);
/*
float t = Math::smootherstep(0.6, 0.7, mountainous);
base_y = std::lerp(64, 85, t);
amplitude = std::lerp(10, 40, t);
*/
float t;
if (mountainous >= 0.8f) {
t = Math::smootherstep(0.80f, 0.85, mountainous);
base_y = std::lerp(72, 88, t);
amplitude = std::lerp(28, 48, t);
} else if (mountainous >= 0.75f) {
t = Math::smootherstep(0.75f, 0.80f, mountainous);
base_y = std::lerp(68, 72, t);
amplitude = std::lerp(18, 28, t);
} else {
t = Math::smootherstep(0.5, 0.75, mountainous);
base_y = std::lerp(60, 68, t);
amplitude = std::lerp(8, 18, t);
}
heightmap[x][z] =
base_y + fbm_height(world_x, world_z, octaves, lacunarity, gain,
amplitude, 0.005f);
} }
} }
} }