mcpbeat

Cesiumjs 3d Tiles

cesiumgs/cesiumjs-3d-tiles

CesiumJS 3D Tiles - Cesium3DTileset, compressed and CAD-style glTF content, MVTDataProvider, styling, metadata, feature picking, voxels, point clouds, I3S, Gaussian splats, clipping. Use when loading 3D Tiles or Mapbox Vector Tiles, rendering KHR meshopt/CAD content, styling or querying features, working with voxels or point clouds, or clipping spatial data.

4k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
110
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/CesiumGS/cesiumjs-skills --skill cesiumjs-3d-tiles

The instruction itself

21 sections, as written by the author

CesiumJS 3D Tiles

Version baseline: CesiumJS v1.143 (ES module imports, async factory methods).

Loading a Tileset

Always use async factory methods -- never call the constructor directly.

import { Cesium3DTileset, HeadingPitchRange, Math as CesiumMath } from "cesium";

// From a URL
const tileset = await Cesium3DTileset.fromUrl(
  "https://example.com/tileset.json",
  { maximumScreenSpaceError: 16 }, // lower = higher quality
);
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset, new HeadingPitchRange(
  0.0, CesiumMath.toRadians(-25.0), tileset.boundingSphere.radius * 2.0,
));

CesiumJS 1.143 applies standalone model loading to glTF embedded in tilesets.

Read the glTF compatibility matrix

for automatic KHR_meshopt_compression, CAD extension behavior, and the unsupported planar-fill boundary.

// From Cesium ion
const tileset = await Cesium3DTileset.fromIonAssetId(75343);
viewer.scene.primitives.add(tileset);
// Google Photorealistic 3D Tiles
import { createGooglePhotorealistic3DTileset } from "cesium";
const google3D = await createGooglePhotorealistic3DTileset({
  onlyUsingWithGoogleGeocoder: true,
});
viewer.scene.primitives.add(google3D);
// OSM Buildings
import { createOsmBuildingsAsync } from "cesium";
const osmBuildings = await createOsmBuildingsAsync();
viewer.scene.primitives.add(osmBuildings);

Key Constructor Options

| Option | Default | Purpose |

|--------|---------|---------|

| maximumScreenSpaceError | 16 | LOD quality threshold (pixels) |

| cacheBytes | 536870912 | Tile cache trim target (bytes) |

| maximumCacheOverflowBytes | 536870912 | Extra cache headroom |

| shadows | ShadowMode.ENABLED | Shadow casting/receiving |

| modelMatrix | Matrix4.IDENTITY | Root transform |

| clippingPlanes | undefined | ClippingPlaneCollection |

| clippingPolygons | undefined | ClippingPolygonCollection (WebGL 2) |

| enableCollision | false | Camera collision with tileset surface |

| pointCloudShading | undefined | Point attenuation options object |

| classificationType | undefined | TERRAIN, CESIUM_3D_TILE, or BOTH |

| dynamicScreenSpaceError | true | Horizon LOD optimization |

| foveatedScreenSpaceError | true | Center-screen tile priority |

| preloadFlightDestinations | true | Prefetch tiles at flight target |

| featureIdLabel | "featureId_0" | EXT_mesh_features ID set label |

| backFaceCulling | true | Cull back faces per glTF material |

| edgeDisplayMode | EdgeDisplayMode.SURFACES_ONLY | Render glTF edge-visibility data when present |

Mapbox Vector Tiles as Runtime 3D Tiles (Experimental, 1.142+)

MVTDataProvider loads {z}/{x}/{y} Mapbox Vector Tile .mvt/.pbf

templates and converts tile payloads into runtime 3D Tiles. Use it when vector

data is naturally tiled and you want 3D Tiles styling, metadata picking, and LOD

instead of a single GeoJSON primitive.

For one in-memory or URL-backed GeoJSON object, prefer GeoJsonPrimitive in

cesiumjs-primitives. For Entity/DataSource conveniences, prefer

GeoJsonDataSource in cesiumjs-entities.

import {
  Cesium3DTileStyle,
  MVTDataProvider,
  Rectangle,
} from "cesium";

const provider = await MVTDataProvider.fromUrl(
  "https://example.com/tiles/{z}/{x}/{y}.pbf",
  {
    minZoom: 4,
    maxZoom: 14,
    extent: Rectangle.fromDegrees(-125, 24, -66, 50),
    featureIdProperty: "id",
  },
);

viewer.scene.primitives.add(provider);

// The provider owns a generated Cesium3DTileset.
provider.tileset.style = new Cesium3DTileStyle({
  color: {
    conditions: [
      ["${kind} === 'park'", "color('seagreen', 0.65)"],
      ["${kind} === 'water'", "color('steelblue', 0.55)"],
      ["true", "color('white', 0.45)"],
    ],
  },
});

Feature properties are encoded as EXT_structural_metadata, so standard

3D Tiles styling and picking patterns apply:

const picked = viewer.scene.pick(windowPosition);
if (picked && typeof picked.getProperty === "function") {
  console.log(picked.getProperty("name"));
}

Notes:

  • URL templates must contain {z}, {x}, and {y} placeholders; tile URLs are parsed from /z/x/y.
  • Empty 204/404 tiles are treated as missing instead of hard failures.
  • provider.show proxies visibility to the generated tileset.
  • Runtime vector glTF content uses draft EXT_mesh_polygon and 3DTILES_content_gltf_vector support; treat this path as experimental.

Tileset Events

tileset.loadProgress.addEventListener((pending, processing) => {
  if (pending === 0 && processing === 0) console.log("Loaded");
});
tileset.initialTilesLoaded.addEventListener(() => { /* first view ready */ });
tileset.allTilesLoaded.addEventListener(() => { /* all visible tiles ready */ });
tileset.tileLoad.addEventListener((tile) => { /* tile content loaded */ });
tileset.tileUnload.addEventListener((tile) => { /* tile evicted from cache */ });
tileset.tileFailed.addEventListener(({ url, message }) => {
  console.error(`Tile ${url}: ${message}`);
});
// Per-frame manual styling
tileset.tileVisible.addEventListener((tile) => {
  const content = tile.content;
  for (let i = 0; i < content.featuresLength; i++) {
    content.getFeature(i).color = Cesium.Color.fromRandom();
  }
});

Runtime Properties

tileset.show = false;                     // toggle visibility
tileset.maximumScreenSpaceError = 8;      // increase quality
const { center, radius } = tileset.boundingSphere;

import { Matrix4, Cartesian3 } from "cesium";
tileset.modelMatrix = Matrix4.fromTranslation(new Cartesian3(0, 0, 100));

Declarative Styling

Assign a Cesium3DTileStyle to tileset.style. Expressions reference feature

properties with ${PropertyName}.

import { Cesium3DTileStyle } from "cesium";

// Color by height conditions
tileset.style = new Cesium3DTileStyle({
  color: {
    conditions: [
      ["${Height} >= 100", "color('purple', 0.5)"],
      ["${Height} >= 50",  "color('red')"],
      ["true",             "color('blue')"],
    ],
  },
  show: "${Height} > 0",
});
// Use defines to simplify repeated sub-expressions
tileset.style = new Cesium3DTileStyle({
  defines: { material: "${feature['building:material']}" },
  color: {
    conditions: [
      ["${material} === null",    "color('white')"],
      ["${material} === 'glass'", "color('skyblue', 0.5)"],
      ["${material} === 'brick'", "color('indianred')"],
      ["true",                    "color('white')"],
    ],
  },
});
// Show/hide by property
tileset.style = new Cesium3DTileStyle({
  show: "${feature['building']} === 'office'",
});
// Point cloud styling
tileset.style = new Cesium3DTileStyle({
  color: "vec4(${Temperature})",
  pointSize: "${Temperature} * 2.0",
});
tileset.style = undefined; // reset to default appearance

Color Blend Modes

import { Cesium3DTileColorBlendMode } from "cesium";
tileset.colorBlendMode = Cesium3DTileColorBlendMode.REPLACE; // HIGHLIGHT | REPLACE | MIX
tileset.colorBlendAmount = 0.5; // only used with MIX

Edge Display Mode (Experimental, 1.142+)

edgeDisplayMode controls edges contributed by the draft glTF

EXT_mesh_primitive_edge_visibility extension. Tiles without that extension

render normally regardless of this setting.

import { Cesium3DTileset, EdgeDisplayMode } from "cesium";

const tileset = await Cesium3DTileset.fromUrl("/cad/tileset.json", {
  edgeDisplayMode: EdgeDisplayMode.SURFACES_AND_EDGES,
});
viewer.scene.primitives.add(tileset);

// CAD-style wireframe for content that carries edge-visibility data.
tileset.edgeDisplayMode = EdgeDisplayMode.EDGES_ONLY;

// Default rendering: hide extension-provided edges.
tileset.edgeDisplayMode = EdgeDisplayMode.SURFACES_ONLY;

Feature Picking and Properties

Scene.pick returns Cesium3DTileFeature for 3D Tiles features. Modifications

persist until the owning tile is evicted from the cache.

import {
  ScreenSpaceEventHandler, ScreenSpaceEventType,
  Cesium3DTileFeature, Color,
} from "cesium";

const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);

// Hover: read properties
handler.setInputAction((movement) => {
  const feature = viewer.scene.pick(movement.endPosition);
  if (feature instanceof Cesium3DTileFeature) {
    const ids = feature.getPropertyIds();
    for (const id of ids) console.log(`${id}: ${feature.getProperty(id)}`);
    feature.color = Color.YELLOW; // highlight
  }
}, ScreenSpaceEventType.MOUSE_MOVE);

// Click: inspect a single property
handler.setInputAction((movement) => {
  const feature = viewer.scene.pick(movement.position);
  if (feature instanceof Cesium3DTileFeature) {
    console.log("Height:", feature.getProperty("Height"));
    feature.setProperty("selected", true); // write custom property
    feature.show = false;                  // hide individual feature
  }
}, ScreenSpaceEventType.LEFT_CLICK);

Inherited Metadata (3D Tiles 1.1 / EXT_structural_metadata)

// Searches: batch table -> content -> tile -> subtree -> group -> tileset
const value = feature.getPropertyInherited("semanticOrPropertyName");

Clipping Planes

ClippingPlaneCollection clips via half-space planes in the tileset's local

coordinate system.

import {
  ClippingPlane, ClippingPlaneCollection,
  Cartesian3, Color, Matrix4,
} from "cesium";

const clippingPlanes = new ClippingPlaneCollection({
  planes: [new ClippingPlane(new Cartesian3(0.0, 0.0, -1.0), 0.0)],
  edgeWidth: 1.0,
  edgeColor: Color.WHITE,
  unionClippingRegions: false, // false = intersection (AND); true = union (OR)
});

const tileset = await Cesium3DTileset.fromUrl(url, { clippingPlanes });
// Or: tileset.clippingPlanes = clippingPlanes;

// Offset the clip boundary at runtime
clippingPlanes.modelMatrix = Matrix4.fromTranslation(new Cartesian3(0, 0, 50));
clippingPlanes.get(0).distance = 25.0;

Clipping Polygons

ClippingPolygonCollection clips using arbitrary polygons. WebGL 2 only.

import { ClippingPolygon, ClippingPolygonCollection, Cartesian3 } from "cesium";

const polygon = new ClippingPolygon({
  positions: Cartesian3.fromDegreesArray([
    -105.0077, 39.7519, -105.0095, 39.7504,
    -105.0071, 39.7513, -105.0077, 39.7519,
  ]),
});

tileset.clippingPolygons = new ClippingPolygonCollection({
  polygons: [polygon],
  inverse: false, // false = clip inside polygon; true = clip outside
});

// Also works on the globe
viewer.scene.globe.clippingPolygons = new ClippingPolygonCollection({
  polygons: [polygon],
});

Point Cloud Shading

const tileset = await Cesium3DTileset.fromUrl(pointCloudUrl, {
  pointCloudShading: {
    attenuation: true,           // scale points by geometric error
    geometricErrorScale: 1.0,
    maximumAttenuation: 10,      // max pixel size; undefined = maximumScreenSpaceError
    eyeDomeLighting: true,       // depth-aware edge enhancement
    eyeDomeLightingStrength: 1.0,
    eyeDomeLightingRadius: 1.0,
    backFaceCulling: false,      // requires normals in point data
    normalShading: true,
  },
});
viewer.scene.primitives.add(tileset);

// Runtime adjustment
tileset.pointCloudShading.eyeDomeLightingStrength = 2.0;

Voxel Primitives

VoxelPrimitive renders volumetric data from a Cesium3DTilesVoxelProvider.

Shapes: BOX, CYLINDER, ELLIPSOID (see VoxelShapeType).

import { VoxelPrimitive, Cesium3DTilesVoxelProvider, CustomShader } from "cesium";

const provider = await Cesium3DTilesVoxelProvider.fromUrl("voxel/tileset.json");

const voxelPrimitive = new VoxelPrimitive({
  provider,
  customShader: new CustomShader({
    fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
      material.diffuse = fsInput.metadata.a.rgb;
      material.alpha = fsInput.metadata.a.a;
    }`,
  }),
});
viewer.scene.primitives.add(voxelPrimitive);
voxelPrimitive.nearestSampling = true;
viewer.camera.flyToBoundingSphere(voxelPrimitive.boundingSphere, { duration: 0 });

// For voxel shader authoring — struct availability, raymarching semantics, metadata
// access — see the cesiumjs-custom-shader skill. This skill covers VoxelPrimitive setup.

// Optional inspector widget
viewer.extend(Cesium.viewerVoxelInspectorMixin);
viewer.voxelInspector.viewModel.voxelPrimitive = voxelPrimitive;

I3S Data Provider

Load Esri I3S scene layers (3D Objects, IntegratedMesh, Building Scene Layer).

import { I3SDataProvider, ArcGISTiledElevationTerrainProvider, Ellipsoid, Rectangle } from "cesium";

const geoidService = await ArcGISTiledElevationTerrainProvider.fromUrl(
  "https://tiles.arcgis.com/tiles/.../EGM2008/ImageServer",
);
const i3sProvider = await I3SDataProvider.fromUrl(
  "https://tiles.arcgis.com/tiles/.../SceneServer/layers/0",
  { geoidTiledTerrainProvider: geoidService },
);
viewer.scene.primitives.add(i3sProvider);

const center = Rectangle.center(i3sProvider.extent);
center.height = 5000.0;
viewer.camera.setView({
  destination: Ellipsoid.WGS84.cartographicToCartesian(center),
});

Gaussian Splats

Loaded as standard 3D Tiles; CesiumJS handles KHR_gaussian_splatting automatically.

const splats = await Cesium3DTileset.fromIonAssetId(3667783);
viewer.scene.primitives.add(splats);
viewer.zoomTo(splats);

Classification

Drape tileset geometry as a classification overlay on terrain or other tilesets.

import { Cesium3DTileset, ClassificationType } from "cesium";
const classified = await Cesium3DTileset.fromUrl(url, {
  classificationType: ClassificationType.BOTH, // TERRAIN | CESIUM_3D_TILE | BOTH
});
viewer.scene.primitives.add(classified);

Adjusting Tileset Height

import { Cartographic, Cartesian3, Matrix4 } from "cesium";
const cartographic = Cartographic.fromCartesian(tileset.boundingSphere.center);
const surface = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
const offset = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
const translation = Cartesian3.subtract(offset, surface, new Cartesian3());
tileset.modelMatrix = Matrix4.fromTranslation(translation);

Performance Tips

  • Keep maximumScreenSpaceError as high as acceptable (16 default; 32+ for mobile).
  • Leave dynamicScreenSpaceError: true for street-level views with large tilesets.
  • Leave foveatedScreenSpaceError: true to prioritize center-screen tiles.
  • Size cacheBytes and maximumCacheOverflowBytes to device memory (512 MB each default).
  • Use preloadFlightDestinations: true to prefetch tiles at the camera flight target.
  • Enable skipLevelOfDetail: true for large replacement-refined tilesets to reduce memory.
  • Avoid maximumScreenSpaceError below 4 -- diminishing returns, many more tile requests.
  • For point clouds, enable attenuation and eyeDomeLighting to fill gaps and add depth.
  • Keep enableCollision: false unless camera collision or CLAMP_TO_GROUND on tiles is needed.

10. Preload hidden tilesets with show: false and preloadWhenHidden: true.

11. Avoid translucent styles when possible -- they add rendering passes and disable optimizations.

12. Listen to tileFailed to log errors; call trimLoadedTiles() after large camera jumps.

See Also

  • cesiumjs-models-particles -- glTF compression and CAD-extension compatibility used by tile content
  • cesiumjs-custom-shader -- GLSL authoring for Cesium3DTileset.customShader and VoxelPrimitive.customShader (struct reference, feature IDs, metadata)
  • cesiumjs-materials-shaders -- ImageBasedLighting, post-processing stages for tilesets
  • cesiumjs-interaction -- Scene.pick, drillPick, ScreenSpaceEventHandler for feature selection
  • cesiumjs-terrain-environment -- Globe, terrain providers, atmosphere, lighting, shadows

How to use it

Copy the folder

Take cesiumgs/cesiumjs-3d-tiles from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.