diff --git a/tilemap/docs/api.md b/tilemap/docs/api.md index bcfd3fb..e26318a 100644 --- a/tilemap/docs/api.md +++ b/tilemap/docs/api.md @@ -7,7 +7,8 @@ The tilemap library provides a flexible system for generating and managing tile- - **TileMap**: The main map container holding chunks of tiles - **Chunk**: 64x64 tile containers with biome information - **Tile**: Individual map tiles with base and surface types -- **TerrainGenerator**: Procedural terrain generation system +- **TerrainGenerator**: Pass-based procedural terrain generation system +- **Generation Passes**: Modular generation components (biome, base terrain) - **Biome System**: Climate-based terrain variation ## Core Classes @@ -41,7 +42,7 @@ Each chunk contains 64×64 tiles and sub-chunk biome information. ```cpp struct Chunk { static constexpr uint8_t size = 64; // Tiles per side - static constexpr uint8_t subchunk_size = 4; // Tiles per sub-chunk side + static constexpr uint8_t subchunk_size = /*default value*/; // Tiles per sub-chunk side static constexpr uint8_t subchunk_count = size / subchunk_size; // Sub-chunks per side Tile tiles[size][size]; // 64x64 tile grid @@ -108,6 +109,8 @@ std::pair subchunk_to_tile_start( ## Terrain Generation +The terrain generation system has been refactored into a modular pass-based architecture, providing better separation of concerns and more flexible generation control. + ### GenerationConfig Configuration parameters for terrain generation. @@ -117,19 +120,19 @@ struct GenerationConfig { Seed seed; // 128-bit seed for random generation // Temperature noise parameters - double temperature_scale = 0.005; // Scale for temperature noise - int temperature_octaves = 3; // Number of octaves for temperature noise - double temperature_persistence = 0.4; // Persistence for temperature noise + double temperature_scale = /*default value*/; // Scale for temperature noise + int temperature_octaves = /*default value*/; // Number of octaves for temperature noise + double temperature_persistence = /*default value*/; // Persistence for temperature noise // Humidity noise parameters - double humidity_scale = 0.005; // Scale for humidity noise - int humidity_octaves = 3; // Number of octaves for humidity noise - double humidity_persistence = 0.4; // Persistence for humidity noise + double humidity_scale = /*default value*/; // Scale for humidity noise + int humidity_octaves = /*default value*/; // Number of octaves for humidity noise + double humidity_persistence = /*default value*/; // Persistence for humidity noise // Base terrain noise parameters - double base_scale = 0.08; // Scale for base terrain noise - int base_octaves = 3; // Number of octaves for base terrain noise - double base_persistence = 0.5; // Persistence for base terrain noise + double base_scale = /*default value*/; // Scale for base terrain noise + int base_octaves = /*default value*/; // Number of octaves for base terrain noise + double base_persistence = /*default value*/; // Persistence for base terrain noise }; ``` @@ -146,21 +149,88 @@ struct GenerationConfig { - `base_octaves`: Number of noise octaves for base terrain - `base_persistence`: How much each octave contributes to base terrain noise (0.0-1.0) +### Generation Passes + +The generation system is organized into distinct passes, each responsible for a specific aspect of terrain generation. + +#### BiomeGenerationPass + +Generates biome data for all sub-chunks based on temperature and humidity noise. + +```cpp +class BiomeGenerationPass { +public: + BiomeGenerationPass( + const GenerationConfig& config, + Xoroshiro128PP r1, + Xoroshiro128PP r2 + ); + + void operator()(TileMap& tilemap); + +private: + std::pair get_climate(double global_x, double global_y) const; +}; +``` + +**Key Features:** +- **Climate Generation**: Uses separate noise generators for temperature and humidity +- **Sub-chunk Resolution**: Assigns biomes to 16×16 sub-chunks for efficient generation +- **Climate Mapping**: Maps noise values to temperature/humidity ranges +- **Biome Determination**: Uses climate values to determine appropriate biomes + +#### BaseTileTypeGenerationPass + +Generates base terrain types for all tiles based on their sub-chunk biomes. + +```cpp +class BaseTileTypeGenerationPass { +public: + BaseTileTypeGenerationPass(const GenerationConfig& config, Xoroshiro128PP rng); + + void operator()(TileMap& tilemap); + void generate_chunk(TileMap& tilemap, std::uint8_t chunk_x, std::uint8_t chunk_y); + void generate_subchunk( + TileMap& tilemap, std::uint8_t chunk_x, std::uint8_t chunk_y, + SubChunkPos sub_pos, BiomeType biome + ); + +private: + BaseTileType determine_base_type( + double noise_value, const BiomeProperties& properties + ) const; +}; +``` + +**Key Features:** +- **Biome-aware Generation**: Uses biome properties to control terrain type ratios +- **Hierarchical Processing**: Processes chunks, then sub-chunks, then individual tiles +- **Noise-based Distribution**: Uses calibrated noise for balanced terrain distribution +- **Tile-level Detail**: Generates terrain at individual tile resolution + ### TerrainGenerator -Main class for procedural terrain generation. +Main orchestrator class that manages the generation process using multiple passes. ```cpp class TerrainGenerator { public: explicit TerrainGenerator(const GenerationConfig& config); - void generate_map(TileMap& tilemap); + void operator()(TileMap& tilemap); + +private: + void biome_pass(TileMap& tilemap); + void base_tile_type_pass(TileMap& tilemap); }; ``` +**Generation Flow:** +1. **Biome Pass**: Generate climate data and assign biomes to sub-chunks +2. **Base Tile Type Pass**: Generate base terrain types based on biomes and noise + ### Generation Function -Convenience function for map generation. +Convenience function for complete map generation. ```cpp void map_generate(TileMap& tilemap, const GenerationConfig& config); @@ -309,12 +379,12 @@ istd::GenerationConfig config; config.seed = istd::Seed::from_string("hello_world"); // 128-bit seed from string // Temperature noise settings -config.temperature_scale = 0.005; +config.temperature_scale = 0.05; config.temperature_octaves = 3; config.temperature_persistence = 0.4; // Humidity noise settings -config.humidity_scale = 0.005; +config.humidity_scale = 0.05; config.humidity_octaves = 3; config.humidity_persistence = 0.4; @@ -327,14 +397,44 @@ config.base_persistence = 0.5; istd::map_generate(tilemap, config); // Access tiles -for (int chunk_y = 0; chunk_y < Chunk::subchunk_count; ++chunk_y) { - for (int chunk_x = 0; chunk_x < Chunk::subchunk_count; ++chunk_x) { +for (int chunk_y = 0; chunk_y < tilemap.get_size(); ++chunk_y) { + for (int chunk_x = 0; chunk_x < tilemap.get_size(); ++chunk_x) { const auto& chunk = tilemap.get_chunk(chunk_x, chunk_y); // Process chunk tiles... } } ``` +### Advanced Generation with Custom Passes + +```cpp +#include "tilemap.h" +#include "generation.h" + +// Create map and config +istd::TileMap tilemap(2); +istd::GenerationConfig config; +config.seed = istd::Seed::from_string("custom_world"); + +// Use TerrainGenerator for step-by-step control +istd::TerrainGenerator generator(config); + +// Generate terrain (runs both biome and base tile passes) +generator(tilemap); + +// Access biome data +const auto& chunk = tilemap.get_chunk(0, 0); +for (int sub_y = 0; sub_y < istd::Chunk::subchunk_count; ++sub_y) { + for (int sub_x = 0; sub_x < istd::Chunk::subchunk_count; ++sub_x) { + istd::SubChunkPos pos(sub_x, sub_y); + istd::BiomeType biome = chunk.get_biome(pos); + const auto& props = istd::get_biome_properties(biome); + // Process biome... + } +} +``` +``` + ### Seed Usage Examples ```cpp @@ -427,10 +527,24 @@ istd::Seed seed = istd::Seed::from_string("consistent_world"); istd::GenerationConfig config; config.seed = seed; -TerrainGenerator generator(config); -generator.generate_map(tilemap); // Uses calibrated uniform noise with Xoroshiro128++ +// TerrainGenerator handles pass coordination and RNG management +istd::TerrainGenerator generator(config); +generator(tilemap); // Uses calibrated uniform noise with Xoroshiro128++ + +// Or use the convenience function +istd::map_generate(tilemap, config); ``` +### Pass-based Architecture Benefits + +The new pass-based system provides: + +1. **Separation of Concerns**: Each pass handles a specific aspect of generation +2. **RNG Independence**: Each pass uses independent random number generators +3. **Reproducible Results**: Same seed produces identical results across passes +4. **Extensibility**: Easy to add new passes or modify existing ones +5. **Performance**: Efficient memory access patterns and reduced redundant calculations + ## Thread Safety The library is not inherently thread-safe. External synchronization is required for concurrent access to TileMap objects. diff --git a/tilemap/include/generation.h b/tilemap/include/generation.h index 86cfa89..47ba735 100644 --- a/tilemap/include/generation.h +++ b/tilemap/include/generation.h @@ -28,34 +28,62 @@ struct GenerationConfig { double base_persistence = 0.5; // Persistence for base terrain noise }; -// Terrain generator class that manages the generation process -class TerrainGenerator { +class BiomeGenerationPass { private: - GenerationConfig config_; + const GenerationConfig &config_; - UniformPerlinNoise base_noise_; // For base terrain - UniformPerlinNoise temperature_noise_; // For temperature - UniformPerlinNoise humidity_noise_; // For humidity + UniformPerlinNoise temperature_noise_; + UniformPerlinNoise humidity_noise_; public: /** - * @brief Construct a terrain generator with the given configuration - * @param config Generation configuration + * @brief Construct a biome generation pass + * @param config Generation configuration parameters + * @param r1 Random number generator for temperature noise + * @param r2 Random number generator for humidity noise */ - explicit TerrainGenerator(const GenerationConfig &config); + BiomeGenerationPass( + const GenerationConfig &config, Xoroshiro128PP r1, Xoroshiro128PP r2 + ); /** - * @brief Generate terrain for the entire tilemap - * @param tilemap The tilemap to generate into + * @brief Generate biomes for the entire tilemap + * @param tilemap The tilemap to generate biomes into */ - void generate_map(TileMap &tilemap); + void operator()(TileMap &tilemap); private: /** - * @brief Generate biome data for all chunks - * @param tilemap The tilemap to generate biomes into + * @brief Get climate values at a global position + * @param global_x Global X coordinate + * @param global_y Global Y coordinate + * @return Pair of (temperature, humidity) in range [0,1] */ - void generate_biomes(TileMap &tilemap); + std::pair get_climate( + double global_x, double global_y + ) const; +}; + +class BaseTileTypeGenerationPass { +private: + const GenerationConfig &config_; + UniformPerlinNoise base_noise_; + +public: + /** + * @brief Construct a base tile type generation pass + * @param config Generation configuration parameters + * @param rng Random number generator for base terrain noise + */ + BaseTileTypeGenerationPass( + const GenerationConfig &config, Xoroshiro128PP rng + ); + + /** + * @brief Generate base tile types for the entire tilemap + * @param tilemap The tilemap to generate base types into + */ + void operator()(TileMap &tilemap); /** * @brief Generate terrain for a single chunk @@ -80,16 +108,6 @@ private: SubChunkPos sub_pos, BiomeType biome ); - /** - * @brief Get climate values at a global position - * @param global_x Global X coordinate - * @param global_y Global Y coordinate - * @return Pair of (temperature, humidity) in range [0,1] - */ - std::pair get_climate( - double global_x, double global_y - ) const; - /** * @brief Determine base terrain type based on noise value and biome * properties @@ -102,6 +120,39 @@ private: ) const; }; +// Terrain generator class that manages the generation process +class TerrainGenerator { +private: + GenerationConfig config_; + Xoroshiro128PP master_rng_; + +public: + /** + * @brief Construct a terrain generator with the given configuration + * @param config Generation configuration + */ + explicit TerrainGenerator(const GenerationConfig &config); + + /** + * @brief Generate terrain for the entire tilemap + * @param tilemap The tilemap to generate into + */ + void operator()(TileMap &tilemap); + +private: + /** + * @brief Generate biome data for all chunks + * @param tilemap The tilemap to generate biomes into + */ + void biome_pass(TileMap &tilemap); + + /** + * @brief Generate base tile types for all chunks + * @param tilemap The tilemap to generate base types into + */ + void base_tile_type_pass(TileMap &tilemap); +}; + /** * @brief Generate a tilemap using the new biome-based system * @param tilemap The tilemap to generate into diff --git a/tilemap/src/generation.cpp b/tilemap/src/generation.cpp index 8636685..a635b86 100644 --- a/tilemap/src/generation.cpp +++ b/tilemap/src/generation.cpp @@ -6,44 +6,21 @@ namespace istd { -TerrainGenerator::TerrainGenerator(const GenerationConfig &config) - : config_(config) { - Xoroshiro128PP rng{config.seed}; - base_noise_ = UniformPerlinNoise(rng); - rng = rng.jump_96(); - temperature_noise_ = UniformPerlinNoise(rng); - rng = rng.jump_96(); - humidity_noise_ = UniformPerlinNoise(rng); - - base_noise_.calibrate( - config.base_scale, config.base_octaves, config.base_persistence - ); - +BiomeGenerationPass::BiomeGenerationPass( + const GenerationConfig &config, Xoroshiro128PP r1, Xoroshiro128PP r2 +) + : config_(config), temperature_noise_(r1), humidity_noise_(r2) { temperature_noise_.calibrate( config.temperature_scale, config.temperature_octaves, config.temperature_persistence ); - humidity_noise_.calibrate( config.humidity_scale, config.humidity_octaves, config.humidity_persistence ); } -void TerrainGenerator::generate_map(TileMap &tilemap) { - // First, generate biome data for all chunks - generate_biomes(tilemap); - - // Then generate terrain for each chunk - std::uint8_t map_size = tilemap.get_size(); - for (std::uint8_t chunk_x = 0; chunk_x < map_size; ++chunk_x) { - for (std::uint8_t chunk_y = 0; chunk_y < map_size; ++chunk_y) { - generate_chunk(tilemap, chunk_x, chunk_y); - } - } -} - -void TerrainGenerator::generate_biomes(TileMap &tilemap) { +void BiomeGenerationPass::operator()(TileMap &tilemap) { std::uint8_t map_size = tilemap.get_size(); // Generate biomes for each sub-chunk @@ -77,7 +54,43 @@ void TerrainGenerator::generate_biomes(TileMap &tilemap) { } } -void TerrainGenerator::generate_chunk( +std::pair BiomeGenerationPass::get_climate( + double global_x, double global_y +) const { + // Generate temperature noise (0-1 range) + double temperature = temperature_noise_.uniform_noise( + global_x * config_.temperature_scale, + global_y * config_.temperature_scale + ); + + // Generate humidity noise (0-1 range) + double humidity = humidity_noise_.uniform_noise( + global_x * config_.humidity_scale, global_y * config_.humidity_scale + ); + + return {temperature, humidity}; +} + +BaseTileTypeGenerationPass::BaseTileTypeGenerationPass( + const GenerationConfig &config, Xoroshiro128PP rng +) + : config_(config), base_noise_(rng) { + base_noise_.calibrate( + config.base_scale, config.base_octaves, config.base_persistence + ); +} + +void BaseTileTypeGenerationPass::operator()(TileMap &tilemap) { + // Generate base tile types for each chunk + std::uint8_t map_size = tilemap.get_size(); + for (std::uint8_t chunk_x = 0; chunk_x < map_size; ++chunk_x) { + for (std::uint8_t chunk_y = 0; chunk_y < map_size; ++chunk_y) { + generate_chunk(tilemap, chunk_x, chunk_y); + } + } +} + +void BaseTileTypeGenerationPass::generate_chunk( TileMap &tilemap, std::uint8_t chunk_x, std::uint8_t chunk_y ) { const Chunk &chunk = tilemap.get_chunk(chunk_x, chunk_y); @@ -92,7 +105,7 @@ void TerrainGenerator::generate_chunk( } } -void TerrainGenerator::generate_subchunk( +void BaseTileTypeGenerationPass::generate_subchunk( TileMap &tilemap, std::uint8_t chunk_x, std::uint8_t chunk_y, SubChunkPos sub_pos, BiomeType biome ) { @@ -130,24 +143,7 @@ void TerrainGenerator::generate_subchunk( } } -std::pair TerrainGenerator::get_climate( - double global_x, double global_y -) const { - // Generate temperature noise (0-1 range) - double temperature = temperature_noise_.uniform_noise( - global_x * config_.temperature_scale, - global_y * config_.temperature_scale - ); - - // Generate humidity noise (0-1 range) - double humidity = humidity_noise_.uniform_noise( - global_x * config_.humidity_scale, global_y * config_.humidity_scale - ); - - return {temperature, humidity}; -} - -BaseTileType TerrainGenerator::determine_base_type( +BaseTileType BaseTileTypeGenerationPass::determine_base_type( double noise_value, const BiomeProperties &properties ) const { const std::pair ratios[] = { @@ -168,10 +164,37 @@ BaseTileType TerrainGenerator::determine_base_type( std::unreachable(); } -// Legacy function for backward compatibility +TerrainGenerator::TerrainGenerator(const GenerationConfig &config) + : config_(config), master_rng_(config.seed) {} + +void TerrainGenerator::biome_pass(TileMap &tilemap) { + // Create two RNGs for temperature and humidity noise + Xoroshiro128PP temp_rng = master_rng_; + master_rng_ = master_rng_.jump_96(); + Xoroshiro128PP humidity_rng = master_rng_; + master_rng_ = master_rng_.jump_96(); + + BiomeGenerationPass biome_pass(config_, temp_rng, humidity_rng); + biome_pass(tilemap); +} + +void TerrainGenerator::operator()(TileMap &tilemap) { + // First, generate biome data for all chunks + biome_pass(tilemap); + + // Then, generate base tile types based on biomes + base_tile_type_pass(tilemap); +} + +void TerrainGenerator::base_tile_type_pass(TileMap &tilemap) { + BaseTileTypeGenerationPass pass(config_, master_rng_); + master_rng_ = master_rng_.jump_96(); + pass(tilemap); +} + void map_generate(TileMap &tilemap, const GenerationConfig &config) { TerrainGenerator generator(config); - generator.generate_map(tilemap); + generator(tilemap); } } // namespace istd