From 45836dc897536984bd6b9e97baefc8b855e46061 Mon Sep 17 00:00:00 2001 From: szdytom Date: Sun, 3 Aug 2025 11:10:03 +0800 Subject: [PATCH] fix: mountain smoothing logic Signed-off-by: szdytom --- tilemap/include/generation.h | 5 ++ tilemap/src/pass/smoothen_mountain.cpp | 90 +++++++++++++++++--------- 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/tilemap/include/generation.h b/tilemap/include/generation.h index 13a2aeb..6fb95f1 100644 --- a/tilemap/include/generation.h +++ b/tilemap/include/generation.h @@ -210,6 +210,11 @@ private: */ 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> &replacements + ); + public: /** * @brief Construct a mountain smoothing pass diff --git a/tilemap/src/pass/smoothen_mountain.cpp b/tilemap/src/pass/smoothen_mountain.cpp index 78e5034..ca60ca4 100644 --- a/tilemap/src/pass/smoothen_mountain.cpp +++ b/tilemap/src/pass/smoothen_mountain.cpp @@ -1,6 +1,7 @@ #include "generation.h" #include #include +#include #include namespace istd { @@ -156,8 +157,9 @@ std::uint32_t SmoothenMountainsPass::bfs_component_size( return size; } -void SmoothenMountainsPass::smoothen_mountains( - TileMap &tilemap, std::uint32_t step_i +void SmoothenMountainsPass::smoothen_mountains_tile( + const TileMap &tilemap, TilePos pos, std::uint32_t step_i, + std::vector> &replacements ) { struct CAConf { int neighbor_count; @@ -175,6 +177,55 @@ void SmoothenMountainsPass::smoothen_mountains( {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(); + + 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> replacements; + 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(); ++chunk_y) { @@ -182,40 +233,15 @@ void SmoothenMountainsPass::smoothen_mountains( for (std::uint8_t local_y = 0; local_y < Chunk::size; ++local_y) { TilePos pos{chunk_x, chunk_y, local_x, local_y}; - auto [global_x, global_y] = pos.to_global(); - 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; - } + smoothen_mountains_tile(tilemap, pos, step_i, replacements); } } } } + + for (const auto &[pos, new_tile] : replacements) { + tilemap.set_tile(pos, new_tile); + } } void TerrainGenerator::smoothen_mountains_pass(TileMap &tilemap) {