@loaders.gl/tile-converter 5.0.0-alpha.8 → 5.0.0-alpha.9
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/README.md +234 -0
- package/dist/converter.min.cjs +122 -122
- package/dist/index.cjs +3852 -1970
- package/dist/index.js +3670 -1789
- package/dist/v5/browser-sink.d.ts +1 -1
- package/dist/v5/browser.cjs +8845 -642
- package/dist/v5/browser.d.ts +12 -0
- package/dist/v5/browser.js +8848 -635
- package/dist/v5/conversion-archive.d.ts +18 -0
- package/dist/v5/index.cjs +9705 -2234
- package/dist/v5/index.d.ts +10 -0
- package/dist/v5/index.js +9715 -2233
- package/dist/v5/mesh-conversion.d.ts +55 -0
- package/dist/v5/mesh-tileset-archive.d.ts +16 -0
- package/dist/v5/mesh-tileset-sink.d.ts +31 -0
- package/dist/v5/mesh.d.ts +73 -0
- package/package.json +23 -23
- package/src/v5/browser-sink.ts +5 -4
- package/src/v5/browser.ts +23 -0
- package/src/v5/conversion-archive.ts +51 -0
- package/src/v5/index.ts +21 -0
- package/src/v5/mesh-conversion.ts +177 -0
- package/src/v5/mesh-tileset-archive.ts +48 -0
- package/src/v5/mesh-tileset-sink.ts +178 -0
- package/src/v5/mesh.ts +503 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { TilesetSpatialReference } from '@loaders.gl/tiles';
|
|
2
|
+
import type { MeshGeometry } from '@loaders.gl/schema';
|
|
3
|
+
import type { TileConversionCodec } from './conversion-api.js';
|
|
4
|
+
import type { Tiles3DConversionSpatialContext } from './spatial-conversion.js';
|
|
5
|
+
import type { MeshTileMaterial } from './mesh.js';
|
|
6
|
+
/** Explicitly selected geometry, with positions already in the spatial context's source frame. */
|
|
7
|
+
export interface MeshConversionInput {
|
|
8
|
+
/** Caller-owned resource identifier, forwarded to the output sink. */
|
|
9
|
+
readonly id: string;
|
|
10
|
+
/** Triangle geometry with absolute packed Float32 or Float64 source positions. */
|
|
11
|
+
readonly mesh: MeshGeometry;
|
|
12
|
+
/** Optional single material with an embedded base-color image, mapped explicitly by the source adapter. */
|
|
13
|
+
readonly material?: MeshTileMaterial;
|
|
14
|
+
/** Finite xyz origin in the target frame, subtracted before float32 encoding. */
|
|
15
|
+
readonly origin: readonly [number, number, number];
|
|
16
|
+
}
|
|
17
|
+
/** A completed GLB resource and the metadata needed to place its local geometry. */
|
|
18
|
+
export interface EncodedMeshConversionResource {
|
|
19
|
+
/** Caller-owned resource identifier. */
|
|
20
|
+
readonly id: string;
|
|
21
|
+
/** Self-contained GLB with local positions and transformed normals. */
|
|
22
|
+
readonly glb: ArrayBuffer;
|
|
23
|
+
/** Target-frame translation to add to the encoded positions. */
|
|
24
|
+
readonly origin: readonly [number, number, number];
|
|
25
|
+
/** Target-frame bounds of the reconstructed encoded vertices, including float32 rounding. */
|
|
26
|
+
readonly boundingBox: readonly [readonly number[], readonly number[]];
|
|
27
|
+
/** Bounds of encoded local vertices before adding the origin, without absolute-coordinate cancellation. */
|
|
28
|
+
readonly localBoundingBox: readonly [readonly number[], readonly number[]];
|
|
29
|
+
/** Metadata describing the output coordinate operation and height reference. */
|
|
30
|
+
readonly spatialReference: TilesetSpatialReference;
|
|
31
|
+
/** Largest measured Euclidean reconstruction error in target coordinate units. */
|
|
32
|
+
readonly maximumPositionError: number;
|
|
33
|
+
}
|
|
34
|
+
/** Spatial policy used by the concrete mesh conversion codec. */
|
|
35
|
+
export interface MeshConversionCodecOptions {
|
|
36
|
+
/** Shared context that accepts absolute source positions and returns absolute target positions. */
|
|
37
|
+
readonly spatialContext: Tiles3DConversionSpatialContext;
|
|
38
|
+
/** Maximum Euclidean reconstruction error per vertex, in target coordinate units. */
|
|
39
|
+
readonly maxPositionError: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a concrete mesh codec for `convertTileset`.
|
|
43
|
+
*
|
|
44
|
+
* Source adapters must select geometry and apply source placement before supplying absolute
|
|
45
|
+
* positions and matching normals. This codec applies the supplied spatial context once, rebases
|
|
46
|
+
* around each input's target origin, and rejects rounding above the explicit error budget.
|
|
47
|
+
* Geographic and unknown output frames are rejected. The returned origin/bounds/reference are
|
|
48
|
+
* external placement metadata; applications own axis conventions, hierarchy, and packaging.
|
|
49
|
+
* Input arrays are not mutated. Core input/output byte gates, cancellation, awaited writes, and
|
|
50
|
+
* destination finalize/abort handling apply when used through `convertTileset`.
|
|
51
|
+
*
|
|
52
|
+
* @param options - Resolved spatial context and finite nonnegative target-unit precision budget.
|
|
53
|
+
* @returns One GLB and matching metadata per input geometry.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createMeshConversionCodec<TInspection = unknown>(options: MeshConversionCodecOptions): TileConversionCodec<TInspection, MeshConversionInput, EncodedMeshConversionResource>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BrowserTileConversionFile } from './browser-sink.js';
|
|
2
|
+
/** Output-size budget for a single-mesh 3TZ archive. */
|
|
3
|
+
export interface SingleMeshTilesetArchiveOptions {
|
|
4
|
+
/** Maximum final archive bytes, including ZIP headers and index; not peak serialization memory. */
|
|
5
|
+
readonly maxArchiveBytes: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Packages the two unmodified files from a finalized single-mesh sink as a downloadable 3TZ Blob.
|
|
9
|
+
* The writer checks the complete archive budget before reading either Blob. Applications own
|
|
10
|
+
* object-URL creation/revocation and download UI. Retained input Blobs and transient ZIP buffers
|
|
11
|
+
* are additional memory. SLPK requires separately authored I3S scene-layer resources.
|
|
12
|
+
* @param files - Files returned by a successfully finalized createSingleMeshTilesetSink.
|
|
13
|
+
* @param options - Required final archive-size budget.
|
|
14
|
+
* @returns An indexed 3TZ Blob; save with the .3tz extension.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSingleMeshTilesetArchive(files: readonly BrowserTileConversionFile[], options: SingleMeshTilesetArchiveOptions): Promise<Blob>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TileConversionSink } from './conversion-api.js';
|
|
2
|
+
import type { BrowserTileConversionFile } from './browser-sink.js';
|
|
3
|
+
import type { EncodedMeshConversionResource } from './mesh-conversion.js';
|
|
4
|
+
/** Limits and fidelity policy for one ECEF mesh tileset. */
|
|
5
|
+
export interface SingleMeshTilesetSinkOptions {
|
|
6
|
+
/** Maximum combined UTF-8 tileset JSON and GLB Blob bytes retained by the sink. */
|
|
7
|
+
readonly maxTotalBytes: number;
|
|
8
|
+
/** Explicit geometric error in meters, including source error and position rounding. */
|
|
9
|
+
readonly geometricError: number;
|
|
10
|
+
}
|
|
11
|
+
/** A single-tile output package retained as two named browser-native Blobs. */
|
|
12
|
+
export interface SingleMeshTilesetSink extends TileConversionSink<EncodedMeshConversionResource> {
|
|
13
|
+
/** Returns mesh.glb and tileset.json only after successful finalization; empty after abort. */
|
|
14
|
+
getFiles(): readonly BrowserTileConversionFile[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Packages exactly one `createMeshConversionCodec` output as a 3D Tiles 1.1 tileset.
|
|
18
|
+
*
|
|
19
|
+
* Requires resolved EPSG:4978 output identified by the compact string, with ellipsoidal heights.
|
|
20
|
+
* The tile transform cancels the standard glTF y-up to z-up rotation and adds the ECEF origin.
|
|
21
|
+
* Bounds come from encoded local vertices, avoiding subtraction of large absolute coordinates.
|
|
22
|
+
* Fixed relative names keep the package deterministic. Save both files together or pass them to
|
|
23
|
+
* createSingleMeshTilesetArchive for 3TZ packaging. Applications own downloads, workers, and
|
|
24
|
+
* source-scene extraction.
|
|
25
|
+
* The byte limit bounds retained Blobs, not peak conversion/serialization memory. Input GLB and
|
|
26
|
+
* local bounds must be unmodified codec outputs; this sink does not independently validate GLB.
|
|
27
|
+
*
|
|
28
|
+
* @param options - Required retained-output budget and geometric error in meters.
|
|
29
|
+
* @returns A bounded sink that exposes files on finalization and clears them on abort.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createSingleMeshTilesetSink(options: SingleMeshTilesetSinkOptions): SingleMeshTilesetSink;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { MeshGeometry } from '@loaders.gl/schema';
|
|
2
|
+
/** Explicit glTF wrapping and filtering for the selected base-color image. */
|
|
3
|
+
export interface MeshTileSampler {
|
|
4
|
+
/** Horizontal wrapping: CLAMP_TO_EDGE, MIRRORED_REPEAT, or REPEAT; omitted values use REPEAT. */
|
|
5
|
+
readonly wrapS?: 33071 | 33648 | 10497;
|
|
6
|
+
/** Vertical wrapping: CLAMP_TO_EDGE, MIRRORED_REPEAT, or REPEAT; omitted values use REPEAT. */
|
|
7
|
+
readonly wrapT?: 33071 | 33648 | 10497;
|
|
8
|
+
/** Magnification: NEAREST or LINEAR; omission leaves filtering to the renderer. */
|
|
9
|
+
readonly magFilter?: 9728 | 9729;
|
|
10
|
+
/** Minification: NEAREST, LINEAR, or a glTF mipmap filter; omission leaves filtering to the renderer. */
|
|
11
|
+
readonly minFilter?: 9728 | 9729 | 9984 | 9985 | 9986 | 9987;
|
|
12
|
+
}
|
|
13
|
+
/** Selected KHR_texture_transform controls for TEXCOORD_0. */
|
|
14
|
+
export interface MeshTileTextureTransform {
|
|
15
|
+
/** Finite UV translation; omitted values use [0, 0]. */
|
|
16
|
+
readonly offset?: readonly [number, number];
|
|
17
|
+
/** Finite counterclockwise rotation in radians around the UV origin; omission uses 0. */
|
|
18
|
+
readonly rotation?: number;
|
|
19
|
+
/** Finite UV scale; zero and negative values are allowed, and omission uses [1, 1]. */
|
|
20
|
+
readonly scale?: readonly [number, number];
|
|
21
|
+
}
|
|
22
|
+
/** One already encoded base-color image using TEXCOORD_0. */
|
|
23
|
+
export interface MeshTileTexture {
|
|
24
|
+
/** Encoded PNG/JPEG bytes; a typed array subview selects only its own bytes. */
|
|
25
|
+
readonly data: Uint8Array;
|
|
26
|
+
/** Declared image format, checked against the encoded header. */
|
|
27
|
+
readonly mimeType: 'image/png' | 'image/jpeg';
|
|
28
|
+
/** Optional wrapping/filtering; omission retains glTF's implicit sampler. */
|
|
29
|
+
readonly sampler?: MeshTileSampler;
|
|
30
|
+
/** Optional UV transform, preserved as a required KHR_texture_transform extension. */
|
|
31
|
+
readonly transform?: MeshTileTextureTransform;
|
|
32
|
+
}
|
|
33
|
+
/** One glTF metallic-roughness material, shared by every triangle in the mesh. */
|
|
34
|
+
export interface MeshTileMaterial {
|
|
35
|
+
/** Linear RGBA base-color multiplier; components must be finite in [0, 1]. */
|
|
36
|
+
readonly baseColorFactor?: readonly [number, number, number, number];
|
|
37
|
+
/** Optional embedded image, multiplied by baseColorFactor and COLOR_0; requires TEXCOORD_0. */
|
|
38
|
+
readonly baseColorTexture?: MeshTileTexture;
|
|
39
|
+
/** Metallic weight in [0, 1]; omitted values use the glTF default of 1. */
|
|
40
|
+
readonly metallicFactor?: number;
|
|
41
|
+
/** Roughness weight in [0, 1]; omitted values use the glTF default of 1. */
|
|
42
|
+
readonly roughnessFactor?: number;
|
|
43
|
+
/** Alpha interpretation; omitted values use OPAQUE. */
|
|
44
|
+
readonly alphaMode?: 'OPAQUE' | 'MASK' | 'BLEND';
|
|
45
|
+
/** Finite nonnegative threshold, allowed only with MASK; omitted values use 0.5. */
|
|
46
|
+
readonly alphaCutoff?: number;
|
|
47
|
+
/** Whether both sides are rendered; omitted values use false. */
|
|
48
|
+
readonly doubleSided?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** Optional appearance for the single-mesh GLB profile. */
|
|
51
|
+
export interface MeshTileOptions {
|
|
52
|
+
/** Explicitly selected material; other texture maps and material extensions are unsupported. */
|
|
53
|
+
readonly material?: MeshTileMaterial;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Encodes one triangle mesh as a self-contained glTF 2.0 GLB resource.
|
|
57
|
+
*
|
|
58
|
+
* POSITION and optional NORMAL must contain packed finite Float32 xyz triples. COLOR_0 accepts
|
|
59
|
+
* packed Float32 linear RGB/RGBA in [0, 1] or normalized Uint8/Uint16 colors per vertex.
|
|
60
|
+
* Normals must have unit length within 0.0001. TEXCOORD_0 accepts packed finite Float32
|
|
61
|
+
* or normalized Uint8/Uint16 UV pairs per vertex. Optional indices use packed unsigned
|
|
62
|
+
* 8-, 16-, or 32-bit values.
|
|
63
|
+
* Unsupported attributes, layouts, and encoded transforms fail rather than being discarded.
|
|
64
|
+
* Coordinates are preserved: callers own CRS conversion, local origins, placement, source material mapping,
|
|
65
|
+
* feature mappings, and tileset packaging. Input arrays are never modified.
|
|
66
|
+
*
|
|
67
|
+
* @param mesh - Triangle-list geometry in the caller's selected local coordinate frame.
|
|
68
|
+
* @param options - Optional single material with an already encoded base-color image.
|
|
69
|
+
* @returns An embedded-buffer GLB containing one mesh, node, and default scene.
|
|
70
|
+
*/
|
|
71
|
+
export declare function encodeMeshTile(mesh: MeshGeometry, options?: MeshTileOptions): ArrayBuffer;
|
|
72
|
+
/** Validates the shared mesh profile; spatial preparation may retain Float64 positions internally. */
|
|
73
|
+
export declare function validateMeshGeometry(mesh: MeshGeometry, allowFloat64Positions?: boolean): MeshGeometry;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loaders.gl/tile-converter",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.9",
|
|
4
4
|
"description": "Converter",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -63,27 +63,27 @@
|
|
|
63
63
|
"copy-certificates": "cp -R src/v4/i3s-server/certs dist/i3s-server"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@loaders.gl/3d-tiles": "5.0.0-alpha.
|
|
67
|
-
"@loaders.gl/compression": "5.0.0-alpha.
|
|
68
|
-
"@loaders.gl/crypto": "5.0.0-alpha.
|
|
69
|
-
"@loaders.gl/draco": "5.0.0-alpha.
|
|
70
|
-
"@loaders.gl/gltf": "5.0.0-alpha.
|
|
71
|
-
"@loaders.gl/i3s": "5.0.0-alpha.
|
|
72
|
-
"@loaders.gl/images": "5.0.0-alpha.
|
|
73
|
-
"@loaders.gl/loader-utils": "5.0.0-alpha.
|
|
74
|
-
"@loaders.gl/polyfills": "5.0.0-alpha.
|
|
75
|
-
"@loaders.gl/schema": "5.0.0-alpha.
|
|
76
|
-
"@loaders.gl/schema-utils": "5.0.0-alpha.
|
|
77
|
-
"@loaders.gl/textures": "5.0.0-alpha.
|
|
78
|
-
"@loaders.gl/tiles": "5.0.0-alpha.
|
|
79
|
-
"@loaders.gl/worker-utils": "5.0.0-alpha.
|
|
80
|
-
"@loaders.gl/zip": "5.0.0-alpha.
|
|
81
|
-
"@math.gl/core": "5.0.0-alpha.
|
|
82
|
-
"@math.gl/crs": "5.0.0-alpha.
|
|
83
|
-
"@math.gl/culling": "5.0.0-alpha.
|
|
84
|
-
"@math.gl/geoid": "5.0.0-alpha.
|
|
85
|
-
"@math.gl/geometry-utils": "5.0.0-alpha.
|
|
86
|
-
"@math.gl/geospatial": "5.0.0-alpha.
|
|
66
|
+
"@loaders.gl/3d-tiles": "5.0.0-alpha.9",
|
|
67
|
+
"@loaders.gl/compression": "5.0.0-alpha.9",
|
|
68
|
+
"@loaders.gl/crypto": "5.0.0-alpha.9",
|
|
69
|
+
"@loaders.gl/draco": "5.0.0-alpha.9",
|
|
70
|
+
"@loaders.gl/gltf": "5.0.0-alpha.9",
|
|
71
|
+
"@loaders.gl/i3s": "5.0.0-alpha.9",
|
|
72
|
+
"@loaders.gl/images": "5.0.0-alpha.9",
|
|
73
|
+
"@loaders.gl/loader-utils": "5.0.0-alpha.9",
|
|
74
|
+
"@loaders.gl/polyfills": "5.0.0-alpha.9",
|
|
75
|
+
"@loaders.gl/schema": "5.0.0-alpha.9",
|
|
76
|
+
"@loaders.gl/schema-utils": "5.0.0-alpha.9",
|
|
77
|
+
"@loaders.gl/textures": "5.0.0-alpha.9",
|
|
78
|
+
"@loaders.gl/tiles": "5.0.0-alpha.9",
|
|
79
|
+
"@loaders.gl/worker-utils": "5.0.0-alpha.9",
|
|
80
|
+
"@loaders.gl/zip": "5.0.0-alpha.9",
|
|
81
|
+
"@math.gl/core": "5.0.0-alpha.12",
|
|
82
|
+
"@math.gl/crs": "5.0.0-alpha.12",
|
|
83
|
+
"@math.gl/culling": "5.0.0-alpha.12",
|
|
84
|
+
"@math.gl/geoid": "5.0.0-alpha.12",
|
|
85
|
+
"@math.gl/geometry-utils": "5.0.0-alpha.12",
|
|
86
|
+
"@math.gl/geospatial": "5.0.0-alpha.12",
|
|
87
87
|
"ajv": "^8.12.0",
|
|
88
88
|
"archiver": "^5.0.0",
|
|
89
89
|
"cors": "^2.8.5",
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
"peerDependencies": {
|
|
109
109
|
"@loaders.gl/core": "~5.0.0-alpha.0"
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "c3be3766791d30e79a9aa1d9b326962f264cbbe7"
|
|
112
112
|
}
|
package/src/v5/browser-sink.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface BrowserTileConversionFile {
|
|
|
27
27
|
|
|
28
28
|
/** Memory limit for a browser conversion sink. */
|
|
29
29
|
export interface BoundedMemoryTileConversionSinkOptions {
|
|
30
|
-
/** Maximum combined Blob size retained by this sink, in bytes. */
|
|
30
|
+
/** Maximum combined Blob size retained by this sink, captured at creation, in bytes. */
|
|
31
31
|
readonly maxTotalBytes: number;
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -50,7 +50,8 @@ export interface BoundedMemoryTileConversionSink
|
|
|
50
50
|
export function createBoundedMemoryTileConversionSink(
|
|
51
51
|
options: BoundedMemoryTileConversionSinkOptions
|
|
52
52
|
): BoundedMemoryTileConversionSink {
|
|
53
|
-
|
|
53
|
+
const {maxTotalBytes} = options;
|
|
54
|
+
if (!Number.isSafeInteger(maxTotalBytes) || maxTotalBytes < 0) {
|
|
54
55
|
throw new TileConversionError(
|
|
55
56
|
'INVALID_OUTPUT_MEMORY_LIMIT',
|
|
56
57
|
'The browser output memory limit must be a non-negative safe integer'
|
|
@@ -85,10 +86,10 @@ export function createBoundedMemoryTileConversionSink(
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
const byteLength = resource.parts.reduce((total, part) => total + measureBlobPart(part), 0);
|
|
88
|
-
if (!Number.isSafeInteger(byteLength) || totalBytes + byteLength >
|
|
89
|
+
if (!Number.isSafeInteger(byteLength) || totalBytes + byteLength > maxTotalBytes) {
|
|
89
90
|
throw new TileConversionError(
|
|
90
91
|
'OUTPUT_MEMORY_LIMIT_EXCEEDED',
|
|
91
|
-
`Browser output exceeds the configured ${
|
|
92
|
+
`Browser output exceeds the configured ${maxTotalBytes} byte memory limit`
|
|
92
93
|
);
|
|
93
94
|
}
|
|
94
95
|
|
package/src/v5/browser.ts
CHANGED
|
@@ -52,3 +52,26 @@ export type {
|
|
|
52
52
|
BrowserTilesetConversionSourceOptions,
|
|
53
53
|
BrowserTilesetResourceDescriptor
|
|
54
54
|
} from './browser-tileset-source.js';
|
|
55
|
+
export {encodeMeshTile} from './mesh.js';
|
|
56
|
+
export type {
|
|
57
|
+
MeshTileMaterial,
|
|
58
|
+
MeshTileOptions,
|
|
59
|
+
MeshTileTexture,
|
|
60
|
+
MeshTileSampler,
|
|
61
|
+
MeshTileTextureTransform
|
|
62
|
+
} from './mesh.js';
|
|
63
|
+
export {createMeshConversionCodec} from './mesh-conversion.js';
|
|
64
|
+
export type {
|
|
65
|
+
MeshConversionInput,
|
|
66
|
+
EncodedMeshConversionResource,
|
|
67
|
+
MeshConversionCodecOptions
|
|
68
|
+
} from './mesh-conversion.js';
|
|
69
|
+
export {createTiles3DConversionSpatialContext} from './spatial-conversion.js';
|
|
70
|
+
export type {Tiles3DConversionSpatialContext} from './spatial-conversion.js';
|
|
71
|
+
export {createSingleMeshTilesetSink} from './mesh-tileset-sink.js';
|
|
72
|
+
export type {SingleMeshTilesetSink, SingleMeshTilesetSinkOptions} from './mesh-tileset-sink.js';
|
|
73
|
+
|
|
74
|
+
export {createTileConversionArchive} from './conversion-archive.js';
|
|
75
|
+
export type {TileConversionArchiveOptions} from './conversion-archive.js';
|
|
76
|
+
export {createSingleMeshTilesetArchive} from './mesh-tileset-archive.js';
|
|
77
|
+
export type {SingleMeshTilesetArchiveOptions} from './mesh-tileset-archive.js';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Tiles3DArchiveWriter} from '@loaders.gl/3d-tiles/3d-tiles-archive-writer';
|
|
6
|
+
import {SLPKWriter} from '@loaders.gl/i3s/i3s-slpk-writer';
|
|
7
|
+
import {TileConversionError} from './conversion-api.js';
|
|
8
|
+
import type {BrowserTileConversionFile} from './browser-sink.js';
|
|
9
|
+
|
|
10
|
+
/** Output profile and required budget for packaging finalized conversion resources. */
|
|
11
|
+
export interface TileConversionArchiveOptions {
|
|
12
|
+
/** Archive format; resources must already use the selected format's layout and encoding. */
|
|
13
|
+
readonly format: '3tz' | 'slpk';
|
|
14
|
+
/** Maximum final archive bytes, including headers/index; not peak serialization memory. */
|
|
15
|
+
readonly maxArchiveBytes: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Packages finalized, already-authored resources as a bounded 3TZ or SLPK Blob.
|
|
20
|
+
* 3TZ requires tileset.json; SLPK requires 3dSceneLayer.json.gz and precompressed I3S resources.
|
|
21
|
+
* Selecting an archive format does not convert geometry, metadata, or coordinate systems.
|
|
22
|
+
* Applications own input qualification, object URLs, and downloads. Inputs must remain unchanged.
|
|
23
|
+
* @param files - Unmodified files from a successfully finalized browser sink.
|
|
24
|
+
* @param options - Archive format and complete output-size budget.
|
|
25
|
+
* @returns Archive Blob to save using the selected .3tz or .slpk extension.
|
|
26
|
+
*/
|
|
27
|
+
export async function createTileConversionArchive(
|
|
28
|
+
files: readonly BrowserTileConversionFile[],
|
|
29
|
+
options: TileConversionArchiveOptions
|
|
30
|
+
): Promise<Blob> {
|
|
31
|
+
const {format, maxArchiveBytes} = options;
|
|
32
|
+
if (!Number.isSafeInteger(maxArchiveBytes) || maxArchiveBytes < 0) {
|
|
33
|
+
throw new TileConversionError(
|
|
34
|
+
'INVALID_ARCHIVE_BYTE_LIMIT',
|
|
35
|
+
'Archive byte limit must be a non-negative safe integer'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (format !== '3tz' && format !== 'slpk') {
|
|
39
|
+
throw new TileConversionError('INVALID_ARCHIVE_FORMAT', 'Expected 3tz or slpk archive format');
|
|
40
|
+
}
|
|
41
|
+
const resources = Object.fromEntries(files.map(file => [file.resourceId, file.blob]));
|
|
42
|
+
if (Object.keys(resources).length !== files.length) {
|
|
43
|
+
throw new TileConversionError(
|
|
44
|
+
'DUPLICATE_ARCHIVE_RESOURCE',
|
|
45
|
+
'Archive resource identifiers must be unique'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const writer = format === '3tz' ? Tiles3DArchiveWriter : SLPKWriter;
|
|
49
|
+
const archive = await writer.encode(resources, {[format]: {maxArchiveBytes}});
|
|
50
|
+
return new Blob([archive], {type: writer.mimeTypes[0]});
|
|
51
|
+
}
|
package/src/v5/index.ts
CHANGED
|
@@ -45,3 +45,24 @@ export type {
|
|
|
45
45
|
TileResourceManifestEntry,
|
|
46
46
|
TileResourceManifestStore
|
|
47
47
|
} from './resource-manifest.js';
|
|
48
|
+
export {encodeMeshTile} from './mesh.js';
|
|
49
|
+
export type {
|
|
50
|
+
MeshTileMaterial,
|
|
51
|
+
MeshTileOptions,
|
|
52
|
+
MeshTileTexture,
|
|
53
|
+
MeshTileSampler,
|
|
54
|
+
MeshTileTextureTransform
|
|
55
|
+
} from './mesh.js';
|
|
56
|
+
export {createMeshConversionCodec} from './mesh-conversion.js';
|
|
57
|
+
export type {
|
|
58
|
+
MeshConversionInput,
|
|
59
|
+
EncodedMeshConversionResource,
|
|
60
|
+
MeshConversionCodecOptions
|
|
61
|
+
} from './mesh-conversion.js';
|
|
62
|
+
export {createSingleMeshTilesetSink} from './mesh-tileset-sink.js';
|
|
63
|
+
export type {SingleMeshTilesetSink, SingleMeshTilesetSinkOptions} from './mesh-tileset-sink.js';
|
|
64
|
+
|
|
65
|
+
export {createTileConversionArchive} from './conversion-archive.js';
|
|
66
|
+
export type {TileConversionArchiveOptions} from './conversion-archive.js';
|
|
67
|
+
export {createSingleMeshTilesetArchive} from './mesh-tileset-archive.js';
|
|
68
|
+
export type {SingleMeshTilesetArchiveOptions} from './mesh-tileset-archive.js';
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {getSpatialCoordinateFrame} from '@loaders.gl/tiles';
|
|
6
|
+
import type {TilesetSpatialReference} from '@loaders.gl/tiles';
|
|
7
|
+
import type {MeshGeometry} from '@loaders.gl/schema';
|
|
8
|
+
import {TileConversionError} from './conversion-api.js';
|
|
9
|
+
import type {TileConversionCodec} from './conversion-api.js';
|
|
10
|
+
import type {Tiles3DConversionSpatialContext} from './spatial-conversion.js';
|
|
11
|
+
import {encodeMeshTile, validateMeshGeometry} from './mesh.js';
|
|
12
|
+
import type {MeshTileMaterial} from './mesh.js';
|
|
13
|
+
|
|
14
|
+
/** Explicitly selected geometry, with positions already in the spatial context's source frame. */
|
|
15
|
+
export interface MeshConversionInput {
|
|
16
|
+
/** Caller-owned resource identifier, forwarded to the output sink. */
|
|
17
|
+
readonly id: string;
|
|
18
|
+
/** Triangle geometry with absolute packed Float32 or Float64 source positions. */
|
|
19
|
+
readonly mesh: MeshGeometry;
|
|
20
|
+
/** Optional single material with an embedded base-color image, mapped explicitly by the source adapter. */
|
|
21
|
+
readonly material?: MeshTileMaterial;
|
|
22
|
+
/** Finite xyz origin in the target frame, subtracted before float32 encoding. */
|
|
23
|
+
readonly origin: readonly [number, number, number];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A completed GLB resource and the metadata needed to place its local geometry. */
|
|
27
|
+
export interface EncodedMeshConversionResource {
|
|
28
|
+
/** Caller-owned resource identifier. */
|
|
29
|
+
readonly id: string;
|
|
30
|
+
/** Self-contained GLB with local positions and transformed normals. */
|
|
31
|
+
readonly glb: ArrayBuffer;
|
|
32
|
+
/** Target-frame translation to add to the encoded positions. */
|
|
33
|
+
readonly origin: readonly [number, number, number];
|
|
34
|
+
/** Target-frame bounds of the reconstructed encoded vertices, including float32 rounding. */
|
|
35
|
+
readonly boundingBox: readonly [readonly number[], readonly number[]];
|
|
36
|
+
/** Bounds of encoded local vertices before adding the origin, without absolute-coordinate cancellation. */
|
|
37
|
+
readonly localBoundingBox: readonly [readonly number[], readonly number[]];
|
|
38
|
+
/** Metadata describing the output coordinate operation and height reference. */
|
|
39
|
+
readonly spatialReference: TilesetSpatialReference;
|
|
40
|
+
/** Largest measured Euclidean reconstruction error in target coordinate units. */
|
|
41
|
+
readonly maximumPositionError: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Spatial policy used by the concrete mesh conversion codec. */
|
|
45
|
+
export interface MeshConversionCodecOptions {
|
|
46
|
+
/** Shared context that accepts absolute source positions and returns absolute target positions. */
|
|
47
|
+
readonly spatialContext: Tiles3DConversionSpatialContext;
|
|
48
|
+
/** Maximum Euclidean reconstruction error per vertex, in target coordinate units. */
|
|
49
|
+
readonly maxPositionError: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a concrete mesh codec for `convertTileset`.
|
|
54
|
+
*
|
|
55
|
+
* Source adapters must select geometry and apply source placement before supplying absolute
|
|
56
|
+
* positions and matching normals. This codec applies the supplied spatial context once, rebases
|
|
57
|
+
* around each input's target origin, and rejects rounding above the explicit error budget.
|
|
58
|
+
* Geographic and unknown output frames are rejected. The returned origin/bounds/reference are
|
|
59
|
+
* external placement metadata; applications own axis conventions, hierarchy, and packaging.
|
|
60
|
+
* Input arrays are not mutated. Core input/output byte gates, cancellation, awaited writes, and
|
|
61
|
+
* destination finalize/abort handling apply when used through `convertTileset`.
|
|
62
|
+
*
|
|
63
|
+
* @param options - Resolved spatial context and finite nonnegative target-unit precision budget.
|
|
64
|
+
* @returns One GLB and matching metadata per input geometry.
|
|
65
|
+
*/
|
|
66
|
+
export function createMeshConversionCodec<TInspection = unknown>(
|
|
67
|
+
options: MeshConversionCodecOptions
|
|
68
|
+
): TileConversionCodec<TInspection, MeshConversionInput, EncodedMeshConversionResource> {
|
|
69
|
+
const {spatialContext, maxPositionError} = options;
|
|
70
|
+
if (!Number.isFinite(maxPositionError) || maxPositionError < 0) {
|
|
71
|
+
throw new TileConversionError(
|
|
72
|
+
'MESH_PRECISION_LIMIT_INVALID',
|
|
73
|
+
'maxPositionError must be finite and nonnegative'
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const spatialReference = spatialContext.spatialReference;
|
|
77
|
+
const outputCrs = spatialReference.targetCrs || spatialReference.sourceCrs;
|
|
78
|
+
const outputFrame =
|
|
79
|
+
spatialReference.status === 'native'
|
|
80
|
+
? spatialReference.coordinateFrame
|
|
81
|
+
: outputCrs
|
|
82
|
+
? getSpatialCoordinateFrame(outputCrs)
|
|
83
|
+
: 'unknown';
|
|
84
|
+
if (
|
|
85
|
+
!['native', 'transformed'].includes(spatialReference.status) ||
|
|
86
|
+
!['projected', 'geocentric', 'local', 'cartesian'].includes(outputFrame)
|
|
87
|
+
) {
|
|
88
|
+
throw new TileConversionError(
|
|
89
|
+
'MESH_OUTPUT_FRAME_UNSUPPORTED',
|
|
90
|
+
'Mesh output requires a resolved Cartesian coordinate frame'
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
/** Prepares and encodes one geometry while honoring cancellation. */
|
|
95
|
+
async *convert(resource, _inspection, signal) {
|
|
96
|
+
signal?.throwIfAborted();
|
|
97
|
+
const geometry = validateMeshGeometry(resource.mesh, true);
|
|
98
|
+
const sourcePositions = geometry.attributes.POSITION.value;
|
|
99
|
+
const origin: [number, number, number] = [...resource.origin];
|
|
100
|
+
if (origin.length !== 3 || origin.some(value => !Number.isFinite(value))) {
|
|
101
|
+
throw new TileConversionError(
|
|
102
|
+
'MESH_ORIGIN_INVALID',
|
|
103
|
+
'Target origin must contain three finite coordinates'
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const targetPositions = spatialContext.transformPositions(sourcePositions);
|
|
107
|
+
if (
|
|
108
|
+
targetPositions.length !== sourcePositions.length ||
|
|
109
|
+
targetPositions.some(value => !Number.isFinite(value))
|
|
110
|
+
) {
|
|
111
|
+
throw new TileConversionError(
|
|
112
|
+
'MESH_TRANSFORM_INVALID',
|
|
113
|
+
'Spatial transformation must return one finite xyz position per vertex'
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const localPositions = new Float32Array(targetPositions.length);
|
|
117
|
+
const minimum = [Infinity, Infinity, Infinity];
|
|
118
|
+
const maximum = [-Infinity, -Infinity, -Infinity];
|
|
119
|
+
const localMinimum = [Infinity, Infinity, Infinity];
|
|
120
|
+
const localMaximum = [-Infinity, -Infinity, -Infinity];
|
|
121
|
+
let maximumPositionError = 0;
|
|
122
|
+
for (let index = 0; index < targetPositions.length; index += 3) {
|
|
123
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
124
|
+
localPositions[index + axis] = targetPositions[index + axis] - origin[axis];
|
|
125
|
+
localMinimum[axis] = Math.min(localMinimum[axis], localPositions[index + axis]);
|
|
126
|
+
localMaximum[axis] = Math.max(localMaximum[axis], localPositions[index + axis]);
|
|
127
|
+
const reconstructed = localPositions[index + axis] + origin[axis];
|
|
128
|
+
minimum[axis] = Math.min(minimum[axis], reconstructed);
|
|
129
|
+
maximum[axis] = Math.max(maximum[axis], reconstructed);
|
|
130
|
+
}
|
|
131
|
+
const positionError = Math.hypot(
|
|
132
|
+
localPositions[index] + origin[0] - targetPositions[index],
|
|
133
|
+
localPositions[index + 1] + origin[1] - targetPositions[index + 1],
|
|
134
|
+
localPositions[index + 2] + origin[2] - targetPositions[index + 2]
|
|
135
|
+
);
|
|
136
|
+
if (!Number.isFinite(positionError) || positionError > maxPositionError) {
|
|
137
|
+
throw new TileConversionError(
|
|
138
|
+
'MESH_POSITION_PRECISION_EXCEEDED',
|
|
139
|
+
'Float32 mesh positions exceed the configured reconstruction error budget'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
maximumPositionError = Math.max(maximumPositionError, positionError);
|
|
143
|
+
}
|
|
144
|
+
geometry.attributes.POSITION = {value: localPositions, size: 3};
|
|
145
|
+
if (geometry.attributes.NORMAL) {
|
|
146
|
+
geometry.attributes.NORMAL = {
|
|
147
|
+
value: spatialContext.transformNormals(geometry.attributes.NORMAL.value, sourcePositions),
|
|
148
|
+
size: 3
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const glb = encodeMeshTile(geometry, {material: resource.material});
|
|
152
|
+
signal?.throwIfAborted();
|
|
153
|
+
yield {
|
|
154
|
+
id: resource.id,
|
|
155
|
+
glb,
|
|
156
|
+
origin,
|
|
157
|
+
boundingBox: [minimum, maximum],
|
|
158
|
+
localBoundingBox: [localMinimum, localMaximum],
|
|
159
|
+
spatialReference,
|
|
160
|
+
maximumPositionError
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
/** Reports position rounding authorized by the configured precision budget. */
|
|
164
|
+
async validateOutput(resource) {
|
|
165
|
+
return resource.maximumPositionError > 0
|
|
166
|
+
? [
|
|
167
|
+
{
|
|
168
|
+
code: 'MESH_POSITION_ROUNDING',
|
|
169
|
+
message: `Encoded positions have up to ${resource.maximumPositionError} target-unit reconstruction error`,
|
|
170
|
+
severity: 'info' as const,
|
|
171
|
+
resourceId: resource.id
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
: [];
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Tiles3DArchiveWriter} from '@loaders.gl/3d-tiles/3d-tiles-archive-writer';
|
|
6
|
+
import {TileConversionError} from './conversion-api.js';
|
|
7
|
+
import type {BrowserTileConversionFile} from './browser-sink.js';
|
|
8
|
+
|
|
9
|
+
/** Output-size budget for a single-mesh 3TZ archive. */
|
|
10
|
+
export interface SingleMeshTilesetArchiveOptions {
|
|
11
|
+
/** Maximum final archive bytes, including ZIP headers and index; not peak serialization memory. */
|
|
12
|
+
readonly maxArchiveBytes: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Packages the two unmodified files from a finalized single-mesh sink as a downloadable 3TZ Blob.
|
|
17
|
+
* The writer checks the complete archive budget before reading either Blob. Applications own
|
|
18
|
+
* object-URL creation/revocation and download UI. Retained input Blobs and transient ZIP buffers
|
|
19
|
+
* are additional memory. SLPK requires separately authored I3S scene-layer resources.
|
|
20
|
+
* @param files - Files returned by a successfully finalized createSingleMeshTilesetSink.
|
|
21
|
+
* @param options - Required final archive-size budget.
|
|
22
|
+
* @returns An indexed 3TZ Blob; save with the .3tz extension.
|
|
23
|
+
*/
|
|
24
|
+
export async function createSingleMeshTilesetArchive(
|
|
25
|
+
files: readonly BrowserTileConversionFile[],
|
|
26
|
+
options: SingleMeshTilesetArchiveOptions
|
|
27
|
+
): Promise<Blob> {
|
|
28
|
+
const {maxArchiveBytes} = options;
|
|
29
|
+
if (!Number.isSafeInteger(maxArchiveBytes) || maxArchiveBytes < 0) {
|
|
30
|
+
throw new TileConversionError(
|
|
31
|
+
'INVALID_ARCHIVE_BYTE_LIMIT',
|
|
32
|
+
'Archive byte limit must be a non-negative safe integer'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (
|
|
36
|
+
files.length !== 2 ||
|
|
37
|
+
!files.some(file => file.resourceId === 'mesh.glb') ||
|
|
38
|
+
!files.some(file => file.resourceId === 'tileset.json')
|
|
39
|
+
) {
|
|
40
|
+
throw new TileConversionError(
|
|
41
|
+
'INVALID_SINGLE_MESH_ARCHIVE',
|
|
42
|
+
'Expected mesh.glb and tileset.json from a finalized single-mesh sink'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const resources = Object.fromEntries(files.map(file => [file.resourceId, file.blob]));
|
|
46
|
+
const archive = await Tiles3DArchiveWriter.encode(resources, {'3tz': {maxArchiveBytes}});
|
|
47
|
+
return new Blob([archive], {type: Tiles3DArchiveWriter.mimeTypes[0]});
|
|
48
|
+
}
|