22 KiB
Tilemap Library API Documentation
Overview
The tilemap library provides a flexible system for generating and managing tile-based terrain with biome support. The library consists of several main components:
- 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: Pass-based procedural terrain generation system
- Generation Passes: Modular generation components (biome, base terrain, hole filling)
- Biome System: Climate-based terrain variation
Core Classes
TileMap
The main container for the entire map, organized as an n×n grid of chunks.
class TileMap {
public:
explicit TileMap(std::uint8_t size);
std::uint8_t get_size() const;
Chunk& get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y);
const Chunk& get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y) const;
Tile& get_tile(TilePos pos);
const Tile& get_tile(TilePos pos) const;
void set_tile(TilePos pos, const Tile& tile);
bool is_at_boundary(TilePos pos) const;
std::vector<TilePos> get_neighbors(TilePos pos, bool chebyshev = false) const;
};
Constructor Parameters:
size
: Number of chunks per side (max 100), creating an n×n grid
New Methods:
is_at_boundary()
: Checks if a tile position is at the map boundaryget_neighbors()
: Returns neighboring tile positions with optional Chebyshev distance support
Chunk
Each chunk contains 64×64 tiles and sub-chunk biome information.
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_count = size / subchunk_size; // Sub-chunks per side
Tile tiles[size][size]; // 64x64 tile grid
BiomeType biome[subchunk_count][subchunk_count]; // Sub-chunk biomes
// Methods for biome access
BiomeType& get_biome(SubChunkPos pos);
const BiomeType& get_biome(SubChunkPos pos) const;
};
Tile
Individual map tiles with base terrain and surface features.
struct Tile {
BaseTileType base : 4; // Base terrain type
SurfaceTileType surface : 4; // Surface features
};
Base Tile Types:
Land
: Standard ground terrainMountain
: Rocky elevated terrainSand
: Desert/beach terrainWater
: Water bodiesIce
: Frozen terrain
Surface Tile Types:
Empty
: No surface featuresWood
: Trees/vegetationStructure
: Player-built structures
TilePos
Position structure for locating tiles within the map with enhanced coordinate conversion support.
struct TilePos {
uint8_t chunk_x; // Chunk X coordinate
uint8_t chunk_y; // Chunk Y coordinate
uint8_t local_x; // Tile X within chunk (0-63)
uint8_t local_y; // Tile Y within chunk (0-63)
// Coordinate conversion methods
std::pair<std::uint16_t, std::uint16_t> to_global() const;
static TilePos from_global(std::uint16_t global_x, std::uint16_t global_y);
};
// Three-way comparison operator for ordering
std::strong_ordering operator<=>(const TilePos& lhs, const TilePos& rhs);
SubChunkPos
Position within a chunk's sub-chunk grid.
struct SubChunkPos {
std::uint8_t sub_x;
std::uint8_t sub_y;
constexpr SubChunkPos(std::uint8_t x, std::uint8_t y);
};
std::pair<std::uint8_t, std::uint8_t> subchunk_to_tile_start(
SubChunkPos pos
);
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.
struct GenerationConfig {
Seed seed; // 128-bit seed for random generation
// Temperature noise parameters
double temperature_scale = 0.05; // Scale for temperature noise
int temperature_octaves = 3; // Number of octaves for temperature noise
double temperature_persistence = 0.4; // Persistence for temperature noise
// Humidity noise parameters
double humidity_scale = 0.05; // Scale for humidity noise
int humidity_octaves = 3; // Number of octaves for humidity noise
double humidity_persistence = 0.4; // 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
// Mountain smoothing parameters
std::uint32_t mountain_remove_threshold = 10; // Remove mountain components smaller than this size
// Hole filling parameters
std::uint32_t fill_threshold = 10; // Fill holes smaller than this size
};
Parameters:
seed
: 128-bit seed for all noise generators (see Seed structure)temperature_scale
: Controls the scale/frequency of temperature variation across the maptemperature_octaves
: Number of noise octaves for temperature (more octaves = more detail)temperature_persistence
: How much each octave contributes to temperature noise (0.0-1.0)humidity_scale
: Controls the scale/frequency of humidity variation across the maphumidity_octaves
: Number of noise octaves for humidityhumidity_persistence
: How much each octave contributes to humidity noise (0.0-1.0)base_scale
: Controls the scale/frequency of base terrain variation across the mapbase_octaves
: Number of noise octaves for base terrainbase_persistence
: How much each octave contributes to base terrain noise (0.0-1.0)mountain_remove_threshold
: Maximum size of mountain components to remove for terrain smoothingfill_threshold
: Maximum size of holes to fill with mountains
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.
class BiomeGenerationPass {
public:
BiomeGenerationPass(
const GenerationConfig& config,
Xoroshiro128PP r1,
Xoroshiro128PP r2
);
void operator()(TileMap& tilemap);
private:
std::pair<double, double> 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.
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
HoleFillPass
Fills small holes in the terrain using breadth-first search (BFS) algorithm.
class HoleFillPass {
public:
explicit HoleFillPass(const GenerationConfig& config);
void operator()(TileMap& tilemap);
private:
bool is_passable(BaseTileType type) const;
std::uint32_t bfs_component_size(
TileMap& tilemap, TilePos start_pos,
std::vector<std::vector<bool>>& visited,
std::vector<TilePos>& positions
);
std::vector<TilePos> get_neighbors(TileMap& tilemap, TilePos pos) const;
bool is_at_boundary(TileMap& tilemap, TilePos pos) const;
};
Key Features:
- BFS Algorithm: Uses breadth-first search to identify connected components
- Boundary Awareness: Preserves holes that touch the map boundary
- Size-based Filtering: Only fills holes smaller than
fill_threshold
- Mountain-as-Impassable: Treats mountains as impassable terrain for connectivity
- Hole Filling: Converts small isolated areas to mountains for cleaner terrain
SmoothenMountainsPass
Removes small mountain components to create smoother terrain using BFS and replacement strategies.
class SmoothenMountainsPass {
public:
SmoothenMountainsPass(const GenerationConfig& config, Xoroshiro128PP rng);
void operator()(TileMap& tilemap);
private:
std::uint32_t bfs_component_size(
TileMap& tilemap, TilePos start_pos,
std::vector<std::vector<bool>>& visited,
std::vector<TilePos>& positions
);
void demountainize(TileMap& tilemap, const std::vector<TilePos>& positions);
};
Key Features:
- Mountain Component Detection: Uses BFS to find connected mountain regions
- Size-based Removal: Removes mountain components smaller than
mountain_remove_threshold
- Boundary Preservation: Preserves mountain components that touch the map boundary
- Intelligent Replacement: Replaces mountains with terrain types based on neighboring tiles
- Smooth Terrain: Creates more natural-looking terrain without isolated mountain clusters
TerrainGenerator
Main orchestrator class that manages the generation process using multiple passes.
class TerrainGenerator {
public:
explicit TerrainGenerator(const GenerationConfig& config);
void operator()(TileMap& tilemap);
private:
void biome_pass(TileMap& tilemap);
void base_tile_type_pass(TileMap& tilemap);
void smoothen_mountains_pass(TileMap& tilemap);
void hole_fill_pass(TileMap& tilemap);
};
Generation Order:
- Biome Pass: Generates climate-based biome data for sub-chunks
- Base Tile Type Pass: Generates base terrain types based on biomes
- Smoothen Mountains Pass: Removes small mountain components for smoother terrain
- Hole Fill Pass: Fills small holes in the terrain
Key Features:
- Multi-pass Architecture: Separates generation concerns for better control
- RNG Management: Uses independent RNGs for each pass with proper seeding
- Deterministic Results: Same seed produces identical terrain across runs
- Configurable: All passes use parameters from GenerationConfig
Generation Flow:
- Biome Pass: Generate climate data and assign biomes to sub-chunks
- Base Tile Type Pass: Generate base terrain types based on biomes and noise
- Hole Fill Pass: Fill small holes in the terrain using BFS algorithm
Generation Function
Convenience function for complete map generation.
void map_generate(TileMap& tilemap, const GenerationConfig& config);
Random Number Generation
Seed
128-bit seed structure for random number generation.
struct Seed {
std::uint64_t s[2]; // 128-bit seed value (two 64-bit components)
static Seed from_string(const char* str); // Create seed from string
static Seed device_random(); // Create seed from hardware random device
};
Key Features:
- 128-bit precision: Uses two 64-bit integers for extended seed space
- String generation: Deterministic seed creation from text strings
- Hardware random: True random seed generation using system entropy
Xoroshiro128++
High-performance random number generator using the Xoroshiro128++ algorithm.
class Xoroshiro128PP {
public:
Xoroshiro128PP() = default;
Xoroshiro128PP(Seed seed);
// STL RandomEngine interface
using result_type = std::uint64_t;
static constexpr result_type min();
static constexpr result_type max();
result_type operator()();
std::uint64_t next(); // Generate next random number
Xoroshiro128PP jump_64() const; // Jump equivalent to 2^64 calls
Xoroshiro128PP jump_96() const; // Jump equivalent to 2^96 calls
};
Key Features:
- High Performance: Optimized for speed with excellent statistical properties
- 128-bit State: Internal state provides long period (2^128 - 1)
- Jump Functions: Enable parallel random number generation
- STL Compatible: Implements standard random engine interface
Noise System
DiscreteRandomNoise
Discrete random noise generator using Xoroshiro128++ for terrain replacement operations.
class DiscreteRandomNoise {
public:
explicit DiscreteRandomNoise(Xoroshiro128PP rng);
std::uint64_t noise(std::uint32_t x, std::uint32_t y, std::uint32_t z = 0) const;
};
Key Features:
- Discrete Output: Produces integer values for discrete selections
- High Quality: Based on Xoroshiro128++ random number generation
- 3D Support: Supports optional Z coordinate for 3D noise
- Fast: Optimized for performance in terrain processing
PerlinNoise
Standard Perlin noise implementation using Xoroshiro128++ for procedural generation.
class PerlinNoise {
public:
explicit PerlinNoise(Xoroshiro128PP rng);
double noise(double x, double y) const;
double octave_noise(double x, double y, int octaves = 4, double persistence = 0.5) const;
};
UniformPerlinNoise
Advanced noise generator using Xoroshiro128++ that provides uniform distribution mapping.
class UniformPerlinNoise {
public:
explicit UniformPerlinNoise(Xoroshiro128PP rng);
void calibrate(double scale, int octaves = 1, double persistence = 0.5, int sample_size = 10000);
double uniform_noise(double x, double y) const;
bool is_calibrated() const;
};
Key Features:
- Calibration: Samples noise distribution to build CDF
- Uniform Mapping: Maps raw Perlin values to uniform [0,1] distribution
- Balanced Output: Ensures even distribution across all value ranges
- Automatic Use: TerrainGenerator uses this internally for balanced terrain
- Xoroshiro128++ Backend: Uses high-quality random number generation
Biome System
BiomeType
Available biome types based on temperature and humidity.
enum class BiomeType : std::uint8_t {
SnowyPeeks, // Cold & Dry
SnowyPlains, // Cold & Moderate
FrozenOcean, // Cold & Wet
Plains, // Temperate & Dry
Forest, // Temperate & Moderate
Ocean, // Temperate & Wet
Desert, // Hot & Dry
Savanna, // Hot & Moderate
LukeOcean, // Hot & Wet
};
BiomeProperties
Properties that control terrain generation for each biome.
struct BiomeProperties {
std::string_view name; // Biome name
double water_ratio; // Water generation ratio
double ice_ratio; // Ice generation ratio
double sand_ratio; // Sand generation ratio
double land_ratio; // Land generation ratio
int base_octaves = 3; // Noise octaves
double base_persistence = 0.5; // Noise persistence
};
Biome Functions
const BiomeProperties& get_biome_properties(BiomeType biome);
BiomeType determine_biome(double temperature, double humidity);
Usage Examples
Basic Map Generation
#include "tilemap.h"
#include "generation.h"
// Create a 4x4 chunk map
istd::TileMap tilemap(4);
// Configure generation
istd::GenerationConfig config;
config.seed = istd::Seed::from_string("hello_world"); // 128-bit seed from string
// Temperature noise settings
config.temperature_scale = 0.05;
config.temperature_octaves = 3;
config.temperature_persistence = 0.4;
// Humidity noise settings
config.humidity_scale = 0.05;
config.humidity_octaves = 3;
config.humidity_persistence = 0.4;
// Base terrain noise settings
config.base_scale = 0.08;
config.base_octaves = 3;
config.base_persistence = 0.5;
// Mountain smoothing settings
config.mountain_remove_threshold = 10; // Remove mountain components smaller than 10 tiles
// Hole filling settings
config.fill_threshold = 10; // Fill holes smaller than 10 tiles
// Generate terrain
istd::map_generate(tilemap, config);
// Access tiles
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
#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
// Create seed from string (deterministic)
istd::Seed seed1 = istd::Seed::from_string("my_world");
// Create random seed from hardware
istd::Seed seed2 = istd::Seed::device_random();
// Manual seed creation
istd::Seed seed3;
seed3.s[0] = 0x123456789abcdef0;
seed3.s[1] = 0xfedcba9876543210;
// Use seed in generation
istd::GenerationConfig config;
config.seed = seed1;
Accessing Individual Tiles
// Using TilePos
istd::TilePos pos{0, 0, 32, 32}; // Chunk (0,0), tile (32,32)
const auto& tile = tilemap.get_tile(pos);
// Direct chunk access
const auto& chunk = tilemap.get_chunk(0, 0);
const auto& tile2 = chunk.tiles[32][32];
Working with Biomes
// Method 1: Direct array access (traditional way)
const auto& chunk = tilemap.get_chunk(0, 0);
istd::BiomeType biome = chunk.biome[1][1]; // Sub-chunk (1,1)
// Method 2: Using SubChunkPos and get_biome method (recommended)
istd::SubChunkPos pos(1, 1); // Sub-chunk (1,1)
istd::BiomeType biome2 = chunk.get_biome(pos);
// Modify biome using the new method
auto& mutable_chunk = tilemap.get_chunk(0, 0);
mutable_chunk.get_biome(pos) = istd::BiomeType::Forest;
// Get biome properties
const auto& props = istd::get_biome_properties(biome);
std::cout << "Biome: " << props.name << std::endl;
Performance Notes
- Each chunk contains 4,096 tiles (64×64)
- Sub-chunks provide efficient biome management
- Tiles are packed into 1 byte each for memory efficiency
- Generation uses Xoroshiro128++ random number generator with uniform distribution mapping for balanced terrain
- Noise calibration is performed once during generator construction
- 128-bit seeds provide excellent randomness and reproducibility
Noise Distribution
The library uses an advanced noise system based on Xoroshiro128++ random number generation that addresses the non-uniform distribution of Perlin noise:
Problem with Raw Perlin Noise
Raw Perlin noise follows a bell-curve distribution, with most values concentrated around 0.5. This leads to unbalanced terrain generation where certain tile types (like Land) dominate the map.
Solution: Xoroshiro128++ + Uniform Distribution Mapping
The library combines two key improvements:
-
Xoroshiro128++ RNG: High-quality pseudo-random number generator with:
- Long Period: 2^128 - 1 sequence length before repetition
- High Performance: Optimized for speed and memory efficiency
- Excellent Statistics: Passes rigorous randomness tests
- 128-bit State: Two 64-bit values providing extensive seed space
-
Uniform Distribution Mapping: The
UniformPerlinNoise
class:- Samples the noise distribution during calibration
- Builds a CDF (Cumulative Distribution Function) from the samples
- Maps raw noise values to uniform [0,1] distribution using quantiles
- Ensures balanced terrain type distribution according to biome properties
Usage in Terrain Generation
// The terrain generator automatically uses Xoroshiro128++ and uniform noise
istd::Seed seed = istd::Seed::from_string("consistent_world");
istd::GenerationConfig config;
config.seed = seed;
// 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:
- Separation of Concerns: Each pass handles a specific aspect of generation
- RNG Independence: Each pass uses independent random number generators
- Reproducible Results: Same seed produces identical results across passes
- Extensibility: Easy to add new passes or modify existing ones
- Performance: Efficient memory access patterns and reduced redundant calculations
- Terrain Quality: SmoothenMountainsPass creates more natural-looking terrain
Recent Improvements
Mountain Smoothing: The new SmoothenMountainsPass
removes small isolated mountain components to create more natural terrain formations. Small mountain clusters that don't connect to the boundary are replaced with terrain types based on their neighboring areas.
Enhanced TileMap: Added utility methods for boundary detection and neighbor finding, supporting both Manhattan and Chebyshev distance calculations.
Improved Noise: Added DiscreteRandomNoise
for high-quality discrete value generation used in terrain replacement operations.
Thread Safety
The library is not inherently thread-safe. External synchronization is required for concurrent access to TileMap objects.