@loaders.gl/tile-converter 4.5.3 → 5.0.0-alpha.10
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 +14 -3
- package/README.md +464 -0
- package/dist/converter.min.cjs +199 -260
- package/dist/i3s-server/bin/i3s-server.min.cjs +106 -341
- package/dist/index.cjs +38482 -0
- package/dist/index.js +38453 -0
- package/dist/v5/adapters.cjs +1992 -0
- package/dist/v5/adapters.d.ts +22 -0
- package/dist/v5/adapters.js +1972 -0
- package/dist/v5/browser-sink.d.ts +37 -0
- package/dist/v5/browser-source.d.ts +38 -0
- package/dist/v5/browser-tileset-source.d.ts +70 -0
- package/dist/v5/browser.cjs +64 -0
- package/dist/v5/browser.d.ts +34 -0
- package/dist/v5/browser.js +57 -0
- package/dist/v5/conversion-api.d.ts +122 -0
- package/dist/v5/conversion-archive.d.ts +32 -0
- package/dist/v5/core.cjs +863 -0
- package/dist/v5/core.d.ts +16 -0
- package/dist/v5/core.js +845 -0
- package/dist/v5/feature-arrow.d.ts +36 -0
- package/dist/v5/i3s-mesh-conversion.d.ts +26 -0
- package/dist/v5/index.cjs +63 -0
- package/dist/v5/index.d.ts +32 -0
- package/dist/v5/index.js +59 -0
- package/dist/v5/mesh-conversion.d.ts +62 -0
- package/dist/v5/mesh-draco.d.ts +8 -0
- package/dist/v5/mesh-source-features.d.ts +21 -0
- package/dist/v5/mesh-source-texture.d.ts +4 -0
- package/dist/v5/mesh-source.d.ts +34 -0
- package/dist/v5/mesh-tileset-archive.d.ts +18 -0
- package/dist/v5/mesh-tileset-sink.d.ts +51 -0
- package/dist/v5/mesh.d.ts +81 -0
- package/dist/v5/point-cloud-conversion.d.ts +31 -0
- package/dist/v5/point-cloud-source-encoder.d.ts +49 -0
- package/dist/v5/point-cloud-source.d.ts +25 -0
- package/dist/v5/point-cloud.d.ts +24 -0
- package/dist/v5/resource-manifest.d.ts +58 -0
- package/dist/v5/spatial-conversion.d.ts +59 -0
- package/dist/v5/tileset-source.d.ts +21 -0
- package/package.json +53 -27
- package/src/common/README.md +5 -0
- package/src/index.ts +1 -2
- package/src/{3d-tiles-converter → v4/3d-tiles-converter}/3d-tiles-converter.ts +14 -3
- package/src/{3d-tiles-converter → v4/3d-tiles-converter}/helpers/3d-tiles-content-converter.ts +2 -2
- package/src/v4/3d-tiles-converter/helpers/validate-i3s-spatial-reference.ts +124 -0
- package/src/{3d-tiles-converter → v4/3d-tiles-converter}/json-templates/tileset.ts +4 -4
- package/src/{converter-cli.ts → v4/converter-cli.ts} +4 -4
- package/src/{deps-installer → v4/deps-installer}/deps-installer.ts +10 -7
- package/src/{i3s-converter → v4/i3s-converter}/helpers/attribute-metadata-info.ts +1 -1
- package/src/{i3s-converter → v4/i3s-converter}/helpers/batch-ids-extensions.ts +1 -1
- package/src/v4/i3s-converter/helpers/encode-string-attribute.ts +23 -0
- package/src/{i3s-converter → v4/i3s-converter}/helpers/geometry-attributes.ts +1 -1
- package/src/{i3s-converter → v4/i3s-converter}/helpers/geometry-converter.ts +34 -59
- package/src/{i3s-converter → v4/i3s-converter}/helpers/gltf-attributes.ts +7 -6
- package/src/{i3s-converter → v4/i3s-converter}/helpers/load-3d-tiles.ts +5 -9
- package/src/{i3s-converter → v4/i3s-converter}/helpers/node-debug.ts +5 -6
- package/src/{i3s-converter → v4/i3s-converter}/i3s-converter.ts +16 -19
- package/src/{i3s-converter → v4/i3s-converter}/json-templates/geometry-definitions.ts +4 -4
- package/src/{i3s-converter → v4/i3s-converter}/json-templates/layers.ts +6 -6
- package/src/{i3s-converter → v4/i3s-converter}/json-templates/node.ts +1 -1
- package/src/{i3s-converter → v4/i3s-converter}/json-templates/scene-server.ts +1 -1
- package/src/{i3s-converter → v4/i3s-converter}/json-templates/shared-resources.ts +1 -1
- package/src/{i3s-converter → v4/i3s-converter}/json-templates/store.ts +1 -1
- package/src/{i3s-server → v4/i3s-server}/README.md +2 -2
- package/src/{i3s-server → v4/i3s-server}/controllers/slpk-controller.ts +1 -1
- package/src/{i3s-server → v4/i3s-server}/routes/slpk-router.ts +2 -1
- package/src/v4/index.ts +2 -0
- package/src/{lib → v4/lib}/utils/compress-util.ts +8 -7
- package/src/{lib → v4/lib}/utils/conversion-dump.ts +4 -4
- package/src/{lib → v4/lib}/utils/file-utils.ts +2 -2
- package/src/{lib → v4/lib}/utils/lod-conversion-utils.ts +2 -4
- package/src/v5/adapters.ts +56 -0
- package/src/v5/browser-sink.ts +130 -0
- package/src/v5/browser-source.ts +155 -0
- package/src/v5/browser-tileset-source.ts +328 -0
- package/src/v5/browser.ts +105 -0
- package/src/v5/conversion-api.ts +287 -0
- package/src/v5/conversion-archive.ts +85 -0
- package/src/v5/core.ts +61 -0
- package/src/v5/feature-arrow.ts +429 -0
- package/src/v5/i3s-mesh-conversion.ts +152 -0
- package/src/v5/index.ts +99 -0
- package/src/v5/mesh-conversion.ts +200 -0
- package/src/v5/mesh-draco.ts +138 -0
- package/src/v5/mesh-source-features.ts +232 -0
- package/src/v5/mesh-source-texture.ts +122 -0
- package/src/v5/mesh-source.ts +360 -0
- package/src/v5/mesh-tileset-archive.ts +50 -0
- package/src/v5/mesh-tileset-sink.ts +324 -0
- package/src/v5/mesh.ts +517 -0
- package/src/v5/point-cloud-conversion.ts +80 -0
- package/src/v5/point-cloud-source-encoder.ts +105 -0
- package/src/v5/point-cloud-source.ts +84 -0
- package/src/v5/point-cloud.ts +271 -0
- package/src/v5/resource-manifest.ts +213 -0
- package/src/v5/spatial-conversion.ts +305 -0
- package/src/v5/tileset-source.ts +54 -0
- /package/src/{3d-tiles-converter → v4/3d-tiles-converter}/helpers/i3s-obb-to-3d-tiles-obb.ts +0 -0
- /package/src/{3d-tiles-converter → v4/3d-tiles-converter}/helpers/load-i3s.ts +0 -0
- /package/src/{3d-tiles-converter → v4/3d-tiles-converter}/helpers/texture-atlas.ts +0 -0
- /package/src/{constants.ts → v4/constants.ts} +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/coordinate-converter.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/create-scene-server-path.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/feature-attributes.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/node-index-document.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/node-pages.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/preprocess-3d-tiles.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/progress.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/helpers/tileset-traversal.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/json-templates/metadata.ts +0 -0
- /package/src/{i3s-converter → v4/i3s-converter}/types.ts +0 -0
- /package/src/{i3s-server → v4/i3s-server}/app.ts +0 -0
- /package/src/{i3s-server → v4/i3s-server}/bin/www.ts +0 -0
- /package/src/{i3s-server → v4/i3s-server}/certs/cert.pem +0 -0
- /package/src/{i3s-server → v4/i3s-server}/certs/key.pem +0 -0
- /package/src/{i3s-server → v4/i3s-server}/utils/create-scene-server.ts +0 -0
- /package/src/{i3s-server → v4/i3s-server}/utils/server-utils.ts +0 -0
- /package/src/{lib → v4/lib}/json-schemas/conversion-dump-json-schema.ts +0 -0
- /package/src/{lib → v4/lib}/utils/cli-utils.ts +0 -0
- /package/src/{lib → v4/lib}/utils/geometry-utils.ts +0 -0
- /package/src/{lib → v4/lib}/utils/queue.ts +0 -0
- /package/src/{lib → v4/lib}/utils/statistic-utils.ts +0 -0
- /package/src/{lib → v4/lib}/utils/write-queue.ts +0 -0
- /package/src/{pgm-loader.ts → v4/pgm-loader.ts} +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {Tiles3DTilesetJSON, Tiles3DTileJSON} from '@loaders.gl/3d-tiles';
|
|
6
|
+
import {TileConversionError} from '@loaders.gl/tile-converter/v5/core';
|
|
7
|
+
import type {TileConversionSink} from '@loaders.gl/tile-converter/v5/core';
|
|
8
|
+
import {createBoundedMemoryTileConversionSink} from '@loaders.gl/tile-converter/v5/core';
|
|
9
|
+
import type {BrowserTileConversionFile} from '@loaders.gl/tile-converter/v5/core';
|
|
10
|
+
import type {EncodedMeshConversionResource} from './mesh-conversion.js';
|
|
11
|
+
|
|
12
|
+
/** Limits and fidelity policy for one ECEF mesh tileset. */
|
|
13
|
+
export interface SingleMeshTilesetSinkOptions {
|
|
14
|
+
/** Maximum combined UTF-8 tileset JSON and GLB Blob bytes retained by the sink. */
|
|
15
|
+
readonly maxTotalBytes: number;
|
|
16
|
+
/** Explicit geometric error in meters, including source error and position rounding. */
|
|
17
|
+
readonly geometricError: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A single-tile output package retained as two named browser-native Blobs. */
|
|
21
|
+
export interface SingleMeshTilesetSink extends TileConversionSink<EncodedMeshConversionResource> {
|
|
22
|
+
/** Returns mesh.glb and tileset.json only after successful finalization; empty after abort. */
|
|
23
|
+
getFiles(): readonly BrowserTileConversionFile[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Packages exactly one `createMeshConversionCodec` output as a 3D Tiles 1.1 tileset.
|
|
28
|
+
*
|
|
29
|
+
* Requires resolved EPSG:4978 output identified by the compact string, with ellipsoidal heights.
|
|
30
|
+
* The tile transform cancels the standard glTF y-up to z-up rotation and adds the ECEF origin.
|
|
31
|
+
* Bounds come from encoded local vertices, avoiding subtraction of large absolute coordinates.
|
|
32
|
+
* Fixed relative names keep the package deterministic. Save both files together or pass them to
|
|
33
|
+
* createSingleMeshTilesetArchive for 3TZ packaging. Applications own downloads, workers, and
|
|
34
|
+
* source-scene extraction.
|
|
35
|
+
* The byte limit bounds retained Blobs, not peak conversion/serialization memory. Input GLB and
|
|
36
|
+
* local bounds must be unmodified codec outputs; this sink does not independently validate GLB.
|
|
37
|
+
*
|
|
38
|
+
* @param options - Required retained-output budget and geometric error in meters.
|
|
39
|
+
* @returns A bounded sink that exposes files on finalization and clears them on abort.
|
|
40
|
+
*/
|
|
41
|
+
export function createSingleMeshTilesetSink(
|
|
42
|
+
options: SingleMeshTilesetSinkOptions
|
|
43
|
+
): SingleMeshTilesetSink {
|
|
44
|
+
const {geometricError} = options;
|
|
45
|
+
if (!Number.isFinite(geometricError) || geometricError < 0) {
|
|
46
|
+
throw new TileConversionError(
|
|
47
|
+
'MESH_GEOMETRIC_ERROR_INVALID',
|
|
48
|
+
'geometricError must be finite and nonnegative, in meters'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const memorySink = createBoundedMemoryTileConversionSink(options);
|
|
52
|
+
let state: 'empty' | 'writing' | 'written' | 'closed' = 'empty';
|
|
53
|
+
let completed = false;
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
getFiles() {
|
|
57
|
+
return completed ? memorySink.getFiles() : [];
|
|
58
|
+
},
|
|
59
|
+
async write(resource, signal) {
|
|
60
|
+
signal?.throwIfAborted();
|
|
61
|
+
if (state !== 'empty') {
|
|
62
|
+
throw new TileConversionError(
|
|
63
|
+
'SINGLE_MESH_SINK_UNAVAILABLE',
|
|
64
|
+
'The sink accepts exactly one mesh before finalization or abort'
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const json = JSON.stringify(createMeshTileset(resource, geometricError));
|
|
68
|
+
state = 'writing';
|
|
69
|
+
await memorySink.write(
|
|
70
|
+
{resourceId: 'mesh.glb', parts: [resource.glb], contentType: 'model/gltf-binary'},
|
|
71
|
+
signal
|
|
72
|
+
);
|
|
73
|
+
await memorySink.write(
|
|
74
|
+
{resourceId: 'tileset.json', parts: [json], contentType: 'application/json'},
|
|
75
|
+
signal
|
|
76
|
+
);
|
|
77
|
+
state = 'written';
|
|
78
|
+
},
|
|
79
|
+
async finalize(report) {
|
|
80
|
+
if (state !== 'written') {
|
|
81
|
+
throw new TileConversionError(
|
|
82
|
+
'SINGLE_MESH_SINK_INCOMPLETE',
|
|
83
|
+
'Finalization requires exactly one successfully written mesh'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
await memorySink.finalize(report);
|
|
87
|
+
state = 'closed';
|
|
88
|
+
completed = true;
|
|
89
|
+
},
|
|
90
|
+
async abort(error) {
|
|
91
|
+
state = 'closed';
|
|
92
|
+
completed = false;
|
|
93
|
+
await memorySink.abort(error);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Builds placement and local bounds for a trusted single-mesh codec output. */
|
|
99
|
+
function createMeshTileset(
|
|
100
|
+
resource: EncodedMeshConversionResource,
|
|
101
|
+
geometricError: number
|
|
102
|
+
): Omit<Tiles3DTilesetJSON, 'root'> & {root: Omit<Tiles3DTileJSON, 'children'>} {
|
|
103
|
+
const reference = resource.spatialReference;
|
|
104
|
+
const outputCrs = reference.targetCrs || reference.sourceCrs;
|
|
105
|
+
const heightReference =
|
|
106
|
+
reference.targetHeightReference === 'native'
|
|
107
|
+
? reference.heightReference
|
|
108
|
+
: reference.targetHeightReference;
|
|
109
|
+
if (
|
|
110
|
+
!['native', 'transformed'].includes(reference.status) ||
|
|
111
|
+
outputCrs !== 'EPSG:4978' ||
|
|
112
|
+
heightReference !== 'ellipsoidal' ||
|
|
113
|
+
(reference.status === 'native' &&
|
|
114
|
+
(reference.coordinateFrame !== 'geocentric' ||
|
|
115
|
+
reference.axisOrder !== 'xyz' ||
|
|
116
|
+
reference.verticalUnitScale !== 1 ||
|
|
117
|
+
reference.units?.some(unit => unit !== 'meter')))
|
|
118
|
+
) {
|
|
119
|
+
throw new TileConversionError(
|
|
120
|
+
'MESH_TILESET_FRAME_UNSUPPORTED',
|
|
121
|
+
'Single-mesh tilesets require resolved EPSG:4978 xyz coordinates in meters with ellipsoidal heights'
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (
|
|
125
|
+
!Number.isFinite(resource.maximumPositionError) ||
|
|
126
|
+
resource.maximumPositionError < 0 ||
|
|
127
|
+
resource.maximumPositionError > geometricError
|
|
128
|
+
) {
|
|
129
|
+
throw new TileConversionError(
|
|
130
|
+
'MESH_GEOMETRIC_ERROR_INVALID',
|
|
131
|
+
'geometricError must include the measured position reconstruction error'
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const origin = [...resource.origin];
|
|
135
|
+
const [minimum, maximum] = resource.localBoundingBox.map(bound => [...bound]);
|
|
136
|
+
if (
|
|
137
|
+
origin.length !== 3 ||
|
|
138
|
+
minimum.length !== 3 ||
|
|
139
|
+
maximum.length !== 3 ||
|
|
140
|
+
[...origin, ...minimum, ...maximum].some(value => !Number.isFinite(value)) ||
|
|
141
|
+
minimum.some((value, axis) => value > maximum[axis])
|
|
142
|
+
) {
|
|
143
|
+
throw new TileConversionError(
|
|
144
|
+
'MESH_TILESET_BOUNDS_INVALID',
|
|
145
|
+
'Origin and local bounds must be finite xyz coordinates with ordered bounds'
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
const center = minimum.map((value, axis) => value / 2 + maximum[axis] / 2);
|
|
149
|
+
const halfSize = minimum.map((value, axis) => maximum[axis] / 2 - value / 2);
|
|
150
|
+
return {
|
|
151
|
+
asset: {version: '1.1'},
|
|
152
|
+
geometricError,
|
|
153
|
+
root: {
|
|
154
|
+
// 3D Tiles applies y-up -> z-up to GLB content before the tile transform.
|
|
155
|
+
transform: [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, ...origin, 1],
|
|
156
|
+
boundingVolume: {
|
|
157
|
+
// Bounds use the same y-up -> z-up rotation before the inverse tile rotation.
|
|
158
|
+
box: [
|
|
159
|
+
center[0],
|
|
160
|
+
-center[2],
|
|
161
|
+
center[1],
|
|
162
|
+
halfSize[0],
|
|
163
|
+
0,
|
|
164
|
+
0,
|
|
165
|
+
0,
|
|
166
|
+
0,
|
|
167
|
+
halfSize[1],
|
|
168
|
+
0,
|
|
169
|
+
-halfSize[2],
|
|
170
|
+
0
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
geometricError,
|
|
174
|
+
refine: 'REPLACE',
|
|
175
|
+
content: {uri: 'mesh.glb'}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Limits for an explicit collection of independent ECEF mesh leaves. */
|
|
181
|
+
export interface MeshTilesetSinkOptions extends SingleMeshTilesetSinkOptions {
|
|
182
|
+
/** Maximum mesh placements, including repeated content at distinct source placements. */
|
|
183
|
+
readonly maxMeshes: number;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Atomic mesh package with a contentless root and independently placed GLB leaves. */
|
|
187
|
+
export interface MeshTilesetSink extends TileConversionSink<EncodedMeshConversionResource> {
|
|
188
|
+
/** Returns deterministically named GLBs and tileset JSON only after finalization. */
|
|
189
|
+
getFiles(): readonly BrowserTileConversionFile[];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Packages independent codec outputs as a bounded 3D Tiles 1.1 leaf collection.
|
|
194
|
+
* Each leaf retains its ECEF placement and encoded bounds; the contentless ADD root encloses
|
|
195
|
+
* all leaves. Its geometric error covers the enclosing diagonal; each leaf retains the
|
|
196
|
+
* supplied error. This authors a flat partial dataset, not the source LOD hierarchy. Callers must
|
|
197
|
+
* exclude overlapping parent/descendant LOD representations and provide a conservative error.
|
|
198
|
+
* Resource IDs identify placements, never output paths. Retained bytes include final JSON;
|
|
199
|
+
* maxMeshes bounds hierarchy metadata, while neither limit bounds peak conversion memory.
|
|
200
|
+
*/
|
|
201
|
+
export function createMeshTilesetSink(options: MeshTilesetSinkOptions): MeshTilesetSink {
|
|
202
|
+
const {geometricError, maxMeshes} = options;
|
|
203
|
+
if (
|
|
204
|
+
!Number.isFinite(geometricError) ||
|
|
205
|
+
geometricError < 0 ||
|
|
206
|
+
!Number.isSafeInteger(maxMeshes) ||
|
|
207
|
+
maxMeshes < 1
|
|
208
|
+
) {
|
|
209
|
+
throw new TileConversionError(
|
|
210
|
+
'MESH_TILESET_OPTIONS_INVALID',
|
|
211
|
+
'A leaf collection requires finite nonnegative geometricError and positive maxMeshes'
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const memory = createBoundedMemoryTileConversionSink(options);
|
|
215
|
+
const children: ReturnType<typeof createMeshTileset>['root'][] = [];
|
|
216
|
+
const identifiers = new Set<string>();
|
|
217
|
+
const minimum = [Infinity, Infinity, Infinity];
|
|
218
|
+
const maximum = [-Infinity, -Infinity, -Infinity];
|
|
219
|
+
let state: 'open' | 'writing' | 'closed' = 'open';
|
|
220
|
+
let completed = false;
|
|
221
|
+
return {
|
|
222
|
+
/** Exposes the complete package atomically. */
|
|
223
|
+
getFiles: () => (completed ? memory.getFiles() : []),
|
|
224
|
+
/** Retains one placement and its corresponding encoded GLB. */
|
|
225
|
+
async write(resource, signal) {
|
|
226
|
+
signal?.throwIfAborted();
|
|
227
|
+
if (state !== 'open')
|
|
228
|
+
throw new TileConversionError(
|
|
229
|
+
'MESH_TILESET_SINK_UNAVAILABLE',
|
|
230
|
+
'The mesh sink is busy or closed'
|
|
231
|
+
);
|
|
232
|
+
if (children.length >= maxMeshes || identifiers.has(resource.id))
|
|
233
|
+
throw new TileConversionError(
|
|
234
|
+
'MESH_TILESET_PLACEMENT_INVALID',
|
|
235
|
+
'Mesh count exceeds maxMeshes or a placement identifier is repeated'
|
|
236
|
+
);
|
|
237
|
+
const tile = createMeshTileset(resource, geometricError).root;
|
|
238
|
+
const lower = resource.localBoundingBox[0].map(
|
|
239
|
+
(value, axis) => value + resource.origin[axis]
|
|
240
|
+
);
|
|
241
|
+
const upper = resource.localBoundingBox[1].map(
|
|
242
|
+
(value, axis) => value + resource.origin[axis]
|
|
243
|
+
);
|
|
244
|
+
if ([...lower, ...upper].some(value => !Number.isFinite(value)))
|
|
245
|
+
throw new TileConversionError(
|
|
246
|
+
'MESH_TILESET_BOUNDS_INVALID',
|
|
247
|
+
'Absolute bounds must be finite'
|
|
248
|
+
);
|
|
249
|
+
const resourceId = `meshes/${children.length}.glb`;
|
|
250
|
+
tile.content = {uri: resourceId};
|
|
251
|
+
state = 'writing';
|
|
252
|
+
await memory.write(
|
|
253
|
+
{resourceId, parts: [resource.glb], contentType: 'model/gltf-binary'},
|
|
254
|
+
signal
|
|
255
|
+
);
|
|
256
|
+
children.push(tile);
|
|
257
|
+
identifiers.add(resource.id);
|
|
258
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
259
|
+
minimum[axis] = Math.min(minimum[axis], lower[axis]);
|
|
260
|
+
maximum[axis] = Math.max(maximum[axis], upper[axis]);
|
|
261
|
+
}
|
|
262
|
+
state = 'open';
|
|
263
|
+
},
|
|
264
|
+
/** Writes the enclosing root only when every leaf has completed. */
|
|
265
|
+
async finalize(report) {
|
|
266
|
+
if (state !== 'open' || !children.length)
|
|
267
|
+
throw new TileConversionError(
|
|
268
|
+
'MESH_TILESET_SINK_INCOMPLETE',
|
|
269
|
+
'Finalization requires written mesh leaves'
|
|
270
|
+
);
|
|
271
|
+
state = 'writing';
|
|
272
|
+
const center = minimum.map((value, axis) => value / 2 + maximum[axis] / 2);
|
|
273
|
+
const halfSize = minimum.map((value, axis) => maximum[axis] / 2 - value / 2);
|
|
274
|
+
// The empty root's error bounds the omitted leaf extent.
|
|
275
|
+
const rootGeometricError = Math.max(geometricError, 2 * Math.hypot(...halfSize));
|
|
276
|
+
if (!Number.isFinite(rootGeometricError))
|
|
277
|
+
throw new TileConversionError(
|
|
278
|
+
'MESH_TILESET_BOUNDS_INVALID',
|
|
279
|
+
'Root bounds must have a finite diagonal'
|
|
280
|
+
);
|
|
281
|
+
const json = JSON.stringify({
|
|
282
|
+
asset: {version: '1.1'},
|
|
283
|
+
geometricError: rootGeometricError,
|
|
284
|
+
root: {
|
|
285
|
+
boundingVolume: {
|
|
286
|
+
box: [
|
|
287
|
+
center[0],
|
|
288
|
+
center[1],
|
|
289
|
+
center[2],
|
|
290
|
+
halfSize[0],
|
|
291
|
+
0,
|
|
292
|
+
0,
|
|
293
|
+
0,
|
|
294
|
+
halfSize[1],
|
|
295
|
+
0,
|
|
296
|
+
0,
|
|
297
|
+
0,
|
|
298
|
+
halfSize[2]
|
|
299
|
+
]
|
|
300
|
+
},
|
|
301
|
+
geometricError: rootGeometricError,
|
|
302
|
+
refine: 'ADD',
|
|
303
|
+
children
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
await memory.write({
|
|
307
|
+
resourceId: 'tileset.json',
|
|
308
|
+
parts: [json],
|
|
309
|
+
contentType: 'application/json'
|
|
310
|
+
});
|
|
311
|
+
await memory.finalize(report);
|
|
312
|
+
state = 'closed';
|
|
313
|
+
completed = true;
|
|
314
|
+
},
|
|
315
|
+
/** Discards files and hierarchy metadata on failure or cancellation. */
|
|
316
|
+
async abort(error) {
|
|
317
|
+
state = 'closed';
|
|
318
|
+
completed = false;
|
|
319
|
+
children.length = 0;
|
|
320
|
+
identifiers.clear();
|
|
321
|
+
await memory.abort(error);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|