Taos Engine ▦ Taos: API Documentation

6 — Clipping

Live demo: run this tutorial in your browser — built from src/06_clipping.ts. Toggle the cuts with F / P.

Clipping carves geometry away at render time: a horizontal plane that slices off everything above a height, or a polygon that punches a hole in the city (or keeps only what's inside it). It works per-fragment across 3D tiles and terrain at once. Source: samples/geo_clipping.ts, src/geo/clipping.ts.

The model#

A single GeoClipping object holds your clip definitions in geodetic terms (lat/lon/height — stable as the floating origin shifts). Each frame you pack it against the current GeoFrame (turning geodetic into the frame's world space) and hand the packed buffer to the geometry pass, which clips every tile and terrain fragment against it.

import { GeoClipping } from 'taos/geo/index.js';
import { GeometryFeature } from 'taos/engine/index.js';   // for the per-frame wiring below

const clip = new GeoClipping();
clip.enabled = true;

GeoClipping fields:

Field Meaning
planes array of ClipPlaneDef — half-space cuts
polygon one ClipPolygonDef or null — a closed ring
unionPlanes false = a fragment must be inside all planes; true = inside any
inversePolygon false = hide inside the ring; true = hide outside it
enabled master on/off

Limits: up to MAX_CLIP_PLANES (8) planes and MAX_CLIP_POLY_VERTS (64) polygon vertices.

Clipping planes#

A plane is anchored at a lat/lon/height with a normal in the local East-North-Up frame. The classic "cut everything above this floor" is an up-facing plane:

clip.planes = [
  {
    lonDeg: LON,
    latDeg: LAT,
    height: 40,             // meters above the ellipsoid — the cut height
    normalEnu: { u: 1 },    // up-facing → keep geometry above, slice it open at 40 m
  },
];

normalEnu components are { e?, n?, u? } (east/north/up; missing = 0). Flip the sign ({ u: -1 }) to keep the bottom instead. Add more planes for box-like cuts; unionPlanes decides whether they intersect (AND) or combine (OR).

Clipping polygons#

A polygon is a ring of lon/lat points. By default it hides what's inside the ring (a hole in the city); set inversePolygon = true to instead keep only what's inside (everything else disappears):

clip.polygon = {
  positions: [
    { lonDeg: LON - 0.001, latDeg: LAT - 0.001 },
    { lonDeg: LON + 0.001, latDeg: LAT - 0.001 },
    { lonDeg: LON + 0.001, latDeg: LAT + 0.001 },
    { lonDeg: LON - 0.001, latDeg: LAT + 0.001 },
  ],
};
clip.inversePolygon = false;   // hole; true = isolate just this footprint

Wiring it to the geometry pass — every frame#

Because the floating origin moves (reanchoring), you must pack and re-apply the clipping each frame, after the reanchor. geo.attach(engine, { cameraGO }) already installs the reanchor in beforeFrame, so register this clip update after it and the ordering is right. The geo geometry feature exposes the pass; push the packed buffer with setClipping:

geo.attach(engine, { cameraGO });   // installs the floating-origin reanchor

engine.beforeFrame(() => {
  const geomFeature = engine.getFeature<GeometryFeature>(GeometryFeature.name);
  geomFeature?.pass?.setClipping(clip.pack(geo.frame));
});

clip.pack(geo.frame) returns the Float32Array the shader expects, resolved into the current frame's basis. Rebuild the clip.planes / clip.polygon arrays whenever the user changes them, then the per-frame pack picks up the change.

To turn clipping off, set clip.enabled = false (it still packs, but as a no-op) or stop calling setClipping.

The clip applies to the shared deferred GeometryPass, so it affects 3D tiles and quantized-mesh terrain together — there's one global clip state, not a per-tileset one. geo_clipping.ts demos a floor plane and a polygon hole with live toggles.

Next#