SDK JS
    Preparing search index...

    Interface Tiles3DMeshLayerOptions

    Options for Tiles3DMeshLayer. Only id and url (the tileset.json) are required; everything else defaults — worker decode, a 1.5 GB VRAM budget, terrain occlusion whenever the map has terrain, and vector drape from the style layers above. Three are worth knowing before the rest: anchor (give a planet-scale tileset an explicit lng/lat), maxScreenSpaceError (detail against memory), and groundSurface, which tells the layer this mesh IS the ground — photogrammetry and integrated meshes need it.

    interface Tiles3DMeshLayerOptions {
        anchor?: { altitude?: number; lat?: number; lng?: number };
        atomicLod?: boolean;
        attribution?: string;
        auth?: RequestAuthorizer;
        baseUrl?: string;
        beforeId?: string;
        budget?: { maxBytes?: number };
        clippingPlanes?: ClippingPlanesOptions;
        crossFadeMs?: number;
        datum?: VerticalDatum;
        decoder?: MeshTileDecoder;
        domeLocalFallback?: boolean;
        drape?: DrapeOption;
        dynamicScreenSpaceError?: boolean;
        dynamicScreenSpaceErrorDensity?: number;
        dynamicScreenSpaceErrorFactor?: number;
        dynamicScreenSpaceErrorHeightFalloff?: number;
        edges?: MeshEdgeOptions;
        farCoarsenFarM?: number;
        farCoarsenMax?: number;
        farCoarsenNearM?: number;
        flyTo?: boolean | { duration?: number; pitch?: number; zoom?: number };
        geoidN?: number;
        ground?: boolean;
        groundSurface?: boolean;
        id: string;
        json?: TilesetJson;
        lodFalloffPow?: number;
        maxDistanceM?: number;
        maxScreenSpaceError?: number;
        occludeTerrain?: boolean;
        onAttributions?: (items: string[]) => void;
        onError?: (error: unknown) => void;
        onLoaded?: () => void;
        onProgress?: (fraction: number) => void;
        opacity?: number;
        poolSize?: number;
        queryParams?: Record<string, string>;
        renderCuspM?: number;
        requestInit?: RequestInit;
        shadows?:
            | boolean
            | {
                bias?: number;
                cascadeCount?: number;
                mapSize?: number;
                maxDistance?: number;
                normalScale?: number;
                strength?: number;
            };
        spawn?: () => Worker;
        stencilFallbackMask?: "auto" | "off";
        underLabels?: boolean;
        url: string;
        visible?: boolean;
        workers?: boolean;
    }
    Index

    Properties

    anchor?: { altitude?: number; lat?: number; lng?: number }

    Scene anchor. By default lng/lat come from the tileset root center and the floor is auto-grounded onto the basemap (z=0) — correct for a localized dataset. For a GLOBAL tileset (Cesium OSM Buildings, Google Photorealistic), the root box spans the planet, so pass an explicit lng/lat for the area you're viewing — the ENU frame is anchored there (f32-safe) and the engine frustum-culls the rest. altitude (m, WGS84) overrides the auto-ground.

    atomicLod?: boolean

    Atomic coherent LOD for REPLACE meshes (default ON): a tile reveals its child level only once the WHOLE in-view set is resident — no "sliced buildings" appearing before the rest of the level (coherent bi-directional refine + coarsen). false reverts to the legacy progressive reveal (A/B).

    attribution?: string

    Dataset attribution (shown in MapLibre's attribution control). Your own markup: it is passed through unescaped so a credit can carry an <a href> link. The per-tile credits the SERVICE supplies are escaped before they are appended to it.

    auth?: RequestAuthorizer

    Request authorizer for tile + tileset fetches (secured ArcGIS 3D-Tiles). For public tilesets, Cesium ion, or Google Photorealistic, leave unset and use requestInit/queryParams. Superseded by a global configureEsriAuth().

    baseUrl?: string

    Base URL for resolving relative tile URIs (with json). Defaults to url's dir.

    beforeId?: string

    Place this layer immediately below the given style layer id on add (see underLabels). Takes precedence over underLabels.

    budget?: { maxBytes?: number }

    Resident VRAM budget (bytes). Default 1.5 GB.

    clippingPlanes?: ClippingPlanesOptions

    Clipping planes (geographic; see ClippingPlanesOptions) applied to this layer's geometry. Fragments on the clipped side are discarded from the color, pick, and shadow-cast passes. Change at runtime via Tiles3DMeshLayer.setClippingPlanes. Default none.

    crossFadeMs?: number

    LOD cross-fade-in duration (ms) for REPLACE meshes (Google/photoreal): a newly-resident fine tile fades in over its retained coarse ancestor instead of hard-popping, so coarse→fine transitions are smooth. Default 0 (OFF → hard swap, byte-identical). Recommended ~250–350. ADD meshes & i3s are unaffected.

    Vertical datum for placement (default 'basemap'): 'basemap' drops the dataset on the map / 3D terrain, 'ellipsoid'/'orthometric' place it at its true absolute height. Switch live with Tiles3DMeshLayer.setDatum. (anchor.altitude still overrides everything.)

    decoder?: MeshTileDecoder

    Custom decoder (advanced) — overrides the default createMeshDecoder.

    domeLocalFallback?: boolean

    ADD foreground fill (Cesium OSM Buildings, default ON): a coarse ADD ancestor whose whole bounding box is within the render distance draws as the near additive base, so buildings right in front of the camera are never missing while finer tiles stream (the planet/continental shell stays suppressed — no buildings past the horizon). false reverts to suppressing all over-coarse tiles. ADD trees only; REPLACE meshes ignore it.

    drape?: DrapeOption

    Drape MapLibre vector layers that sit ABOVE this layer in the style onto its geometry (parcels, zones, site plans painting onto the buildings).

    Defaults to on, but only actually engages when the style has something drapeable above this layer: enabling stands up a hidden MapLibre map to capture the drape atlas, which is not worth paying for when nothing would be drawn. Pass an options object to configure it, or false to never drape.

    dynamicScreenSpaceError?: boolean

    Cesium-parity dynamicScreenSpaceError: a smooth fog-curve far-field coarsening (up to dynamicScreenSpaceErrorFactor px subtracted from each tile's SSE with distance), strongest for a street-level camera looking toward the horizon and fading out top-down / at altitude. Cesium ships this ON by default for every tileset including Google Photorealistic — it is what bounds Google's far-field working set. Default: ON for REPLACE tilesets, OFF for ADD (Cesium OSM Buildings).

    dynamicScreenSpaceErrorDensity?: number

    Base fog density for dynamicScreenSpaceError. Default 2.0e-4 (Cesium's default).

    dynamicScreenSpaceErrorFactor?: number

    Max px subtracted by dynamicScreenSpaceError. Default 24 (Cesium's default).

    dynamicScreenSpaceErrorHeightFalloff?: number

    Height-range fraction where the taper starts fading with camera altitude. Default 0.25.

    Draw the model's hard edges (silhouettes + creases) over the surface. Extraction runs per tile at decode, so this is opt-in; turn it on later with Tiles3DMeshLayer.setEdges, which re-streams. Intended for discrete-feature datasets (Cesium OSM Buildings, individual building services), not photogrammetry. Default off.

    farCoarsenFarM?: number

    Distance (m) where far-coarsening reaches full strength. Default 20000. See farCoarsenMax.

    farCoarsenMax?: number

    Far-field LOD coarsening (advanced). The refine target is multiplied by up to ×(1+farCoarsenMax) ramping over farCoarsenNearM..farCoarsenFarM (metres) — it bounds the horizon-fill cost of a planet-scale REPLACE mesh (Google), so the far field uses few coarse tiles. Set farCoarsenMax: 0 to DISABLE it (full detail out to the horizon) — appropriate for a LOCALIZED mesh, where the "far field" is the same bounded dataset and there is no horizon to fill. Defaults: max 2, near 8000, far 20000.

    farCoarsenNearM?: number

    Distance (m) where far-coarsening starts (×1 nearer than this). Default 8000. See farCoarsenMax.

    flyTo?: boolean | { duration?: number; pitch?: number; zoom?: number }

    Fly to the dataset once its location is known (default off).

    geoidN?: number

    Optional — you should not normally need this.* The geoid undulation N (m) at the dataset, used to place absolutely-georeferenced data on mean-sea-level terrain. The SDK resolves it automatically (it fetches the EGM96 grid on demand and samples it at the dataset), so pass this only to override that — e.g. for a dataset referenced to a local vertical datum EGM96 does not model.

    An override shifts placement by the DIFFERENCE between your value and the EGM96 undulation at the dataset — so passing the conventional EGM96 value for your area is a no-op rather than a ~30 m jump.

    ground?: boolean

    Auto-ground (default true): measure the real ground from the first decoded tile and seat it on the datum, instead of anchoring to the root bounding-volume floor.

    The OBB floor is the lowest point ANYWHERE in the dataset, which is not the ground once a tileset covers any area — Soest's is 122 m low, 3DBAG's country-wide box centres 4.4 km below the ellipsoid. The measured ground is the lowest vertex of a real tile: the surface for a mesh, the building bases for a discrete-object tileset. Set false only when the geometry's lowest point genuinely is not the ground.

    groundSurface?: boolean

    Set true when this dataset's mesh IS the ground surface — a photogrammetry/integrated mesh (e.g. Google Photorealistic 3D Tiles) rather than discrete buildings on a basemap. Such a mesh self-shadows its own ground per-fragment, so the separate basemap-DEM terrain-receiver grid is skipped (it would z-fight the coincident mesh ground into a camera-following "blob"). It also stops depth-testing against the DEM, which is coincident with it, so neither surface can win patches of the other: see occludeTerrain. Default false: discrete buildings (e.g. OSM Buildings) keep the receiver so they cast onto bare terrain. Esri IntegratedMesh datasets are detected automatically by I3sMeshLayer.

    id: string

    Layer id.

    Pre-fetched tileset JSON — pass ResolvedSource.json to skip a redundant root request. REQUIRED for Google: the root must be fetched key-only to establish the session, so re-fetching it with the session appended 400s.

    lodFalloffPow?: number

    LOD distance-falloff exponent (advanced). 1 = standard 3D-Tiles SSE; <1 keeps higher LOD further out (more far detail, more data). Default 1 (REPLACE); ADD tilesets default 0.5.

    maxDistanceM?: number

    Hard cap (m) on the mesh's load+render distance. 0/undefined = no cap (fill to the horizon). When set, the 3D mesh only appears within this radius and BEYOND it the basemap raster on the terrain shows — a "detailed mesh near, textured terrain far" hybrid that keeps tilted-horizon views fast and gives a clean continuous horizon. Use with basemap: true (a textured terrain to fall back to).

    maxScreenSpaceError?: number

    Target screen-space error (CSS px). Lower = more detail. Default 16 (Cesium parity) for REPLACE tilesets; ADD-refine tilesets (Cesium OSM Buildings) default to 4 — the tuned profile that fully loads the near/mid building field out to the ~8 km draw cusp at normal viewing zooms (OSM's geometric errors are tiny at the depths carrying the visual mass, so Cesium-parity targets stop the descent levels short of them; see addRefineLodDefaults in @bitruvius/tiles3d). Pass 16 to restore the old default.

    occludeTerrain?: boolean

    Let 3D terrain OCCLUDE this layer's geometry (a hillside hides buildings behind it) instead of the geometry drawing over it.

    Defaults to true whenever the map has terrain. Without terrain there is nothing to occlude against, so it is inert. When off, the depth buffer is cleared before the layer draws, which makes its geometry composite over everything MapLibre already rendered — that is what made buildings visible straight THROUGH a hillside.

    A groundSurface layer defaults to false instead, because that mesh IS the ground. It is coincident with the terrain rather than standing on it, so depth-testing the two against each other just lets whichever surface the DEM puts on top win in patches, and the basemap shows through the mesh. Leave it alone unless you know you want otherwise.

    Set false deliberately if you want the draw-over behaviour, e.g. a proposed-design model that must stay visible through the existing terrain.

    onAttributions?: (items: string[]) => void

    Fired (only on change) with the unique DATA attributions of the currently-visible tiles — the dynamic per-region credits Google Photorealistic 3D Tiles legally require. The consumer must display these (MapLibre won't show a custom layer's source attribution). Also available via Tiles3DMeshLayer.getAttributions.

    RAW service text, not HTML: these strings come from each tile's asset.copyright, i.e. from the remote service. Render them with textContent (or escape them) — the copy this layer publishes into MapLibre's own control is escaped for you, this one is not.

    onError?: (error: unknown) => void

    Fired if the tileset fails to load/initialize.

    onLoaded?: () => void

    Fired once the working set first fully resolves.

    onProgress?: (fraction: number) => void

    Fired with the working-set load fraction (0–1) as it streams.

    opacity?: number

    Initial opacity 0–1 (default 1).

    poolSize?: number

    Worker pool size (worker mode only).

    queryParams?: Record<string, string>

    Query params appended to every tile + external-tileset request (e.g. Google Photorealistic's key + session). Pass ResolvedSource.queryParams through.

    renderCuspM?: number

    RENDER-distance floor (m) for an ADD foreground (Cesium OSM Buildings) — the minimum distance buildings are DRAWN to; the descent already loads further. 0/undefined = the ~8 km camera-height auto floor — an intentional governor: past it buildings float above MapLibre's flat-mercator horizon into the sky, and the far field costs load/VRAM for unreadable detail. Raise it only for scenes whose atmosphere/fog hides the horizon. Still grows when zoomed out and is horizon-capped. REPLACE meshes (Google) ignore it.

    requestInit?: RequestInit

    Static auth/headers for tile + tileset fetches (e.g. a Cesium ion / Google key).

    shadows?:
        | boolean
        | {
            bias?: number;
            cascadeCount?: number;
            mapSize?: number;
            maxDistance?: number;
            normalScale?: number;
            strength?: number;
        }

    Cast real sun shadows on this layer (depth-from-sun shadow map). Default false — existing scenes render identically. Needs a sun via Tiles3DMeshLayer.setSunState (or a GeoSunSystem). Pass an object to tune the shadow map (size / bias / strength / distance).

    spawn?: () => Worker

    Override worker creation (tests).

    stencilFallbackMask?: "auto" | "off"

    Fine-coverage stencil masking of coarse REPLACE fallbacks (kills coarse-over-fine show-through + linger on Google/photoreal REPLACE meshes). 'auto' (default) engages it only when the shared main-FB stencil is provably safe (terrain on, or this layer last in the order); 'off' forces the depth-only path.

    underLabels?: boolean

    Keep the basemap's labels (street names, POI icons) drawn ON TOP of this layer's 3D content, by placing the layer below the style's lowest symbol layer.

    Symbol layers are screen-space billboards drawn in style order, so this is purely a placement question. No-op on a style with no labels. Use beforeId instead to name an explicit layer to sit below.

    url: string

    URL of the 3D-Tiles tileset.json.

    visible?: boolean

    Initial visibility (default true).

    workers?: boolean

    Decode tiles off the main thread in a worker pool (default true).