<-- Back to Home

Dev Log

// Active development diary for Urban Sprawl

v0.9.0-dev
AAA Terrain Refactor: From Flat Planes to Living Landscapes

This is the biggest single change to Urban Sprawl's renderer since the project began. We ripped out the entire procedural generation pipeline, rebuilt the terrain system from scratch, and added real geographic features — rolling hills, coastal shores, river valleys, islands, and plateaus. The city no longer floats on a flat plane; it sits on the land.

What Changed

  • Multi-layer heightfield — 5 noise layers (Continental, Ridge, Hills, Detail, Erosion) blended with geographic features like terracing, coastal shelves, and island radial falloff
  • 6 map layouts — Flat, Rolling Hills, Coastal Shore, River Valley, Island, and Plateaus. Each configures its own noise layers, water, and geographic modifiers
  • Auto-textured terrain shader — Height + slope-based blending: grass on flat lowlands, dirt on moderate slopes, rock on cliffs, gravel on peaks. Underwater darkening with blue shift for riverbeds
  • Water overhaul — Gerstner wave ocean planes for Shore/Island layouts, plus river strip meshes for valleys. Forward-rendered with alpha blending
  • Terrain-aware everything — Buildings, roads, sidewalks, vehicles, pedestrians — all properly sample and flatten terrain. No more floating/buried geometry

The Performance Problem (And How We Solved It)

After the initial implementation, we had a nasty periodic stutter — the game would freeze for ~200ms every couple of seconds. The root cause was terrain mesh regeneration. Every time a building spawned (which sets the terrain dirty flag), the system would regenerate the entire terrain mesh: 1,001,001 vertices, each requiring ~27 Perlin noise evaluations plus an O(N) scan through every terrain modification region.

Before: 1M vertices × 27 Perlin evals × O(all_regions) per vertex = massive stutter
After: 251K vertices × O(1) cache lookup × O(nearby_regions) per vertex = smooth 60fps

Three changes eliminated the stutter entirely:

  • HeightfieldCache — A pre-sampled 501×501 height grid built once at startup. All terrain queries now use O(1) bilinear interpolation instead of computing 9 Perlin noise instances per sample
  • Resolution reduction — Terrain mesh dropped from 1000×1000 to 500×500 subdivisions (~5m per vertex). 4× fewer vertices with no visible quality loss at gameplay distances
  • Spatial-indexed modifications — Terrain flattening regions (roads, intersections, buildings) are bucketed into a 50m-cell spatial hash. apply_at() now only checks nearby regions instead of scanning every region for every vertex

The Brown Terrain Bug

After shipping the terrain shader, every map looked brown instead of green. The culprit: WGSL's smoothstep() is undefined when the low edge ≥ high edge. Several of our smoothstep calls had reversed arguments (e.g., smoothstep(0.25, 0.15, x)), which returned garbage values on most GPUs. The grass slope weight was always ~0, so dirt dominated everywhere. Fixed by rewriting all weight calculations with correct argument ordering.

Architecture Cleanup

  • Deleted the entire src/procgen/ directory (14 files) — the old procedural city generation pipeline
  • Extracted shared types (road types, building types, zone types, parcels, river) into src/world/
  • Migrated 19 files from direct noise sampling to the HeightfieldCache resource pattern
  • Added rebuild_heightfield_cache_on_config_change system to keep cache synchronized when map size changes
v0.8.0
Visual Quality Sprint: Cinematic Polish Pass

A 4-week focused sprint to push Urban Sprawl's visuals from "tech demo" to "cinematic." Added quality tier presets (Low/Medium/High via Shift+F10), a first-person walk camera, procedural sky atmosphere, screen-space reflections for wet surfaces, PBR glass, and a full cinematic polish pass with SSAO, film grain, vignette, and auto-exposure.

Highlights

  • Procedural sky atmosphere — Rayleigh/Mie scattering with sun disc, time-of-day color shifts
  • First-person camera — Walk your city at street level (F1 to toggle)
  • Road surface shaders — Asphalt (9 layers), sidewalk concrete, road markings with MUTCD paint
  • Vehicle paint shader — Metallic flake, clearcoat Fresnel, orange peel, grime
  • Profile-extruded vehicles — 12 types with 7-10 cross-section profiles each
  • Pedestrian overhaul — Articulated humanoids with 60M+ unique visual combinations
  • Quality tiers — Configurable presets affecting shadows, SSAO, bloom, volumetric fog
v0.8.0
Performance Scale-Up: 1km² to 57km²

A 7-phase performance initiative to scale the city from a single 1km² sandbox to Cities: Skylines 2-scale maps. Introduced world chunk streaming (250m chunks), entity pooling, macro simulation for dormant chunks, traffic CA partitioning, and configurable map sizes from 500m to 7.5km.

Key Systems

  • World chunk streaming — 250m chunks with camera-driven load/unload radius, throttled spawn/despawn (50/frame)
  • Entity pooling — VehiclePool and PedestrianPool recycle entities instead of spawn/despawn churn
  • Macro simulation — 1Hz lightweight sim for dormant chunks maintains city-wide economic accuracy
  • 5 map sizes — Small (500m) to Massive (7.5km) with auto-scaled terrain, camera, and fog
  • EntityBudget — Configurable caps: 2K vehicles, 5K pedestrians, 2M total entities