fix: mountain smoothing logic

Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
方而静 2025-08-03 11:10:03 +08:00
parent e7b9fd856f
commit 45836dc897
Signed by: szTom
GPG Key ID: 072D999D60C6473C
2 changed files with 63 additions and 32 deletions

View File

@ -210,6 +210,11 @@ private:
*/ */
void smoothen_mountains(TileMap &tilemap, std::uint32_t step_i); void smoothen_mountains(TileMap &tilemap, std::uint32_t step_i);
void smoothen_mountains_tile(
const TileMap &tilemap, TilePos pos, std::uint32_t step_i,
std::vector<std::pair<TilePos, Tile>> &replacements
);
public: public:
/** /**
* @brief Construct a mountain smoothing pass * @brief Construct a mountain smoothing pass

View File

@ -1,6 +1,7 @@
#include "generation.h" #include "generation.h"
#include <map> #include <map>
#include <queue> #include <queue>
#include <ranges>
#include <set> #include <set>
namespace istd { namespace istd {
@ -156,8 +157,9 @@ std::uint32_t SmoothenMountainsPass::bfs_component_size(
return size; return size;
} }
void SmoothenMountainsPass::smoothen_mountains( void SmoothenMountainsPass::smoothen_mountains_tile(
TileMap &tilemap, std::uint32_t step_i const TileMap &tilemap, TilePos pos, std::uint32_t step_i,
std::vector<std::pair<TilePos, Tile>> &replacements
) { ) {
struct CAConf { struct CAConf {
int neighbor_count; int neighbor_count;
@ -175,6 +177,55 @@ void SmoothenMountainsPass::smoothen_mountains(
{4, 16, 0 } {4, 16, 0 }
}; };
auto [global_x, global_y] = pos.to_global();
auto neighbors = tilemap.get_neighbors(pos);
// Ignore if adjacent to the boundary
if (neighbors.size() < 4) {
return;
}
// Count neighboring mountains
int mountain_count = 0;
for (const auto &neighbor : neighbors) {
const Tile &tile = tilemap.get_tile(neighbor);
if (tile.base == BaseTileType::Mountain) {
mountain_count += 1;
}
}
// Get the configuration for this count
const CAConf &conf = cellularAutomataConfigurations[mountain_count];
auto sample = noise_.noise(global_x, global_y, step_i);
auto rd = sample & 0xF;
auto sel = sample >> 4;
Tile tile = tilemap.get_tile(pos);
if (tile.base == BaseTileType::Mountain && conf.remove_chance > rd) {
auto filterer = [&tilemap](const TilePos &p) {
auto neighbor_tile = tilemap.get_tile(p);
return neighbor_tile.base != BaseTileType::Mountain;
};
auto non_mountain_neighbors = neighbors | std::views::filter(filterer)
| std::ranges::to<std::vector>();
if (!non_mountain_neighbors.empty()) {
auto n = non_mountain_neighbors.size();
auto replacement = non_mountain_neighbors[sel % n];
tile.base = tilemap.get_tile(replacement).base;
replacements.emplace_back(pos, tile);
}
} else if (tile.base != BaseTileType::Mountain && conf.fill_chance > rd) {
tile.base = BaseTileType::Mountain;
replacements.emplace_back(pos, tile);
}
}
void SmoothenMountainsPass::smoothen_mountains(
TileMap &tilemap, std::uint32_t step_i
) {
std::vector<std::pair<TilePos, Tile>> replacements;
for (std::uint8_t chunk_x = 0; chunk_x < tilemap.get_size(); ++chunk_x) { for (std::uint8_t chunk_x = 0; chunk_x < tilemap.get_size(); ++chunk_x) {
for (std::uint8_t chunk_y = 0; chunk_y < tilemap.get_size(); for (std::uint8_t chunk_y = 0; chunk_y < tilemap.get_size();
++chunk_y) { ++chunk_y) {
@ -182,40 +233,15 @@ void SmoothenMountainsPass::smoothen_mountains(
for (std::uint8_t local_y = 0; local_y < Chunk::size; for (std::uint8_t local_y = 0; local_y < Chunk::size;
++local_y) { ++local_y) {
TilePos pos{chunk_x, chunk_y, local_x, local_y}; TilePos pos{chunk_x, chunk_y, local_x, local_y};
auto [global_x, global_y] = pos.to_global(); smoothen_mountains_tile(tilemap, pos, step_i, replacements);
auto neighbors = tilemap.get_neighbors(pos);
// Ignore if adjacent to the boundary
if (neighbors.size() < 4) {
continue;
}
// Count neighboring mountains
int mountain_count = 0;
for (const auto &neighbor : neighbors) {
const Tile &tile = tilemap.get_tile(neighbor);
if (tile.base == BaseTileType::Mountain) {
mountain_count += 1;
}
}
// Get the configuration for this count
const CAConf &conf
= cellularAutomataConfigurations[mountain_count];
int rd = noise_.noise(global_x, global_y, step_i) & 0xF;
Tile &tile = tilemap.get_tile(pos);
if (tile.base == BaseTileType::Mountain
&& conf.remove_chance > rd) {
demountainize(tilemap, {pos});
} else if (tile.base != BaseTileType::Mountain
&& conf.fill_chance > rd) {
tile.base = BaseTileType::Mountain;
}
} }
} }
} }
} }
for (const auto &[pos, new_tile] : replacements) {
tilemap.set_tile(pos, new_tile);
}
} }
void TerrainGenerator::smoothen_mountains_pass(TileMap &tilemap) { void TerrainGenerator::smoothen_mountains_pass(TileMap &tilemap) {