17 KiB
Raw Blame History

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)
  • 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);
};

Constructor Parameters:

  • size: Number of chunks per side (max 100), creating an n×n grid

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 = /*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
    BiomeType biome[subchunk_count][subchunk_count]; // Sub-chunk biomes

    // Get biome for a specific sub-chunk position
    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 terrain
  • Mountain: Rocky elevated terrain
  • Sand: Desert/beach terrain
  • Water: Water bodies
  • Ice: Frozen terrain

Surface Tile Types:

  • Empty: No surface features
  • Wood: Trees/vegetation
  • Structure: Player-built structures

TilePos

Position structure for locating tiles within the map.

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)
};

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 = /*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 = /*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 = /*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
};

Parameters:

  • seed: 128-bit seed for all noise generators (see Seed structure)
  • temperature_scale: Controls the scale/frequency of temperature variation across the map
  • temperature_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 map
  • humidity_octaves: Number of noise octaves for humidity
  • humidity_persistence: How much each octave contributes to humidity noise (0.0-1.0)
  • base_scale: Controls the scale/frequency of base terrain height variation
  • 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.

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

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);
};

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 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

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;

// 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:

  1. 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
  2. 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:

  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.