@bitruvius/geo-core 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +167 -0
- package/README.md +133 -0
- package/dist/contracts/cesium-point-decoder.d.ts +34 -0
- package/dist/contracts/decoder.d.ts +191 -0
- package/dist/contracts/feature-metadata.d.ts +161 -0
- package/dist/contracts/gltf-splat-decoder.d.ts +53 -0
- package/dist/contracts/i3s-mesh-decoder.d.ts +80 -0
- package/dist/contracts/i3s-point-decoder.d.ts +90 -0
- package/dist/contracts/mesh-codecs.d.ts +135 -0
- package/dist/contracts/mesh.d.ts +304 -0
- package/dist/contracts/renderer.d.ts +34 -0
- package/dist/contracts/viewer-adapter.d.ts +68 -0
- package/dist/dequant/formulas.d.ts +93 -0
- package/dist/dequant/stray-cull.d.ts +29 -0
- package/dist/dequant/tile-bounds.d.ts +109 -0
- package/dist/geo/ecef.d.ts +70 -0
- package/dist/geo/hard-edges.d.ts +71 -0
- package/dist/geo/mat4.d.ts +115 -0
- package/dist/geo/measure.d.ts +134 -0
- package/dist/geo/mercator.d.ts +95 -0
- package/dist/geo/model-placement.d.ts +141 -0
- package/dist/geo/pack-gltf-points-enu.d.ts +41 -0
- package/dist/geo/pack-mesh-edges.d.ts +16 -0
- package/dist/geo/pack-mesh-enu.d.ts +46 -0
- package/dist/geo/pack-point-ecef.d.ts +42 -0
- package/dist/geo/pack-point-enu.d.ts +126 -0
- package/dist/geo/plane.d.ts +38 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +1 -0
- package/dist/style/expression.d.ts +122 -0
- package/package.json +51 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized per-feature **metadata** — the format-agnostic target that I3S BSL/3DObject
|
|
3
|
+
* attributes, 3D-Tiles 1.0 batch tables, and glTF 1.1 `EXT_structural_metadata` property tables
|
|
4
|
+
* all decode into. It stays **CPU-side** (never uploaded — only `featureId`s go to the GPU) and
|
|
5
|
+
* powers picking + filter/style evaluation (a feature's values feed {@link PropertyGetter}).
|
|
6
|
+
*
|
|
7
|
+
* Grounded in the audited Revit/BIM field set (Category, Family, BldgLevel, Discipline,
|
|
8
|
+
* ElementType, AssemblyCode, OmniClass, IsStructural, …): I3S stores these as string/number
|
|
9
|
+
* per-node attribute blobs; 3D-Tiles stores them as property-table columns (with enums). Both
|
|
10
|
+
* normalize to {@link FeatureColumn}s consumed by {@link ColumnFeatureProperties}.
|
|
11
|
+
*/
|
|
12
|
+
import type { PropertyGetter } from '../style/expression';
|
|
13
|
+
/** Normalized property kind (the union across I3S valueType, batch-table type, and
|
|
14
|
+
* `EXT_structural_metadata` class-property type). */
|
|
15
|
+
export type FeaturePropertyType = 'scalar' | 'string' | 'boolean' | 'enum';
|
|
16
|
+
/** A named enumeration: the integer→label map an `'enum'` property resolves against. */
|
|
17
|
+
export interface FeatureEnum {
|
|
18
|
+
/** The enum's name (referenced by {@link FeaturePropertyDef.enumName}). */
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/** Integer value → label (e.g. `0 → 'Architectural'`). */
|
|
21
|
+
readonly values: ReadonlyMap<number, string>;
|
|
22
|
+
}
|
|
23
|
+
/** The definition of one feature property/column: its name, kind, and (for enums) the
|
|
24
|
+
* enum it resolves against. */
|
|
25
|
+
export interface FeaturePropertyDef {
|
|
26
|
+
/** Property/column name (the key a {@link PropertyGetter} looks up). */
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/** Normalized kind of the property. */
|
|
29
|
+
readonly type: FeaturePropertyType;
|
|
30
|
+
/** Storage component type, informational (`Int32`, `Float64`, `UInt8`, `String`, …). */
|
|
31
|
+
readonly componentType?: string;
|
|
32
|
+
/** Enum name in the {@link FeatureSchema} when `type === 'enum'`. */
|
|
33
|
+
readonly enumName?: string;
|
|
34
|
+
/** Human label (I3S field alias, structural-metadata property name). */
|
|
35
|
+
readonly label?: string;
|
|
36
|
+
}
|
|
37
|
+
/** A feature class: a named set of property definitions (one schema entry). */
|
|
38
|
+
export interface FeatureClass {
|
|
39
|
+
/** Class name. */
|
|
40
|
+
readonly name: string;
|
|
41
|
+
/** The properties of this class. */
|
|
42
|
+
readonly properties: readonly FeaturePropertyDef[];
|
|
43
|
+
}
|
|
44
|
+
/** Schema of feature classes + enums (I3S `fields`/`attributeStorageInfo`, or the
|
|
45
|
+
* `EXT_structural_metadata` / batch-table-hierarchy schema). */
|
|
46
|
+
export interface FeatureSchema {
|
|
47
|
+
readonly classes: ReadonlyMap<string, FeatureClass>;
|
|
48
|
+
readonly enums: ReadonlyMap<string, FeatureEnum>;
|
|
49
|
+
}
|
|
50
|
+
/** A resolved feature value (enum integers already mapped to their label), or `null`. */
|
|
51
|
+
export type FeatureValue = number | string | boolean | null;
|
|
52
|
+
/**
|
|
53
|
+
* Read-only accessor over one tile/node's per-feature property columns, addressed by
|
|
54
|
+
* `featureId` in `0..count-1`. This is what picking and filter/style evaluation read;
|
|
55
|
+
* adapt it to a {@link PropertyGetter} with {@link featureGetter}.
|
|
56
|
+
*/
|
|
57
|
+
export interface FeatureProperties {
|
|
58
|
+
/** Number of features (valid `featureId` range is `0..count-1`). */
|
|
59
|
+
readonly count: number;
|
|
60
|
+
/** All available property names. */
|
|
61
|
+
readonly names: readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* One property's value for a feature, with enum integers resolved to their label.
|
|
64
|
+
* @param featureId - Feature index in `0..count-1`.
|
|
65
|
+
* @param name - Property name.
|
|
66
|
+
* @returns The value, or `null` when the feature/property is absent or out of range.
|
|
67
|
+
*/
|
|
68
|
+
value(featureId: number, name: string): FeatureValue;
|
|
69
|
+
/**
|
|
70
|
+
* All properties for a feature, as a plain record.
|
|
71
|
+
* @param featureId - Feature index in `0..count-1`.
|
|
72
|
+
* @returns A `name → value` record, or `null` when `featureId` is out of range.
|
|
73
|
+
*/
|
|
74
|
+
record(featureId: number): Record<string, FeatureValue> | null;
|
|
75
|
+
}
|
|
76
|
+
/** One decoded property column: its definition plus `count` parallel values (strings
|
|
77
|
+
* for a `'string'` property, numbers/booleans otherwise; enums stored as integers). */
|
|
78
|
+
export interface FeatureColumn {
|
|
79
|
+
/** This column's {@link FeaturePropertyDef}. */
|
|
80
|
+
readonly def: FeaturePropertyDef;
|
|
81
|
+
/** The per-feature raw values (index = `featureId`). */
|
|
82
|
+
readonly values: ReadonlyArray<number | string | boolean>;
|
|
83
|
+
}
|
|
84
|
+
/** A structured-cloneable plain form of {@link FeatureProperties} — for handing decoded metadata
|
|
85
|
+
* across the worker → main-thread boundary (a `ColumnFeatureProperties` is a class with `Map`s and
|
|
86
|
+
* can't be `postMessage`d). Reconstruct with {@link materializeFeatureProperties}. */
|
|
87
|
+
export interface FeaturePropertiesData {
|
|
88
|
+
/** Feature count. */
|
|
89
|
+
readonly count: number;
|
|
90
|
+
/** The decoded columns (each keyed by its `def.name`). */
|
|
91
|
+
readonly columns: readonly FeatureColumn[];
|
|
92
|
+
/** Enum definitions as plain `[value, label]` entry arrays (Maps don't clone). */
|
|
93
|
+
readonly enums?: readonly {
|
|
94
|
+
readonly name: string;
|
|
95
|
+
readonly entries: readonly (readonly [number, string])[];
|
|
96
|
+
}[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* In-memory {@link FeatureProperties} backed by decoded columns — built by the I3S
|
|
100
|
+
* attribute decode and the 3D-Tiles property-table / batch-table decode alike. Enum
|
|
101
|
+
* columns are resolved to labels on read, so a filter like `Discipline='Architectural'`
|
|
102
|
+
* matches regardless of the source encoding.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const props = materializeFeatureProperties(tile.featureMetadata!); // worker → main
|
|
107
|
+
* const level = props.value(featureId, 'BldgLevel');
|
|
108
|
+
* const all = props.record(featureId); // { Category: 'Walls', BldgLevel: 3, … }
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @see {@link materializeFeatureProperties}, {@link featureGetter}
|
|
112
|
+
*/
|
|
113
|
+
export declare class ColumnFeatureProperties implements FeatureProperties {
|
|
114
|
+
#private;
|
|
115
|
+
/** Number of features. */
|
|
116
|
+
readonly count: number;
|
|
117
|
+
/** All available property names (the column keys). */
|
|
118
|
+
readonly names: readonly string[];
|
|
119
|
+
/**
|
|
120
|
+
* @param count - Feature count (valid `featureId` range `0..count-1`).
|
|
121
|
+
* @param columns - Decoded columns keyed by property name.
|
|
122
|
+
* @param enums - Enum definitions referenced by `'enum'` columns (default empty).
|
|
123
|
+
*/
|
|
124
|
+
constructor(count: number, columns: ReadonlyMap<string, FeatureColumn>, enums?: ReadonlyMap<string, FeatureEnum>);
|
|
125
|
+
/**
|
|
126
|
+
* One property's value for a feature, enum integers resolved to their label.
|
|
127
|
+
* @param featureId - Feature index in `0..count-1`.
|
|
128
|
+
* @param name - Property name.
|
|
129
|
+
* @returns The resolved value, or `null` when out of range / absent.
|
|
130
|
+
*/
|
|
131
|
+
value(featureId: number, name: string): FeatureValue;
|
|
132
|
+
/**
|
|
133
|
+
* All properties for a feature as a plain record.
|
|
134
|
+
* @param featureId - Feature index in `0..count-1`.
|
|
135
|
+
* @returns A `name → value` record, or `null` when `featureId` is out of range.
|
|
136
|
+
*/
|
|
137
|
+
record(featureId: number): Record<string, FeatureValue> | null;
|
|
138
|
+
/** Plain, structured-cloneable snapshot — for the worker → main-thread hand-off. */
|
|
139
|
+
toData(): FeaturePropertiesData;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Rebuild a {@link ColumnFeatureProperties} from its structured-cloneable
|
|
143
|
+
* {@link FeaturePropertiesData} — the main-thread side of the worker hand-off.
|
|
144
|
+
* @param data - The plain form produced by {@link ColumnFeatureProperties.toData}.
|
|
145
|
+
* @returns A live {@link ColumnFeatureProperties}.
|
|
146
|
+
*/
|
|
147
|
+
export declare function materializeFeatureProperties(data: FeaturePropertiesData): ColumnFeatureProperties;
|
|
148
|
+
/**
|
|
149
|
+
* Adapt one feature's properties into a {@link PropertyGetter} for the expression
|
|
150
|
+
* evaluator ({@link compileSqlFilter} / {@link compileStyleExpression}).
|
|
151
|
+
* @param props - The feature properties for the tile/node.
|
|
152
|
+
* @param featureId - The feature to bind the getter to (`0..props.count-1`).
|
|
153
|
+
* @returns A `(name) => value` getter that reads that feature's properties.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* const pass = compileSqlFilter("BldgLevel >= 3");
|
|
158
|
+
* const visible = pass(featureGetter(props, featureId));
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare function featureGetter(props: FeatureProperties, featureId: number): PropertyGetter;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GltfSplatDecoder` — the loose-coupling seam for 3D-Tiles gaussian-splat
|
|
3
|
+
* streaming. A 3D-Tiles tile is a `.glb` (glTF binary) whose mesh carries a
|
|
4
|
+
* `KHR_gaussian_splatting` primitive with a compressed splat buffer. This
|
|
5
|
+
* contract decodes one such `.glb` to a flat {@link GltfSplatCloud}, regardless
|
|
6
|
+
* of WHICH compression the buffer uses:
|
|
7
|
+
*
|
|
8
|
+
* - `@bitruvius/turbo-spz` implements it for Esri/Cesium `…_compression_spz_2`.
|
|
9
|
+
* - `@bitruvius/bvc` implements it for `…_compression_bvc` (Bitruvius BVC, the
|
|
10
|
+
* anticipated glTF drop-in).
|
|
11
|
+
*
|
|
12
|
+
* The `@bitruvius/tiles3d` engine and the MapLibre layer depend ONLY on this
|
|
13
|
+
* interface — never on a concrete codec — so a consumer swaps decoders without
|
|
14
|
+
* touching streaming or rendering. The cloud shape matches what both wasm
|
|
15
|
+
* decoders already return (turbo-spz `SpzGaussianCloud`; the source GltfSplatCloud).
|
|
16
|
+
*/
|
|
17
|
+
/** A decoded gaussian-splat cloud in the tile's glb-local frame (float SoA). */
|
|
18
|
+
export interface GltfSplatCloud {
|
|
19
|
+
/** Splat count. */
|
|
20
|
+
readonly numPoints: number;
|
|
21
|
+
/** Spherical-harmonics degree present (0 = DC only). */
|
|
22
|
+
readonly shDegree: number;
|
|
23
|
+
/** Column-major 4×4 glTF node transform (16). Identity if the tile declares none. */
|
|
24
|
+
readonly nodeMatrix: Float32Array;
|
|
25
|
+
/** 3·N glb-local meters. */
|
|
26
|
+
readonly positions: Float32Array;
|
|
27
|
+
/** 3·N log-scales. */
|
|
28
|
+
readonly scales: Float32Array;
|
|
29
|
+
/** 4·N quaternion (x,y,z,w). */
|
|
30
|
+
readonly rotations: Float32Array;
|
|
31
|
+
/** N opacity logit (pre-sigmoid). */
|
|
32
|
+
readonly alphas: Float32Array;
|
|
33
|
+
/** 3·N DC colour (raw SH band-0 coefficient). */
|
|
34
|
+
readonly colors: Float32Array;
|
|
35
|
+
/** N·coeffs(shDegree) SH rest coefficients, splat-major coeff-outer/RGB-inner;
|
|
36
|
+
* band-1 = first 9 floats/splat. Empty when shDegree === 0. */
|
|
37
|
+
readonly sh: Float32Array;
|
|
38
|
+
}
|
|
39
|
+
/** Decodes a whole 3D-Tiles `.glb` gaussian-splat tile to a {@link GltfSplatCloud}.
|
|
40
|
+
* Satisfied by `@bitruvius/turbo-spz` (`spz`) and `@bitruvius/bvc` (`bvc`). */
|
|
41
|
+
export interface GltfSplatDecoder {
|
|
42
|
+
/** Decode one `.glb` tile (may be gzip-framed, e.g. Esri `spz_2`).
|
|
43
|
+
* @param glb - The `.glb` tile bytes.
|
|
44
|
+
* @returns The decoded {@link GltfSplatCloud} in the tile's glb-local frame. */
|
|
45
|
+
decode(glb: ArrayBuffer): Promise<GltfSplatCloud>;
|
|
46
|
+
}
|
|
47
|
+
/** Optional inverse: write a {@link GltfSplatCloud} back to a `.glb` tile. */
|
|
48
|
+
export interface GltfSplatEncoder {
|
|
49
|
+
/** @param cloud - The splat cloud. @returns The encoded `.glb` bytes. */
|
|
50
|
+
encode(cloud: GltfSplatCloud): Promise<ArrayBuffer>;
|
|
51
|
+
}
|
|
52
|
+
/** Built-in decoder identifiers a layer/engine can resolve to a concrete impl. */
|
|
53
|
+
export type GltfSplatDecoderKind = 'spz' | 'bvc';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The I3S **mesh** decode seam — the IntegratedMesh / 3DObject analog of
|
|
3
|
+
* `I3sPointCloudDecoder`. The engine fetches a node's geometry blob (Draco or
|
|
4
|
+
* legacy PerAttributeArray) + an optional texture blob and hands them, with the
|
|
5
|
+
* per-node reference frame, to an injected {@link I3sMeshDecoder} that produces a
|
|
6
|
+
* GPU-ready, ENU-rebased {@link PackedMeshTile} the `@bitruvius/mesh` renderer draws.
|
|
7
|
+
* Codec-agnostic: the concrete decoder lives in a codec-aware package (it injects a
|
|
8
|
+
* {@link DracoGeometryDecoder}); the i3s engine depends only on this interface.
|
|
9
|
+
*/
|
|
10
|
+
import type { Mat4 } from '../geo/mat4';
|
|
11
|
+
import type { AlphaMode, EdgeExtractOptions, PackedMeshTile } from './mesh';
|
|
12
|
+
/** The raw blobs for ONE I3S mesh node (fetched by the engine). */
|
|
13
|
+
export interface I3sMeshBlobs {
|
|
14
|
+
/** `…/nodes/{resource}/geometries/{buffer}` — Draco or legacy PAA geometry. */
|
|
15
|
+
readonly geometry: ArrayBuffer;
|
|
16
|
+
/** `…/nodes/{resource}/textures/{name}` — the chosen baseColor texture, if any. */
|
|
17
|
+
readonly texture?: ArrayBuffer;
|
|
18
|
+
}
|
|
19
|
+
/** The unlit material params the decoder bakes into the packed tile. */
|
|
20
|
+
export interface I3sDecodeMaterial {
|
|
21
|
+
readonly baseColorFactor?: readonly [number, number, number, number];
|
|
22
|
+
readonly doubleSided: boolean;
|
|
23
|
+
readonly alphaMode: AlphaMode;
|
|
24
|
+
readonly hasBaseColorTexture: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** One member of an uncompressed PerAttributeArray (PAA) geometry buffer, in stored order
|
|
27
|
+
* (`position`, `normal`, `uv0`, `color`, `uvRegion`/`region`, `featureId`, `faceRange`). */
|
|
28
|
+
export interface I3sPaaMember {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
/** I3S valueType: `Float32` | `UInt8` | `UInt16` | `UInt32` | `UInt64` | … */
|
|
31
|
+
readonly valueType: string;
|
|
32
|
+
readonly valuesPerElement: number;
|
|
33
|
+
/** `true` for the per-FEATURE section (`featureId`, `faceRange`); else per-VERTEX. */
|
|
34
|
+
readonly perFeature: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** Binary layout of an uncompressed PAA geometry buffer — present in
|
|
37
|
+
* {@link I3sMeshDecodeContext} when the node's chosen buffer is NOT Draco. */
|
|
38
|
+
export interface I3sPaaLayout {
|
|
39
|
+
/** Leading header bytes to skip (the buffer def's `offset`; the header holds vertex/feature counts). */
|
|
40
|
+
readonly offset: number;
|
|
41
|
+
readonly members: readonly I3sPaaMember[];
|
|
42
|
+
/** Per-VERTEX element count (non-indexed: 3·faces) — from the node's `geometry.vertexCount`. */
|
|
43
|
+
readonly vertexCount: number;
|
|
44
|
+
/** Per-FEATURE element count — from the node's `geometry.featureCount`. */
|
|
45
|
+
readonly featureCount: number;
|
|
46
|
+
}
|
|
47
|
+
/** Everything the decoder needs to georeference + assemble one mesh node. */
|
|
48
|
+
export interface I3sMeshDecodeContext {
|
|
49
|
+
/** ECEF → shared-scene ENU (meters), column-major 4×4. */
|
|
50
|
+
readonly enuFromEcef: Mat4;
|
|
51
|
+
/** node-local ENU → ECEF (the engine builds it from the node center). */
|
|
52
|
+
readonly tileTransform: Mat4;
|
|
53
|
+
/** Bake the exact per-vertex web-mercator offset at decode (kills the per-tile ENU-tangent parallax-
|
|
54
|
+
* swim on planet-scale layers). Off ⇒ the ENU-only path, byte-identical. Set by the engine. */
|
|
55
|
+
readonly bakeMercator?: boolean;
|
|
56
|
+
/** WGS84 meters-per-degree of longitude / latitude at the node center — I3S global-mode
|
|
57
|
+
* vertex X/Y are lng/lat DEGREE offsets; multiply to get local ENU east/north meters. */
|
|
58
|
+
readonly lngToMeters: number;
|
|
59
|
+
readonly latToMeters: number;
|
|
60
|
+
/** Geometry blob is Draco-compressed; else uncompressed PerAttributeArray (see {@link paa}). */
|
|
61
|
+
readonly draco: boolean;
|
|
62
|
+
/** Uncompressed PAA buffer layout — REQUIRED when `draco` is false (the decoder reads the blob
|
|
63
|
+
* per this layout); absent for Draco nodes. */
|
|
64
|
+
readonly paa?: I3sPaaLayout;
|
|
65
|
+
/** Texture blob format, or null when the node carries no texture. */
|
|
66
|
+
readonly textureFormat: 'jpg' | 'png' | 'dds' | 'ktx2' | null;
|
|
67
|
+
readonly material: I3sDecodeMaterial | null;
|
|
68
|
+
/** Extract hard edges for this node ({@link PackedMeshTile.edges}). Absent ⇒ no extraction. */
|
|
69
|
+
readonly edges?: EdgeExtractOptions;
|
|
70
|
+
}
|
|
71
|
+
/** Decodes one I3S mesh node's blobs into a GPU-ready, ENU-rebased {@link PackedMeshTile}. */
|
|
72
|
+
export interface I3sMeshDecoder {
|
|
73
|
+
/** @param blobs - The node's geometry + optional texture ({@link I3sMeshBlobs}).
|
|
74
|
+
* @param ctx - Georeference + assembly context ({@link I3sMeshDecodeContext}).
|
|
75
|
+
* @returns The ENU-rebased {@link PackedMeshTile}. */
|
|
76
|
+
decode(blobs: I3sMeshBlobs, ctx: I3sMeshDecodeContext): Promise<PackedMeshTile>;
|
|
77
|
+
/** Worker pool size (worker variants) — size the engine's decode concurrency to match. */
|
|
78
|
+
readonly concurrency?: number;
|
|
79
|
+
dispose?(): void;
|
|
80
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `I3sPointCloudDecoder` — the loose-coupling seam for Esri **I3S Point Cloud (PCSL)**
|
|
3
|
+
* streaming, the point-cloud analog of {@link GltfSplatDecoder}. An I3S node is
|
|
4
|
+
* NOT a single buffer: it is a geometry blob (`…/geometries/0`, `lepcc-xyz`) plus
|
|
5
|
+
* zero or more per-attribute blobs (`…/attributes/{key}`). This contract decodes
|
|
6
|
+
* ONE such node's blobs into a GPU-ready, ENU-rebased `PackedPointTile`,
|
|
7
|
+
* regardless of WHICH codec the blobs use:
|
|
8
|
+
*
|
|
9
|
+
* - `@bitruvius/turbo-lepcc` implements it for native Esri LEPCC services.
|
|
10
|
+
* - `@bitruvius/bvc` implements it for bvc-transcoded I3S nodes.
|
|
11
|
+
*
|
|
12
|
+
* The `@bitruvius/i3s` engine and the MapLibre layer depend ONLY on this
|
|
13
|
+
* interface — never on a concrete codec — so a consumer swaps `lepcc`↔`bvc`
|
|
14
|
+
* without touching streaming or rendering. Projection (geodetic→ECEF→ENU),
|
|
15
|
+
* requantization to the renderer's i32 grid, and interleaving all happen INSIDE
|
|
16
|
+
* the decoder (off the main thread for the worker variants); the engine just
|
|
17
|
+
* hands the packed tile to the renderer.
|
|
18
|
+
*/
|
|
19
|
+
import type { PackedPointTile } from '../geo/pack-point-enu';
|
|
20
|
+
/** I3S attribute storage value type (from `attributeStorageInfo[].valueType`). */
|
|
21
|
+
export type I3sValueType = 'UInt8' | 'Int8' | 'UInt16' | 'Int16' | 'UInt32' | 'Int32' | 'UInt64' | 'Int64' | 'Float32' | 'Float64';
|
|
22
|
+
/** Logical attribute meaning, derived from `attributeStorageInfo[].name`. Drives
|
|
23
|
+
* how a blob is decoded and which `PackedPointTile` channel it fills. */
|
|
24
|
+
export type I3sAttributeSemantic = 'ELEVATION' | 'INTENSITY' | 'RGB' | 'CLASS_CODE' | 'FLAGS' | 'RETURNS' | 'USER_DATA' | 'POINT_SRC_ID' | 'GPS_TIME' | 'SCAN_ANGLE' | 'NEAR_INFRARED' | 'UNKNOWN';
|
|
25
|
+
/** One entry of the layer's `attributeStorageInfo`, semantic-mapped. The `key` is
|
|
26
|
+
* the per-node attribute resource id used to build the blob URL. */
|
|
27
|
+
export interface I3sAttributeInfo {
|
|
28
|
+
readonly key: number;
|
|
29
|
+
/** The RAW attribute key as it appears in `attributeStorageInfo` and the resource URL
|
|
30
|
+
* (`…/attributes/{keyName}/0`). Numeric for point layers (`'1'`), but `'f_0'`-style for
|
|
31
|
+
* 3DObject feature attributes — where {@link key} (a parsed number) is meaningless. */
|
|
32
|
+
readonly keyName?: string;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly semantic: I3sAttributeSemantic;
|
|
35
|
+
readonly valueType: I3sValueType;
|
|
36
|
+
readonly valuesPerElement: number;
|
|
37
|
+
/** e.g. `'lepcc-intensity'`, `'lepcc-rgb'`, `'embedded-elevation'`, or undefined (raw). */
|
|
38
|
+
readonly encoding?: string;
|
|
39
|
+
}
|
|
40
|
+
/** The raw blobs for ONE I3S node, fetched by the engine and handed to the decoder.
|
|
41
|
+
* `attributes` is keyed by the I3S attribute resource id (see {@link I3sAttributeInfo.key}). */
|
|
42
|
+
export interface I3sNodeBlobs {
|
|
43
|
+
/** `…/nodes/{id}/geometries/0` — the lepcc-xyz (or bvc) geometry blob. */
|
|
44
|
+
readonly geometry: ArrayBuffer;
|
|
45
|
+
/** `…/nodes/{id}/attributes/{key}` blobs the engine chose to fetch (may be empty). */
|
|
46
|
+
readonly attributes: ReadonlyMap<number, ArrayBuffer>;
|
|
47
|
+
}
|
|
48
|
+
/** Everything the decoder needs to georeference + requantize a node's points into
|
|
49
|
+
* the shared scene ENU frame the renderer draws. */
|
|
50
|
+
export interface I3sDecodeContext {
|
|
51
|
+
/** Node OBB center in ECEF (meters) — the per-tile requantization origin. */
|
|
52
|
+
readonly nodeCenterEcef: readonly [number, number, number];
|
|
53
|
+
/** ECEF → shared-scene local ENU (meters), column-major 4×4 (16 floats). */
|
|
54
|
+
readonly enuFromEcef: Float64Array;
|
|
55
|
+
/** Geoid undulation N (m): geometry heights are orthometric (vcsWkid 5773), so
|
|
56
|
+
* ellipsoidal h = orthometric H + N before `geodeticToEcef`. 0 = treat as ellipsoidal. */
|
|
57
|
+
readonly geoidN: number;
|
|
58
|
+
/** Requantization step (meters) for the i32 position grid (e.g. 0.001 = 1 mm). */
|
|
59
|
+
readonly grid: number;
|
|
60
|
+
/** Layer attribute schema (semantic + valueType + encoding) for the fetched blobs. */
|
|
61
|
+
readonly attributes: readonly I3sAttributeInfo[];
|
|
62
|
+
/** Horizontal CRS wkid. 4326/4979 = geographic (decode→lng/lat/h). */
|
|
63
|
+
readonly wkid: number;
|
|
64
|
+
/** Vertical CRS wkid (5773 = EGM96 orthometric). */
|
|
65
|
+
readonly vcsWkid?: number;
|
|
66
|
+
/** Projected-CRS reprojection hook: (x,y,z) in the layer CRS → [lng°, lat°, h_m].
|
|
67
|
+
* REQUIRED when `wkid` is a projected CRS; ignored for geographic scenes. */
|
|
68
|
+
readonly reproject?: (x: number, y: number, z: number) => [number, number, number];
|
|
69
|
+
}
|
|
70
|
+
/** Decodes one I3S point node's blobs into a GPU-ready, ENU-rebased point tile.
|
|
71
|
+
* Satisfied by `@bitruvius/turbo-lepcc` (`lepcc`) and `@bitruvius/bvc` (`bvc`). */
|
|
72
|
+
export interface I3sPointCloudDecoder {
|
|
73
|
+
/** Decode one node into the shared scene ENU frame.
|
|
74
|
+
* @param blobs - The node's geometry + attribute blobs (`I3sNodeBlobs`).
|
|
75
|
+
* @param ctx - Georeference + requantize context (`I3sDecodeContext`).
|
|
76
|
+
* @returns The ENU-rebased, requantized `PackedPointTile`. */
|
|
77
|
+
decode(blobs: I3sNodeBlobs, ctx: I3sDecodeContext): Promise<PackedPointTile>;
|
|
78
|
+
/** Worker pool size (worker variants) — size the engine's decode concurrency to match. */
|
|
79
|
+
readonly concurrency?: number;
|
|
80
|
+
/** True when the geometry blob already carries ALL attributes (e.g. a bvc point
|
|
81
|
+
* tile) — the engine then fetches NO separate `/attributes/{key}` blobs. Native
|
|
82
|
+
* Esri LEPCC leaves this false (attributes are separate per-node resources). */
|
|
83
|
+
readonly selfContainedAttributes?: boolean;
|
|
84
|
+
dispose?(): void;
|
|
85
|
+
}
|
|
86
|
+
/** Built-in decoder identifiers a layer/engine can resolve to a concrete impl. */
|
|
87
|
+
export type I3sPointCloudDecoderKind = 'lepcc' | 'bvc';
|
|
88
|
+
/** Map an I3S `attributeStorageInfo[].name` to a logical {@link I3sAttributeSemantic}.
|
|
89
|
+
* @param name - The raw attribute name. @returns The semantic, or `'UNKNOWN'`. */
|
|
90
|
+
export declare function attributeSemantic(name: string): I3sAttributeSemantic;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Third-party-codec **seams** for the mesh/point decode path — interfaces only,
|
|
3
|
+
* no codec. The Bitruvius WASM wrapper packages implement these and are injected
|
|
4
|
+
* into the parser/decoder, keeping `@bitruvius/gltf` (and the engine) codec- and
|
|
5
|
+
* vendor-agnostic:
|
|
6
|
+
*
|
|
7
|
+
* - `@bitruvius/draco` → {@link DracoGeometryDecoder} (KHR_draco_mesh_compression,
|
|
8
|
+
* 3DTILES_draco_point_compression)
|
|
9
|
+
* - `@bitruvius/meshopt` → {@link MeshoptDecoder} (EXT_meshopt_compression)
|
|
10
|
+
* - `@bitruvius/ktx2` → {@link Ktx2Transcoder} (KHR_texture_basisu)
|
|
11
|
+
*
|
|
12
|
+
* They live in geo-core (alongside the other cross-package contracts) so a codec
|
|
13
|
+
* package depends only on `@bitruvius/geo-core`, never on the glTF parser.
|
|
14
|
+
*/
|
|
15
|
+
/** Any numeric typed array (decoded attribute / index storage). */
|
|
16
|
+
export type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
|
|
17
|
+
export interface DracoDecodedAttribute {
|
|
18
|
+
readonly array: TypedArray;
|
|
19
|
+
/** Components per vertex (e.g. POSITION → 3). */
|
|
20
|
+
readonly componentCount: number;
|
|
21
|
+
}
|
|
22
|
+
export interface DracoDecoded {
|
|
23
|
+
/** Triangle indices, or undefined for point clouds. */
|
|
24
|
+
readonly indices: Uint32Array | undefined;
|
|
25
|
+
/** Decoded attributes keyed by the glTF/I3S attribute semantic. */
|
|
26
|
+
readonly attributes: ReadonlyMap<string, DracoDecodedAttribute>;
|
|
27
|
+
/** Vertex (or point) count. */
|
|
28
|
+
readonly count: number;
|
|
29
|
+
}
|
|
30
|
+
/** I3S Draco-compressed mesh geometry, decoded by NAMED attribute + Draco metadata
|
|
31
|
+
* (I3S carries no explicit unique-id map; it uses Draco's POSITION/TEX_COORD/NORMAL
|
|
32
|
+
* types and `i3s-scale_x/y` metadata on POSITION). Positions are RAW node-local — the
|
|
33
|
+
* i3s-scale is returned separately in `scaleX/scaleY` (NOT pre-multiplied), so the caller
|
|
34
|
+
* folds it into the node matrix instead of an extra full pass over every vertex.
|
|
35
|
+
* `featureIndex` (the GENERIC per-vertex object id) is for picking. */
|
|
36
|
+
export interface I3sDracoMesh {
|
|
37
|
+
/** 3·N node-local positions (Float32, RAW — i3s-scale NOT applied; see scaleX/scaleY). */
|
|
38
|
+
readonly positions: Float32Array;
|
|
39
|
+
readonly uv0?: Float32Array;
|
|
40
|
+
readonly normals?: Float32Array;
|
|
41
|
+
/** 4·N RAW RGBA bytes (0..255) — the I3S `color` vertex attribute (per-feature tint on
|
|
42
|
+
* untextured 3DObject features). Kept as bytes; the renderer GPU-normalizes at upload. */
|
|
43
|
+
readonly color0?: Uint8Array;
|
|
44
|
+
/** 4·N RAW atlas region `(uMin,vMin,uMax,vMax)` as UInt16 (0..65535) — the I3S `uv-region`
|
|
45
|
+
* attribute for texture-atlased 3DObject. GPU-normalized at upload; the shader wraps uv0
|
|
46
|
+
* into the region. Absent when the node isn't atlased. */
|
|
47
|
+
readonly uvRegion?: Uint16Array;
|
|
48
|
+
readonly featureIndex?: Uint32Array;
|
|
49
|
+
/** Triangle indices — present for Draco (indexed); ABSENT for uncompressed PAA (non-indexed,
|
|
50
|
+
* 3·faces vertices drawn directly). */
|
|
51
|
+
readonly indices?: Uint32Array;
|
|
52
|
+
readonly count: number;
|
|
53
|
+
/** `i3s-scale_x/y` POSITION metadata (default 1) — fold into the node matrix's X/Y columns. */
|
|
54
|
+
readonly scaleX: number;
|
|
55
|
+
readonly scaleY: number;
|
|
56
|
+
}
|
|
57
|
+
/** I3S Draco-compressed POINT geometry — the modern **Point feature** profile (`layerType:
|
|
58
|
+
* "Point"`): a Draco buffer with `position` + `feature-index` (geometryType points). Like
|
|
59
|
+
* {@link I3sDracoMesh} but a POINT CLOUD — no faces/indices, no uv/normal/color. Positions are
|
|
60
|
+
* RAW node-local (i3s-scale returned separately, NOT pre-multiplied); `featureIndex` is the
|
|
61
|
+
* per-point feature ordinal for the attribute join / symbol selection / picking. */
|
|
62
|
+
export interface I3sDracoPoints {
|
|
63
|
+
/** 3·N node-local positions (Float32, RAW — i3s-scale NOT applied; see scaleX/scaleY). */
|
|
64
|
+
readonly positions: Float32Array;
|
|
65
|
+
/** N per-point feature ordinals (GENERIC i3s `feature-index`); absent if the buffer omits it. */
|
|
66
|
+
readonly featureIndex?: Uint32Array;
|
|
67
|
+
readonly count: number;
|
|
68
|
+
/** `i3s-scale_x/y` POSITION metadata (default 1) — fold into the node matrix's X/Y columns. */
|
|
69
|
+
readonly scaleX: number;
|
|
70
|
+
readonly scaleY: number;
|
|
71
|
+
}
|
|
72
|
+
/** The Draco decode seam, satisfied by `@bitruvius/draco`. */
|
|
73
|
+
export interface DracoGeometryDecoder {
|
|
74
|
+
/** Decode a Draco buffer by glTF unique-id map.
|
|
75
|
+
* @param compressed - The Draco buffer.
|
|
76
|
+
* @param attributeIds - Maps a semantic (`POSITION`, `NORMAL`, `TEXCOORD_0`,
|
|
77
|
+
* `COLOR_0`, `_FEATURE_ID_0`, …) to the Draco unique-id to extract.
|
|
78
|
+
* @returns The decoded indices + attributes ({@link DracoDecoded}). */
|
|
79
|
+
decode(compressed: Uint8Array, attributeIds: Readonly<Record<string, number>>): Promise<DracoDecoded>;
|
|
80
|
+
/** Decode an I3S Draco mesh by named attribute + metadata (no unique-id map). */
|
|
81
|
+
decodeI3sMesh?(compressed: Uint8Array): Promise<I3sDracoMesh>;
|
|
82
|
+
/** Decode I3S Draco POINT geometry (modern Point feature profile: position + feature-index). */
|
|
83
|
+
decodeI3sPoints?(compressed: Uint8Array): Promise<I3sDracoPoints>;
|
|
84
|
+
readonly concurrency?: number;
|
|
85
|
+
dispose?(): void;
|
|
86
|
+
}
|
|
87
|
+
export interface MeshoptDecodeRequest {
|
|
88
|
+
/** Destination buffer (written in place), sized `count * byteStride`. */
|
|
89
|
+
readonly target: Uint8Array;
|
|
90
|
+
readonly count: number;
|
|
91
|
+
readonly byteStride: number;
|
|
92
|
+
readonly source: Uint8Array;
|
|
93
|
+
/** EXT_meshopt_compression `mode`: ATTRIBUTES | TRIANGLES | INDICES. */
|
|
94
|
+
readonly mode: 'ATTRIBUTES' | 'TRIANGLES' | 'INDICES';
|
|
95
|
+
/** Optional `filter`: NONE | OCTAHEDRAL | QUATERNION | EXPONENTIAL. */
|
|
96
|
+
readonly filter?: 'NONE' | 'OCTAHEDRAL' | 'QUATERNION' | 'EXPONENTIAL';
|
|
97
|
+
}
|
|
98
|
+
/** The EXT_meshopt_compression decode seam, satisfied by `@bitruvius/meshopt`. */
|
|
99
|
+
export interface MeshoptDecoder {
|
|
100
|
+
/** Decode one meshopt buffer in place into `req.target`.
|
|
101
|
+
* @param req - The decode request ({@link MeshoptDecodeRequest}). */
|
|
102
|
+
decodeGltfBuffer(req: MeshoptDecodeRequest): Promise<void> | void;
|
|
103
|
+
dispose?(): void;
|
|
104
|
+
}
|
|
105
|
+
/** Backend-neutral GPU texture format the transcoder targets (the renderer picks
|
|
106
|
+
* the best supported one per device; `rgba32` is the universal fallback). */
|
|
107
|
+
export type GpuTextureFormatHint = 'rgba32' | 'bc7' | 'bc3' | 'bc1' | 'etc2' | 'astc4x4' | 'pvrtc';
|
|
108
|
+
export interface TranscodedTextureLevel {
|
|
109
|
+
readonly width: number;
|
|
110
|
+
readonly height: number;
|
|
111
|
+
readonly data: Uint8Array;
|
|
112
|
+
}
|
|
113
|
+
export interface TranscodedTexture {
|
|
114
|
+
readonly format: GpuTextureFormatHint;
|
|
115
|
+
readonly levels: readonly TranscodedTextureLevel[];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Encode a {@link GpuTextureFormatHint} as the small integer a renderer maps to a GL
|
|
119
|
+
* enum ({@link PackedTexture.compressed}.format).
|
|
120
|
+
*
|
|
121
|
+
* It is an INDEX into a fixed order, so every producer has to agree on that order or
|
|
122
|
+
* a texture uploads as the wrong format. It lives here, next to the type, because two
|
|
123
|
+
* decoders now emit transcoded textures — the glTF path and the I3S path — and a
|
|
124
|
+
* private copy in each would drift silently the first time the list grew.
|
|
125
|
+
*/
|
|
126
|
+
export declare function gpuFormatTag(hint: GpuTextureFormatHint): number;
|
|
127
|
+
/** The KHR_texture_basisu (KTX2/Basis) transcode seam, satisfied by `@bitruvius/ktx2`. */
|
|
128
|
+
export interface Ktx2Transcoder {
|
|
129
|
+
/** Transcode a KTX2/Basis texture to a GPU-native format.
|
|
130
|
+
* @param ktx2 - The KTX2 file bytes.
|
|
131
|
+
* @param target - The desired {@link GpuTextureFormatHint} (renderer-selected).
|
|
132
|
+
* @returns The transcoded mip levels ({@link TranscodedTexture}). */
|
|
133
|
+
transcode(ktx2: Uint8Array, target: GpuTextureFormatHint): Promise<TranscodedTexture>;
|
|
134
|
+
dispose?(): void;
|
|
135
|
+
}
|