diff --git a/.clang-format b/.clang-format index 20aae66..f2d1033 100644 --- a/.clang-format +++ b/.clang-format @@ -46,6 +46,7 @@ BraceWrapping: SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false +BracedInitializerIndentWidth: 4 BreakBeforeBinaryOperators: All BreakBeforeConceptDeclarations: true BreakBeforeBraces: Attach diff --git a/tilemap/include/biome.h b/tilemap/include/biome.h index 3b88539..d7482b7 100644 --- a/tilemap/include/biome.h +++ b/tilemap/include/biome.h @@ -8,41 +8,43 @@ namespace istd { // Biome types based on temperature and humidity enum class BiomeType : std::uint8_t { - Desert = 0, // Hot & Dry - Savanna = 1, // Hot & Moderate - TropicalRainforest = 2, // Hot & Wet - Grassland = 3, // Temperate & Dry - DeciduousForest = 4, // Temperate & Moderate - TemperateRainforest = 5, // Temperate & Wet - Tundra = 6, // Cold & Dry - Taiga = 7, // Cold & Moderate - FrozenOcean = 8 // Cold & Wet + SnowyPeeks = 0, // Cold & Dry + SnowyPlains = 1, // Cold & Moderate + FrozenOcean = 2, // Cold & Wet + Plains = 3, // Temperate & Dry + Forest = 4, // Temperate & Moderate + Ocean = 5, // Temperate & Wet + Desert = 6, // Hot & Dry + Savanna = 7, // Hot & Moderate + LukeOcean = 8, // Hot & Wet +}; + +enum class BiomeTemperature : std::uint8_t { + Cold = 0, + Temperate = 1, + Hot = 2, +}; + +enum class BiomeHumidity : std::uint8_t { + Dry = 0, + Moderate = 1, + Wet = 2, }; // Biome properties for terrain generation struct BiomeProperties { - // Base terrain thresholds (0.0 - 1.0) - double water_threshold; - double mountain_threshold; - double sand_threshold; - double ice_threshold; - - // Surface coverage thresholds (0.0 - 1.0) - double wood_threshold; - double snow_threshold; - - // Noise parameters for base terrain - double base_scale; - int base_octaves; - double base_persistence; - - // Noise parameters for surface features - double surface_scale; - int surface_octaves; - double surface_persistence; - // Biome name for debugging std::string_view name; + + // Base terrain thresholds (0.0 - 1.0) + double water_threshold; + double ice_threshold; + double sand_threshold; + double land_threshold; + + // Noise parameters for base terrain + int base_octaves = 3; + double base_persistence = 0.5; }; // Get biome properties for terrain generation @@ -59,13 +61,6 @@ struct SubChunkPos { constexpr SubChunkPos(std::uint8_t x, std::uint8_t y): sub_x(x), sub_y(y) {} }; -// Convert local tile coordinates to sub-chunk position -constexpr SubChunkPos tile_to_subchunk( - std::uint8_t local_x, std::uint8_t local_y -) { - return SubChunkPos(local_x / 16, local_y / 16); -} - // Get the starting tile coordinates for a sub-chunk constexpr std::pair subchunk_to_tile_start( const SubChunkPos &pos diff --git a/tilemap/include/generation.h b/tilemap/include/generation.h index 96e1bec..f1f7610 100644 --- a/tilemap/include/generation.h +++ b/tilemap/include/generation.h @@ -14,17 +14,25 @@ namespace istd { struct GenerationConfig { std::uint64_t seed = 0; // Seed for random generation - // Climate noise parameters + // Noise parameters double temperature_scale = 0.005; // Scale for temperature noise double humidity_scale = 0.007; // Scale for humidity noise + double base_scale = 0.08; // Scale for base terrain noise }; // Terrain generator class that manages the generation process class TerrainGenerator { private: GenerationConfig config_; + + // Just some random numbers to mask the seeds + static constexpr std::uint64_t base_seed_mask = 0x06'a9'cb'b1'b3'96'f3'50; + static constexpr std::uint64_t temperature_seed_mask + = 0x79'c8'a7'a1'09'99'd0'e3; + static constexpr std::uint64_t humidity_seed_mask + = 0x5e'10'be'e4'd2'6f'34'c2; + PerlinNoise base_noise_; // For base terrain generation - PerlinNoise surface_noise_; // For surface feature generation PerlinNoise temperature_noise_; // For temperature PerlinNoise humidity_noise_; // For humidity @@ -91,19 +99,6 @@ private: BaseTileType determine_base_type( double noise_value, const BiomeProperties &properties ) const; - - /** - * @brief Determine surface feature type based on noise value and biome - * properties - * @param noise_value Surface feature noise value [0,1] - * @param properties Biome properties to use - * @param base_type The base terrain type (affects surface placement) - * @return The appropriate surface tile type - */ - SurfaceTileType determine_surface_type( - double noise_value, const BiomeProperties &properties, - BaseTileType base_type - ) const; }; /** diff --git a/tilemap/include/tile.h b/tilemap/include/tile.h index bfda0d0..1f30d7d 100644 --- a/tilemap/include/tile.h +++ b/tilemap/include/tile.h @@ -18,7 +18,6 @@ enum class BaseTileType : std::uint8_t { enum class SurfaceTileType : std::uint8_t { Empty, Wood, - Snow, Structure, // Indicates this tile is occupied by a player-built structure, // should never be natually generated. _count diff --git a/tilemap/src/biome.cpp b/tilemap/src/biome.cpp index 0062a0f..6020c97 100644 --- a/tilemap/src/biome.cpp +++ b/tilemap/src/biome.cpp @@ -1,198 +1,118 @@ #include "biome.h" +#include #include #include -#include namespace istd { // Biome properties lookup table -constexpr std::array biome_properties = {{ - // Desert: Hot & Dry - { - .water_threshold = 0.1, - .mountain_threshold = 0.85, - .sand_threshold = 0.8, - .ice_threshold = 0.0, // No ice in desert - .wood_threshold = 0.05, // Very sparse vegetation - .snow_threshold = 0.0, // No snow in desert - .base_scale = 0.03, - .base_octaves = 3, - .base_persistence = 0.4, - .surface_scale = 0.05, - .surface_octaves = 2, - .surface_persistence = 0.3, - .name = "Desert" - }, - // Savanna: Hot & Moderate - { - .water_threshold = 0.15, - .mountain_threshold = 0.8, - .sand_threshold = 0.3, - .ice_threshold = 0.0, - .wood_threshold = 0.25, // Scattered trees - .snow_threshold = 0.0, - .base_scale = 0.025, - .base_octaves = 4, - .base_persistence = 0.5, - .surface_scale = 0.04, - .surface_octaves = 3, - .surface_persistence = 0.4, - .name = "Savanna" - }, - // TropicalRainforest: Hot & Wet - { - .water_threshold = 0.25, - .mountain_threshold = 0.85, - .sand_threshold = 0.1, - .ice_threshold = 0.0, - .wood_threshold = 0.7, // Dense forest - .snow_threshold = 0.0, - .base_scale = 0.02, - .base_octaves = 5, - .base_persistence = 0.6, - .surface_scale = 0.03, - .surface_octaves = 4, - .surface_persistence = 0.5, - .name = "Tropical Rainforest" - }, - // Grassland: Temperate & Dry - { - .water_threshold = 0.2, - .mountain_threshold = 0.8, - .sand_threshold = 0.15, - .ice_threshold = 0.0, - .wood_threshold = 0.15, // Sparse trees - .snow_threshold = 0.05, // Occasional snow - .base_scale = 0.035, - .base_octaves = 3, - .base_persistence = 0.45, - .surface_scale = 0.06, - .surface_octaves = 2, - .surface_persistence = 0.35, - .name = "Grassland" - }, - // DeciduousForest: Temperate & Moderate - { - .water_threshold = 0.3, - .mountain_threshold = 0.82, - .sand_threshold = 0.05, - .ice_threshold = 0.0, - .wood_threshold = 0.6, // Dense deciduous forest - .snow_threshold = 0.1, // Some snow coverage - .base_scale = 0.025, - .base_octaves = 4, - .base_persistence = 0.55, - .surface_scale = 0.04, - .surface_octaves = 3, - .surface_persistence = 0.45, - .name = "Deciduous Forest" - }, - // TemperateRainforest: Temperate & Wet - { - .water_threshold = 0.35, - .mountain_threshold = 0.85, - .sand_threshold = 0.05, - .ice_threshold = 0.0, - .wood_threshold = 0.8, // Very dense forest - .snow_threshold = 0.15, // More snow - .base_scale = 0.02, - .base_octaves = 5, - .base_persistence = 0.6, - .surface_scale = 0.03, - .surface_octaves = 4, - .surface_persistence = 0.5, - .name = "Temperate Rainforest" - }, - // Tundra: Cold & Dry - { - .water_threshold = 0.15, - .mountain_threshold = 0.75, - .sand_threshold = 0.05, - .ice_threshold = 0.3, // Lots of ice - .wood_threshold = 0.05, // Very sparse vegetation - .snow_threshold = 0.4, // Lots of snow - .base_scale = 0.04, - .base_octaves = 2, - .base_persistence = 0.3, - .surface_scale = 0.08, - .surface_octaves = 2, - .surface_persistence = 0.25, - .name = "Tundra" - }, - // Taiga: Cold & Moderate - { - .water_threshold = 0.25, - .mountain_threshold = 0.8, - .sand_threshold = 0.02, - .ice_threshold = 0.15, - .wood_threshold = 0.5, // Coniferous forest - .snow_threshold = 0.6, // Heavy snow coverage - .base_scale = 0.03, - .base_octaves = 4, - .base_persistence = 0.5, - .surface_scale = 0.05, - .surface_octaves = 3, - .surface_persistence = 0.4, - .name = "Taiga" - }, - // FrozenOcean: Cold & Wet - { - .water_threshold = 0.4, - .mountain_threshold = 0.85, - .sand_threshold = 0.02, - .ice_threshold = 0.6, // Mostly ice - .wood_threshold = 0.1, // Very sparse trees - .snow_threshold = 0.8, // Almost all snow - .base_scale = 0.025, - .base_octaves = 3, - .base_persistence = 0.4, - .surface_scale = 0.04, - .surface_octaves = 2, - .surface_persistence = 0.3, - .name = "Frozen Ocean" - } -}}; +constexpr BiomeProperties biome_properties[] = { + // Snowy Peeks (Cold & Dry) + { + .name = "Snowy Peeks", -const BiomeProperties& get_biome_properties(BiomeType biome) { - return biome_properties[static_cast(biome)]; + .water_threshold = .05, + .ice_threshold = .15, + .sand_threshold = .1, + .land_threshold = .2, + }, + // Snowy Plains (Cold & Moderate) + { + .name = "Snowy Plains", + .water_threshold = .05, + .ice_threshold = .25, + .sand_threshold = .1, + .land_threshold = .4, + }, + // Frozen Ocean (Cold & Wet) + { + .name = "Frozen Ocean", + .water_threshold = .3, + .ice_threshold = .4, + .sand_threshold = .25, + .land_threshold = .05, + }, + // Plains (Temperate & Dry) + { + .name = "Plains", + .water_threshold = .1, + .ice_threshold = .0, + .sand_threshold = .05, + .land_threshold = .65, + }, + // Forest (Temperate & Moderate) + { + .name = "Forest", + .water_threshold = .2, + .ice_threshold = .0, + .sand_threshold = .1, + .land_threshold = .5, + }, + // Ocean (Temperate & Wet) + { + .name = "Ocean", + .water_threshold = .7, + .ice_threshold = .0, + .sand_threshold = .2, + .land_threshold = .1, + }, + // Desert (Hot & Dry) + { + .name = "Desert", + .water_threshold = .0, + .ice_threshold = .0, + .sand_threshold = .75, + .land_threshold = .05, + }, + // Savanna (Hot & Moderate) + { + .name = "Savanna", + .water_threshold = .2, + .ice_threshold = .0, + .sand_threshold = .1, + .land_threshold = .5, + }, + // Luke Ocean (Hot & Wet) + { + .name = "Luke Ocean", + .water_threshold = .8, + .ice_threshold = .0, + .sand_threshold = .2, + .land_threshold = .0, + }, +}; + +const BiomeProperties &get_biome_properties(BiomeType biome) { + return biome_properties[static_cast(biome)]; } BiomeType determine_biome(double temperature, double humidity) { - // Normalize temperature and humidity to 0-1 range if needed - temperature = std::clamp(temperature, 0.0, 1.0); - humidity = std::clamp(humidity, 0.0, 1.0); - - // Determine temperature category - int temp_category; - if (temperature < 0.33) { - temp_category = 0; // Cold - } else if (temperature < 0.67) { - temp_category = 1; // Temperate - } else { - temp_category = 2; // Hot - } - - // Determine humidity category - int humidity_category; - if (humidity < 0.33) { - humidity_category = 0; // Dry - } else if (humidity < 0.67) { - humidity_category = 1; // Moderate - } else { - humidity_category = 2; // Wet - } - - // Map to biome type (3x3 grid) - static constexpr BiomeType biome_matrix[3][3] = { - // Cold row - {BiomeType::Tundra, BiomeType::Taiga, BiomeType::FrozenOcean}, - // Temperate row - {BiomeType::Grassland, BiomeType::DeciduousForest, BiomeType::TemperateRainforest}, - // Hot row - {BiomeType::Desert, BiomeType::Savanna, BiomeType::TropicalRainforest} - }; - - return biome_matrix[temp_category][humidity_category]; + // Normalize temperature and humidity to 0-1 range if needed + temperature = std::clamp(temperature, 0.0, 1.0); + humidity = std::clamp(humidity, 0.0, 1.0); + + BiomeTemperature temp_category; + if (temperature < 0.33) { + temp_category = BiomeTemperature::Cold; + } else if (temperature < 0.66) { + temp_category = BiomeTemperature::Temperate; + } else { + temp_category = BiomeTemperature::Hot; + } + + BiomeHumidity humidity_category; + if (humidity < 0.33) { + humidity_category = BiomeHumidity::Dry; + } else if (humidity < 0.66) { + humidity_category = BiomeHumidity::Moderate; + } else { + humidity_category = BiomeHumidity::Wet; + } + + int mat_x = static_cast(temp_category); + int mat_y = static_cast(humidity_category); + int index = mat_x * 3 + mat_y; // 3 humidity categories + return static_cast(index); } } // namespace istd diff --git a/tilemap/src/generation.cpp b/tilemap/src/generation.cpp index b109c97..646895b 100644 --- a/tilemap/src/generation.cpp +++ b/tilemap/src/generation.cpp @@ -2,16 +2,15 @@ #include "biome.h" #include #include +#include namespace istd { TerrainGenerator::TerrainGenerator(const GenerationConfig &config) : config_(config) - , base_noise_(config.seed) - , surface_noise_(config.seed + 500) // Different seed for surface features - , temperature_noise_(config.seed + 1000) // Different seed for temperature - , humidity_noise_(config.seed + 2000) // Different seed for humidity -{} + , base_noise_(config.seed ^ base_seed_mask) + , temperature_noise_(config.seed ^ temperature_seed_mask) + , humidity_noise_(config.seed ^ humidity_seed_mask) {} void TerrainGenerator::generate_map(TileMap &tilemap) { // First, generate biome data for all chunks @@ -93,31 +92,18 @@ void TerrainGenerator::generate_subchunk( // Generate base terrain noise value double base_noise_value = base_noise_.octave_noise( - global_x * properties.base_scale, - global_y * properties.base_scale, properties.base_octaves, - properties.base_persistence - ); - - // Generate surface feature noise value - double surface_noise_value = surface_noise_.octave_noise( - global_x * properties.surface_scale, - global_y * properties.surface_scale, properties.surface_octaves, - properties.surface_persistence + global_x * config_.base_scale, global_y * config_.base_scale, + properties.base_octaves, properties.base_persistence ); // Determine base terrain type BaseTileType base_type = determine_base_type(base_noise_value, properties); - // Determine surface feature type - SurfaceTileType surface_type = determine_surface_type( - surface_noise_value, properties, base_type - ); - // Create tile with base and surface components Tile tile; tile.base = base_type; - tile.surface = surface_type; + tile.surface = SurfaceTileType::Empty; // Set the tile TilePos pos{chunk_x, chunk_y, local_x, local_y}; @@ -147,37 +133,22 @@ std::pair TerrainGenerator::get_climate( BaseTileType TerrainGenerator::determine_base_type( double noise_value, const BiomeProperties &properties ) const { - if (noise_value < properties.water_threshold) { - return BaseTileType::Water; - } else if (noise_value < properties.sand_threshold) { - return BaseTileType::Sand; - } else if (noise_value < properties.mountain_threshold) { - return BaseTileType::Mountain; - } else if (properties.ice_threshold > 0.0 - && noise_value < properties.ice_threshold) { - return BaseTileType::Ice; - } else { - return BaseTileType::Land; - } -} + const std::pair thresholds[] = { + {BaseTileType::Water, properties.water_threshold}, + {BaseTileType::Ice, properties.ice_threshold }, + {BaseTileType::Sand, properties.sand_threshold }, + {BaseTileType::Land, properties.land_threshold }, + {BaseTileType::Mountain, 1.0 }, + }; -SurfaceTileType TerrainGenerator::determine_surface_type( - double noise_value, const BiomeProperties &properties, - BaseTileType base_type -) const { - // Don't place surface features on water or ice - if (base_type == BaseTileType::Water || base_type == BaseTileType::Ice) { - return SurfaceTileType::Empty; + for (const auto &[type, threshold] : thresholds) { + if (noise_value < threshold) { + return type; + } + noise_value -= threshold; // Adjust noise value for next type } - // Check for surface features based on thresholds - if (noise_value < properties.wood_threshold) { - return SurfaceTileType::Wood; - } else if (noise_value < properties.snow_threshold) { - return SurfaceTileType::Snow; - } else { - return SurfaceTileType::Empty; - } + std::unreachable(); } // Legacy function for backward compatibility