Taos Engine · Agent Tutorials

Agent Tutorials · Step 10

Agents on the Globe

▶ Live demo: run this tutorial — built from src/10_agents_on_the_globe.ts. Distilled from samples/geo_agents_ground.ts.

AgentScript has Leaflet and MapLibre integrations — agents on a real map. Taos Engine goes one step further: agents on real 3D terrain. This capstone runs a herd that walks the streamed Grand Canyon, using everything from the earlier tutorials — neighbor flocking, a patch field, per-agent state — plus the geo stack. Keyless (AWS Terrarium elevation + Esri imagery; no account).

The key insight#

A GeoScene's world frame is a local East-North-Up tangent plane (Y-up) at its origin — which is exactly the flat XZ plane the agent sim already runs in. So anchoring a GeoFrame at a lat/lon drops the whole model onto the planet with no coordinate gymnastics. The sim still computes in local XZ; geo streams the terrain under it. (Read geo tutorial 1 for the geo basics — GeoFrame, GeoScene, attach.)

Composing the two stacks#

Geo renders deferred, so the agents (additive HDR arrows) go in just before tonemap. The herd is a neighbor-flock (tutorial 4) authored over a bounce world:

TypeScript
deferredPreset({ sky: { kind: 'color', color: [0.62, 0.74, 0.92, 1] }, ao: 'ssao' })(engine);
const geo = new GeoScene(engine.ctx.device, GeoFrame.atLonLat(LON, LAT, ALTITUDE));
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: ESRI_WORLD_IMAGERY });   // keyless
geo.attach(engine, { cameraGO });

const particles = new ParticleFeature({ config: cfg, worldTransform });
engine.addFeatureBefore(particles, 'TonemapFeature');

worldTransform keeps the flock locked to the globe as the floating origin reanchors during camera flight:

TypeScript
const snap = geo.frame.snapshot();
const worldTransform = () => mat4dToEngine(geo.frame.rigidFromSnapshot(snap));

Walking the terrain#

Three pieces make the herd follow the ground instead of flying:

  1. A geo-referenced height field. Once terrain covers the anchor, sample its surface into a patch field — GeoScene.sampleHeightGrid reads the terrain over the local grid and returns curvature-corrected local-Y offsets:

    TypeScript
    particles.pass?.setPatchField(engine.ctx, HEIGHT_FIELD,
      geo.sampleHeightGrid([-HALF, -HALF], [WORLD, WORLD], GRID));

    Re-sample a few times as deeper LODs stream in.

  2. Terrain-follow in the step. Each turtle sets its Y by sensing that field:

    TypeScript
    action.set('position', expr.vec3(
      expr.swizzle(pos, 'x'),
      expr.add(agent.sense(HEIGHT_FIELD, 'bilinear'), SURFACE_OFFSET),   // +small lift
      expr.swizzle(pos, 'z'),
    )),
  3. Surface tilt. The flat arrows lie on the slope by storing the terrain normal (central differences of the height field) in velocity and setting render.tiltToSurface: true. Because that repurposes velocity as a render channel, set cfg.integrateVelocity = false so the stored normal isn't treated as motion.

Together: a coherent flock draped on the canyon walls, depth-occluded by the ridges, that tracks the globe as you fly. The other geo agent samples in the repo do a flying flock and a 2D top-down map (the Leaflet/MapLibre look, via an orthographic camera over aerial imagery).

That's the series#

You've gone from turtles wandering a torus to a flock walking a real canyon, with the full AgentScript triad — turtles, patches, links — plus breeds, reproduction, measurement, a visual editor, and geo. The module guide is the reference for everything; the samples are the ground truth.