Skip to main content

Overview

Hyperscape uses a hierarchical quadtree LOD system for infinite terrain rendering with dynamic chunk splitting based on camera distance. The system provides:
  • 5 LOD levels (depth 0-4) from 1600m root chunks to 100m leaf chunks
  • Uniform 32x32 vertex resolution across all LOD levels
  • Skirt geometry (15m drop) to hide LOD seams
  • Dynamic splitting/unsplitting based on player position
  • Client-only visual system (server uses flat 100m tile grid)
Added in commits 82a5365 and 6c14c8e (March 12, 2026). This is a client-only visual system. Server and gameplay logic still use the flat 100m tile grid (TerrainTile). getHeightAt() is unaffected.

TerrainQuadTree

The TerrainQuadTree class manages the hierarchical quad-tree of terrain chunks.

Configuration

LOD Levels

Split/Unsplit Logic

Split Condition:
Unsplit Condition:
Hysteresis: The unsplitMultiplier (1.2) creates a 20% buffer zone to prevent rapid split/unsplit cycles at LOD boundaries.

Usage

Performance Optimizations

Numeric Grid Coordinates (Commit 6c14c8e):
  • Uses numeric grid coordinates instead of string keys
  • Eliminates per-frame string allocation and GC pressure
  • Compares gridX === lastGridX && gridZ === lastGridZ instead of string comparison
Structural Dirty Flag (Commit 6c14c8e):
  • Set true whenever tree structure changes (split/unsplit)
  • Skips neighbor resolution when tree is stable
  • Cleared after updateAllNeighbours() completes
Lazy Terrain Generation:
  • Only generates terrain when all 4 neighbors are resolved
  • Prevents seam artifacts from missing neighbor data
  • terrainNeedsUpdate flag tracks pending generation

TerrainQuadNode

Individual nodes in the quad-tree representing square terrain regions.

Properties

Methods


Integration with TerrainVisualManager

The TerrainVisualManager listens to quad-tree events and generates/destroys terrain geometry:

Skirt Geometry

Skirts hide LOD seams by extending terrain geometry downward at chunk edges.

Implementation

Why Skirts Work

  • Overlap: Skirts from adjacent chunks overlap underground
  • Hidden: Skirts are below terrain surface, invisible to player
  • Seamless: Prevents gaps between LOD levels from being visible

Performance Characteristics

Memory Usage

Per-Node Overhead:
  • Node object: ~200 bytes
  • Children map: ~100 bytes
  • Neighbours map: ~100 bytes
  • Total: ~400 bytes per node
Typical World:
  • 3×3 root chunks (9 nodes at depth 0)
  • Average 2-3 subdivisions per root
  • ~500-1000 total nodes
  • Total memory: ~200-400 KB

CPU Usage

Per-Frame Operations:
  • Grid coordinate comparison (numeric, not string)
  • Neighbor resolution (only when structure changes)
  • Terrain generation requests (only for new final nodes)
Typical Frame:
  • No structure change: ~0.1ms
  • Structure change: ~1-2ms (neighbor resolution)

GPU Usage

Draw Calls:
  • One draw call per visible terrain chunk
  • Typical: 20-40 chunks visible
  • Total: 20-40 draw calls (vs 100+ without LOD)

Debugging

Debug Stats

Visualization

Enable terrain LOD visualization in dev tools:

Best Practices

Set minSize: 100 to match the server’s 100m tile grid. This ensures terrain chunks align with gameplay tiles.
Set unsplitMultiplier > 1.0 (recommended: 1.2) to prevent rapid split/unsplit cycles at LOD boundaries.
Use the same vertex resolution (32x32) for all LOD levels. This simplifies shader code and ensures consistent visual quality.
Set skirtDrop: 15 to extend terrain geometry downward at chunk edges. This hides gaps between LOD levels.
Only generate terrain when all 4 neighbors are resolved. This prevents seam artifacts from missing neighbor data.

Biome System

Biome-specific terrain generation with per-biome tree configs

Tree Instancing

Multi-variant tree rendering with BatchedMesh

Terrain Shaders

TSL-based terrain materials with biome blending

World System

World management and tile streaming