7.3 KiB
Tilemap Library Developer Guide
Project Overview
The tilemap library is a C++ terrain generation system that creates tile-based worlds with biome support. It uses a multi-pass generation pipeline to create realistic, balanced terrain.
Project Structure
tilemap/
├── include/ # Public headers
│ ├── tilemap.h # Main map container
│ ├── chunk.h # 64x64 tile chunks
│ ├── tile.h # Individual tile types
│ ├── generation.h # Generation system
│ ├── biome.h # Biome system
│ ├── noise.h # Noise generators
│ └── xoroshiro.h # RNG implementation
├── src/ # Implementation files
│ ├── tilemap.cpp # TileMap implementation
│ ├── chunk.cpp # Chunk utilities
│ ├── generation.cpp # Main generation orchestrator
│ ├── biome.cpp # Biome mapping logic
│ ├── noise.cpp # Noise implementations
│ ├── xoroshiro.cpp # Xoroshiro128++ RNG
│ └── pass/ # Generation passes
│ ├── biome.cpp # Climate-based biome generation
│ ├── base_tile_type.cpp # Base terrain generation
│ ├── smoothen_mountain.cpp # Mountain smoothing
│ ├── smoothen_island.cpp # Island smoothing
│ ├── mountain_hole_fill.cpp # Hole filling
│ ├── deepwater.cpp # Deep water placement
│ └── oil.cpp # Oil resource generation
├── examples/ # Usage examples
└── docs/ # Documentation
Core Architecture
Data Organization
The system uses a hierarchical structure:
- TileMap: n×n grid of chunks
- Chunk: 64×64 tiles with biome metadata
- Tile: Individual terrain cell (1 byte packed)
Each chunk also contains a 16×16 grid of sub-chunk biomes, providing efficient biome lookup without per-tile storage.
Pass-Based Generation
Terrain generation uses a multi-pass pipeline for modularity and control:
- Biome Pass: Generates climate data and assigns biomes to sub-chunks
- Base Tile Type Pass: Creates base terrain based on biomes
- Mountain Smoothing Pass: Removes isolated mountain clusters
- Island Smoothing Pass: Smooths island coastlines using cellular automata
- Hole Fill Pass: Fills small terrain holes
- Deep Water Pass: Places deep water areas
- Oil Pass: Generates sparse oil deposits as surface features
Each pass operates independently with its own RNG state, ensuring deterministic results.
Terrain Generation Pipeline
Climate Generation
The biome pass uses dual noise generators for temperature and humidity:
- Separate Perlin noise for temperature/humidity at sub-chunk resolution
- Climate values mapped to 9 biome types in a 3×3 grid
- Sub-chunks store biome data for efficient terrain generation
Noise System
The library addresses Perlin noise distribution issues:
- Problem: Raw Perlin noise has bell-curve distribution
- Solution: UniformPerlinNoise calibrates distribution to uniform [0,1]
- Result: Balanced terrain type ratios according to biome properties
Terrain Generation Process
- Climate Sampling: Sample temperature/humidity at sub-chunk centers
- Biome Assignment: Map climate values to biome types
- Terrain Generation: Generate tiles based on biome properties and noise
- Post-processing: Apply smoothing and hole-filling algorithms
Connected Component Analysis
Several passes use BFS (Breadth-First Search) for terrain analysis:
- Mountain Smoothing: Find and remove small mountain components
- Island Smoothing: Apply cellular automata for natural coastlines
- Hole Filling: Identify and fill isolated terrain holes
- Components touching map boundaries are preserved
Oil Resource Generation
The oil generation pass creates sparse resource deposits:
- Poisson Disk Sampling: Ensures minimum distance between oil fields
- Biome Preference: Higher probability in desert and plains biomes
- Cluster Growth: Random walk creates 2-6 tile clusters
- Surface Placement: Oil appears as surface features on land/sand tiles
Random Number Generation
Xoroshiro128++ Implementation
High-quality PRNG with excellent statistical properties:
- 128-bit internal state
- Period of 2^128 - 1
- Jump functions for parallel generation
- STL-compatible interface
Seed Management
128-bit seeds provide extensive randomness:
- String-based deterministic seed creation
- Hardware random seed generation
- Independent RNG streams for each generation pass
Biome System
Climate Mapping
Biomes are determined by temperature/humidity combinations:
Dry Moderate Wet
Cold Snowy Snowy Frozen
Peaks Plains Ocean
Temp Plains Forest Ocean
Hot Desert Savanna Luke Ocean
Biome Properties
Each biome defines terrain generation ratios:
- Water/Ice/Sand/Land ratios
- Noise parameters (octaves, persistence)
- Used by base terrain generation pass
Performance Considerations
Memory Layout
- Tiles packed into 1 byte (4 bits base + 4 bits surface)
- Chunks use contiguous 64×64 arrays for cache efficiency
- Sub-chunk biomes reduce memory overhead vs per-tile storage
Generation Efficiency
- Sub-chunk resolution for biome generation (16×16 vs 64×64)
- Single-pass algorithms where possible
- Efficient connected component analysis using BFS
Determinism
- Same seed produces identical results
- Independent RNG streams prevent cross-pass contamination
- Floating-point operations use consistent precision
Adding New Generation Passes
To add a new generation pass:
- Create Pass Declaration: Add a header file in
include/tilemap/pass/
, declaring a Pass class (e.g.,MyCustomPass
) that overloadsvoid operator()(TileMap &tilemap)
. See existing passes for structure and required members. - Create Pass Implementation: Add the corresponding implementation file in
src/tilemap/pass/
, implementing the declared class and its methods. - Add to Pipeline: Update
TerrainGenerator::operator()
to invoke your new pass in the desired order. - RNG Management: Use jump functions to create an independent RNG stream for your pass, ensuring deterministic results. Pass the RNG to your class constructor as needed.
- Configuration: If your pass requires configuration parameters, add them to
GenerationConfig
and pass them to your class.
Example header (include/tilemap/pass/my_custom_pass.h):
class MyCustomPass {
public:
MyCustomPass(const GenerationConfig &config, Xoroshiro128PP rng);
void operator()(TileMap &tilemap);
// ...other methods as needed...
};
Example implementation (src/tilemap/pass/my_custom_pass.cpp):
MyCustomPass::MyCustomPass(const GenerationConfig &config, Xoroshiro128PP rng)
: config_(config), rng_(rng) {}
void MyCustomPass::operator()(TileMap &tilemap) {
// Process tilemap using config_ and rng_
// ...implementation...
}
Pipeline integration:
void TerrainGenerator::operator()(TileMap &tilemap) {
// ...existing passes...
MyCustomPass custom_pass(config_, master_rng_.jump_96());
custom_pass(tilemap);
// ...other passes...
}
Refer to existing passes in include/tilemap/pass/
and src/tilemap/pass/
for detailed structure, member variables, and best practices.