@loaders.gl/gltf 4.0.0-alpha.22 → 4.0.0-alpha.24

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.
Files changed (100) hide show
  1. package/dist/dist.min.js +3626 -3028
  2. package/dist/es5/index.js +21 -0
  3. package/dist/es5/index.js.map +1 -1
  4. package/dist/es5/lib/api/gltf-extensions.js +3 -1
  5. package/dist/es5/lib/api/gltf-extensions.js.map +1 -1
  6. package/dist/es5/lib/extensions/EXT_mesh_features.js +111 -0
  7. package/dist/es5/lib/extensions/EXT_mesh_features.js.map +1 -0
  8. package/dist/es5/lib/extensions/EXT_structural_metadata.js +388 -0
  9. package/dist/es5/lib/extensions/EXT_structural_metadata.js.map +1 -0
  10. package/dist/es5/lib/extensions/deprecated/EXT_feature_metadata.js +32 -22
  11. package/dist/es5/lib/extensions/deprecated/EXT_feature_metadata.js.map +1 -1
  12. package/dist/es5/lib/extensions/utils/3d-tiles-utils.js +200 -0
  13. package/dist/es5/lib/extensions/utils/3d-tiles-utils.js.map +1 -0
  14. package/dist/es5/lib/types/gltf-ext-mesh-features-schema.js +2 -0
  15. package/dist/es5/lib/types/gltf-ext-mesh-features-schema.js.map +1 -0
  16. package/dist/es5/lib/types/gltf-ext-structural-metadata-schema.js +2 -0
  17. package/dist/es5/lib/types/gltf-ext-structural-metadata-schema.js.map +1 -0
  18. package/dist/es5/lib/types/gltf-json-schema.js.map +1 -1
  19. package/dist/es5/lib/utils/version.js +1 -1
  20. package/dist/esm/index.js +3 -0
  21. package/dist/esm/index.js.map +1 -1
  22. package/dist/esm/lib/api/gltf-extensions.js +3 -1
  23. package/dist/esm/lib/api/gltf-extensions.js.map +1 -1
  24. package/dist/esm/lib/extensions/EXT_mesh_features.js +55 -0
  25. package/dist/esm/lib/extensions/EXT_mesh_features.js.map +1 -0
  26. package/dist/esm/lib/extensions/EXT_structural_metadata.js +324 -0
  27. package/dist/esm/lib/extensions/EXT_structural_metadata.js.map +1 -0
  28. package/dist/esm/lib/extensions/deprecated/EXT_feature_metadata.js +30 -20
  29. package/dist/esm/lib/extensions/deprecated/EXT_feature_metadata.js.map +1 -1
  30. package/dist/esm/lib/extensions/utils/3d-tiles-utils.js +164 -0
  31. package/dist/esm/lib/extensions/utils/3d-tiles-utils.js.map +1 -0
  32. package/dist/esm/lib/types/gltf-ext-mesh-features-schema.js +2 -0
  33. package/dist/esm/lib/types/gltf-ext-mesh-features-schema.js.map +1 -0
  34. package/dist/esm/lib/types/gltf-ext-structural-metadata-schema.js +2 -0
  35. package/dist/esm/lib/types/gltf-ext-structural-metadata-schema.js.map +1 -0
  36. package/dist/esm/lib/types/gltf-json-schema.js.map +1 -1
  37. package/dist/esm/lib/utils/version.js +1 -1
  38. package/dist/index.d.ts +6 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/lib/api/gltf-extensions.d.ts.map +1 -1
  41. package/dist/lib/extensions/EXT_mesh_features.d.ts +7 -0
  42. package/dist/lib/extensions/EXT_mesh_features.d.ts.map +1 -0
  43. package/dist/lib/extensions/EXT_structural_metadata.d.ts +17 -0
  44. package/dist/lib/extensions/EXT_structural_metadata.d.ts.map +1 -0
  45. package/dist/lib/extensions/deprecated/EXT_feature_metadata.d.ts.map +1 -1
  46. package/dist/lib/extensions/utils/3d-tiles-utils.d.ts +52 -0
  47. package/dist/lib/extensions/utils/3d-tiles-utils.d.ts.map +1 -0
  48. package/dist/lib/types/gltf-ext-mesh-features-schema.d.ts +45 -0
  49. package/dist/lib/types/gltf-ext-mesh-features-schema.d.ts.map +1 -0
  50. package/dist/lib/types/gltf-ext-structural-metadata-schema.d.ts +310 -0
  51. package/dist/lib/types/gltf-ext-structural-metadata-schema.d.ts.map +1 -0
  52. package/dist/lib/types/gltf-json-schema.d.ts +12 -58
  53. package/dist/lib/types/gltf-json-schema.d.ts.map +1 -1
  54. package/package.json +6 -6
  55. package/src/index.ts +11 -3
  56. package/src/lib/api/gltf-extensions.ts +6 -2
  57. package/src/lib/extensions/EXT_mesh_features.ts +117 -0
  58. package/src/lib/extensions/EXT_structural_metadata.ts +797 -0
  59. package/src/lib/extensions/deprecated/EXT_feature_metadata.ts +129 -36
  60. package/src/lib/extensions/utils/3d-tiles-utils.ts +325 -0
  61. package/src/lib/types/gltf-ext-mesh-features-schema.ts +48 -0
  62. package/src/lib/types/gltf-ext-structural-metadata-schema.ts +359 -0
  63. package/src/lib/types/gltf-json-schema.ts +13 -61
  64. package/dist/bundle.js +0 -5
  65. package/dist/glb-loader.js +0 -34
  66. package/dist/glb-writer.js +0 -35
  67. package/dist/gltf-loader.js +0 -50
  68. package/dist/gltf-writer.js +0 -32
  69. package/dist/index.js +0 -28
  70. package/dist/lib/api/gltf-extensions.js +0 -83
  71. package/dist/lib/api/gltf-scenegraph.js +0 -580
  72. package/dist/lib/api/normalize-gltf-v1.js +0 -299
  73. package/dist/lib/api/post-process-gltf.js +0 -433
  74. package/dist/lib/encoders/encode-glb.js +0 -72
  75. package/dist/lib/encoders/encode-gltf.js +0 -32
  76. package/dist/lib/extensions/EXT_meshopt_compression.js +0 -41
  77. package/dist/lib/extensions/EXT_texture_webp.js +0 -36
  78. package/dist/lib/extensions/KHR_binary_gltf.js +0 -39
  79. package/dist/lib/extensions/KHR_draco_mesh_compression.js +0 -137
  80. package/dist/lib/extensions/KHR_texture_basisu.js +0 -29
  81. package/dist/lib/extensions/KHR_texture_transform.js +0 -227
  82. package/dist/lib/extensions/deprecated/EXT_feature_metadata.js +0 -291
  83. package/dist/lib/extensions/deprecated/KHR_lights_punctual.js +0 -59
  84. package/dist/lib/extensions/deprecated/KHR_materials_unlit.js +0 -44
  85. package/dist/lib/extensions/deprecated/KHR_techniques_webgl.js +0 -79
  86. package/dist/lib/gltf-utils/get-typed-array.js +0 -41
  87. package/dist/lib/gltf-utils/gltf-attribute-utils.js +0 -73
  88. package/dist/lib/gltf-utils/gltf-constants.js +0 -43
  89. package/dist/lib/gltf-utils/gltf-utils.js +0 -90
  90. package/dist/lib/gltf-utils/resolve-url.js +0 -18
  91. package/dist/lib/parsers/parse-glb.js +0 -166
  92. package/dist/lib/parsers/parse-gltf.js +0 -185
  93. package/dist/lib/types/glb-types.js +0 -2
  94. package/dist/lib/types/gltf-json-schema.js +0 -4
  95. package/dist/lib/types/gltf-postprocessed-schema.js +0 -4
  96. package/dist/lib/types/gltf-types.js +0 -3
  97. package/dist/lib/utils/assert.js +0 -12
  98. package/dist/lib/utils/version.js +0 -7
  99. package/dist/meshopt/meshopt-decoder.js +0 -118
  100. package/dist/webp/webp.js +0 -38
@@ -0,0 +1,797 @@
1
+ /* eslint-disable camelcase */
2
+ import type {BigTypedArray, TypedArray} from '@loaders.gl/schema';
3
+ import type {GLTF, GLTFTextureInfoMetadata, GLTFMeshPrimitive} from '../types/gltf-json-schema';
4
+ import type {
5
+ GLTF_EXT_structural_metadata_Schema,
6
+ GLTF_EXT_structural_metadata_ClassProperty,
7
+ GLTF_EXT_structural_metadata_Enum,
8
+ GLTF_EXT_structural_metadata_EnumValue,
9
+ GLTF_EXT_structural_metadata_PropertyTable,
10
+ GLTF_EXT_structural_metadata,
11
+ GLTF_EXT_structural_metadata_PropertyTexture,
12
+ GLTF_EXT_structural_metadata_PropertyTable_Property,
13
+ GLTF_EXT_structural_metadata_Primitive
14
+ } from '../types/gltf-ext-structural-metadata-schema';
15
+ import type {GLTFLoaderOptions} from '../../gltf-loader';
16
+
17
+ import {GLTFScenegraph} from '../api/gltf-scenegraph';
18
+ import {
19
+ convertRawBufferToMetadataArray,
20
+ getPrimitiveTextureData,
21
+ primitivePropertyDataToAttributes,
22
+ getArrayElementByteSize,
23
+ NumericComponentType,
24
+ getOffsetsForProperty
25
+ } from './utils/3d-tiles-utils';
26
+
27
+ const EXT_STRUCTURAL_METADATA_NAME = 'EXT_structural_metadata';
28
+ export const name = EXT_STRUCTURAL_METADATA_NAME;
29
+
30
+ export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions): Promise<void> {
31
+ const scenegraph = new GLTFScenegraph(gltfData);
32
+ decodeExtStructuralMetadata(scenegraph, options);
33
+ }
34
+
35
+ /*
36
+ // Example of the extension.
37
+ // See more info at https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata
38
+ const extensions = {
39
+ "extensions": {
40
+ "EXT_structural_metadata": {
41
+ "schema": {
42
+ "classes": {
43
+ "tree": {
44
+ "name": "Tree",
45
+ "description": "Woody, perennial plant.",
46
+ "properties": {
47
+ "species": {
48
+ "description": "Type of tree.",
49
+ "type": "ENUM",
50
+ "enumType": "speciesEnum",
51
+ "required": true
52
+ },
53
+ "age": {
54
+ "description": "The age of the tree, in years",
55
+ "type": "SCALAR",
56
+ "componentType": "UINT8",
57
+ "required": true
58
+ }
59
+ }
60
+ }
61
+ },
62
+ "enums": {
63
+ "speciesEnum": {
64
+ "name": "Species",
65
+ "description": "An example enum for tree species.",
66
+ // valueType is not defined here. Default is "UINT16"
67
+ "values": [
68
+ { "name": "Unspecified", "value": 0 },
69
+ { "name": "Oak", "value": 1 }
70
+ ]
71
+ }
72
+ }
73
+ },
74
+ "propertyTables": [{
75
+ "name": "tree_survey_2021-09-29",
76
+ "class": "tree",
77
+ "count": 10, // The number of elements in each property array (in `species`, in `age`).
78
+ "properties": {
79
+ "species": {
80
+ "values": 0, // It's an index of the buffer view containing property values.
81
+ },
82
+ "age": {
83
+ "values": 1
84
+ }
85
+ }
86
+ }]
87
+ }
88
+ }
89
+ }
90
+ */
91
+
92
+ /**
93
+ * Returns the property table populated with the data taken according to the extension schema.
94
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
95
+ * @param propertyTableIndex - Index of the property table to locate.
96
+ * @returns Property table populated with the data.
97
+ * Throws an exception if no property table was found for propertyTableIndex provided.
98
+ */
99
+ export function getPropertyTablePopulated(
100
+ scenegraph: GLTFScenegraph,
101
+ propertyTableIndex: number
102
+ ): GLTF_EXT_structural_metadata_PropertyTable {
103
+ const extension: GLTF_EXT_structural_metadata | null = scenegraph.getExtension(
104
+ EXT_STRUCTURAL_METADATA_NAME
105
+ );
106
+ const propertyTable = extension?.propertyTables?.[propertyTableIndex];
107
+ if (extension?.schema && propertyTable) {
108
+ processPropertyTable(scenegraph, extension.schema, propertyTable);
109
+ return propertyTable;
110
+ }
111
+ throw new Error(
112
+ `Incorrect data in the EXT_structural_metadata extension: no property table with index ${propertyTableIndex}`
113
+ );
114
+ }
115
+
116
+ /**
117
+ * Decodes feature metadata from extension
118
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
119
+ * @param options - loader options.
120
+ */
121
+ function decodeExtStructuralMetadata(scenegraph: GLTFScenegraph, options: GLTFLoaderOptions): void {
122
+ const extension: GLTF_EXT_structural_metadata | null = scenegraph.getExtension(
123
+ EXT_STRUCTURAL_METADATA_NAME
124
+ );
125
+ if (!extension?.schema) {
126
+ return;
127
+ }
128
+
129
+ const propertyTextures = extension.propertyTextures;
130
+ const json = scenegraph.gltf.json;
131
+ if (propertyTextures && json.meshes && options?.gltf?.loadImages) {
132
+ // Iterate through all meshes/primitives.
133
+ for (const mesh of json.meshes) {
134
+ for (const primitive of mesh.primitives) {
135
+ processPrimitivePropertyTextures(scenegraph, propertyTextures, primitive, extension);
136
+ }
137
+ }
138
+ }
139
+
140
+ const schemaClasses = extension.schema.classes;
141
+ const propertyTables = extension.propertyTables;
142
+ if (schemaClasses && propertyTables) {
143
+ for (const schemaName in schemaClasses) {
144
+ const propertyTable = findPropertyTableByClass(propertyTables, schemaName);
145
+ if (propertyTable) {
146
+ processPropertyTable(scenegraph, extension.schema, propertyTable);
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Find the property table by class name.
154
+ * @param propertyTables - propertyTable definition taken from the top-level extension
155
+ * @param schemaClassName - class name in the extension schema
156
+ */
157
+ function findPropertyTableByClass(
158
+ propertyTables: GLTF_EXT_structural_metadata_PropertyTable[],
159
+ schemaClassName: string
160
+ ): GLTF_EXT_structural_metadata_PropertyTable | null {
161
+ for (let i = 0, len = propertyTables.length; i < len; i++) {
162
+ const propertyTable = propertyTables[i];
163
+
164
+ if (propertyTable.class === schemaClassName) {
165
+ return propertyTable;
166
+ }
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ /**
173
+ * Takes data from property textures reffered by the primitive
174
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
175
+ * @param propertyTextures - propertyTexture definition taken from the top-level extention
176
+ * @param primitive - Primitive object
177
+ * @param extension - top-level extension
178
+ */
179
+ function processPrimitivePropertyTextures(
180
+ scenegraph: GLTFScenegraph,
181
+ propertyTextures: GLTF_EXT_structural_metadata_PropertyTexture[],
182
+ primitive: GLTFMeshPrimitive,
183
+ extension: GLTF_EXT_structural_metadata
184
+ ): void {
185
+ if (!propertyTextures) {
186
+ return;
187
+ }
188
+ const primitiveExtension: GLTF_EXT_structural_metadata_Primitive = primitive.extensions?.[
189
+ EXT_STRUCTURAL_METADATA_NAME
190
+ ] as GLTF_EXT_structural_metadata_Primitive;
191
+ const primitivePropertyTextureIndices = primitiveExtension?.propertyTextures;
192
+ if (!primitivePropertyTextureIndices) {
193
+ return;
194
+ }
195
+
196
+ for (const primitivePropertyTextureIndex of primitivePropertyTextureIndices) {
197
+ const propertyTexture = propertyTextures[primitivePropertyTextureIndex];
198
+ processPrimitivePropertyTexture(scenegraph, propertyTexture, primitive, extension);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Takes property data from the texture pointed by the primitive and appends them to `exension.data`
204
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
205
+ * @param propertyTexture - propertyTexture definition taken from the top-level extension.
206
+ * @param primitive - Primitive object
207
+ * @param extension - top-level extension
208
+ */
209
+ function processPrimitivePropertyTexture(
210
+ scenegraph: GLTFScenegraph,
211
+ propertyTexture: GLTF_EXT_structural_metadata_PropertyTexture,
212
+ primitive: GLTFMeshPrimitive,
213
+ extension: GLTF_EXT_structural_metadata
214
+ ): void {
215
+ if (!propertyTexture.properties) {
216
+ return;
217
+ }
218
+
219
+ if (!extension.dataAttributeNames) {
220
+ extension.dataAttributeNames = [];
221
+ }
222
+
223
+ /* Iterate through all properties defined in propertyTexture, e.g. "speed" and "direction":
224
+ {
225
+ "class": "wind",
226
+ "properties": {
227
+ "speed": {
228
+ "index": 0,
229
+ "texCoord": 0,
230
+ "channels": [0]
231
+ },
232
+ "direction": {
233
+ "index": 0,
234
+ "texCoord": 0,
235
+ "channels": [1, 2]
236
+ }
237
+ }
238
+ }
239
+ */
240
+ const className = propertyTexture.class;
241
+ for (const propName in propertyTexture.properties) {
242
+ // propName has values like "speed", "direction"
243
+ // Make attributeName as a combination of the class name and the propertyName like "wind_speed" or "wind_direction"
244
+ const attributeName = `${className}_${propName}`;
245
+ const textureInfoTopLevel: GLTFTextureInfoMetadata | undefined =
246
+ propertyTexture.properties?.[propName];
247
+ if (!textureInfoTopLevel) {
248
+ // eslint-disable-next-line no-continue
249
+ continue;
250
+ }
251
+
252
+ // The data taken from all meshes/primitives (the same property, e.g. "speed" or "direction") will be combined into one array and saved in textureInfoTopLevel.data
253
+ // Initially textureInfoTopLevel.data will be initialized with an empty array.
254
+ if (!textureInfoTopLevel.data) {
255
+ textureInfoTopLevel.data = [];
256
+ }
257
+ const featureTextureTable: number[] = textureInfoTopLevel.data as number[];
258
+
259
+ const propertyData: number[] | null = getPrimitiveTextureData(
260
+ scenegraph,
261
+ textureInfoTopLevel,
262
+ primitive
263
+ );
264
+ if (propertyData === null) {
265
+ // eslint-disable-next-line no-continue
266
+ continue;
267
+ }
268
+ primitivePropertyDataToAttributes(
269
+ scenegraph,
270
+ attributeName,
271
+ propertyData,
272
+ featureTextureTable,
273
+ primitive
274
+ );
275
+ textureInfoTopLevel.data = featureTextureTable;
276
+ extension.dataAttributeNames.push(attributeName);
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Navigates through all properies in the property table, gets properties data,
282
+ * and put the data to `propertyTable.data` as an array.
283
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
284
+ * @param schema - schema object
285
+ * @param propertyTable - propertyTable definition taken from the top-level extension
286
+ */
287
+ function processPropertyTable(
288
+ scenegraph: GLTFScenegraph,
289
+ schema: GLTF_EXT_structural_metadata_Schema,
290
+ propertyTable: GLTF_EXT_structural_metadata_PropertyTable
291
+ ): void {
292
+ const schemaClass = schema.classes?.[propertyTable.class];
293
+ if (!schemaClass) {
294
+ throw new Error(
295
+ `Incorrect data in the EXT_structural_metadata extension: no schema class with name ${propertyTable.class}`
296
+ );
297
+ }
298
+
299
+ const numberOfElements = propertyTable.count; // `propertyTable.count` is a number of elements in each property array.
300
+
301
+ for (const propertyName in schemaClass.properties) {
302
+ const classProperty = schemaClass.properties[propertyName];
303
+ const propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property | undefined =
304
+ propertyTable.properties?.[propertyName];
305
+
306
+ if (propertyTableProperty) {
307
+ // Getting all elements (`numberOfElements`) of the array in the `propertyTableProperty`
308
+ const data = getPropertyDataFromBinarySource(
309
+ scenegraph,
310
+ schema,
311
+ classProperty,
312
+ numberOfElements,
313
+ propertyTableProperty
314
+ );
315
+ propertyTableProperty.data = data;
316
+ }
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Decodes a propertyTable column from binary source based on property type
322
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
323
+ * @param schema - Schema object
324
+ * @param classProperty - class property object
325
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table.
326
+ * @param propertyTableProperty - propertyTable's property metadata
327
+ * @returns {string[] | number[] | string[][] | number[][]}
328
+ */
329
+ function getPropertyDataFromBinarySource(
330
+ scenegraph: GLTFScenegraph,
331
+ schema: GLTF_EXT_structural_metadata_Schema,
332
+ classProperty: GLTF_EXT_structural_metadata_ClassProperty,
333
+ numberOfElements: number,
334
+ propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property
335
+ ): string[] | BigTypedArray | string[][] | BigTypedArray[] {
336
+ let data: string[] | BigTypedArray | string[][] | BigTypedArray[] = [];
337
+ const valuesBufferView = propertyTableProperty.values;
338
+ const valuesDataBytes: Uint8Array = scenegraph.getTypedArrayForBufferView(valuesBufferView);
339
+
340
+ const arrayOffsets = getArrayOffsetsForProperty(
341
+ scenegraph,
342
+ classProperty,
343
+ propertyTableProperty,
344
+ numberOfElements
345
+ );
346
+ const stringOffsets = getStringOffsetsForProperty(
347
+ scenegraph,
348
+ propertyTableProperty,
349
+ numberOfElements
350
+ );
351
+
352
+ switch (classProperty.type) {
353
+ case 'SCALAR':
354
+ case 'VEC2':
355
+ case 'VEC3':
356
+ case 'VEC4':
357
+ case 'MAT2':
358
+ case 'MAT3':
359
+ case 'MAT4': {
360
+ data = getPropertyDataNumeric(classProperty, numberOfElements, valuesDataBytes, arrayOffsets);
361
+ break;
362
+ }
363
+ case 'BOOLEAN': {
364
+ // TODO: implement it as soon as we have the corresponding tileset
365
+ throw new Error(`Not implemented - classProperty.type=${classProperty.type}`);
366
+ }
367
+ case 'STRING': {
368
+ data = getPropertyDataString(
369
+ classProperty,
370
+ numberOfElements,
371
+ valuesDataBytes,
372
+ arrayOffsets,
373
+ stringOffsets
374
+ );
375
+ break;
376
+ }
377
+ case 'ENUM': {
378
+ data = getPropertyDataENUM(
379
+ schema,
380
+ classProperty,
381
+ numberOfElements,
382
+ valuesDataBytes,
383
+ arrayOffsets
384
+ );
385
+ break;
386
+ }
387
+ default:
388
+ throw new Error(`Unknown classProperty type ${classProperty.type}`);
389
+ }
390
+
391
+ return data;
392
+ }
393
+
394
+ /**
395
+ * Parse propertyTable.property.arrayOffsets that are offsets of sub-arrays in a flatten array of values
396
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
397
+ * @param classProperty - class property object
398
+ * @param propertyTableProperty - propertyTable's property metadata
399
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table.
400
+ * @returns typed array with offset values
401
+ * @see https://github.com/CesiumGS/glTF/blob/2976f1183343a47a29e4059a70961371cd2fcee8/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTable.property.schema.json#L21
402
+ */
403
+ function getArrayOffsetsForProperty(
404
+ scenegraph: GLTFScenegraph,
405
+ classProperty: GLTF_EXT_structural_metadata_ClassProperty,
406
+ propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property,
407
+ numberOfElements: number
408
+ ): TypedArray | null {
409
+ if (
410
+ classProperty.array &&
411
+ // `count` is a number of array elements. May only be defined when `array` is true.
412
+ // If `count` is NOT defined, it's a VARIABLE-length array
413
+ typeof classProperty.count === 'undefined' &&
414
+ // `arrayOffsets` is an index of the buffer view containing offsets for variable-length arrays.
415
+ typeof propertyTableProperty.arrayOffsets !== 'undefined' &&
416
+ typeof propertyTableProperty.arrayOffsetType !== 'undefined'
417
+ ) {
418
+ // Data are in a VARIABLE-length array
419
+ return getOffsetsForProperty(
420
+ scenegraph,
421
+ propertyTableProperty.arrayOffsets,
422
+ propertyTableProperty.arrayOffsetType,
423
+ numberOfElements
424
+ );
425
+ }
426
+ return null;
427
+ }
428
+
429
+ /**
430
+ * Parse propertyTable.property.stringOffsets
431
+ * @param scenegraph - Instance of the class for structured access to GLTF data
432
+ * @param propertyTableProperty - propertyTable's property metadata
433
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table
434
+ * @returns typed array with offset values
435
+ * @see https://github.com/CesiumGS/glTF/blob/2976f1183343a47a29e4059a70961371cd2fcee8/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTable.property.schema.json#L29C10-L29C23
436
+ */
437
+ function getStringOffsetsForProperty(
438
+ scenegraph: GLTFScenegraph,
439
+ propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property,
440
+ numberOfElements: number
441
+ ): TypedArray | null {
442
+ if (
443
+ typeof propertyTableProperty.stringOffsets !== 'undefined' && // `stringOffsets` is an index of the buffer view containing offsets for strings.
444
+ typeof propertyTableProperty.stringOffsetType !== 'undefined'
445
+ ) {
446
+ // Data are in a FIXED-length array
447
+ return getOffsetsForProperty(
448
+ scenegraph,
449
+ propertyTableProperty.stringOffsets,
450
+ propertyTableProperty.stringOffsetType,
451
+ numberOfElements
452
+ );
453
+ }
454
+ return null;
455
+ }
456
+
457
+ /**
458
+ * Decodes properties of SCALAR, VEC-N, MAT-N types from binary sourse.
459
+ * @param classProperty - class property object
460
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table.
461
+ * @param valuesDataBytes - data taken from values property of the property table property.
462
+ * @param arrayOffsets - offsets for variable-length arrays. It's null for fixed-length arrays or scalar types.
463
+ * @returns property values in a typed array or in an array of typed arrays
464
+ */
465
+ function getPropertyDataNumeric(
466
+ classProperty: GLTF_EXT_structural_metadata_ClassProperty,
467
+ numberOfElements: number,
468
+ valuesDataBytes: Uint8Array,
469
+ arrayOffsets: TypedArray | null
470
+ ): BigTypedArray | BigTypedArray[] {
471
+ const isArray = classProperty.array;
472
+ const arrayCount = classProperty.count;
473
+
474
+ const elementSize = getArrayElementByteSize(classProperty.type, classProperty.componentType);
475
+ const elementCount = valuesDataBytes.byteLength / elementSize;
476
+
477
+ let valuesData: BigTypedArray | null;
478
+ if (classProperty.componentType) {
479
+ valuesData = convertRawBufferToMetadataArray(
480
+ valuesDataBytes,
481
+ classProperty.type,
482
+ // The datatype of the element's components. Only applicable to `SCALAR`, `VECN`, and `MATN` types.
483
+ classProperty.componentType as NumericComponentType,
484
+ elementCount
485
+ );
486
+ if (!valuesData) {
487
+ valuesData = valuesDataBytes;
488
+ }
489
+ } else {
490
+ // The spec doesn't provide any info what to do if componentType is not set.
491
+ valuesData = valuesDataBytes;
492
+ }
493
+
494
+ if (isArray) {
495
+ if (arrayOffsets) {
496
+ // VARIABLE-length array
497
+ return parseVariableLengthArrayNumeric(
498
+ valuesData,
499
+ numberOfElements,
500
+ arrayOffsets,
501
+ valuesDataBytes.length,
502
+ elementSize
503
+ );
504
+ }
505
+ if (arrayCount) {
506
+ // FIXED-length array
507
+ return parseFixedLengthArrayNumeric(valuesData, numberOfElements, arrayCount);
508
+ }
509
+ return [];
510
+ }
511
+
512
+ return valuesData;
513
+ }
514
+
515
+ /**
516
+ * Parse variable-length array data.
517
+ * In this case every value of the property in the table will be an array
518
+ * of arbitrary length
519
+ * @param valuesData - values in a flat typed array
520
+ * @param numberOfElements - number of rows in the property table
521
+ * @param arrayOffsets - offsets of nested arrays in the flat values array
522
+ * @param valuesDataBytesLength - data byte length
523
+ * @param valueSize - value size in bytes
524
+ * @returns array of typed arrays
525
+ */
526
+ function parseVariableLengthArrayNumeric(
527
+ valuesData: BigTypedArray,
528
+ numberOfElements: number,
529
+ arrayOffsets: TypedArray,
530
+ valuesDataBytesLength: number,
531
+ valueSize: number
532
+ ): BigTypedArray[] {
533
+ const attributeValueArray: BigTypedArray[] = [];
534
+ for (let index = 0; index < numberOfElements; index++) {
535
+ const arrayOffset = arrayOffsets[index];
536
+ const arrayByteSize = arrayOffsets[index + 1] - arrayOffsets[index];
537
+ if (arrayByteSize + arrayOffset > valuesDataBytesLength) {
538
+ break;
539
+ }
540
+ const typedArrayOffset = arrayOffset / valueSize;
541
+ const elementCount = arrayByteSize / valueSize;
542
+ attributeValueArray.push(valuesData.slice(typedArrayOffset, typedArrayOffset + elementCount));
543
+ }
544
+ return attributeValueArray;
545
+ }
546
+
547
+ /**
548
+ * Parse fixed-length array data
549
+ * In this case every value of the property in the table will be an array
550
+ * of constant length equal to `arrayCount`
551
+ * @param valuesData - values in a flat typed array
552
+ * @param numberOfElements - number of rows in the property table
553
+ * @param arrayCount - nested arrays length
554
+ * @returns array of typed arrays
555
+ */
556
+ function parseFixedLengthArrayNumeric(
557
+ valuesData: BigTypedArray,
558
+ numberOfElements: number,
559
+ arrayCount: number
560
+ ): BigTypedArray[] {
561
+ const attributeValueArray: BigTypedArray[] = [];
562
+ for (let index = 0; index < numberOfElements; index++) {
563
+ const elementOffset = index * arrayCount;
564
+ attributeValueArray.push(valuesData.slice(elementOffset, elementOffset + arrayCount));
565
+ }
566
+ return attributeValueArray;
567
+ }
568
+
569
+ /**
570
+ * Decodes properties of string type from binary source.
571
+ * @param classProperty - class property object
572
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table.
573
+ * @param valuesDataBytes - data taken from values property of the property table property.
574
+ * @param arrayOffsets - offsets for variable-length arrays. It's null for fixed-length arrays or scalar types.
575
+ * @param stringOffsets - index of the buffer view containing offsets for strings. It should be available for string type.
576
+ * @returns string property values
577
+ */
578
+ function getPropertyDataString(
579
+ classProperty: GLTF_EXT_structural_metadata_ClassProperty,
580
+ numberOfElements: number,
581
+ valuesDataBytes: Uint8Array,
582
+ arrayOffsets: TypedArray | null,
583
+ stringOffsets: TypedArray | null
584
+ ): string[] | string[][] {
585
+ if (arrayOffsets) {
586
+ // TODO: implement it as soon as we have the corresponding tileset
587
+ throw new Error(`Not implemented - classProperty.type=${classProperty.type}`);
588
+ }
589
+
590
+ if (stringOffsets) {
591
+ const stringsArray: string[] = [];
592
+ const textDecoder = new TextDecoder('utf8');
593
+
594
+ let stringOffset = 0;
595
+ for (let index = 0; index < numberOfElements; index++) {
596
+ const stringByteSize = stringOffsets[index + 1] - stringOffsets[index];
597
+
598
+ if (stringByteSize + stringOffset <= valuesDataBytes.length) {
599
+ const stringData = valuesDataBytes.subarray(stringOffset, stringByteSize + stringOffset);
600
+ const stringAttribute = textDecoder.decode(stringData);
601
+
602
+ stringsArray.push(stringAttribute);
603
+ stringOffset += stringByteSize;
604
+ }
605
+ }
606
+
607
+ return stringsArray;
608
+ }
609
+ return [];
610
+ }
611
+
612
+ /**
613
+ * Decodes properties of enum type from binary source.
614
+ * @param schema - schema object
615
+ * @param classProperty - class property object
616
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table.
617
+ * @param valuesDataBytes - data taken from values property of the property table property.
618
+ * @param arrayOffsets - offsets for variable-length arrays. It's null for fixed-length arrays or scalar types.
619
+ * @returns strings array of nested strings array
620
+ */
621
+ function getPropertyDataENUM(
622
+ schema: GLTF_EXT_structural_metadata_Schema,
623
+ classProperty: GLTF_EXT_structural_metadata_ClassProperty,
624
+ numberOfElements: number,
625
+ valuesDataBytes: Uint8Array,
626
+ arrayOffsets: TypedArray | null
627
+ ): string[] | string[][] {
628
+ const enumType = classProperty.enumType;
629
+ // Enum ID as declared in the `enums` dictionary. Required when `type` is `ENUM`.
630
+ if (!enumType) {
631
+ throw new Error(
632
+ 'Incorrect data in the EXT_structural_metadata extension: classProperty.enumType is not set for type ENUM'
633
+ );
634
+ }
635
+
636
+ const enumEntry: GLTF_EXT_structural_metadata_Enum | undefined = schema.enums?.[enumType];
637
+ if (!enumEntry) {
638
+ throw new Error(
639
+ `Incorrect data in the EXT_structural_metadata extension: schema.enums does't contain ${enumType}`
640
+ );
641
+ }
642
+
643
+ const enumValueType = enumEntry.valueType || 'UINT16';
644
+ const elementSize = getArrayElementByteSize(classProperty.type, enumValueType);
645
+ const elementCount = valuesDataBytes.byteLength / elementSize;
646
+ let valuesData: BigTypedArray | null = convertRawBufferToMetadataArray(
647
+ valuesDataBytes,
648
+ classProperty.type,
649
+ enumValueType,
650
+ elementCount
651
+ );
652
+ if (!valuesData) {
653
+ valuesData = valuesDataBytes;
654
+ }
655
+
656
+ if (classProperty.array) {
657
+ if (arrayOffsets) {
658
+ // VARIABLE-length array
659
+ return parseVariableLengthArrayENUM({
660
+ valuesData,
661
+ numberOfElements,
662
+ arrayOffsets,
663
+ valuesDataBytesLength: valuesDataBytes.length,
664
+ elementSize,
665
+ enumEntry
666
+ });
667
+ }
668
+
669
+ const arrayCount = classProperty.count;
670
+ if (arrayCount) {
671
+ // FIXED-length array
672
+ return parseFixedLengthArrayENUM(valuesData, numberOfElements, arrayCount, enumEntry);
673
+ }
674
+ return [];
675
+ }
676
+
677
+ // Single value (not an array)
678
+ return getEnumsArray(valuesData, 0, numberOfElements, enumEntry);
679
+ }
680
+
681
+ /**
682
+ * Parse variable length nested ENUM arrays
683
+ * @param params.valuesData - values in a flat typed array
684
+ * @param params.numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table.
685
+ * @param params.arrayOffsets - offsets for variable-length arrays. It's null for fixed-length arrays or scalar types.
686
+ * @param params.valuesDataBytesLength - byte length of values array
687
+ * @param params.elementSize - single element byte size
688
+ * @param params.enumEntry - enums dictionary
689
+ * @returns nested strings array
690
+ */
691
+ function parseVariableLengthArrayENUM(params: {
692
+ valuesData: BigTypedArray;
693
+ numberOfElements: number;
694
+ arrayOffsets: TypedArray;
695
+ valuesDataBytesLength: number;
696
+ elementSize: number;
697
+ enumEntry: GLTF_EXT_structural_metadata_Enum;
698
+ }): string[][] {
699
+ const {
700
+ valuesData,
701
+ numberOfElements,
702
+ arrayOffsets,
703
+ valuesDataBytesLength,
704
+ elementSize,
705
+ enumEntry
706
+ } = params;
707
+ const attributeValueArray: string[][] = [];
708
+ for (let index = 0; index < numberOfElements; index++) {
709
+ const arrayOffset = arrayOffsets[index];
710
+ const arrayByteSize = arrayOffsets[index + 1] - arrayOffsets[index];
711
+ if (arrayByteSize + arrayOffset > valuesDataBytesLength) {
712
+ break;
713
+ }
714
+
715
+ const typedArrayOffset = arrayOffset / elementSize;
716
+ const elementCount = arrayByteSize / elementSize;
717
+ const array: string[] = getEnumsArray(valuesData, typedArrayOffset, elementCount, enumEntry);
718
+ attributeValueArray.push(array);
719
+ }
720
+ return attributeValueArray;
721
+ }
722
+
723
+ /**
724
+ * Parse fixed length ENUM arrays
725
+ * @param valuesData - values in a flat typed array
726
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table.
727
+ * @param arrayCount - nested arrays length
728
+ * @param enumEntry - enums dictionary
729
+ * @returns nested strings array
730
+ */
731
+ function parseFixedLengthArrayENUM(
732
+ valuesData: BigTypedArray,
733
+ numberOfElements: number,
734
+ arrayCount: number,
735
+ enumEntry: GLTF_EXT_structural_metadata_Enum
736
+ ): string[][] {
737
+ const attributeValueArray: string[][] = [];
738
+ for (let index = 0; index < numberOfElements; index++) {
739
+ const elementOffset = arrayCount * index;
740
+ const array: string[] = getEnumsArray(valuesData, elementOffset, arrayCount, enumEntry);
741
+ attributeValueArray.push(array);
742
+ }
743
+ return attributeValueArray;
744
+ }
745
+
746
+ /**
747
+ * Parse ENUM values into a string array
748
+ * @param valuesData - values in a flat typed array
749
+ * @param offset - offset to start parse from
750
+ * @param count - values length to parse
751
+ * @param enumEntry - enums dictionary
752
+ * @returns array of string with parsed ENUM names
753
+ */
754
+ function getEnumsArray(
755
+ valuesData: BigTypedArray,
756
+ offset: number,
757
+ count: number,
758
+ enumEntry: GLTF_EXT_structural_metadata_Enum
759
+ ): string[] {
760
+ const array: string[] = [];
761
+ for (let i = 0; i < count; i++) {
762
+ // At the moment we don't support BigInt. It requires additional calculations logic
763
+ // and might be an issue in Safari
764
+ if (valuesData instanceof BigInt64Array || valuesData instanceof BigUint64Array) {
765
+ array.push('');
766
+ } else {
767
+ const value = valuesData[offset + i];
768
+
769
+ const enumObject = getEnumByValue(enumEntry, value);
770
+ if (enumObject) {
771
+ array.push(enumObject.name);
772
+ } else {
773
+ array.push('');
774
+ }
775
+ }
776
+ }
777
+ return array;
778
+ }
779
+
780
+ /**
781
+ * Looks up ENUM whose `value` property matches the specified number in the parameter `value`.
782
+ * @param {GLTF_EXT_structural_metadata_Enum} enumEntry - ENUM entry containing the array of possible enums.
783
+ * @param {number} value - the value of the ENUM to locate.
784
+ * @returns {GLTF_EXT_structural_metadata_EnumValue | null} ENUM matcihng the specified value or null of no ENUM object was found.
785
+ */
786
+ function getEnumByValue(
787
+ enumEntry: GLTF_EXT_structural_metadata_Enum,
788
+ value: number
789
+ ): GLTF_EXT_structural_metadata_EnumValue | null {
790
+ for (const enumValue of enumEntry.values) {
791
+ if (enumValue.value === value) {
792
+ return enumValue;
793
+ }
794
+ }
795
+
796
+ return null;
797
+ }