@loaders.gl/gltf 4.0.0-alpha.23 → 4.0.0-alpha.25

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 (123) hide show
  1. package/dist/dist.min.js +378 -241
  2. package/dist/es5/index.js +12 -0
  3. package/dist/es5/index.js.map +1 -1
  4. package/dist/es5/lib/api/gltf-extensions.js +1 -1
  5. package/dist/es5/lib/api/gltf-extensions.js.map +1 -1
  6. package/dist/es5/lib/extensions/EXT_mesh_features.js +13 -25
  7. package/dist/es5/lib/extensions/EXT_mesh_features.js.map +1 -1
  8. package/dist/es5/lib/extensions/EXT_structural_metadata.js +152 -106
  9. package/dist/es5/lib/extensions/EXT_structural_metadata.js.map +1 -1
  10. package/dist/es5/lib/extensions/deprecated/EXT_feature_metadata.js +64 -16
  11. package/dist/es5/lib/extensions/deprecated/EXT_feature_metadata.js.map +1 -1
  12. package/dist/es5/lib/extensions/{data-processing.js → utils/3d-tiles-utils.js} +51 -24
  13. package/dist/es5/lib/extensions/utils/3d-tiles-utils.js.map +1 -0
  14. package/dist/es5/lib/gltf-utils/gltf-utils.js +29 -0
  15. package/dist/es5/lib/gltf-utils/gltf-utils.js.map +1 -1
  16. package/dist/es5/lib/types/gltf-ext-feature-metadata-schema.js +2 -0
  17. package/dist/es5/lib/types/gltf-ext-feature-metadata-schema.js.map +1 -0
  18. package/dist/es5/lib/types/gltf-ext-mesh-features-schema.js.map +1 -1
  19. package/dist/es5/lib/types/gltf-ext-structural-metadata-schema.js.map +1 -1
  20. package/dist/es5/lib/types/gltf-json-schema.js.map +1 -1
  21. package/dist/es5/lib/types/gltf-types.js.map +1 -1
  22. package/dist/es5/lib/utils/version.js +1 -1
  23. package/dist/esm/index.js +2 -0
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/lib/api/gltf-extensions.js +1 -1
  26. package/dist/esm/lib/api/gltf-extensions.js.map +1 -1
  27. package/dist/esm/lib/extensions/EXT_mesh_features.js +13 -25
  28. package/dist/esm/lib/extensions/EXT_mesh_features.js.map +1 -1
  29. package/dist/esm/lib/extensions/EXT_structural_metadata.js +127 -89
  30. package/dist/esm/lib/extensions/EXT_structural_metadata.js.map +1 -1
  31. package/dist/esm/lib/extensions/deprecated/EXT_feature_metadata.js +64 -17
  32. package/dist/esm/lib/extensions/deprecated/EXT_feature_metadata.js.map +1 -1
  33. package/dist/esm/lib/extensions/{data-processing.js → utils/3d-tiles-utils.js} +50 -24
  34. package/dist/esm/lib/extensions/utils/3d-tiles-utils.js.map +1 -0
  35. package/dist/esm/lib/gltf-utils/gltf-utils.js +30 -0
  36. package/dist/esm/lib/gltf-utils/gltf-utils.js.map +1 -1
  37. package/dist/esm/lib/types/gltf-ext-feature-metadata-schema.js +2 -0
  38. package/dist/esm/lib/types/gltf-ext-feature-metadata-schema.js.map +1 -0
  39. package/dist/esm/lib/types/gltf-ext-mesh-features-schema.js.map +1 -1
  40. package/dist/esm/lib/types/gltf-ext-structural-metadata-schema.js.map +1 -1
  41. package/dist/esm/lib/types/gltf-json-schema.js.map +1 -1
  42. package/dist/esm/lib/types/gltf-types.js.map +1 -1
  43. package/dist/esm/lib/utils/version.js +1 -1
  44. package/dist/index.d.ts +6 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/lib/extensions/EXT_mesh_features.d.ts.map +1 -1
  47. package/dist/lib/extensions/EXT_structural_metadata.d.ts +12 -4
  48. package/dist/lib/extensions/EXT_structural_metadata.d.ts.map +1 -1
  49. package/dist/lib/extensions/deprecated/EXT_feature_metadata.d.ts +9 -0
  50. package/dist/lib/extensions/deprecated/EXT_feature_metadata.d.ts.map +1 -1
  51. package/dist/lib/extensions/utils/3d-tiles-utils.d.ts +52 -0
  52. package/dist/lib/extensions/utils/3d-tiles-utils.d.ts.map +1 -0
  53. package/dist/lib/gltf-utils/gltf-utils.d.ts +2 -0
  54. package/dist/lib/gltf-utils/gltf-utils.d.ts.map +1 -1
  55. package/dist/lib/types/gltf-ext-feature-metadata-schema.d.ts +421 -0
  56. package/dist/lib/types/gltf-ext-feature-metadata-schema.d.ts.map +1 -0
  57. package/dist/lib/types/gltf-ext-mesh-features-schema.d.ts +4 -6
  58. package/dist/lib/types/gltf-ext-mesh-features-schema.d.ts.map +1 -1
  59. package/dist/lib/types/gltf-ext-structural-metadata-schema.d.ts +48 -29
  60. package/dist/lib/types/gltf-ext-structural-metadata-schema.d.ts.map +1 -1
  61. package/dist/lib/types/gltf-json-schema.d.ts +1 -420
  62. package/dist/lib/types/gltf-json-schema.d.ts.map +1 -1
  63. package/dist/lib/types/gltf-types.d.ts +3 -0
  64. package/dist/lib/types/gltf-types.d.ts.map +1 -1
  65. package/package.json +6 -6
  66. package/src/index.ts +10 -5
  67. package/src/lib/api/gltf-extensions.ts +1 -1
  68. package/src/lib/extensions/EXT_mesh_features.ts +18 -44
  69. package/src/lib/extensions/EXT_structural_metadata.ts +364 -217
  70. package/src/lib/extensions/deprecated/EXT_feature_metadata.ts +193 -30
  71. package/src/lib/extensions/{data-processing.ts → utils/3d-tiles-utils.ts} +128 -56
  72. package/src/lib/gltf-utils/gltf-utils.ts +38 -0
  73. package/src/lib/types/gltf-ext-feature-metadata-schema.ts +470 -0
  74. package/src/lib/types/gltf-ext-mesh-features-schema.ts +4 -6
  75. package/src/lib/types/gltf-ext-structural-metadata-schema.ts +52 -31
  76. package/src/lib/types/gltf-json-schema.ts +1 -468
  77. package/src/lib/types/gltf-types.ts +4 -0
  78. package/dist/bundle.js +0 -5
  79. package/dist/es5/lib/extensions/data-processing.js.map +0 -1
  80. package/dist/esm/lib/extensions/data-processing.js.map +0 -1
  81. package/dist/glb-loader.js +0 -34
  82. package/dist/glb-writer.js +0 -35
  83. package/dist/gltf-loader.js +0 -50
  84. package/dist/gltf-writer.js +0 -32
  85. package/dist/index.js +0 -34
  86. package/dist/lib/api/gltf-extensions.js +0 -88
  87. package/dist/lib/api/gltf-scenegraph.js +0 -580
  88. package/dist/lib/api/normalize-gltf-v1.js +0 -299
  89. package/dist/lib/api/post-process-gltf.js +0 -433
  90. package/dist/lib/encoders/encode-glb.js +0 -72
  91. package/dist/lib/encoders/encode-gltf.js +0 -32
  92. package/dist/lib/extensions/EXT_mesh_features.js +0 -89
  93. package/dist/lib/extensions/EXT_meshopt_compression.js +0 -41
  94. package/dist/lib/extensions/EXT_structural_metadata.js +0 -504
  95. package/dist/lib/extensions/EXT_texture_webp.js +0 -36
  96. package/dist/lib/extensions/KHR_binary_gltf.js +0 -39
  97. package/dist/lib/extensions/KHR_draco_mesh_compression.js +0 -137
  98. package/dist/lib/extensions/KHR_texture_basisu.js +0 -29
  99. package/dist/lib/extensions/KHR_texture_transform.js +0 -227
  100. package/dist/lib/extensions/data-processing.d.ts +0 -34
  101. package/dist/lib/extensions/data-processing.d.ts.map +0 -1
  102. package/dist/lib/extensions/data-processing.js +0 -212
  103. package/dist/lib/extensions/deprecated/EXT_feature_metadata.js +0 -282
  104. package/dist/lib/extensions/deprecated/KHR_lights_punctual.js +0 -59
  105. package/dist/lib/extensions/deprecated/KHR_materials_unlit.js +0 -44
  106. package/dist/lib/extensions/deprecated/KHR_techniques_webgl.js +0 -79
  107. package/dist/lib/gltf-utils/get-typed-array.js +0 -41
  108. package/dist/lib/gltf-utils/gltf-attribute-utils.js +0 -73
  109. package/dist/lib/gltf-utils/gltf-constants.js +0 -43
  110. package/dist/lib/gltf-utils/gltf-utils.js +0 -90
  111. package/dist/lib/gltf-utils/resolve-url.js +0 -18
  112. package/dist/lib/parsers/parse-glb.js +0 -166
  113. package/dist/lib/parsers/parse-gltf.js +0 -185
  114. package/dist/lib/types/glb-types.js +0 -2
  115. package/dist/lib/types/gltf-ext-mesh-features-schema.js +0 -2
  116. package/dist/lib/types/gltf-ext-structural-metadata-schema.js +0 -2
  117. package/dist/lib/types/gltf-json-schema.js +0 -4
  118. package/dist/lib/types/gltf-postprocessed-schema.js +0 -4
  119. package/dist/lib/types/gltf-types.js +0 -3
  120. package/dist/lib/utils/assert.js +0 -12
  121. package/dist/lib/utils/version.js +0 -7
  122. package/dist/meshopt/meshopt-decoder.js +0 -118
  123. package/dist/webp/webp.js +0 -38
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable camelcase */
2
+ import type {GLTF} from '../../types/gltf-json-schema';
2
3
  import type {
3
- GLTF,
4
4
  GLTF_EXT_feature_metadata_Class,
5
5
  GLTF_EXT_feature_metadata_ClassProperty,
6
6
  GLTF_EXT_feature_metadata_FeatureTable,
@@ -8,13 +8,16 @@ import type {
8
8
  GLTF_EXT_feature_metadata_FeatureTexture,
9
9
  GLTF_EXT_feature_metadata_GLTF,
10
10
  GLTF_EXT_feature_metadata_TextureAccessor
11
- } from '../../types/gltf-json-schema';
11
+ } from '../../types/gltf-ext-feature-metadata-schema';
12
+ import type {BigTypedArray, TypedArray} from '@loaders.gl/schema';
13
+ import type {FeatureTableJson} from '../../types/gltf-types';
12
14
  import {GLTFScenegraph} from '../../api/gltf-scenegraph';
13
15
  import {getImageData} from '@loaders.gl/images';
14
16
  import {GLTFMeshPrimitive} from '../../types/gltf-json-schema';
15
- import {getComponentTypeFromArray} from '../../gltf-utils/gltf-utils';
17
+ import {getComponentTypeFromArray, getFloat32ArrayForAccessor} from '../../gltf-utils/gltf-utils';
16
18
  import {GLTFLoaderOptions} from '../../../gltf-loader';
17
19
  import {emod} from '@loaders.gl/math';
20
+ import {convertRawBufferToMetadataArray, getOffsetsForProperty} from '../utils/3d-tiles-utils';
18
21
 
19
22
  /** Extension name */
20
23
  const EXT_FEATURE_METADATA_NAME = 'EXT_feature_metadata';
@@ -25,6 +28,65 @@ export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions)
25
28
  decodeExtFeatureMetadata(scenegraph, options);
26
29
  }
27
30
 
31
+ /**
32
+ * Handles EXT_feature_metadata to get property table.
33
+ * @param extension - Global level of EXT_FEATURE_METADATA extension.
34
+ * @param metadataClass - User selected feature metadata class name.
35
+ * @returns {FeatureTableJson | null} Property table or null if the extension can't be handled properly.
36
+ */
37
+ export function getPropertyTableFromExtFeatureMetadata(
38
+ extension: GLTF_EXT_feature_metadata_GLTF,
39
+ metadataClass?: string
40
+ ): FeatureTableJson | null {
41
+ if (extension.featureTables) {
42
+ /**
43
+ * Take only first feature table to generate attributes storage info object.
44
+ * TODO: Think about getting data from all feature tables?
45
+ * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
46
+ * In I3S we should decide which featureIds attribute will be passed to geometry data.
47
+ */
48
+ const firstFeatureTableName = Object.keys(extension.featureTables)?.[0];
49
+
50
+ if (firstFeatureTableName) {
51
+ const featureTable = extension.featureTables[firstFeatureTableName];
52
+ const propertyTable = {};
53
+
54
+ for (const propertyName in featureTable.properties) {
55
+ propertyTable[propertyName] = featureTable.properties[propertyName].data;
56
+ }
57
+
58
+ return propertyTable;
59
+ }
60
+ }
61
+
62
+ if (extension.featureTextures) {
63
+ let featureTexture: string | undefined;
64
+ for (const textureKey in extension.featureTextures) {
65
+ const texture = extension.featureTextures[textureKey];
66
+ if (texture.class === metadataClass) {
67
+ featureTexture = textureKey;
68
+ }
69
+ }
70
+
71
+ if (typeof featureTexture === 'string') {
72
+ const featureTable = extension.featureTextures[featureTexture];
73
+ const propertyTable = {};
74
+
75
+ for (const propertyName in featureTable.properties) {
76
+ propertyTable[propertyName] = featureTable.properties[propertyName].data;
77
+ }
78
+
79
+ return propertyTable;
80
+ }
81
+ }
82
+
83
+ // eslint-disable-next-line no-console
84
+ console.warn(
85
+ 'Cannot get property table from EXT_feature_metadata extension. There is neither featureTables, nor featureTextures in the extension.'
86
+ );
87
+ return null;
88
+ }
89
+
28
90
  /**
29
91
  * Decodes feature metadata from extension
30
92
  * @param scenegraph
@@ -125,24 +187,134 @@ function getPropertyDataFromBinarySource(
125
187
  schemaProperty: GLTF_EXT_feature_metadata_ClassProperty,
126
188
  numberOfFeatures: number,
127
189
  featureTableProperty: GLTF_EXT_feature_metadata_FeatureTableProperty
128
- ): Uint8Array | string[] {
190
+ ): BigTypedArray | string[] {
129
191
  const bufferView = featureTableProperty.bufferView;
130
- // TODO think maybe we shouldn't get data only in Uint8Array format.
131
192
  const dataArray: Uint8Array = scenegraph.getTypedArrayForBufferView(bufferView);
132
193
 
133
- switch (schemaProperty.type) {
134
- case 'STRING': {
135
- // stringOffsetBufferView should be available for string type.
136
- const stringOffsetBufferView = featureTableProperty.stringOffsetBufferView!;
137
- const offsetsData = scenegraph.getTypedArrayForBufferView(stringOffsetBufferView);
138
- return getStringAttributes(dataArray, offsetsData, numberOfFeatures);
194
+ if (schemaProperty.type === 'STRING') {
195
+ const offsetsData = getStringOffsets(scenegraph, featureTableProperty, numberOfFeatures);
196
+ if (!offsetsData) {
197
+ return [];
139
198
  }
140
- default:
199
+ return getStringAttributes(dataArray, offsetsData, numberOfFeatures);
200
+ } else if (isNumericProperty(schemaProperty.type)) {
201
+ return getNumericAttributes(
202
+ dataArray,
203
+ schemaProperty.type as
204
+ | 'INT8'
205
+ | 'UINT8'
206
+ | 'INT16'
207
+ | 'UINT16'
208
+ | 'INT32'
209
+ | 'UINT32'
210
+ | 'INT64'
211
+ | 'UINT64'
212
+ | 'FLOAT32'
213
+ | 'FLOAT64',
214
+ numberOfFeatures
215
+ );
141
216
  }
142
217
 
143
218
  return dataArray;
144
219
  }
145
220
 
221
+ /**
222
+ * Check if the feature table property is of numeric type
223
+ * @param schemaPropertyType - feature table property
224
+ * @returns true if property is numeric, else - false
225
+ */
226
+ function isNumericProperty(
227
+ schemaPropertyType:
228
+ | 'INT8'
229
+ | 'UINT8'
230
+ | 'INT16'
231
+ | 'UINT16'
232
+ | 'INT32'
233
+ | 'UINT32'
234
+ | 'INT64'
235
+ | 'UINT64'
236
+ | 'FLOAT32'
237
+ | 'FLOAT64'
238
+ | 'BOOLEAN'
239
+ | 'STRING'
240
+ | 'ENUM'
241
+ | 'ARRAY'
242
+ | string
243
+ ): boolean {
244
+ return [
245
+ 'UINT8',
246
+ 'INT16',
247
+ 'UINT16',
248
+ 'INT32',
249
+ 'UINT32',
250
+ 'INT64',
251
+ 'UINT64',
252
+ 'FLOAT32',
253
+ 'FLOAT64'
254
+ ].includes(schemaPropertyType);
255
+ }
256
+
257
+ /**
258
+ * Parse featureTable.property.stringOffsetBufferView.
259
+ * String offsets is an array of offsets of strings in the united array of charactersz
260
+ * @param scenegraph - Instance of the class for structured access to GLTF data
261
+ * @param propertyTableProperty - propertyTable's property metadata
262
+ * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table
263
+ * @returns typed array with offset values
264
+ * @see https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTable.property.schema.json#L50C10-L50C32
265
+ */
266
+ function getStringOffsets(
267
+ scenegraph: GLTFScenegraph,
268
+ featureTableProperty: GLTF_EXT_feature_metadata_FeatureTableProperty,
269
+ numberOfElements: number
270
+ ): TypedArray | null {
271
+ if (typeof featureTableProperty.stringOffsetBufferView !== 'undefined') {
272
+ // Data are in a FIXED-length array
273
+ return getOffsetsForProperty(
274
+ scenegraph,
275
+ featureTableProperty.stringOffsetBufferView,
276
+ featureTableProperty.offsetType || 'UINT32', // UINT32 is the default by the spec
277
+ numberOfElements
278
+ );
279
+ }
280
+ return null;
281
+ }
282
+
283
+ /**
284
+ * Parse numeric property values
285
+ * @param valuesDataBytes - values data array
286
+ * @param propertyType - type of the property
287
+ * @param elementCount - number of rows in the featureTable
288
+ * @returns Number data in a typed array
289
+ */
290
+ function getNumericAttributes(
291
+ valuesDataBytes: Uint8Array,
292
+ propertyType:
293
+ | 'INT8'
294
+ | 'UINT8'
295
+ | 'INT16'
296
+ | 'UINT16'
297
+ | 'INT32'
298
+ | 'UINT32'
299
+ | 'INT64'
300
+ | 'UINT64'
301
+ | 'FLOAT32'
302
+ | 'FLOAT64',
303
+ elementCount: number
304
+ ): BigTypedArray {
305
+ let valuesData = convertRawBufferToMetadataArray(
306
+ valuesDataBytes,
307
+ 'SCALAR',
308
+ // The datatype of the element's components. Only applicable to `SCALAR`, `VECN`, and `MATN` types.
309
+ propertyType,
310
+ elementCount
311
+ );
312
+ if (!valuesData) {
313
+ valuesData = valuesDataBytes;
314
+ }
315
+ return valuesData;
316
+ }
317
+
146
318
  /**
147
319
  * Get properties from texture associated with all mesh primitives.
148
320
  * @param scenegraph
@@ -208,16 +380,15 @@ function processPrimitiveTextures(
208
380
  const textureData: number[] = [];
209
381
  const texCoordAccessorKey = `TEXCOORD_${featureTextureProperty.texture.texCoord}`;
210
382
  const texCoordAccessorIndex = primitive.attributes[texCoordAccessorKey];
211
- const texCoordBufferView = scenegraph.getBufferView(texCoordAccessorIndex);
212
- const texCoordArray: Uint8Array = scenegraph.getTypedArrayForBufferView(texCoordBufferView);
213
-
214
- const textureCoordinates: Float32Array = new Float32Array(
215
- texCoordArray.buffer,
216
- texCoordArray.byteOffset,
217
- texCoordArray.length / 4
218
- );
219
383
  // textureCoordinates contains UV coordinates of the actual data stored in the texture
220
384
  // accessor.count is a number of UV pairs (they are stored as VEC2)
385
+ const textureCoordinates: Float32Array | null = getFloat32ArrayForAccessor(
386
+ scenegraph.gltf,
387
+ texCoordAccessorIndex
388
+ );
389
+ if (!textureCoordinates) {
390
+ return;
391
+ }
221
392
 
222
393
  const textureIndex = featureTextureProperty.texture.index;
223
394
  const texture = json.textures?.[textureIndex];
@@ -371,24 +542,16 @@ function findFeatureTextureByName(
371
542
  */
372
543
  function getStringAttributes(
373
544
  data: Uint8Array,
374
- offsetsData: Uint8Array,
545
+ offsetsData: TypedArray,
375
546
  stringsCount: number
376
547
  ): string[] {
377
548
  const stringsArray: string[] = [];
378
549
  const textDecoder = new TextDecoder('utf8');
379
550
 
380
- let stringOffset = 0;
381
- const bytesPerStringSize = 4;
382
-
383
551
  for (let index = 0; index < stringsCount; index++) {
384
- // TODO check if it is multiplication on bytesPerStringSize is valid operation?
385
- const stringByteSize =
386
- offsetsData[(index + 1) * bytesPerStringSize] - offsetsData[index * bytesPerStringSize];
387
- const stringData = data.subarray(stringOffset, stringByteSize + stringOffset);
552
+ const stringData = data.slice(offsetsData[index], offsetsData[index + 1]);
388
553
  const stringAttribute = textDecoder.decode(stringData);
389
-
390
554
  stringsArray.push(stringAttribute);
391
- stringOffset += stringByteSize;
392
555
  }
393
556
 
394
557
  return stringsArray;
@@ -1,12 +1,33 @@
1
- import type {GLTFTextureInfoMetadata, GLTFMeshPrimitive} from '../types/gltf-json-schema';
2
- import type {TypedArray} from '@loaders.gl/schema';
1
+ /**
2
+ * loaders.gl, MIT license
3
+ *
4
+ * Shared code for 3DTiles extensions:
5
+ * * EXT_feature_metadata
6
+ * * EXT_mesh_features
7
+ * * EXT_structural_metadata
8
+ */
9
+
10
+ import type {GLTFTextureInfoMetadata, GLTFMeshPrimitive} from '../../types/gltf-json-schema';
11
+ import type {BigTypedArray, TypedArray} from '@loaders.gl/schema';
3
12
  import type {ImageType} from '@loaders.gl/images';
4
13
 
5
- import {GLTFScenegraph} from '../api/gltf-scenegraph';
6
- import {getComponentTypeFromArray} from '../gltf-utils/gltf-utils';
14
+ import {GLTFScenegraph} from '../../api/gltf-scenegraph';
15
+ import {getComponentTypeFromArray, getFloat32ArrayForAccessor} from '../../gltf-utils/gltf-utils';
7
16
  import {getImageData} from '@loaders.gl/images';
8
17
  import {emod} from '@loaders.gl/math';
9
18
 
19
+ export type NumericComponentType =
20
+ | 'INT8'
21
+ | 'UINT8'
22
+ | 'INT16'
23
+ | 'UINT16'
24
+ | 'INT32'
25
+ | 'UINT32'
26
+ | 'INT64'
27
+ | 'UINT64'
28
+ | 'FLOAT32'
29
+ | 'FLOAT64';
30
+
10
31
  const ATTRIBUTE_TYPE_TO_COMPONENTS = {
11
32
  SCALAR: 1,
12
33
  VEC2: 2,
@@ -53,39 +74,79 @@ export function getArrayElementByteSize(attributeType, componentType): number {
53
74
  );
54
75
  }
55
76
 
77
+ /**
78
+ * Gets offset array from `arrayOffsets` or `stringOffsets`.
79
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
80
+ * @param bufferViewIndex - Buffer view index
81
+ * @param offsetType - The type of values in `arrayOffsets` or `stringOffsets`.
82
+ * @param numberOfElements - The number of elements in each property array.
83
+ * @returns Array of values offsets. The number of offsets in the array is equal to `numberOfElements` plus one.
84
+ */
85
+ export function getOffsetsForProperty(
86
+ scenegraph: GLTFScenegraph,
87
+ bufferViewIndex: number,
88
+ offsetType: 'UINT8' | 'UINT16' | 'UINT32' | 'UINT64' | string,
89
+ numberOfElements: number
90
+ ): TypedArray | null {
91
+ if (
92
+ offsetType !== 'UINT8' &&
93
+ offsetType !== 'UINT16' &&
94
+ offsetType !== 'UINT32' &&
95
+ offsetType !== 'UINT64'
96
+ ) {
97
+ return null;
98
+ }
99
+ const arrayOffsetsBytes = scenegraph.getTypedArrayForBufferView(bufferViewIndex);
100
+ const arrayOffsets = convertRawBufferToMetadataArray(
101
+ arrayOffsetsBytes,
102
+ 'SCALAR', // offsets consist of ONE component
103
+ offsetType,
104
+ numberOfElements + 1 // The number of offsets is equal to the property table `count` plus one.
105
+ );
106
+
107
+ // We don't support BigInt offsets at the moment. It requires additional logic and potential issues in Safari
108
+ if (arrayOffsets instanceof BigInt64Array || arrayOffsets instanceof BigUint64Array) {
109
+ return null;
110
+ }
111
+ return arrayOffsets;
112
+ }
113
+
56
114
  /**
57
115
  * Converts raw bytes that are in the buffer to an array of the type defined by the schema.
58
- * @param {Uint8Array} typedArray - raw bytes in the buffer
59
- * @param {string} attributeType - SCALAR, VECN, MATN
60
- * @param {string} componentType - type of the component in elements, e.g. 'UINT8' or 'FLOAT32'
61
- * @param {number} elementCount - number of elements in the array. Default value is 1.
62
- * @returns {TypedArray} Data array
116
+ * @param data - Raw bytes in the buffer.
117
+ * @param attributeType - SCALAR, VECN, MATN.
118
+ * @param componentType - Type of the component in elements, e.g. 'UINT8' or 'FLOAT32'.
119
+ * @param elementCount - Number of elements in the array. Default value is 1.
120
+ * @returns Data array
63
121
  */
64
122
  export function convertRawBufferToMetadataArray(
65
- typedArray: Uint8Array,
123
+ data: Uint8Array,
66
124
  attributeType: string,
67
- componentType: string,
125
+ componentType: NumericComponentType,
68
126
  elementCount: number = 1
69
- ): TypedArray {
127
+ ): BigTypedArray | null {
70
128
  const numberOfComponents = ATTRIBUTE_TYPE_TO_COMPONENTS[attributeType];
71
129
  const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[componentType];
72
- const length = elementCount * numberOfComponents;
73
130
  const size = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[componentType];
74
- // the buffer view `byteOffset` must be aligned to a multiple of the `componentType` size.
75
- const offset =
76
- typedArray.byteOffset % size
77
- ? Math.ceil(typedArray.byteOffset / size) * size
78
- : typedArray.byteOffset;
79
- return new ArrayType(typedArray.buffer, offset, length);
131
+ const length = elementCount * numberOfComponents;
132
+ const byteLength = length * size;
133
+ let buffer = data.buffer;
134
+ let offset = data.byteOffset;
135
+ if (offset % size !== 0) {
136
+ const bufferArray = new Uint8Array(buffer);
137
+ buffer = bufferArray.slice(offset, offset + byteLength).buffer;
138
+ offset = 0;
139
+ }
140
+ return new ArrayType(buffer, offset, length);
80
141
  }
81
142
 
82
143
  /**
83
144
  * Processes data encoded in the texture associated with the primitive.
84
145
  * If Ext_mesh_featues is combined with the Ext_structural_metadata, propertyTable will also be processed.
85
- * @param {GLTFScenegraph} scenegraph - Instance of the class for structured access to GLTF data.
86
- * @param {GLTFTextureInfoMetadata} textureInfo - reference to the texture where extension data are stored.
87
- * @param {GLTFMeshPrimitive} primitive - primitive object in the mesh
88
- * @returns {number[] | null} Array of data taken. Null if data can't be taken from the texture.
146
+ * @param scenegraph - Instance of the class for structured access to GLTF data.
147
+ * @param textureInfo - Reference to the texture where extension data are stored.
148
+ * @param primitive - Primitive object in the mesh.
149
+ * @returns Array of data taken. Null if data can't be taken from the texture.
89
150
  */
90
151
  export function getPrimitiveTextureData(
91
152
  scenegraph: GLTFScenegraph,
@@ -109,15 +170,14 @@ export function getPrimitiveTextureData(
109
170
 
110
171
  const texCoordAccessorKey = `TEXCOORD_${textureInfo.texCoord || 0}`;
111
172
  const texCoordAccessorIndex = primitive.attributes[texCoordAccessorKey];
112
- const texCoordBufferView = scenegraph.getBufferView(texCoordAccessorIndex);
113
- const texCoordArray: Uint8Array = scenegraph.getTypedArrayForBufferView(texCoordBufferView);
114
173
 
115
- // textureCoordinates array contains UV coordinates of the actual data stored in the texture
116
- const textureCoordinates: Float32Array = new Float32Array(
117
- texCoordArray.buffer,
118
- texCoordArray.byteOffset,
119
- texCoordArray.length / 4
174
+ const textureCoordinates: Float32Array | null = getFloat32ArrayForAccessor(
175
+ scenegraph.gltf,
176
+ texCoordAccessorIndex
120
177
  );
178
+ if (!textureCoordinates) {
179
+ return null;
180
+ }
121
181
 
122
182
  const textureIndex: number = textureInfo.index;
123
183
  const imageIndex = json.textures?.[textureIndex]?.source;
@@ -147,11 +207,11 @@ export function getPrimitiveTextureData(
147
207
  * Puts property data to attributes.
148
208
  * It creates corresponding buffer, bufferView and accessor
149
209
  * so the data can be accessed like regular data stored in buffers.
150
- * @param {GLTFScenegraph} scenegraph - scenegraph object
151
- * @param {string} attributeName - name of the attribute
152
- * @param {number[]} propertyData - property data to store
153
- * @param {number[]} featureTable - an array where unique data from the property data are being stored
154
- * @param {GLTFMeshPrimitive} primitive - primitive object
210
+ * @param scenegraph - Scenegraph object.
211
+ * @param attributeName - Name of the attribute.
212
+ * @param propertyData - Property data to store.
213
+ * @param featureTable - Array where unique data from the property data are being stored.
214
+ * @param primitive - Primitive object.
155
215
  */
156
216
  export function primitivePropertyDataToAttributes(
157
217
  scenegraph: GLTFScenegraph,
@@ -160,7 +220,10 @@ export function primitivePropertyDataToAttributes(
160
220
  featureTable: number[],
161
221
  primitive: GLTFMeshPrimitive
162
222
  ): void {
163
- if (propertyData === null) return;
223
+ // No reason to create an empty buffer if there is no property data to store.
224
+ if (!propertyData?.length) {
225
+ return;
226
+ }
164
227
  /*
165
228
  featureTable will contain unique values, e.g.
166
229
  propertyData = [24, 35, 28, 24]
@@ -194,26 +257,26 @@ export function primitivePropertyDataToAttributes(
194
257
 
195
258
  /**
196
259
  * Gets the value from the texture by coordinates provided.
197
- * @param {ImageType} parsedImage - image where the data are stored.
198
- * @param {string | undefined} mimeType - MIME type
199
- * @param {Float32Array} textureCoordinates - uv coordinates to access data in the image.
200
- * @param {number} index - index of uv coordinates in the array textureCoordinates
201
- * @param {channels} channels - image channels where data are stored. Channels of an RGBA texture are numbered 0..3 respectively.
202
- * @returns {number} Value taken from the image.
260
+ * @param parsedImage - Image where the data are stored.
261
+ * @param mimeType - MIME type.
262
+ * @param textureCoordinates - uv coordinates to access data in the image.
263
+ * @param index - Index of uv coordinates in the array textureCoordinates.
264
+ * @param channels - Image channels where data are stored. Channels of an RGBA texture are numbered 0..3 respectively.
265
+ * @returns Value taken from the image.
203
266
  */
204
267
  function getImageValueByCoordinates(
205
268
  parsedImage: ImageType,
206
269
  mimeType: string | undefined,
207
270
  textureCoordinates: Float32Array,
208
271
  index: number,
209
- channels: number[] = [0]
272
+ channels: number[] | string = [0]
210
273
  ) {
211
- const CHANNELS_MAP = [
212
- {offset: 0, shift: 0},
213
- {offset: 1, shift: 8},
214
- {offset: 2, shift: 16},
215
- {offset: 3, shift: 24}
216
- ];
274
+ const CHANNELS_MAP = {
275
+ r: {offset: 0, shift: 0},
276
+ g: {offset: 1, shift: 8},
277
+ b: {offset: 2, shift: 16},
278
+ a: {offset: 3, shift: 24}
279
+ };
217
280
 
218
281
  const u = textureCoordinates[index];
219
282
  const v = textureCoordinates[index + 1];
@@ -224,7 +287,16 @@ function getImageValueByCoordinates(
224
287
  const offset = coordinatesToOffset(u, v, parsedImage, components);
225
288
  let value: number = 0;
226
289
  for (const c of channels) {
227
- const map = CHANNELS_MAP[c];
290
+ /*
291
+ According to the EXT_feature_metadata extension specification:
292
+ Channels are labeled by rgba and are swizzled with a string of 1-4 characters.
293
+ According to the EXT_mesh_features extension specification:
294
+ The channels array contains non-negative integer values corresponding to channels of the source texture that the feature ID consists of.
295
+ Channels of an RGBA texture are numbered 0–3 respectively.
296
+ Function getImageValueByCoordinates is used to process both extensions.
297
+ So, there should be possible to get the element of CHANNELS_MAP by either index (0, 1, 2, 3) or key (r, g, b, a).
298
+ */
299
+ const map = typeof c === 'number' ? Object.values(CHANNELS_MAP)[c] : CHANNELS_MAP[c];
228
300
  const imageOffset = offset + map.offset;
229
301
  const imageData = getImageData(parsedImage);
230
302
  if (imageData.data.length <= imageOffset) {
@@ -237,12 +309,12 @@ function getImageValueByCoordinates(
237
309
  }
238
310
 
239
311
  /**
240
- * Retrieves the offset in the image where the data are stored
241
- * @param u - u-coordinate
242
- * @param v - v-coordinate
243
- * @param parsedImage - image where the data are stored
244
- * @param componentsCount - number of components the data consists of.
245
- * @returns offset in the image where the data are stored
312
+ * Retrieves the offset in the image where the data are stored.
313
+ * @param u - u-coordinate.
314
+ * @param v - v-coordinate.
315
+ * @param parsedImage - Image where the data are stored.
316
+ * @param componentsCount - Number of components the data consists of.
317
+ * @returns Offset in the image where the data are stored.
246
318
  */
247
319
  function coordinatesToOffset(
248
320
  u: number,
@@ -1,5 +1,8 @@
1
1
  import {assert} from '../utils/assert';
2
+
3
+ import type {GLTFWithBuffers} from '../types/gltf-types';
2
4
  import type {GLTFPostprocessed} from '../types/gltf-postprocessed-schema';
5
+ import {BYTES, COMPONENTS} from '../gltf-utils/gltf-constants';
3
6
 
4
7
  /**
5
8
  * Memory needed to store texture and all mipmap levels 1 + 1/4 + 1/16 + 1/64 + ...
@@ -85,6 +88,41 @@ export function getAccessorArrayTypeAndLength(accessor, bufferView) {
85
88
  return {ArrayType, length, byteLength};
86
89
  }
87
90
 
91
+ export function getFloat32ArrayForAccessor(
92
+ gltfData: GLTFWithBuffers,
93
+ texCoordAccessor: number
94
+ ): Float32Array | null {
95
+ const accessor = gltfData.json.accessors?.[texCoordAccessor];
96
+ if (accessor && typeof accessor.bufferView !== 'undefined') {
97
+ // Get `bufferView` of the `accessor`
98
+ const bufferView = gltfData.json.bufferViews?.[accessor.bufferView];
99
+ if (bufferView) {
100
+ // Get `arrayBuffer` the `bufferView` look at
101
+ const {arrayBuffer, byteOffset: bufferByteOffset} = gltfData.buffers[bufferView.buffer];
102
+ // Resulting byteOffset is sum of the buffer, accessor and bufferView byte offsets
103
+ const byteOffset =
104
+ (bufferByteOffset || 0) + (accessor.byteOffset || 0) + (bufferView.byteOffset || 0);
105
+ // Deduce TypedArray type and its length from `accessor` and `bufferView` data
106
+ const {ArrayType, length} = getAccessorArrayTypeAndLength(accessor, bufferView);
107
+ // Number of bytes each component occupies
108
+ const bytes = BYTES[accessor.componentType];
109
+ // Number of components. For the `TEXCOORD_0` with `VEC2` type, it must return 2
110
+ const components = COMPONENTS[accessor.type];
111
+ // Multiplier to calculate the address of the `TEXCOORD_0` element in the arrayBuffer
112
+ const elementAddressScale = bufferView.byteStride || bytes * components;
113
+ // Data transform to Float32Array
114
+ const result = new Float32Array(length);
115
+ for (let i = 0; i < accessor.count; i++) {
116
+ // Take [u, v] couple from the arrayBuffer
117
+ const uv = new ArrayType(arrayBuffer, byteOffset + i * elementAddressScale, 2);
118
+ result.set(uv, i * components);
119
+ }
120
+ return result;
121
+ }
122
+ }
123
+ return null;
124
+ }
125
+
88
126
  /**
89
127
  * Calculate the GPU memory used by a GLTF tile, for both buffer and texture memory
90
128
  * @param gltf - the gltf content of a GLTF tile