Chunk-Based Generation

The world is divided into square chunks of configurable size (default 64 m × 64 m). Each chunk is generated independently — a self-contained MC3 XML scene file.

Chunks are keyed by (x, y) coordinate. Once generated and validated, they are cached to disk and never regenerated unless the cache is cleared. This makes large exports (400+ chunks) fast on subsequent runs.

  • Configurable grid size (grid_w × grid_h)
  • Configurable chunk size in metres (chunk_size_m)
  • Zone and region types per chunk coordinate
  • Variation seed per chunk (deterministic noise)
  • Disk cache with file-level invalidation
json — examples/world.json
{
  "name": "demo_city",
  "seed": 42,
  "style": "central_europe_small_city",
  "grid_w": 20,
  "grid_h": 20,
  "chunk_size_m": 64,
  "zones": [
    {
      "type": "city",
      "x_min": 0, "x_max": 18,
      "y_min": 0, "y_max": 18,
      "region_default": "small_house_block",
      "regions": [
        {
          "x_min": 9, "x_max": 10,
          "y_min": 9, "y_max": 10,
          "type": "square"
        }
      ]
    }
  ]
}

Dual-Language Pipeline

Every chunk goes through a Lua-first pipeline. If a Lua generator with the matching ID exists, it runs. If not, MeshWorld falls back to the equivalent C++ generator. This makes it trivial to override any built-in generator with a custom Lua version.

✓ Lua-first

The LuaGeneratorRegistry scans generators/lua/ at startup and maps each generator's M.id string. The LuaSandbox blocks all dangerous modules (io, os, debug, require) for safe execution.

✓ C++ fallback

20 production-ready C++ generators cover every zone type. If no Lua override is found, get_generator(zone, region) returns the appropriate C++ generator.

How it works: When lua.zone.park exists in the registry, it runs instead of cpp.chunk.park. Override only what you want to change — the rest stays untouched.
Zero-config discovery: No registration step needed. Drop a .lua file in the right subdirectory, run the app, and the generator is live.
Sandbox restrictions: Lua generators cannot access the file system, network, OS, or spawned processes. Only the scene API and math/string/table libraries are available.

MC3 Validation Pipeline

Every generated chunk is validated before it is cached to disk. The MC3Validator checks four categories of correctness:

CheckDescription
Well-formed XMLThe output parses as valid XML with the mc3 root element.
MetadataGenerator ID, version, and language fields are present and non-empty.
BoundsAll geometry is within the declared chunk boundary (0 to chunk_size_m).
Material namesEvery material reference exists in the MaterialRegistry.

Validation failures are reported with the chunk coordinate and a descriptive error message. Invalid chunks are not cached.

ValidationResult: The validator returns a ValidationResult struct containing a boolean valid flag and a vector of human-readable error strings.

The validation pipeline ensures that every MC3 file in the cache is guaranteed to load correctly in the MeshCraft viewer. Broken generators cannot corrupt the cache.

~100 Built-in Materials

The MaterialRegistry ships with approximately 100 procedural materials covering all major surface categories. Each material entry stores:

  • RGB colour (linear float triplet)
  • Roughness value (0.0 – 1.0)
  • Category tag (stone, wood, metal, glass, foliage…)
  • License metadata (MIT, CC0, etc.)

Material names are validated during MC3 validation. Any unknown material name is a validation error, ensuring generators never produce invalid material references.

bash
# List all registered materials
./build/MeshWorldMaterials

# Example output:
grass                rgb(0.28,0.52,0.22) rough=0.95  MIT
stone_wall           rgb(0.55,0.52,0.48) rough=0.88  MIT
wood_bench           rgb(0.55,0.38,0.22) rough=0.75  MIT
metal_lamp           rgb(0.72,0.72,0.72) rough=0.20  MIT
glass_clear          rgb(0.80,0.90,0.95) rough=0.05  MIT
foliage_oak          rgb(0.20,0.45,0.15) rough=0.90  MIT

Material Categories

Stone & Masonry

stone_wall, stone_pavement, stone_path, brick_red, brick_dark, concrete, asphalt…

Wood

wood_bench, wood_door_panel, wood_birch, wood_bark_dark, wood_bark_light…

Metal & Glass

metal_lamp, glass_clear, metal_railing, metal_frame…

Foliage

foliage_oak, foliage_linden, foliage_birch, foliage_chestnut, grass, grass_park…

Soil & Ground

soil, sand, gravel, dirt, mud…

Flowers & Accents

flowers_red, flowers_yellow, flowers_white, flowers_purple, plaster_white…

Taxonomy & Containment Rules

The TaxonomyRegistry loads a JSON node hierarchy that describes how world objects relate to each other — what can contain what, at which level of detail.

The ContainmentRuleRegistry extends this with LOD-aware rules: at low detail, a city zone contains only block shapes; at high detail, blocks contain individual buildings; buildings contain rooms; rooms contain furniture.

This hierarchy drives generator selection and will underpin future multi-LOD streaming.

json — data/taxonomy/containment.json (excerpt)
{
  "rules": [
    {
      "parent": "city",
      "child":  "block",
      "lod_min": 0, "lod_max": 2
    },
    {
      "parent": "block",
      "child":  "building",
      "lod_min": 2, "lod_max": 4
    },
    {
      "parent": "building",
      "child":  "room",
      "lod_min": 4, "lod_max": 6
    }
  ]
}

SQLite Content Packs

The MeshWorldPack tool bundles all generators, taxonomy, and materials into a single portable .sqlite file. Content packs can be distributed, versioned, and swapped independently of the main executable.

The ContentPackLoader reads these bundles at runtime. Multiple packs can be loaded, with later packs overriding earlier ones — enabling a clean mod/override system.

bash
# Pack everything into one file
./build/MeshWorldPack meshworld_content.sqlite

# The resulting .sqlite file contains:
#   generators  — Lua source + C++ metadata
#   taxonomy    — node hierarchy JSON
#   materials   — RGB + roughness + license
#   styles      — style name → parameter set

World Streaming & Rendering

WorldStreamer tracks the player's current chunk coordinate and maintains a configurable load radius. As the player moves, chunks outside the radius are unloaded and new ones are loaded (or generated on demand).

WorldRenderer wraps the streamer and drives Mc3Renderer, which uses MeshCraft's SceneRenderer to draw geometry on screen. Mc3MeshBuilder tessellates MC3 primitives (boxes, cylinders, planes) into triangle meshes at load time.

  • Configurable load radius (default 2 chunks)
  • LOADED / UNLOADED event callbacks
  • FPCamera: position, yaw, pitch, fov_y, near/far, aspect
  • SDL3 + OpenGL rendering via CNA framework
  • WASD / Q / E movement, mouse look
MeshWorldApp is a standalone binary built from apps/mesh-world-app/ with its own CMake project. It requires the cna, sharp-runtime, easy-gl, and mesh-craft sibling repositories.
Controls:
WASD — move horizontally
Q / E — move down / up
Mouse — look around
ESC — exit to main menu

Five Command-Line Tools

MeshWorld

Loads a world config, prints world info, and samples 4 chunks with their zone/region assignments and generation stats.

./build/MeshWorld examples/world.json

MeshWorldMap

Prints an ASCII zone layout map of the world grid — zone type and region type per chunk. Useful for visualising world structure at a glance.

./build/MeshWorldMap examples/world.json

MeshWorldExport

Exports all chunks to MC3 XML files. Pass --mcb to also compile to the MCB binary format. Output goes to a specified directory.

./build/MeshWorldExport \
  --mcb world.json output/

MeshWorldMaterials

Lists all registered materials with their RGB values, roughness, category, and license. Useful for finding the right material name for a Lua generator.

./build/MeshWorldMaterials

MeshWorldPack

Packs all generators, taxonomy, materials, and styles into a portable SQLite content pack file for distribution or archival.

./build/MeshWorldPack \
  meshworld_content.sqlite

MeshWorldTests

GTest-based test suite. 119 tests covering all major subsystems — ChunkPipeline, LuaSandbox, MaterialRegistry, MC3Validator, WorldStreamer, and more.

cd build
ctest --output-on-failure