@loaders.gl/tile-converter 5.0.0-alpha.12 → 5.0.0-alpha.14
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 +1 -1
- package/README.md +4 -4
- package/dist/converter.min.cjs +149 -158
- package/dist/i3s-server/bin/i3s-server.min.cjs +113 -158
- package/dist/index.cjs +17486 -17479
- package/dist/index.js +25572 -25560
- package/dist/v5/adapters.cjs +540 -190
- package/dist/v5/adapters.d.ts +4 -0
- package/dist/v5/adapters.js +501 -151
- package/dist/v5/archive-source.d.ts +1 -1
- package/dist/v5/browser.d.ts +1 -1
- package/dist/v5/conversion-api.d.ts +25 -0
- package/dist/v5/converter-cli.js +23 -7
- package/dist/v5/core.cjs +76 -17
- package/dist/v5/core.d.ts +1 -1
- package/dist/v5/core.js +69 -10
- package/dist/v5/feature-arrow.d.ts +1 -1
- package/dist/v5/i3s-mesh-conversion.d.ts +1 -1
- package/dist/v5/i3s-mesh-source-features.d.ts +2 -2
- package/dist/v5/i3s-mesh-source-material.d.ts +1 -1
- package/dist/v5/i3s-mesh-source.d.ts +2 -2
- package/dist/v5/i3s-selection-source.d.ts +3 -3
- package/dist/v5/index.cjs +11 -6
- package/dist/v5/index.d.ts +5 -1
- package/dist/v5/index.js +8 -0
- package/dist/v5/mesh-conversion.d.ts +3 -3
- package/dist/v5/mesh-draco.d.ts +2 -2
- package/dist/v5/mesh-features.d.ts +2 -2
- package/dist/v5/mesh-source-features.d.ts +4 -4
- package/dist/v5/mesh-source-schema.d.ts +2 -2
- package/dist/v5/mesh-source-texture.d.ts +3 -3
- package/dist/v5/mesh-source.d.ts +5 -5
- package/dist/v5/mesh-tileset-sink.d.ts +1 -1
- package/dist/v5/mesh.d.ts +1 -1
- package/dist/v5/node-conversion.d.ts +2 -0
- package/dist/v5/node-i3s-source.d.ts +3 -3
- package/dist/v5/node-source.d.ts +1 -1
- package/dist/v5/node.cjs +19 -6
- package/dist/v5/node.js +19 -6
- package/dist/v5/point-cloud-attributes.d.ts +19 -0
- package/dist/v5/point-cloud-conversion.d.ts +1 -1
- package/dist/v5/point-cloud-copc.d.ts +44 -0
- package/dist/v5/point-cloud-file.d.ts +4 -4
- package/dist/v5/point-cloud-potree.d.ts +3 -3
- package/dist/v5/point-cloud-source-encoder.d.ts +1 -1
- package/dist/v5/point-cloud-source.d.ts +1 -1
- package/dist/v5/point-cloud-tileset.d.ts +3 -3
- package/dist/v5/point-cloud.d.ts +1 -1
- package/dist/v5/spatial-conversion.d.ts +1 -1
- package/dist/v5/tileset-source.d.ts +1 -1
- package/package.json +17 -32
- package/src/v4/3d-tiles-converter/3d-tiles-converter.ts +11 -12
- package/src/v4/3d-tiles-converter/helpers/3d-tiles-content-converter.ts +7 -3
- package/src/v4/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.ts +2 -2
- package/src/v4/3d-tiles-converter/helpers/load-i3s.ts +3 -3
- package/src/v4/3d-tiles-converter/helpers/texture-atlas.ts +1 -1
- package/src/v4/3d-tiles-converter/helpers/validate-i3s-spatial-reference.ts +2 -2
- package/src/v4/3d-tiles-converter/json-templates/tileset.ts +58 -39
- package/src/v4/converter-cli.ts +3 -3
- package/src/v4/deps-installer/deps-installer.ts +3 -3
- package/src/v4/i3s-converter/helpers/attribute-metadata-info.ts +1 -1
- package/src/v4/i3s-converter/helpers/batch-ids-extensions.ts +9 -10
- package/src/v4/i3s-converter/helpers/conversion-gltf.ts +108 -0
- package/src/v4/i3s-converter/helpers/coordinate-converter.ts +6 -5
- package/src/v4/i3s-converter/helpers/create-scene-server-path.ts +5 -6
- package/src/v4/i3s-converter/helpers/feature-attributes.ts +4 -4
- package/src/v4/i3s-converter/helpers/geometry-attributes.ts +1 -1
- package/src/v4/i3s-converter/helpers/geometry-converter.ts +69 -77
- package/src/v4/i3s-converter/helpers/gltf-attributes.ts +58 -74
- package/src/v4/i3s-converter/helpers/load-3d-tiles.ts +5 -5
- package/src/v4/i3s-converter/helpers/node-debug.ts +7 -3
- package/src/v4/i3s-converter/helpers/node-index-document.ts +6 -7
- package/src/v4/i3s-converter/helpers/node-pages.ts +3 -4
- package/src/v4/i3s-converter/helpers/preprocess-3d-tiles.ts +1 -1
- package/src/v4/i3s-converter/helpers/tileset-traversal.ts +1 -1
- package/src/v4/i3s-converter/i3s-converter.ts +29 -31
- package/src/v4/i3s-converter/json-templates/geometry-definitions.ts +71 -66
- package/src/v4/i3s-converter/json-templates/layers.ts +97 -132
- package/src/v4/i3s-converter/json-templates/metadata.ts +31 -21
- package/src/v4/i3s-converter/json-templates/node.ts +60 -74
- package/src/v4/i3s-converter/json-templates/scene-server.ts +36 -27
- package/src/v4/i3s-converter/json-templates/shared-resources.ts +62 -123
- package/src/v4/i3s-converter/json-templates/store.ts +39 -58
- package/src/v4/i3s-converter/types.ts +4 -4
- package/src/v4/i3s-server/controllers/slpk-controller.ts +3 -3
- package/src/v4/i3s-server/utils/create-scene-server.ts +3 -3
- package/src/v4/lib/utils/builder-input.ts +8 -0
- package/src/v4/lib/utils/conversion-dump.ts +5 -1
- package/src/v4/lib/utils/file-utils.ts +1 -1
- package/src/v4/lib/utils/hash-string.ts +21 -0
- package/src/v4/lib/utils/lod-conversion-utils.ts +3 -3
- package/src/v4/pgm-loader.ts +2 -2
- package/src/v5/adapters.ts +5 -0
- package/src/v5/archive-source.ts +4 -4
- package/src/v5/browser.ts +1 -0
- package/src/v5/conversion-api.ts +115 -8
- package/src/v5/conversion-archive.ts +2 -2
- package/src/v5/core.ts +1 -0
- package/src/v5/feature-arrow.ts +2 -2
- package/src/v5/i3s-mesh-conversion.ts +2 -2
- package/src/v5/i3s-mesh-source-features.ts +3 -3
- package/src/v5/i3s-mesh-source-material.ts +18 -11
- package/src/v5/i3s-mesh-source.ts +2 -2
- package/src/v5/i3s-selection-source.ts +4 -4
- package/src/v5/index.ts +15 -0
- package/src/v5/mesh-conversion.ts +4 -4
- package/src/v5/mesh-draco.ts +2 -2
- package/src/v5/mesh-features.ts +3 -3
- package/src/v5/mesh-hierarchy.ts +1 -1
- package/src/v5/mesh-source-features.ts +137 -55
- package/src/v5/mesh-source-schema.ts +10 -4
- package/src/v5/mesh-source-texture.ts +49 -19
- package/src/v5/mesh-source.ts +65 -46
- package/src/v5/mesh-tileset-archive.ts +1 -1
- package/src/v5/mesh-tileset-sink.ts +1 -1
- package/src/v5/mesh.ts +1 -1
- package/src/v5/node-cli.ts +4 -0
- package/src/v5/node-conversion.ts +19 -2
- package/src/v5/node-source.ts +2 -2
- package/src/v5/point-cloud-attributes.ts +113 -0
- package/src/v5/point-cloud-conversion.ts +1 -1
- package/src/v5/point-cloud-copc.ts +289 -0
- package/src/v5/point-cloud-file.ts +8 -8
- package/src/v5/point-cloud-potree.ts +8 -4
- package/src/v5/point-cloud-source-encoder.ts +1 -1
- package/src/v5/point-cloud-source.ts +1 -1
- package/src/v5/point-cloud-spatial.ts +2 -2
- package/src/v5/point-cloud-tileset.ts +4 -4
- package/src/v5/point-cloud.ts +3 -3
- package/src/v5/selected-mesh-source.ts +4 -4
- package/src/v5/spatial-conversion.ts +2 -2
- package/src/v5/tileset-source.ts +2 -2
|
@@ -2,15 +2,29 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {MeshGeometry} from '@loaders.gl/schema';
|
|
6
|
-
import type {
|
|
7
|
-
|
|
5
|
+
import type {MeshGeometry} from '@loaders.gl/core/schema';
|
|
6
|
+
import type {
|
|
7
|
+
GLTFWithBuffers,
|
|
8
|
+
GLTFMeshPrimitive,
|
|
9
|
+
GLTF_EXT_mesh_features,
|
|
10
|
+
GLTF_EXT_structural_metadata_GLTF
|
|
11
|
+
} from '@loaders.gl/gltf';
|
|
12
|
+
import {getGLTFAccessor, getGLTFAccessorValue} from '@loaders.gl/gltf';
|
|
13
|
+
import type {I3SMeshFeatures} from '@loaders.gl/tiles-3d/i3s';
|
|
8
14
|
import {convertFeatureAttributesToArrowBatches} from './feature-arrow.js';
|
|
9
15
|
import type {FeatureArrowBatchOptions} from './feature-arrow.js';
|
|
10
16
|
import {createMeshSourceFeatureOptions} from './mesh-source-schema.js';
|
|
11
17
|
import type {MeshSourceSchemaOptions} from './mesh-source-schema.js';
|
|
12
18
|
import {TileConversionError} from '@loaders.gl/tile-converter/v5/core';
|
|
13
19
|
|
|
20
|
+
/** Data-only rule for absent columns and decoded noData values. */
|
|
21
|
+
interface MeshSourcePropertyMapping {
|
|
22
|
+
/** Value used for an absent column or a decoded noData cell. */
|
|
23
|
+
readonly defaultValue: unknown;
|
|
24
|
+
/** Decoded sentinel; omitted for absent-column defaults. */
|
|
25
|
+
readonly noData?: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
/** Explicit source feature mapping; target type changes must be authorized separately. */
|
|
15
29
|
export interface MeshSourceFeatureOptions extends FeatureArrowBatchOptions {
|
|
16
30
|
/** Batch-table class label, or the exact structural-metadata class to select. */
|
|
@@ -23,8 +37,8 @@ export interface MeshSourceFeatureOptions extends FeatureArrowBatchOptions {
|
|
|
23
37
|
|
|
24
38
|
/** Extracts legacy batch tables or one decoded modern property table and validates geometry ownership. */
|
|
25
39
|
export function extractMeshFeatures(
|
|
26
|
-
gltf:
|
|
27
|
-
primitive:
|
|
40
|
+
gltf: GLTFWithBuffers,
|
|
41
|
+
primitive: GLTFMeshPrimitive,
|
|
28
42
|
payload: {
|
|
29
43
|
batchTableJson?: Record<string, unknown>;
|
|
30
44
|
header?: {batchLength?: number};
|
|
@@ -33,12 +47,16 @@ export function extractMeshFeatures(
|
|
|
33
47
|
mesh: MeshGeometry,
|
|
34
48
|
options?: MeshSourceFeatureOptions | MeshSourceSchemaOptions
|
|
35
49
|
): I3SMeshFeatures | undefined {
|
|
36
|
-
const declarations =
|
|
37
|
-
|
|
50
|
+
const declarations = (
|
|
51
|
+
primitive.extensions?.EXT_mesh_features as GLTF_EXT_mesh_features | undefined
|
|
52
|
+
)?.featureIds;
|
|
53
|
+
const metadata = gltf.json.extensions?.EXT_structural_metadata as
|
|
54
|
+
| GLTF_EXT_structural_metadata_GLTF
|
|
55
|
+
| undefined;
|
|
38
56
|
const legacy = primitive.attributes._BATCHID;
|
|
39
57
|
if (
|
|
40
58
|
!declarations &&
|
|
41
|
-
|
|
59
|
+
legacy === undefined &&
|
|
42
60
|
!metadata &&
|
|
43
61
|
!Object.keys(payload.batchTableJson || {}).length &&
|
|
44
62
|
!payload.header?.batchLength
|
|
@@ -60,13 +78,15 @@ export function extractMeshFeatures(
|
|
|
60
78
|
'MESH_FEATURE_CLASS_REQUIRED',
|
|
61
79
|
'Feature metadata class must be explicit'
|
|
62
80
|
);
|
|
63
|
-
const mapping =
|
|
81
|
+
const mapping =
|
|
82
|
+
'schema' in options ? options : createMeshSourceFeatureOptions(gltf.json, options);
|
|
83
|
+
const propertyMappings = new Map<string, MeshSourcePropertyMapping>();
|
|
64
84
|
let count: number;
|
|
65
|
-
let
|
|
85
|
+
let attributeReference = legacy;
|
|
66
86
|
let properties: Record<string, ArrayLike<unknown>>;
|
|
67
87
|
if (declarations) {
|
|
68
88
|
if (
|
|
69
|
-
legacy ||
|
|
89
|
+
legacy !== undefined ||
|
|
70
90
|
declarations.length !== 1 ||
|
|
71
91
|
metadata?.propertyTables?.length !== 1 ||
|
|
72
92
|
metadata.propertyTextures?.length ||
|
|
@@ -78,6 +98,7 @@ export function extractMeshFeatures(
|
|
|
78
98
|
);
|
|
79
99
|
const declaration = declarations[0];
|
|
80
100
|
if (
|
|
101
|
+
typeof declaration.attribute !== 'number' ||
|
|
81
102
|
!Number.isInteger(declaration.attribute) ||
|
|
82
103
|
declaration.attribute < 0 ||
|
|
83
104
|
declaration.propertyTable !== 0 ||
|
|
@@ -89,26 +110,27 @@ export function extractMeshFeatures(
|
|
|
89
110
|
'MESH_FEATURE_MAPPING_UNSUPPORTED',
|
|
90
111
|
'Features require an attribute and property table 0 without null/texture/implicit IDs'
|
|
91
112
|
);
|
|
92
|
-
|
|
113
|
+
attributeReference = primitive.attributes[`_FEATURE_ID_${declaration.attribute}`];
|
|
93
114
|
const table = metadata.propertyTables[0];
|
|
94
|
-
if (
|
|
115
|
+
if (
|
|
116
|
+
table.class !== options.metadataClass ||
|
|
117
|
+
!Number.isSafeInteger(declaration.featureCount) ||
|
|
118
|
+
declaration.featureCount <= 0 ||
|
|
119
|
+
declaration.featureCount > table.count
|
|
120
|
+
)
|
|
95
121
|
throw new TileConversionError(
|
|
96
122
|
'MESH_FEATURE_CLASS_MISMATCH',
|
|
97
|
-
'Feature class and declared
|
|
123
|
+
'Feature class must agree and declared feature count must fit the property table'
|
|
98
124
|
);
|
|
99
125
|
count = table.count;
|
|
100
|
-
//
|
|
101
|
-
if (
|
|
102
|
-
!Number.isSafeInteger(count) ||
|
|
103
|
-
count <= 0 ||
|
|
104
|
-
count > mesh.attributes.POSITION.value.length / 3
|
|
105
|
-
) {
|
|
126
|
+
// Source tables may be shared by primitives; dense output indices are uint32.
|
|
127
|
+
if (!Number.isSafeInteger(count) || count <= 0 || count > 0xffffffff) {
|
|
106
128
|
throw new TileConversionError(
|
|
107
129
|
'MESH_FEATURE_IDS_INVALID',
|
|
108
|
-
'Feature row count
|
|
130
|
+
'Feature row count must fit uint32 indices'
|
|
109
131
|
);
|
|
110
132
|
}
|
|
111
|
-
properties =
|
|
133
|
+
properties = Object.create(null);
|
|
112
134
|
const classProperties = metadata.schema?.classes?.[table.class]?.properties;
|
|
113
135
|
if (!classProperties)
|
|
114
136
|
throw new TileConversionError(
|
|
@@ -179,9 +201,10 @@ export function extractMeshFeatures(
|
|
|
179
201
|
integer64 && property.default !== undefined
|
|
180
202
|
? BigInt(property.default as number)
|
|
181
203
|
: (property.default ?? null);
|
|
182
|
-
const decoded = data
|
|
204
|
+
const decoded = data;
|
|
183
205
|
if (property.noData === undefined || data === undefined) {
|
|
184
|
-
properties[name] = decoded
|
|
206
|
+
if (decoded !== undefined) properties[name] = decoded as ArrayLike<unknown>;
|
|
207
|
+
else propertyMappings.set(name, {defaultValue});
|
|
185
208
|
continue;
|
|
186
209
|
}
|
|
187
210
|
const sentinel = property.noData;
|
|
@@ -200,9 +223,9 @@ export function extractMeshFeatures(
|
|
|
200
223
|
`Property ${name} requires a valid decoded noData mapping`
|
|
201
224
|
);
|
|
202
225
|
const comparable = integer64 ? BigInt(sentinel as number) : sentinel;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
);
|
|
226
|
+
const values = decoded as ArrayLike<unknown>;
|
|
227
|
+
properties[name] = values;
|
|
228
|
+
propertyMappings.set(name, {noData: comparable, defaultValue});
|
|
206
229
|
}
|
|
207
230
|
} else {
|
|
208
231
|
if (metadata)
|
|
@@ -213,6 +236,15 @@ export function extractMeshFeatures(
|
|
|
213
236
|
count = payload.header?.batchLength ?? Number(payload.featureTableJson?.BATCH_LENGTH);
|
|
214
237
|
properties = (payload.batchTableJson as Record<string, ArrayLike<unknown>>) || {};
|
|
215
238
|
}
|
|
239
|
+
const accessor =
|
|
240
|
+
attributeReference === undefined ? undefined : getGLTFAccessor(gltf, attributeReference);
|
|
241
|
+
const attribute = accessor
|
|
242
|
+
? {
|
|
243
|
+
...accessor,
|
|
244
|
+
components: accessor.type === 'SCALAR' ? 1 : Number(accessor.type.slice(3)),
|
|
245
|
+
value: getGLTFAccessorValue(gltf, accessor)
|
|
246
|
+
}
|
|
247
|
+
: undefined;
|
|
216
248
|
const vertexCount = mesh.attributes.POSITION.value.length / 3;
|
|
217
249
|
if (
|
|
218
250
|
!Number.isSafeInteger(count) ||
|
|
@@ -220,15 +252,26 @@ export function extractMeshFeatures(
|
|
|
220
252
|
!attribute ||
|
|
221
253
|
attribute.components !== 1 ||
|
|
222
254
|
attribute.normalized ||
|
|
223
|
-
attribute.value.length !== vertexCount
|
|
224
|
-
Array.from(attribute.value as ArrayLike<number>).some(
|
|
225
|
-
value => !Number.isSafeInteger(value) || value < 0 || value >= count
|
|
226
|
-
)
|
|
255
|
+
attribute.value.length !== vertexCount
|
|
227
256
|
)
|
|
228
257
|
throw new TileConversionError(
|
|
229
258
|
'MESH_FEATURE_IDS_INVALID',
|
|
230
259
|
'Feature IDs must be one valid property-table row index per vertex'
|
|
231
260
|
);
|
|
261
|
+
const vertexRows = declarations ? new Set<number>() : undefined;
|
|
262
|
+
for (const value of attribute.value) {
|
|
263
|
+
if (!Number.isSafeInteger(value) || value < 0 || value >= count)
|
|
264
|
+
throw new TileConversionError(
|
|
265
|
+
'MESH_FEATURE_IDS_INVALID',
|
|
266
|
+
'Feature IDs must reference valid property-table rows'
|
|
267
|
+
);
|
|
268
|
+
vertexRows?.add(Number(value));
|
|
269
|
+
}
|
|
270
|
+
if (declarations && vertexRows!.size !== declarations[0].featureCount)
|
|
271
|
+
throw new TileConversionError(
|
|
272
|
+
'MESH_FEATURE_IDS_INVALID',
|
|
273
|
+
'Declared feature count must match distinct vertex feature IDs'
|
|
274
|
+
);
|
|
232
275
|
const selectedAttribute = declarations ? `_FEATURE_ID_${declarations[0].attribute}` : '_BATCHID';
|
|
233
276
|
if (
|
|
234
277
|
Object.keys(primitive.attributes).some(
|
|
@@ -247,45 +290,84 @@ export function extractMeshFeatures(
|
|
|
247
290
|
)
|
|
248
291
|
throw new TileConversionError(
|
|
249
292
|
'MESH_FEATURE_PROPERTY_UNAVAILABLE',
|
|
250
|
-
`Property ${name} must be a decoded column matching the
|
|
293
|
+
`Property ${name} must be a decoded column matching the feature row count`
|
|
251
294
|
);
|
|
252
295
|
}
|
|
253
296
|
const sourceIdentifier = options.sourceFeatureIdProperty;
|
|
254
|
-
if (sourceIdentifier && !properties[sourceIdentifier])
|
|
297
|
+
if (sourceIdentifier && !properties[sourceIdentifier] && !propertyMappings.has(sourceIdentifier))
|
|
255
298
|
throw new TileConversionError(
|
|
256
299
|
'MESH_FEATURE_ID_PROPERTY_REQUIRED',
|
|
257
300
|
'Stable source identifier property is missing'
|
|
258
301
|
);
|
|
259
|
-
const batches = convertFeatureAttributesToArrowBatches(
|
|
260
|
-
Array.from({length: count}, (_, index) => ({
|
|
261
|
-
featureId: (sourceIdentifier ? properties[sourceIdentifier][index] : index) as
|
|
262
|
-
| string
|
|
263
|
-
| number
|
|
264
|
-
| bigint,
|
|
265
|
-
metadataClass: options.metadataClass,
|
|
266
|
-
properties: Object.fromEntries(
|
|
267
|
-
Object.entries(properties)
|
|
268
|
-
.filter(([name]) => name !== sourceIdentifier)
|
|
269
|
-
.map(([name, values]) => [name, values[index]])
|
|
270
|
-
)
|
|
271
|
-
})),
|
|
272
|
-
mapping
|
|
273
|
-
);
|
|
274
302
|
const indices = mesh.indices?.value;
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
303
|
+
const cornerCount = indices?.length ?? vertexCount;
|
|
304
|
+
if (cornerCount % 3)
|
|
305
|
+
throw new TileConversionError(
|
|
306
|
+
'MESH_FEATURE_IDS_INVALID',
|
|
307
|
+
'Triangle geometry requires complete triples'
|
|
279
308
|
);
|
|
280
|
-
|
|
309
|
+
const triangleFeatureIndices = new Uint32Array(cornerCount / 3);
|
|
310
|
+
const referencedRows = new Set<number>();
|
|
311
|
+
/** Reads a validated triangle corner without allocating per-triangle arrays. */
|
|
312
|
+
const readFeatureRow = (corner: number): number => {
|
|
313
|
+
if (!Number.isSafeInteger(corner) || corner < 0 || corner >= vertexCount)
|
|
314
|
+
throw new TileConversionError(
|
|
315
|
+
'MESH_FEATURE_IDS_INVALID',
|
|
316
|
+
'Triangle indices must reference valid mesh vertices'
|
|
317
|
+
);
|
|
318
|
+
return Number(attribute.value[corner]);
|
|
319
|
+
};
|
|
320
|
+
for (let triangle = 0; triangle < triangleFeatureIndices.length; triangle++) {
|
|
321
|
+
const firstCorner = indices?.[triangle * 3] ?? triangle * 3;
|
|
322
|
+
const secondCorner = indices?.[triangle * 3 + 1] ?? triangle * 3 + 1;
|
|
323
|
+
const thirdCorner = indices?.[triangle * 3 + 2] ?? triangle * 3 + 2;
|
|
324
|
+
const firstRow = readFeatureRow(firstCorner);
|
|
325
|
+
const secondRow = readFeatureRow(secondCorner);
|
|
326
|
+
const thirdRow = readFeatureRow(thirdCorner);
|
|
281
327
|
// Repeated-index strip connectors have no surface; retain the first corner's feature row.
|
|
282
|
-
if (
|
|
328
|
+
if (
|
|
329
|
+
(firstRow !== secondRow || firstRow !== thirdRow) &&
|
|
330
|
+
firstCorner !== secondCorner &&
|
|
331
|
+
firstCorner !== thirdCorner &&
|
|
332
|
+
secondCorner !== thirdCorner
|
|
333
|
+
)
|
|
283
334
|
throw new TileConversionError(
|
|
284
335
|
'MESH_FEATURE_TRIANGLE_MIXED',
|
|
285
336
|
'All vertices of a nondegenerate triangle must reference the same feature row'
|
|
286
337
|
);
|
|
287
|
-
triangleFeatureIndices[triangle] =
|
|
338
|
+
triangleFeatureIndices[triangle] = firstRow;
|
|
339
|
+
referencedRows.add(firstRow);
|
|
288
340
|
}
|
|
341
|
+
// Sort once to retain source table order, then materialize only geometry-owned rows.
|
|
342
|
+
const selectedRows = [...referencedRows].sort((left, right) => left - right);
|
|
343
|
+
const denseRows = new Map(selectedRows.map((row, index) => [row, index]));
|
|
344
|
+
for (let triangle = 0; triangle < triangleFeatureIndices.length; triangle++) {
|
|
345
|
+
triangleFeatureIndices[triangle] = denseRows.get(triangleFeatureIndices[triangle])!;
|
|
346
|
+
}
|
|
347
|
+
/** Reads one referenced source value without expanding defaults or noData columns. */
|
|
348
|
+
const readProperty = (name: string, row: number): unknown => {
|
|
349
|
+
const values = properties[name];
|
|
350
|
+
const propertyMapping = propertyMappings.get(name);
|
|
351
|
+
if (!values) return propertyMapping?.defaultValue;
|
|
352
|
+
const value = values[row];
|
|
353
|
+
return propertyMapping && value === propertyMapping.noData
|
|
354
|
+
? propertyMapping.defaultValue
|
|
355
|
+
: value;
|
|
356
|
+
};
|
|
357
|
+
const propertyNames = [
|
|
358
|
+
...new Set([...Object.keys(properties), ...propertyMappings.keys()])
|
|
359
|
+
].filter(name => name !== sourceIdentifier);
|
|
360
|
+
const batches = convertFeatureAttributesToArrowBatches(
|
|
361
|
+
selectedRows.map(row => ({
|
|
362
|
+
featureId: (sourceIdentifier ? readProperty(sourceIdentifier, row) : row) as
|
|
363
|
+
| string
|
|
364
|
+
| number
|
|
365
|
+
| bigint,
|
|
366
|
+
metadataClass: options.metadataClass,
|
|
367
|
+
properties: Object.fromEntries(propertyNames.map(name => [name, readProperty(name, row)]))
|
|
368
|
+
})),
|
|
369
|
+
mapping
|
|
370
|
+
);
|
|
289
371
|
return {
|
|
290
372
|
batches,
|
|
291
373
|
triangleFeatureIndices,
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
|
|
5
|
+
import type {
|
|
6
|
+
GLTF,
|
|
7
|
+
GLTF_EXT_structural_metadata_ClassProperty,
|
|
8
|
+
GLTF_EXT_structural_metadata_GLTF
|
|
9
|
+
} from '@loaders.gl/gltf';
|
|
10
|
+
import type {DataType, Field} from '@loaders.gl/core/schema';
|
|
7
11
|
import {TileConversionError} from '@loaders.gl/tile-converter/v5/core';
|
|
8
12
|
import type {MeshSourceFeatureOptions} from './mesh-source-features.js';
|
|
9
13
|
|
|
@@ -32,10 +36,12 @@ const SCALAR_TYPES: Readonly<Record<string, DataType>> = {
|
|
|
32
36
|
* This does not authorize destination type changes or loss of source class semantics.
|
|
33
37
|
*/
|
|
34
38
|
export function createMeshSourceFeatureOptions(
|
|
35
|
-
gltf:
|
|
39
|
+
gltf: GLTF,
|
|
36
40
|
options: MeshSourceSchemaOptions
|
|
37
41
|
): MeshSourceFeatureOptions {
|
|
38
|
-
const metadata = gltf.extensions?.EXT_structural_metadata
|
|
42
|
+
const metadata = gltf.extensions?.EXT_structural_metadata as
|
|
43
|
+
| GLTF_EXT_structural_metadata_GLTF
|
|
44
|
+
| undefined;
|
|
39
45
|
const table = metadata?.propertyTables?.[0];
|
|
40
46
|
const properties = metadata?.schema?.classes?.[options.metadataClass]?.properties;
|
|
41
47
|
if (
|
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {getBinaryImageMetadata} from '@loaders.gl/images';
|
|
6
|
-
import type {
|
|
6
|
+
import type {GLTFMaterial, GLTFImage, GLTFWithBuffers} from '@loaders.gl/gltf';
|
|
7
|
+
import {GLTFScenegraph} from '@loaders.gl/gltf';
|
|
7
8
|
import {TileConversionError} from '@loaders.gl/tile-converter/v5/core';
|
|
8
9
|
import type {MeshTileSampler, MeshTileTexture, MeshTileTextureTransform} from './mesh.js';
|
|
9
10
|
|
|
10
11
|
/** Maps a buffer-view or base64 PNG/JPEG image, sampling and UV transform without decoding pixels. */
|
|
11
12
|
export function mapMeshSourceTexture(
|
|
12
|
-
|
|
13
|
+
gltf: GLTFWithBuffers,
|
|
14
|
+
textureInfo: NonNullable<GLTFMaterial['pbrMetallicRoughness']>['baseColorTexture']
|
|
13
15
|
): MeshTileTexture {
|
|
14
|
-
const texture = textureInfo?.
|
|
15
|
-
const image = texture?.source;
|
|
16
|
-
const sampler =
|
|
16
|
+
const texture = textureInfo ? gltf.json.textures?.[textureInfo.index] : undefined;
|
|
17
|
+
const image = texture?.source === undefined ? undefined : gltf.json.images?.[texture.source];
|
|
18
|
+
const sampler =
|
|
19
|
+
texture?.sampler === undefined ? undefined : gltf.json.samplers?.[texture.sampler];
|
|
17
20
|
const extensions = textureInfo?.extensions || {};
|
|
18
21
|
const hasTransform = Object.hasOwn(extensions, 'KHR_texture_transform');
|
|
19
22
|
const transform = extensions.KHR_texture_transform;
|
|
@@ -43,8 +46,9 @@ export function mapMeshSourceTexture(
|
|
|
43
46
|
(image.uri !== undefined && image.bufferView !== undefined) ||
|
|
44
47
|
Object.keys(image.extensions || {}).length ||
|
|
45
48
|
(image.uri === undefined &&
|
|
46
|
-
(
|
|
47
|
-
|
|
49
|
+
(image.bufferView === undefined ||
|
|
50
|
+
!gltf.json.bufferViews?.[image.bufferView] ||
|
|
51
|
+
Object.keys(gltf.json.bufferViews?.[image.bufferView!]?.extensions || {}).length ||
|
|
48
52
|
!['image/png', 'image/jpeg'].includes(image.mimeType || ''))) ||
|
|
49
53
|
(sampler &&
|
|
50
54
|
(Object.keys(sampler.extensions || {}).length ||
|
|
@@ -77,7 +81,7 @@ export function mapMeshSourceTexture(
|
|
|
77
81
|
} as MeshTileSampler)
|
|
78
82
|
: undefined;
|
|
79
83
|
return {
|
|
80
|
-
...mapMeshSourceImage(image),
|
|
84
|
+
...mapMeshSourceImage(gltf, image),
|
|
81
85
|
...(hasTransform
|
|
82
86
|
? {
|
|
83
87
|
transform: {
|
|
@@ -95,10 +99,14 @@ export function mapMeshSourceTexture(
|
|
|
95
99
|
|
|
96
100
|
/** Reads self-contained encoded bytes without fetch, pixel decoding, or Node APIs. */
|
|
97
101
|
function mapMeshSourceImage(
|
|
98
|
-
|
|
102
|
+
gltf: GLTFWithBuffers,
|
|
103
|
+
image: GLTFImage
|
|
99
104
|
): Pick<MeshTileTexture, 'data' | 'mimeType'> {
|
|
100
105
|
if (image.uri === undefined) {
|
|
101
|
-
return {
|
|
106
|
+
return {
|
|
107
|
+
data: new GLTFScenegraph(gltf).getTypedArrayForBufferView(image.bufferView!),
|
|
108
|
+
mimeType: image.mimeType as MeshTileTexture['mimeType']
|
|
109
|
+
};
|
|
102
110
|
}
|
|
103
111
|
const match =
|
|
104
112
|
typeof image.uri === 'string'
|
|
@@ -124,16 +132,18 @@ function mapMeshSourceImage(
|
|
|
124
132
|
|
|
125
133
|
/** Resolves one external encoded image through an explicitly supplied application reader. */
|
|
126
134
|
export async function resolveMeshSourceTexture(
|
|
127
|
-
|
|
135
|
+
gltf: GLTFWithBuffers,
|
|
136
|
+
textureInfo: NonNullable<GLTFMaterial['pbrMetallicRoughness']>['baseColorTexture'],
|
|
128
137
|
readExternalResource:
|
|
129
138
|
| ((uri: string, contentUri: string | undefined, signal?: AbortSignal) => Promise<Uint8Array>)
|
|
130
139
|
| undefined,
|
|
131
140
|
contentUri: string | undefined,
|
|
132
141
|
signal?: AbortSignal
|
|
133
142
|
): Promise<MeshTileTexture> {
|
|
134
|
-
const
|
|
143
|
+
const texture = textureInfo ? gltf.json.textures?.[textureInfo.index] : undefined;
|
|
144
|
+
const image = texture?.source === undefined ? undefined : gltf.json.images?.[texture.source];
|
|
135
145
|
if (!image || image.uri === undefined || image.uri.startsWith('data:'))
|
|
136
|
-
return mapMeshSourceTexture(textureInfo);
|
|
146
|
+
return mapMeshSourceTexture(gltf, textureInfo);
|
|
137
147
|
if (!readExternalResource || image.bufferView !== undefined)
|
|
138
148
|
throw new TileConversionError(
|
|
139
149
|
'MESH_SOURCE_TEXTURE_UNSUPPORTED',
|
|
@@ -158,12 +168,32 @@ export async function resolveMeshSourceTexture(
|
|
|
158
168
|
'External images require matching PNG/JPEG headers'
|
|
159
169
|
);
|
|
160
170
|
// Select encoded bytes without decoding pixels or modifying the caller-owned glTF image.
|
|
161
|
-
const
|
|
162
|
-
...
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
const resolvedImage = {
|
|
172
|
+
...image,
|
|
173
|
+
uri: undefined,
|
|
174
|
+
mimeType: metadata.mimeType,
|
|
175
|
+
bufferView: (gltf.json.bufferViews || []).length
|
|
176
|
+
};
|
|
177
|
+
const resolved: GLTFWithBuffers = {
|
|
178
|
+
...gltf,
|
|
179
|
+
buffers: [
|
|
180
|
+
...gltf.buffers,
|
|
181
|
+
{
|
|
182
|
+
arrayBuffer: data.buffer as ArrayBuffer,
|
|
183
|
+
byteOffset: data.byteOffset,
|
|
184
|
+
byteLength: data.byteLength
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
json: {
|
|
188
|
+
...gltf.json,
|
|
189
|
+
images: gltf.json.images!.map((entry, index) =>
|
|
190
|
+
index === texture!.source ? resolvedImage : entry
|
|
191
|
+
),
|
|
192
|
+
bufferViews: [
|
|
193
|
+
...(gltf.json.bufferViews || []),
|
|
194
|
+
{buffer: gltf.buffers.length, byteLength: data.byteLength}
|
|
195
|
+
]
|
|
166
196
|
}
|
|
167
197
|
};
|
|
168
|
-
return mapMeshSourceTexture(resolved
|
|
198
|
+
return mapMeshSourceTexture(resolved, textureInfo);
|
|
169
199
|
}
|