import { type ShaderModule } from "@luma.gl/shadertools"; import { Model, Geometry } from "@luma.gl/engine"; import { Matrix4 } from "@math.gl/core"; import { SimpleMeshLayer, ScenegraphLayer, type SimpleMeshLayerProps } from "@deck.gl/mesh-layers"; import { CompositeLayer, type DefaultProps, type UpdateParameters } from "@deck.gl/core"; import { type GltfExtStructuralMetadata } from "../tile3d-metadata"; import { type FeatureColorMapping } from "../feature-colors"; /** * A primitive whose POSITION accessor did not resolve (unsupported * componentType, a missing buffer, `loadBuffers` off, a corrupt bufferView — * `toLumaGeometry` silently omits the key rather than fabricating one) has * nothing to draw at all, so this can't degrade to a partially-rendered mesh * the way a missing property table degrades to "no properties" — there is no * useful "unstyled but visible" state for zero vertices. What it CAN do is * fail with a message that says why, instead of the opaque "Cannot read * properties of undefined (reading 'length')" a non-null assertion produced * here previously — deck.gl's LayerManager already catches a layer update * throwing (this doesn't crash the page), so the only actual regression from * throwing plainly is a worse error message, which this fixes. */ export declare function validateGeometryAttributes(attributes: Record; } | undefined>): void; /** Loose shape of the `tile` prop Tile3DLayer's `_make3DModelLayer` passes through. `featureIdPropertyTableIndex` is set by the outer `FeatureMeshLayer` composite below, during its own `renderLayers()`, right before constructing this inner layer — not by a separate async step. */ interface FeatureTileProp { content?: { gltf?: { extensions?: { EXT_structural_metadata?: GltfExtStructuralMetadata; }; }; featureIdPropertyTableIndex?: number; }; } /** * One row of the per-feature style table, keyed by feature ID (the array index * IS the feature ID). Every field is optional; an omitted or absent row leaves * that feature rendered exactly as it would be unstyled. */ export interface FeatureStyle { /** Tint colour, `[r, g, b]` 0-255. */ color?: [number, number, number]; /** How strongly `color` replaces the lit colour, 0-1. Defaults to 1 when a colour is given, 0 otherwise. */ strength?: number; /** Alpha multiplier, 0-1. Defaults to 1 (opaque). */ opacity?: number; } /** The extra props this layer adds on top of SimpleMeshLayer's own (mesh/getPosition/getColor/…). */ interface FeatureMeshLayerExtraProps { pbrMaterial: Record | null; /** Per-vertex feature ID for the single primitive `FeatureMeshLayer` resolved — see this file's module doc. */ featureIds?: ArrayLike | null; /** Per-feature style table indexed by feature ID — see featureStyleUniforms. Null/empty renders unstyled. */ featureStyles?: FeatureStyle[] | null; /** Raw feature-ID texture image, when the dataset carries IDs per texel. Enables the per-fragment path. */ featureIdImage?: unknown; /** Which RGBA channels compose the ID, low byte first — the extension's own `channels` array. */ featureIdChannels?: number[] | null; /** EXT_mesh_features' "belongs to no feature" sentinel — a pick of this ID reports as no feature. */ featureIdNull?: number | null; tile?: FeatureTileProp; } /** * Turns the extension's `channels` array into the four per-channel byte * weights the shader multiplies by — channels[i] names an RGBA channel and its * position sets its significance, so [0,1] means R is the low byte and G the * next (weights 1 and 256). */ export declare function featureIdChannelWeights(channels: number[] | null | undefined): [number, number, number, number]; /** 0-1 style fraction → a 0-255 texel byte (the tables are rgba8unorm). */ export declare const styleByte: (value: number) => number; /** Builds the two `count x 1` RGBA lookup tables described in featureStyleUniforms' doc. */ export declare function encodeFeatureStyleTables(styles: FeatureStyle[]): { tint: Uint8Array; opacity: Uint8Array; }; /** True when any row asks for transparency — the layer must then enable blending. */ export declare function hasTranslucentFeature(styles: FeatureStyle[] | null | undefined): boolean; type DrawParams = Parameters["draw"]>[0]; type MeshParam = Parameters["getModel"]>[0]; type PickingInfoParams = Parameters["getPickingInfo"]>[0]; declare class SinglePrimitiveFeatureLayer extends SimpleMeshLayer { static layerName: string; static defaultProps: DefaultProps & FeatureMeshLayerExtraProps>; getShaders(): { vs: string; fs: string; modules: ShaderModule[]; defines?: Record; }; initializeState(): void; /** Registers the per-vertex feature-ID attributes exactly once — callable late, from the texture-failure fallback. */ private registerFeatureIdAttributes; updateState(params: UpdateParameters): void; /** * Uploads the feature-ID texture once per tile and keeps it — this is the * cache. The image itself is already cached upstream by loaders.gl for the * tile's lifetime, so the only per-tile cost here is the single upload, and * it replaces what was previously one CPU texture sample per vertex. */ private updateFeatureIdTexture; /** Rebuilds both style lookup textures. Cheap enough to redo wholesale on any style change — each table is `featureCount x 1` texels. */ private updateFeatureStyleTexture; draw(opts: DrawParams): void; protected getModel(mesh: MeshParam): Model; private updatePbrMaterialUniforms; private parseMaterial; private calculateFeatureIdsPickingColors; private calculateFeatureIdValues; finalizeState(context: Parameters["finalizeState"]>[0]): void; getPickingInfo(params: PickingInfoParams): { color: Uint8Array | null; layer: import("@deck.gl/core").Layer | null; sourceLayer?: import("@deck.gl/core").Layer | null; viewport?: import("@deck.gl/core").Viewport; index: number; picked: boolean; object?: any; x: number; y: number; pixel?: [number, number]; coordinate?: number[]; devicePixel?: [number, number]; pixelRatio: number; } & { featureId?: number; properties?: Record; class?: string | null; guid?: string | null; }; } /** The extra props `_make3DModelLayer` passes that this composite wrapper reads directly (everything else is forwarded to the fallback ScenegraphLayer verbatim). */ interface FeatureMeshLayerOuterExtraProps { scenegraph?: unknown; tile?: FeatureTileProp; featureIdProperty?: string; /** Per-feature style table, forwarded to the picking-aware sublayer. Ignored on the ScenegraphLayer fallback, which has no feature IDs to key on. */ featureStyles?: FeatureStyle[] | null; /** * The GLOBAL colour mapping (feature-color-by / feature-color-scale), * applied to THIS tile's own property rows in composeFeatureStyles — * feature IDs are tile-local, so the style table must be built per tile * from per-tile rows (a shared globally-indexed table was only ever * correct for the first tile). Identity is memoized upstream * (runtime-core.featureColorMappingFor), which keeps the derived table — * and its GPU textures — stable across reconciles and animation frames. */ featureColorMapping?: FeatureColorMapping | null; /** Property-table field the three visibility lists below match against. */ featureFilterField?: string | null; /** Show ONLY features whose field matches; everything else is hidden. */ isolateFeatures?: unknown[] | null; hideFeatures?: unknown[] | null; ghostFeatures?: unknown[] | null; ghostOpacity?: number; } /** * Property values are compared as strings so a manifest can write * `hide-features='["Windows"]'` or `'[3]'` without caring whether the column * decoded to strings, numbers or booleans — an attribute is text either way. */ export declare function matchToken(value: unknown): string; /** Normalizes an attribute list to a lookup set, or null when there is nothing to match. */ export declare function normalizeMatchList(values: unknown[] | null | undefined): Set | null; /** See this file's module doc — dispatches between the per-vertex feature-picking single-primitive path and a plain ScenegraphLayer fallback. */ export declare class FeatureMeshLayer extends CompositeLayer { static layerName: string; static defaultProps: DefaultProps; /** * Resolves the primitive, builds its Geometry and locates its feature-ID * image ONCE, whenever the glTF itself changes — never per render. * * Doing this inside renderLayers (as an earlier version did) handed * SimpleMeshLayer a brand-new Geometry instance every time ANY prop changed. * A `mesh` identity change makes it destroy and rebuild its Model, so simply * switching style mode re-uploaded every visible tile's vertex buffers at * once and the model blanked out until something forced another redraw — * which is why it only reappeared after a pan or zoom. */ updateState({ props, oldProps, changeFlags }: UpdateParameters): void; /** * Folds the declarative `isolate-features`/`hide-features`/`ghost-features` * lists onto the author's own `feature-styles` table. * * Precedence is deliberate: the authored table supplies COLOUR, the * declarative lists supply VISIBILITY, and visibility wins on `opacity`. * That means switching a colour scheme never silently un-hides anything, and * isolating never discards the colouring — the two are orthogonal, which is * what makes them composable in a story or an undo step. * * `isolate` is evaluated first and is exclusive: anything not named is * hidden outright, so it is a scope, not another kind of hide. */ private composeFeatureStyles; private resolvePrimitives; renderLayers(): SinglePrimitiveFeatureLayer[] | ScenegraphLayer; } | Iterable | AsyncIterable | Promise> | null; mesh: string | Geometry | { attributes: import("@loaders.gl/schema").MeshAttributes; indices?: import("@loaders.gl/schema").MeshAttribute; } | import("@loaders.gl/schema").MeshAttributes | Promise | null; texture: string | import("@luma.gl/core").Texture | import("@luma.gl/core").TextureProps | HTMLImageElement | ImageData | HTMLCanvasElement | HTMLVideoElement | ImageBitmap | Promise; textureParameters: import("@luma.gl/core").SamplerProps | null; getPosition: readonly [number, number, number] | readonly [number, number] | Readonly> | Readonly> | import("@deck.gl/core").AccessorFunction; getColor: readonly [number, number, number] | readonly [number, number, number, number] | Readonly> | Readonly> | import("@deck.gl/core").AccessorFunction; getOrientation: readonly [number, number, number] | import("@deck.gl/core").AccessorFunction; getScale: readonly [number, number, number] | import("@deck.gl/core").AccessorFunction; getTranslation: readonly [number, number, number] | import("@deck.gl/core").AccessorFunction; getTransformMatrix: number[] | import("@deck.gl/core").AccessorFunction; sizeScale: number; _instanced: boolean; wireframe: boolean; material: import("@deck.gl/core").Material; id: string; dataComparator: (>(newData: LayerDataT, oldData?: LayerDataT) => boolean) | null; _dataDiff: (>(newData: LayerDataT, oldData?: LayerDataT) => { startRow: number; endRow?: number; }[]) | null; dataTransform: (>(data: unknown, previousData?: LayerDataT) => LayerDataT) | null; fetch: (url: string, context: { propName: string; layer: import("@deck.gl/core").Layer; loaders?: import("@loaders.gl/loader-utils").Loader[]; loadOptions?: any; signal?: AbortSignal; }) => any; updateTriggers: Record; operation: "mask" | "draw" | "terrain" | "mask+mask" | "mask+draw" | "mask+terrain" | "draw+mask" | "draw+draw" | "draw+terrain" | "terrain+mask" | "terrain+draw" | "terrain+terrain"; visible: boolean; pickable: boolean | "3d"; opacity: number; coordinateSystem: "default" | "lnglat" | "meter-offsets" | "lnglat-offsets" | "cartesian"; coordinateOrigin: [number, number, number]; modelMatrix: Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | Matrix4 | import("@math.gl/types").NumberArray16 | null; wrapLongitude: boolean; positionFormat: "XYZ" | "XY"; colorFormat: "RGBA" | "RGB"; parameters: { cullMode?: import("@luma.gl/core").CullMode; frontFace?: import("@luma.gl/core").FrontFace; depthBias?: number; depthBiasSlopeScale?: number; depthBiasClamp?: number; unclippedDepth?: boolean; provokingVertex?: "first" | "last"; polygonMode?: "fill" | "line"; polygonOffsetLine?: boolean; clipDistance0?: boolean; clipDistance1?: boolean; clipDistance2?: boolean; clipDistance3?: boolean; clipDistance4?: boolean; clipDistance5?: boolean; clipDistance6?: boolean; clipDistance7?: boolean; } & import("@luma.gl/core").DepthStencilParameters & import("@luma.gl/core").ColorParameters & import("@luma.gl/core").MultisampleParameters; transitions: Record | null; extensions: import("@deck.gl/core").LayerExtension[]; loaders: import("@loaders.gl/loader-utils").Loader[]; loadOptions: any; getPolygonOffset: ((params: { layerIndex: number; }) => [number, number]) | null; autoHighlight: boolean; highlightedObjectIndex: number | null; highlightColor: number[] | ((pickingInfo: import("@deck.gl/core").PickingInfo) => number[]); onDataLoad: (>(data: LayerDataT, context: { propName: string; layer: import("@deck.gl/core").Layer; }) => void) | null; onError: ((error: Error) => boolean | void) | null; onHover: ((pickingInfo: import("@deck.gl/core").PickingInfo, event: import("mjolnir.js").MjolnirEvent) => boolean | void) | null; onClick: ((pickingInfo: import("@deck.gl/core").PickingInfo, event: import("mjolnir.js").MjolnirEvent) => boolean | void) | null; onDragStart: ((pickingInfo: import("@deck.gl/core").PickingInfo, event: import("mjolnir.js").MjolnirEvent) => boolean | void) | null; onDrag: ((pickingInfo: import("@deck.gl/core").PickingInfo, event: import("mjolnir.js").MjolnirEvent) => boolean | void) | null; onDragEnd: ((pickingInfo: import("@deck.gl/core").PickingInfo, event: import("mjolnir.js").MjolnirEvent) => boolean | void) | null; numInstances: number | null; startIndices: number[] | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | null; }> | null; } export {};