Taos Engine · Modules

Modules

Geo (Geospatial Globe Streaming)

The geo module streams planet-scale geospatial data — photorealistic 3D Tiles, OSM buildings, quantized-mesh terrain, imagery — and renders it through the standard Taos Engine pipeline. The hard parts (floating-origin f64 precision, LOD by screen-space error, frustum culling, tile caching/eviction, attribution) are handled for you; you supply a data source and a camera.

New to the module? The Geo Tutorials are a guided, step-by-step path — start with Hello, Geo and work through anchors, layers, atmosphere, splats, clipping, and more. This page is the reference they build on.

It's the largest subsystem. Source lives in src/geo/. The three types you touch most:

TypeScript
import { GeoFrame } from 'taos/geo/geo.js';
import { GeoScene } from 'taos/geo/geo_scene.js';
import { GeoFeature, reanchorCamera } from 'taos/geo/geo_feature.js';
import { resolveIonAsset, ION_ASSETS } from 'taos/geo/cesium_ion.js';

(Everything is also re-exported from the taos/geo/index.js barrel.)


The mental model#

So: frame anchors the world → scene streams tiles → feature feeds them to the renderer.


Minimal app#

The complete samples/geo_minimal.ts — OSM buildings on Cesium World Terrain at New York — is essentially this:

TypeScript
// Reversed-Z gives the depth precision a planet-scale far plane needs.
const engine = await Engine.create({
  canvas,
  contextOptions: { enableErrorHandling: true, reversedZ: true },
});
deferredPreset({
  sky: { kind: 'color', color: [0.52, 0.72, 0.92, 1] },
  ao: 'ssao',
  // aerial perspective is opt-in (`lighting: { enableAerial: true }`); it's tuned
  // for an atmosphere sky, so leave it off with this flat color.
})(engine);

// Sun + camera (note the huge far plane and reversed-Z above).
const cameraGO = new GameObject({ name: 'Camera' });
cameraGO.setPosition(0, 400, 2000);
cameraGO.addComponent(Camera.createPerspective(60, 1, 5_000_000, engine.ctx.width / engine.ctx.height));
// Fly controller as a component: the engine updates it and attaches its input.
cameraGO.addComponent(CameraController.create({ pitch: -0.15, speed: 250 }));
engine.scene.add(cameraGO);

// Floating origin at New York + two streamed ion tilesets.
const geo = new GeoScene(engine.ctx.device, GeoFrame.atLonLat(-74.0060, 40.7128, 0));
geo.addTerrain(await resolveIonAsset(ION_ASSETS.worldTerrain, token));
geo.add3DTiles(await resolveIonAsset(ION_ASSETS.osmBuildings, token));

// `attach` registers a GeoFeature (which appends to frame.opaque / frame.shadowCasters)
// BEFORE the stock Shadow + Geometry features that read those lists, and — given the
// camera — installs the floating-origin auto-reanchor that keeps world coords f32-safe.
geo.attach(engine, { cameraGO });
engine.run();

GeoFrame — the floating origin#

TypeScript
GeoFrame.atLonLat(lonDeg, latDeg, height = 0);  // anchor on the WGS84 ellipsoid
GeoFrame.atEcef(originEcef, up);                 // anchor anywhere (Moon, other bodies)

It exposes the transforms between ECEF (f64) and engine world space:

TypeScript
frame.worldFromEcefPoint(p);   // ECEF → world position
frame.worldFromEcefDir(d);     // ECEF → world direction (rotation only)
frame.ecefFromWorldPoint(w);   // world → ECEF
frame.reanchor(newOriginEcef); // move the origin, keep the tangent basis fixed

Reanchoring. As the camera roams away from the origin, world coordinates grow until f32 precision suffers. Call reanchorCamera(frame, cameraGO, reanchorDist = 10_000) from beforeFrame (before the engine caches camera matrices). When the camera passes reanchorDist meters from the origin, it rebases the frame and shifts the camera so nothing moves on screen.

Geodesy helpers (geodeticToEcef, lonLatToEcef, ecefToGeodetic, the WGS84 constants) live in geo.ts.


GeoScene#

TypeScript
const geo = new GeoScene(device, frame, opts?);

GeoSceneOptions: budgetBytes (GPU budget per tileset, default 512 MiB), budgetTris (~30M), maxConcurrent (in-flight loads, 16), retainFrames (eviction grace window, 240), evictHard (soft-overage multiplier, 2).

Add datasets — both return immediately with the tileset and a ready promise (it's safe to update() before ready resolves; the tileset no-ops):

TypeScript
const { tileset, ready } = geo.add3DTiles(asset, {
  // GeoTilesetOptions
  lit: true,                 // light unlit materials (default true)
  collision: true,           // retain CPU triangle soup for physics (TileContent.collision)
  albedoBoost: 1.0,          // brighten dark content
  bodyOffset,                // ECEF offset for non-Earth bodies
  regionEllipsoid: { a, b }, // ellipsoid for region-bound tiles
  maxRenderDistance: 5000,   // render-distance cap (m); default 5 km, Infinity = draw to horizon
});
await ready;
tileset.maxSSE = 12;         // target screen-space error in pixels (default 16; lower = sharper, costlier)

const terr = geo.addTerrain(asset, { imagery: true });   // quantized-mesh terrain

Other methods:

TypeScript
geo.addStatic(content);  geo.removeStatic(content);   // one-off baked content (see anchors)
geo.remove(tileset);     geo.clear();
geo.heightAt(lonRad, latRad);   // terrain height (e.g. to clamp buildings); null if unloaded
geo.setBudget(bytes, tris);
geo.cacheStats();
geo.lastResult;          // the previous frame's GeoFrameResult (copyrights, triangles, …)

You normally don't call geo.update() yourself — GeoFeature does it. Its GeoFrameResult carries opaque, shadowCasters, water, copyrights, triangles, renderedLodLevels, terrainContents, and tileContents.


GeoFeature — and geo.attach#

GeoScene.attach is the one-call wiring used above: it constructs a GeoFeature, registers it in the right slot, and (given a camera) installs the floating-origin reanchor.

TypeScript
const feature = geo.attach(engine, {
  cameraGO,               // installs autoOriginShift; omit to pin the origin
  reanchorDist: 10_000,   // reanchor threshold (m) for the auto origin shift
  // ...any GeoFeature option below also passes through...
});

The underlying GeoFeature options:

TypeScript
engine.addFeatureBefore(new GeoFeature({
  scene: geo,
  fovY: undefined,        // screen-space-error FOV; defaults to the camera's live `fov`
                          // (read every frame) — pass a number only to override it
  cull: () => true,       // live frustum-cull toggle
  dt: (f) => f.dt,        // LOD cross-fade dt; return 0 to disable the fade
  draw: () => true,       // stream but emit nothing when false
  shadows: () => true,
  materialOverride: (lod) => null,  // LOD-debug coloring
}), ShadowFeature.name);

Order is critical: GeoFeature appends to frame.opaque / frame.shadowCasters, so it must run before ShadowFeature and the geometry feature that consume those lists — hence addFeatureBefore(..., ShadowFeature.name) (which attach does for you).

geo.autoOriginShift(engine, cameraGO, reanchorDist?) is the reanchor half on its own — attach calls it when you pass cameraGO; call it directly if you wire the GeoFeature by hand but still want automatic origin shifting.


Data sources#

ProviderHow to get a sourceCredential
Cesium ionawait resolveIonAsset(assetId, ionToken) → IonAssetBearer token
Google 3D Tiles (direct)resolveGoogleTiles(apiKey) → GoogleTilesSourceAPI key in URL
MapTiler terrainresolveMapTilerTerrain(apiKey) → MapTilerTerrainSourceAPI key in URL

ION_ASSETS has the well-known ion asset IDs:

TypeScript
ION_ASSETS.googlePhotorealistic;  // 2275207 — Google Photorealistic 3D Tiles via ion
ION_ASSETS.osmBuildings;          // 96188
ION_ASSETS.worldTerrain;          // 1 — Cesium World Terrain (quantized-mesh)
ION_ASSETS.bingAerial;            // 2
ION_ASSETS.cesiumMoon;            // 2684829 — Moon terrain (3D Tiles)

All implement the GeoDataSource interface (kind, label, rootUrl, attributions, fetchJson, fetchArrayBuffer).


Anchors — placing models on the globe#

Drop a glTF model at a geodetic position and add it as static content:

TypeScript
import { loadAnchor } from 'taos/geo/anchor.js';

const content = await loadAnchor(device, geoFrame, '/models/duck.glb', {
  lonDeg: -122.4194, latDeg: 37.7749, height: 50,
  scale: 10, headingDeg: 45,         // rotation about local up (0 = north)
  // bodyCenter, ellipsoid — for non-Earth bodies
});
geo.addStatic(content);

Water & wireframe overlays#

GeoScene exposes waterDraws (quantized-mesh watermask surfaces) and opaqueDraws, which feed two optional features:


Samples#


Gotchas#

See also#