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#
- Tutorial 7 — Vector Data: GeoJSON layers, declarative tile styling, and time-dynamic CZML animation.