@loaders.gl/tile-converter 4.0.0-alpha.22 → 4.0.0-alpha.23

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 (73) hide show
  1. package/dist/constants.d.ts +0 -2
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +1 -3
  4. package/dist/converter.min.js +106 -106
  5. package/dist/dist.min.js +1744 -1157
  6. package/dist/es5/constants.js +1 -5
  7. package/dist/es5/constants.js.map +1 -1
  8. package/dist/es5/deps-installer/deps-installer.js +1 -1
  9. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +26 -11
  10. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  11. package/dist/es5/i3s-converter/helpers/feature-attributes.js +14 -12
  12. package/dist/es5/i3s-converter/helpers/feature-attributes.js.map +1 -1
  13. package/dist/es5/i3s-converter/helpers/geometry-converter.js +61 -10
  14. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  15. package/dist/es5/i3s-converter/helpers/node-index-document.js +3 -2
  16. package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -1
  17. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js +1 -2
  18. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -1
  19. package/dist/es5/i3s-converter/i3s-converter.js +36 -26
  20. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  21. package/dist/es5/i3s-server/controllers/slpk-controller.js +2 -2
  22. package/dist/es5/i3s-server/controllers/slpk-controller.js.map +1 -1
  23. package/dist/es5/pgm-loader.js +1 -1
  24. package/dist/es5/slpk-extractor/slpk-extractor.js +1 -1
  25. package/dist/es5/slpk-extractor/slpk-extractor.js.map +1 -1
  26. package/dist/esm/constants.js +0 -2
  27. package/dist/esm/constants.js.map +1 -1
  28. package/dist/esm/deps-installer/deps-installer.js +1 -1
  29. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +21 -6
  30. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  31. package/dist/esm/i3s-converter/helpers/feature-attributes.js +6 -4
  32. package/dist/esm/i3s-converter/helpers/feature-attributes.js.map +1 -1
  33. package/dist/esm/i3s-converter/helpers/geometry-converter.js +59 -8
  34. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  35. package/dist/esm/i3s-converter/helpers/node-index-document.js +2 -1
  36. package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -1
  37. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js +1 -1
  38. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -1
  39. package/dist/esm/i3s-converter/i3s-converter.js +20 -17
  40. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  41. package/dist/esm/i3s-server/bin/i3s-server.min.js +71 -71
  42. package/dist/esm/i3s-server/controllers/slpk-controller.js +1 -1
  43. package/dist/esm/i3s-server/controllers/slpk-controller.js.map +1 -1
  44. package/dist/esm/pgm-loader.js +1 -1
  45. package/dist/esm/slpk-extractor/slpk-extractor.js +2 -1
  46. package/dist/esm/slpk-extractor/slpk-extractor.js.map +1 -1
  47. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -1
  48. package/dist/i3s-converter/helpers/batch-ids-extensions.js +37 -16
  49. package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -1
  50. package/dist/i3s-converter/helpers/feature-attributes.js +6 -4
  51. package/dist/i3s-converter/helpers/geometry-converter.d.ts +2 -2
  52. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  53. package/dist/i3s-converter/helpers/geometry-converter.js +93 -12
  54. package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -1
  55. package/dist/i3s-converter/helpers/node-index-document.js +4 -1
  56. package/dist/i3s-converter/helpers/preprocess-3d-tiles.js +2 -2
  57. package/dist/i3s-converter/i3s-converter.d.ts +1 -1
  58. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  59. package/dist/i3s-converter/i3s-converter.js +32 -13
  60. package/dist/i3s-server/controllers/slpk-controller.js +2 -2
  61. package/dist/slpk-extractor/slpk-extractor.d.ts.map +1 -1
  62. package/dist/slpk-extractor/slpk-extractor.js +2 -1
  63. package/dist/slpk-extractor.min.js +38 -38
  64. package/package.json +14 -14
  65. package/src/constants.ts +0 -3
  66. package/src/i3s-converter/helpers/batch-ids-extensions.ts +53 -14
  67. package/src/i3s-converter/helpers/feature-attributes.ts +8 -6
  68. package/src/i3s-converter/helpers/geometry-converter.ts +135 -12
  69. package/src/i3s-converter/helpers/node-index-document.ts +5 -1
  70. package/src/i3s-converter/helpers/preprocess-3d-tiles.ts +1 -1
  71. package/src/i3s-converter/i3s-converter.ts +42 -17
  72. package/src/i3s-server/controllers/slpk-controller.ts +1 -1
  73. package/src/slpk-extractor/slpk-extractor.ts +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loaders.gl/tile-converter",
3
- "version": "4.0.0-alpha.22",
3
+ "version": "4.0.0-alpha.23",
4
4
  "description": "Converter",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -45,18 +45,18 @@
45
45
  "build-i3s-server-bundle": "esbuild src/i3s-server/bin/www.ts --outfile=dist/esm/i3s-server/bin/i3s-server.min.js --platform=node --target=esnext,node14 --external:join-images --minify --bundle --define:__VERSION__=\\\"$npm_package_version\\\""
46
46
  },
47
47
  "dependencies": {
48
- "@loaders.gl/3d-tiles": "4.0.0-alpha.22",
49
- "@loaders.gl/crypto": "4.0.0-alpha.22",
50
- "@loaders.gl/draco": "4.0.0-alpha.22",
51
- "@loaders.gl/gltf": "4.0.0-alpha.22",
52
- "@loaders.gl/i3s": "4.0.0-alpha.22",
53
- "@loaders.gl/images": "4.0.0-alpha.22",
54
- "@loaders.gl/loader-utils": "4.0.0-alpha.22",
55
- "@loaders.gl/polyfills": "4.0.0-alpha.22",
56
- "@loaders.gl/textures": "4.0.0-alpha.22",
57
- "@loaders.gl/tiles": "4.0.0-alpha.22",
58
- "@loaders.gl/worker-utils": "4.0.0-alpha.22",
59
- "@loaders.gl/zip": "4.0.0-alpha.22",
48
+ "@loaders.gl/3d-tiles": "4.0.0-alpha.23",
49
+ "@loaders.gl/crypto": "4.0.0-alpha.23",
50
+ "@loaders.gl/draco": "4.0.0-alpha.23",
51
+ "@loaders.gl/gltf": "4.0.0-alpha.23",
52
+ "@loaders.gl/i3s": "4.0.0-alpha.23",
53
+ "@loaders.gl/images": "4.0.0-alpha.23",
54
+ "@loaders.gl/loader-utils": "4.0.0-alpha.23",
55
+ "@loaders.gl/polyfills": "4.0.0-alpha.23",
56
+ "@loaders.gl/textures": "4.0.0-alpha.23",
57
+ "@loaders.gl/tiles": "4.0.0-alpha.23",
58
+ "@loaders.gl/worker-utils": "4.0.0-alpha.23",
59
+ "@loaders.gl/zip": "4.0.0-alpha.23",
60
60
  "@math.gl/core": "^3.5.1",
61
61
  "@math.gl/culling": "^3.5.1",
62
62
  "@math.gl/geoid": "^3.5.1",
@@ -80,7 +80,7 @@
80
80
  "join-images": "^1.1.3",
81
81
  "sharp": "^0.31.3"
82
82
  },
83
- "gitHead": "0da838c506d1275383f2fd3d244d9c72b25397d2",
83
+ "gitHead": "e212f2a0c0e342f7cb65ce84fa2ff39f64b7d94b",
84
84
  "devDependencies": {
85
85
  "@types/express": "^4.17.17",
86
86
  "@types/node": "^20.4.2"
package/src/constants.ts CHANGED
@@ -1,5 +1,2 @@
1
1
  export const BROWSER_ERROR_MESSAGE =
2
2
  'Tile converter does not work in browser, only in node js environment';
3
-
4
- export const EXT_MESH_FEATURES = 'EXT_mesh_features';
5
- export const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
@@ -3,11 +3,16 @@ import type {NumericArray} from '@loaders.gl/loader-utils';
3
3
  import type {
4
4
  GLTF_EXT_feature_metadata_FeatureIdTexture,
5
5
  GLTF_EXT_feature_metadata_GLTF,
6
- GLTF_EXT_feature_metadata_Primitive
6
+ GLTF_EXT_feature_metadata_Primitive,
7
+ GLTF_EXT_structural_metadata
7
8
  } from '@loaders.gl/gltf';
9
+
10
+ import type {GLTF_EXT_mesh_features} from '@loaders.gl/gltf';
11
+
8
12
  import {TypedArray} from '@math.gl/core';
9
13
  import {TextureImageProperties} from '../types';
10
- import {EXT_FEATURE_METADATA, EXT_MESH_FEATURES} from '../../constants';
14
+ import {emod} from '@loaders.gl/math';
15
+ import {EXT_STRUCTURAL_METADATA, EXT_MESH_FEATURES, EXT_FEATURE_METADATA} from '@loaders.gl/gltf';
11
16
  import {Tiles3DTileContent} from '@loaders.gl/3d-tiles';
12
17
 
13
18
  /**
@@ -52,7 +57,6 @@ export function handleBatchIdsExtensions(
52
57
  featureTexture: string | null
53
58
  ): NumericArray {
54
59
  const extensions = primitive?.extensions;
55
-
56
60
  if (!extensions) {
57
61
  return [];
58
62
  }
@@ -67,8 +71,13 @@ export function handleBatchIdsExtensions(
67
71
  featureTexture
68
72
  );
69
73
  case EXT_MESH_FEATURES:
70
- console.warn('EXT_mesh_features extension is not supported yet');
71
- return [];
74
+ return handleExtMeshFeaturesExtension(attributes, extensionData as GLTF_EXT_mesh_features);
75
+ case EXT_STRUCTURAL_METADATA:
76
+ return handleExtStructuralMetadataExtension(
77
+ attributes,
78
+ extensionData as GLTF_EXT_structural_metadata
79
+ );
80
+
72
81
  default:
73
82
  return [];
74
83
  }
@@ -77,6 +86,45 @@ export function handleBatchIdsExtensions(
77
86
  return [];
78
87
  }
79
88
 
89
+ function handleExtStructuralMetadataExtension(
90
+ attributes: {
91
+ [key: string]: GLTFAccessorPostprocessed;
92
+ },
93
+ extStructuralMetadata: GLTF_EXT_structural_metadata
94
+ ): NumericArray {
95
+ // Take only first extension object to get batchIds attribute name.
96
+ const dataAttributeNames = extStructuralMetadata?.dataAttributeNames;
97
+ if (dataAttributeNames?.length) {
98
+ // Let's use the first element of the array
99
+ // TODO: What to do with others if any?
100
+ const batchIdsAttribute = attributes[dataAttributeNames[0]];
101
+ return batchIdsAttribute.value;
102
+ }
103
+ return [];
104
+ }
105
+
106
+ /**
107
+ * Getting batchIds from EXT_mesh_features extensions.
108
+ * @param attributes - gltf accessors
109
+ * @param extMeshFeatures - EXT_mesh_features extension
110
+ * @returns an array of attribute values
111
+ */
112
+ function handleExtMeshFeaturesExtension(
113
+ attributes: {
114
+ [key: string]: GLTFAccessorPostprocessed;
115
+ },
116
+ extMeshFeatures: GLTF_EXT_mesh_features
117
+ ): NumericArray {
118
+ const dataAttributeNames = extMeshFeatures?.dataAttributeNames;
119
+ if (dataAttributeNames?.length) {
120
+ // Let's use the first element of the array
121
+ // TODO: What to do with others if any?
122
+ const batchIdsAttribute = attributes[dataAttributeNames[0]];
123
+ return batchIdsAttribute.value;
124
+ }
125
+ return [];
126
+ }
127
+
80
128
  /**
81
129
  * Get batchIds from EXT_feature_metadata extension.
82
130
  * Docs - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata
@@ -218,12 +266,3 @@ function generateBatchIdsFromTexture(
218
266
 
219
267
  return batchIds;
220
268
  }
221
-
222
- /**
223
- * Handle UVs if they are out of range [0,1].
224
- * @param n
225
- * @param m
226
- */
227
- function emod(n: number): number {
228
- return ((n % 1) + 1) % 1;
229
- }
@@ -39,12 +39,14 @@ export function flattenPropertyTableByFeatureIds(
39
39
  * @param properties
40
40
  * @param featureIds
41
41
  */
42
- function getPropertiesByFeatureIds(properties: any[], featureIds: number[]): any[] {
43
- const resultProperties: any = [];
42
+ function getPropertiesByFeatureIds(properties: unknown[], featureIds: number[]): unknown[] {
43
+ const resultProperties: unknown[] = [];
44
44
 
45
- for (const featureId of featureIds) {
46
- const property = properties[featureId] || null;
47
- resultProperties.push(property);
45
+ if (properties) {
46
+ for (const featureId of featureIds) {
47
+ const property = properties[featureId] || null;
48
+ resultProperties.push(property);
49
+ }
48
50
  }
49
51
 
50
52
  return resultProperties;
@@ -64,7 +66,7 @@ export function checkPropertiesLength(
64
66
  let needFlatten = false;
65
67
 
66
68
  for (const attribute of Object.values(propertyTable)) {
67
- if (featureIds.length !== attribute.length) {
69
+ if (!featureIds || !attribute || featureIds.length !== attribute.length) {
68
70
  needFlatten = true;
69
71
  }
70
72
  }
@@ -1,5 +1,7 @@
1
1
  import type {FeatureTableJson, Tiles3DTileContent} from '@loaders.gl/3d-tiles';
2
2
  import type {
3
+ GLTF_EXT_mesh_features,
4
+ GLTF_EXT_structural_metadata,
3
5
  GLTFAccessorPostprocessed,
4
6
  GLTFMaterialPostprocessed,
5
7
  GLTFNodePostprocessed,
@@ -47,7 +49,8 @@ import {GL} from '@loaders.gl/math';
47
49
  import type {GLTFAttributesData, TextureImageProperties, TypedArrayConstructor} from '../types';
48
50
  import {generateSyntheticIndices} from '../../lib/utils/geometry-utils';
49
51
  import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling';
50
- import {EXT_FEATURE_METADATA, EXT_MESH_FEATURES} from '../../constants';
52
+
53
+ import {EXT_MESH_FEATURES, EXT_FEATURE_METADATA, EXT_STRUCTURAL_METADATA} from '@loaders.gl/gltf';
51
54
 
52
55
  // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
53
56
  const DEFAULT_ROUGHNESS_FACTOR = 1;
@@ -1358,10 +1361,12 @@ function convertPropertyTableToAttributeBuffers(
1358
1361
 
1359
1362
  for (const propertyName in propertyTableWithObjectIds) {
1360
1363
  const type = getAttributeType(propertyName, attributeStorageInfo);
1361
- const value = propertyTableWithObjectIds[propertyName];
1362
- const attributeBuffer = generateAttributeBuffer(type, value);
1364
+ if (type) {
1365
+ const value = propertyTableWithObjectIds[propertyName];
1366
+ const attributeBuffer = generateAttributeBuffer(type, value);
1363
1367
 
1364
- attributeBuffers.push(attributeBuffer);
1368
+ attributeBuffers.push(attributeBuffer);
1369
+ }
1365
1370
  }
1366
1371
 
1367
1372
  return attributeBuffers;
@@ -1401,6 +1406,20 @@ function generateAttributeBuffer(type: string, value: any): ArrayBuffer {
1401
1406
  */
1402
1407
  function getAttributeType(key: string, attributeStorageInfo: any[]): string {
1403
1408
  const attribute = attributeStorageInfo.find((attr) => attr.name === key);
1409
+ if (!attribute) {
1410
+ console.error(
1411
+ `attribute is null, key=${key}, attributeStorageInfo=${JSON.stringify(
1412
+ attributeStorageInfo,
1413
+ null,
1414
+ 2
1415
+ )}`
1416
+ );
1417
+ return '';
1418
+ }
1419
+ if (!attribute.attributeValues) {
1420
+ console.error(`attributeValues is null, attribute=${attribute}`);
1421
+ return '';
1422
+ }
1404
1423
  return attribute.attributeValues.valueType;
1405
1424
  }
1406
1425
 
@@ -1577,8 +1596,8 @@ function generateFeatureIndexAttribute(
1577
1596
  * Find property table in tile
1578
1597
  * For example it can be batchTable for b3dm files or property table in gLTF extension.
1579
1598
  * @param tileContent - 3DTiles tile content
1580
- * @param metadataClass - - user selected feature metadata class name
1581
- * @return batch table from b3dm / feature properties from EXT_FEATURE_METADATA
1599
+ * @param metadataClass - user selected feature metadata class name
1600
+ * @return batch table from b3dm / feature properties from EXT_FEATURE_METADATA, EXT_MESH_FEATURES or EXT_STRUCTURAL_METADATA
1582
1601
  */
1583
1602
  export function getPropertyTable(
1584
1603
  tileContent: Tiles3DTileContent | null,
@@ -1587,7 +1606,7 @@ export function getPropertyTable(
1587
1606
  if (!tileContent) {
1588
1607
  return null;
1589
1608
  }
1590
-
1609
+ let propertyTable: FeatureTableJson | null;
1591
1610
  const batchTableJson = tileContent?.batchTableJson;
1592
1611
 
1593
1612
  if (batchTableJson) {
@@ -1598,14 +1617,25 @@ export function getPropertyTable(
1598
1617
 
1599
1618
  switch (extensionName) {
1600
1619
  case EXT_MESH_FEATURES: {
1601
- console.warn('The I3S converter does not yet support the EXT_mesh_features extension');
1602
- return null;
1620
+ propertyTable = getPropertyTableFromExtMeshFeatures(
1621
+ extension as GLTF_EXT_mesh_features,
1622
+ metadataClass
1623
+ );
1624
+ return propertyTable;
1625
+ }
1626
+ case EXT_STRUCTURAL_METADATA: {
1627
+ propertyTable = getPropertyTableFromExtStructuralMetadata(
1628
+ extension as GLTF_EXT_structural_metadata,
1629
+ metadataClass
1630
+ );
1631
+ return propertyTable;
1603
1632
  }
1604
1633
  case EXT_FEATURE_METADATA: {
1605
- return getPropertyTableFromExtFeatureMetadata(
1634
+ propertyTable = getPropertyTableFromExtFeatureMetadata(
1606
1635
  extension as GLTF_EXT_feature_metadata_GLTF,
1607
1636
  metadataClass
1608
1637
  );
1638
+ return propertyTable;
1609
1639
  }
1610
1640
  default:
1611
1641
  return null;
@@ -1618,9 +1648,18 @@ export function getPropertyTable(
1618
1648
  */
1619
1649
  function getPropertyTableExtension(tileContent: Tiles3DTileContent): {
1620
1650
  extensionName: null | string;
1621
- extension: string | GLTF_EXT_feature_metadata_GLTF | null;
1651
+ extension:
1652
+ | string
1653
+ | GLTF_EXT_feature_metadata_GLTF
1654
+ | GLTF_EXT_structural_metadata
1655
+ | GLTF_EXT_mesh_features
1656
+ | null;
1622
1657
  } {
1623
- const extensionsWithPropertyTables = [EXT_FEATURE_METADATA, EXT_MESH_FEATURES];
1658
+ const extensionsWithPropertyTables = [
1659
+ EXT_FEATURE_METADATA,
1660
+ EXT_STRUCTURAL_METADATA,
1661
+ EXT_MESH_FEATURES
1662
+ ];
1624
1663
  const extensionsUsed = tileContent?.gltf?.extensionsUsed;
1625
1664
 
1626
1665
  if (!extensionsUsed) {
@@ -1649,6 +1688,8 @@ function getPropertyTableExtension(tileContent: Tiles3DTileContent): {
1649
1688
  /**
1650
1689
  * Handle EXT_feature_metadata to get property table
1651
1690
  * @param extension - global level of EXT_FEATURE_METADATA extension
1691
+ * @param metadataClass - user selected feature metadata class name
1692
+ * @returns {FeatureTableJson | null} Property table or null if the extension can't be handled properly.
1652
1693
  */
1653
1694
  function getPropertyTableFromExtFeatureMetadata(
1654
1695
  extension: GLTF_EXT_feature_metadata_GLTF,
@@ -1701,3 +1742,85 @@ function getPropertyTableFromExtFeatureMetadata(
1701
1742
  );
1702
1743
  return null;
1703
1744
  }
1745
+
1746
+ /**
1747
+ * Handle EXT_structural_metadata to get property table
1748
+ * @param extension - global level of EXT_STRUCTURAL_METADATA extension
1749
+ * @param metadataClass - user selected feature metadata class name
1750
+ * @returns {FeatureTableJson | null} Property table or null if the extension can't be handled properly.
1751
+ */
1752
+ function getPropertyTableFromExtStructuralMetadata(
1753
+ extension: GLTF_EXT_structural_metadata,
1754
+ metadataClass?: string
1755
+ ): FeatureTableJson | null {
1756
+ if (extension?.propertyTables) {
1757
+ /**
1758
+ * Take only first feature table to generate attributes storage info object.
1759
+ * TODO: Think about getting data from all feature tables?
1760
+ * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
1761
+ * In I3S we should decide which featureIds attribute will be passed to geometry data.
1762
+ */
1763
+ const firstPropertyTable = extension?.propertyTables[0];
1764
+ const propertyTableWithData = {};
1765
+
1766
+ for (const propertyName in firstPropertyTable.properties) {
1767
+ propertyTableWithData[propertyName] = firstPropertyTable.properties[propertyName].data;
1768
+ }
1769
+
1770
+ return propertyTableWithData;
1771
+ }
1772
+
1773
+ if (extension?.propertyTextures) {
1774
+ /**
1775
+ * Take only first feature table to generate attributes storage info object.
1776
+ * TODO: Think about getting data from all feature tables?
1777
+ * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
1778
+ * In I3S we should decide which featureIds attribute will be passed to geometry data.
1779
+ */
1780
+ if (extension?.propertyTextures) {
1781
+ const firstPropertyTexture = extension?.propertyTextures[0];
1782
+ const propertyTableWithData = {};
1783
+
1784
+ for (const propertyName in firstPropertyTexture.properties) {
1785
+ propertyTableWithData[propertyName] = firstPropertyTexture.properties[propertyName].data;
1786
+ }
1787
+
1788
+ return propertyTableWithData;
1789
+ }
1790
+ }
1791
+
1792
+ console.warn(
1793
+ "The I3S converter couldn't handle EXT_structural_metadata extension: There is neither propertyTables, no propertyTextures in the extension."
1794
+ );
1795
+ return null;
1796
+ }
1797
+
1798
+ /**
1799
+ * Handle EXT_mesh_features to get property table
1800
+ * @param extension - global level of EXT_MESH_FEATURES extension
1801
+ * @param metadataClass - user selected feature metadata class name
1802
+ * @returns {FeatureTableJson | null} Property table or null if the extension can't be handled properly.
1803
+ */
1804
+ function getPropertyTableFromExtMeshFeatures(
1805
+ extension: GLTF_EXT_mesh_features,
1806
+ metadataClass?: string
1807
+ ): FeatureTableJson | null {
1808
+ if (extension?.featureIds) {
1809
+ const firstFeatureId = extension?.featureIds[0];
1810
+ const propertyTableWithData = {};
1811
+
1812
+ // When firstFeatureId.propertyTable is defined, the property data will be taken from EXT_structural_metadata extension
1813
+ if (!firstFeatureId.propertyTable) {
1814
+ console.warn(
1815
+ 'Should be implemented as we have the tileset with Ext_mesh_features not linked with EXT_structural_metadata extension'
1816
+ );
1817
+ }
1818
+
1819
+ return propertyTableWithData;
1820
+ }
1821
+
1822
+ console.warn(
1823
+ "The I3S converter couldn't handle EXT_mesh_features extension: There is no featureIds in the extension."
1824
+ );
1825
+ return null;
1826
+ }
@@ -323,7 +323,11 @@ export class NodeIndexDocument {
323
323
  parentNode.converter.layers0?.attributeStorageInfo?.length
324
324
  ) {
325
325
  node.attributeData = [];
326
- for (let index = 0; index < attributes.length; index++) {
326
+ const minimumLength =
327
+ attributes.length < parentNode.converter.layers0.attributeStorageInfo.length
328
+ ? attributes.length
329
+ : parentNode.converter.layers0.attributeStorageInfo.length;
330
+ for (let index = 0; index < minimumLength; index++) {
327
331
  const folderName = parentNode.converter.layers0.attributeStorageInfo[index].key;
328
332
  node.attributeData.push({href: `./attributes/${folderName}/0`});
329
333
  }
@@ -2,7 +2,7 @@ import {Tiles3DTileContent} from '@loaders.gl/3d-tiles';
2
2
  import {GLTFPrimitiveModeString, PreprocessData} from '../types';
3
3
  import {GLTF, GLTFLoader, GLTF_EXT_feature_metadata_GLTF} from '@loaders.gl/gltf';
4
4
  import {parse} from '@loaders.gl/core';
5
- import {EXT_FEATURE_METADATA} from '../../constants';
5
+ import {EXT_FEATURE_METADATA} from '@loaders.gl/gltf';
6
6
 
7
7
  /**
8
8
  * glTF primitive modes
@@ -12,7 +12,8 @@ import type {
12
12
  SceneLayer3D,
13
13
  BoundingVolumes,
14
14
  MaxScreenThresholdSQ,
15
- NodeInPage
15
+ NodeInPage,
16
+ AttributeStorageInfo
16
17
  } from '@loaders.gl/i3s';
17
18
  import {load, encode, isBrowser} from '@loaders.gl/core';
18
19
  import {CesiumIonLoader, Tiles3DLoader} from '@loaders.gl/3d-tiles';
@@ -649,7 +650,14 @@ export default class I3SConverter {
649
650
 
650
651
  const propertyTable = getPropertyTable(tileContent, this.options.metadataClass);
651
652
 
652
- if (propertyTable && !this.layers0?.attributeStorageInfo?.length) {
653
+ if (propertyTable) {
654
+ /*
655
+ Call the convertion procedure even if the node attributes have been already created.
656
+ We will append new attributes only in case the property table is updated.
657
+ According to ver 1.9 (see https://github.com/Esri/i3s-spec/blob/master/docs/1.9/attributeStorageInfo.cmn.md):
658
+ "The attributeStorageInfo object describes the structure of the binary attribute data resource of a layer, which is the same for every node in the layer."
659
+ But the specification of ver 2.1 doesn't have such a requirement ("...the same for every node...")
660
+ */
653
661
  this._convertPropertyTableToNodeAttributes(propertyTable);
654
662
  }
655
663
 
@@ -735,7 +743,7 @@ export default class I3SConverter {
735
743
  * @param boundingVolume - initialized bounding volume of the source tile
736
744
  * @param tileContent - content of the source tile
737
745
  * @param parentId - id of parent node in node pages
738
- * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA
746
+ * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA, EXT_MESH_FEATURES or EXT_STRUCTURAL_METADATA
739
747
  * @returns - converted node resources
740
748
  */
741
749
  private async _convertResources(
@@ -1068,7 +1076,12 @@ export default class I3SConverter {
1068
1076
  slpkChildPath: string
1069
1077
  ): Promise<void> {
1070
1078
  if (attributes?.length && this.layers0?.attributeStorageInfo?.length) {
1071
- for (let index = 0; index < attributes.length; index++) {
1079
+ const minimumLength =
1080
+ attributes.length < this.layers0.attributeStorageInfo.length
1081
+ ? attributes.length
1082
+ : this.layers0.attributeStorageInfo.length;
1083
+
1084
+ for (let index = 0; index < minimumLength; index++) {
1072
1085
  const folderName = this.layers0.attributeStorageInfo[index].key;
1073
1086
  const fileBuffer = new Uint8Array(attributes[index]);
1074
1087
 
@@ -1150,19 +1163,31 @@ export default class I3SConverter {
1150
1163
  };
1151
1164
 
1152
1165
  for (const key in propertyTableWithObjectId) {
1153
- const firstAttribute = propertyTableWithObjectId[key][0];
1154
- const attributeType = getAttributeType(key, firstAttribute);
1155
-
1156
- const storageAttribute = createdStorageAttribute(attributeIndex, key, attributeType);
1157
- const fieldAttributeType = getFieldAttributeType(attributeType);
1158
- const fieldAttribute = createFieldAttribute(key, fieldAttributeType);
1159
- const popupInfo = createPopupInfo(propertyTableWithObjectId);
1160
-
1161
- this.layers0!.attributeStorageInfo!.push(storageAttribute);
1162
- this.layers0!.fields!.push(fieldAttribute);
1163
- this.layers0!.popupInfo = popupInfo;
1164
- this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE;
1165
-
1166
+ /*
1167
+ We will append new attributes only in case the property table is updated.
1168
+ According to ver 1.9 (see https://github.com/Esri/i3s-spec/blob/master/docs/1.9/attributeStorageInfo.cmn.md):
1169
+ "The attributeStorageInfo object describes the structure of the binary attribute data resource of a layer, which is the same for every node in the layer."
1170
+ But the specification of ver 2.1 doesn't have such a requirement ("...the same for every node...")
1171
+ */
1172
+ const found = this.layers0!.attributeStorageInfo!.find((element) => element.name === key);
1173
+ if (!found) {
1174
+ const firstAttribute = propertyTableWithObjectId[key][0];
1175
+ const attributeType = getAttributeType(key, firstAttribute);
1176
+
1177
+ const storageAttribute: AttributeStorageInfo = createdStorageAttribute(
1178
+ attributeIndex,
1179
+ key,
1180
+ attributeType
1181
+ );
1182
+ const fieldAttributeType = getFieldAttributeType(attributeType);
1183
+ const fieldAttribute = createFieldAttribute(key, fieldAttributeType);
1184
+ const popupInfo = createPopupInfo(propertyTableWithObjectId);
1185
+
1186
+ this.layers0!.attributeStorageInfo!.push(storageAttribute);
1187
+ this.layers0!.fields!.push(fieldAttribute);
1188
+ this.layers0!.popupInfo = popupInfo;
1189
+ this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE;
1190
+ }
1166
1191
  attributeIndex += 1;
1167
1192
  }
1168
1193
  }
@@ -1,6 +1,6 @@
1
1
  import '@loaders.gl/polyfills';
2
2
  import {parseSLPK} from '@loaders.gl/i3s';
3
- import {FileHandleFile} from '@loaders.gl/zip';
3
+ import {FileHandleFile} from '@loaders.gl/loader-utils';
4
4
 
5
5
  let slpkArchive;
6
6
 
@@ -2,7 +2,8 @@ import {isBrowser} from '@loaders.gl/core';
2
2
 
3
3
  import {BROWSER_ERROR_MESSAGE} from '../constants';
4
4
  import {path} from '@loaders.gl/loader-utils';
5
- import {FileHandleFile, parseZipLocalFileHeader} from '@loaders.gl/zip';
5
+ import {FileHandleFile} from '@loaders.gl/loader-utils';
6
+ import {parseZipLocalFileHeader} from '@loaders.gl/zip';
6
7
  import {GZipCompression} from '@loaders.gl/compression';
7
8
  import {writeFile} from '../lib/utils/file-utils';
8
9