@loaders.gl/tile-converter 4.0.0-alpha.19 → 4.0.0-alpha.20

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/constants.d.ts +2 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +3 -1
  4. package/dist/converter-cli.js +10 -2
  5. package/dist/converter.min.js +172 -103
  6. package/dist/dist.min.js +151 -86
  7. package/dist/es5/constants.js +5 -1
  8. package/dist/es5/constants.js.map +1 -1
  9. package/dist/es5/converter-cli.js +7 -2
  10. package/dist/es5/converter-cli.js.map +1 -1
  11. package/dist/es5/deps-installer/deps-installer.js +1 -1
  12. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +21 -8
  13. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  14. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +7 -6
  15. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  16. package/dist/es5/i3s-converter/helpers/geometry-converter.js +67 -59
  17. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  18. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js +38 -9
  19. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -1
  20. package/dist/es5/i3s-converter/i3s-converter.js +55 -13
  21. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  22. package/dist/es5/i3s-converter/types.js +11 -11
  23. package/dist/es5/i3s-converter/types.js.map +1 -1
  24. package/dist/es5/i3s-server/controllers/slpk-controller.js +1 -1
  25. package/dist/es5/i3s-server/controllers/slpk-controller.js.map +1 -1
  26. package/dist/es5/index.js +3 -3
  27. package/dist/es5/index.js.map +1 -1
  28. package/dist/es5/pgm-loader.js +1 -1
  29. package/dist/es5/slpk-extractor/helpers/{file-handle-provider.js → file-handle-file.js} +9 -9
  30. package/dist/es5/slpk-extractor/helpers/file-handle-file.js.map +1 -0
  31. package/dist/es5/slpk-extractor/slpk-extractor.js +5 -5
  32. package/dist/es5/slpk-extractor/slpk-extractor.js.map +1 -1
  33. package/dist/esm/constants.js +2 -0
  34. package/dist/esm/constants.js.map +1 -1
  35. package/dist/esm/converter-cli.js +7 -2
  36. package/dist/esm/converter-cli.js.map +1 -1
  37. package/dist/esm/deps-installer/deps-installer.js +1 -1
  38. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +18 -6
  39. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  40. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +7 -6
  41. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  42. package/dist/esm/i3s-converter/helpers/geometry-converter.js +28 -20
  43. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  44. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js +28 -9
  45. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -1
  46. package/dist/esm/i3s-converter/i3s-converter.js +39 -9
  47. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  48. package/dist/esm/i3s-converter/types.js +9 -9
  49. package/dist/esm/i3s-converter/types.js.map +1 -1
  50. package/dist/esm/i3s-server/bin/i3s-server.min.js +71 -71
  51. package/dist/esm/i3s-server/controllers/slpk-controller.js +2 -2
  52. package/dist/esm/i3s-server/controllers/slpk-controller.js.map +1 -1
  53. package/dist/esm/index.js +1 -1
  54. package/dist/esm/index.js.map +1 -1
  55. package/dist/esm/pgm-loader.js +1 -1
  56. package/dist/esm/slpk-extractor/helpers/{file-handle-provider.js → file-handle-file.js} +3 -3
  57. package/dist/esm/slpk-extractor/helpers/file-handle-file.js.map +1 -0
  58. package/dist/esm/slpk-extractor/slpk-extractor.js +3 -3
  59. package/dist/esm/slpk-extractor/slpk-extractor.js.map +1 -1
  60. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +11 -1
  61. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -1
  62. package/dist/i3s-converter/helpers/batch-ids-extensions.js +33 -13
  63. package/dist/i3s-converter/helpers/geometry-attributes.js +7 -6
  64. package/dist/i3s-converter/helpers/geometry-converter.d.ts +6 -3
  65. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  66. package/dist/i3s-converter/helpers/geometry-converter.js +42 -35
  67. package/dist/i3s-converter/helpers/preprocess-3d-tiles.d.ts +2 -2
  68. package/dist/i3s-converter/helpers/preprocess-3d-tiles.d.ts.map +1 -1
  69. package/dist/i3s-converter/helpers/preprocess-3d-tiles.js +39 -14
  70. package/dist/i3s-converter/i3s-converter.d.ts +2 -0
  71. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  72. package/dist/i3s-converter/i3s-converter.js +40 -10
  73. package/dist/i3s-converter/types.d.ts +4 -2
  74. package/dist/i3s-converter/types.d.ts.map +1 -1
  75. package/dist/i3s-converter/types.js +11 -11
  76. package/dist/i3s-server/controllers/slpk-controller.js +1 -1
  77. package/dist/index.d.ts +1 -1
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +3 -3
  80. package/dist/slpk-extractor/helpers/{file-handle-provider.d.ts → file-handle-file.d.ts} +5 -5
  81. package/dist/slpk-extractor/helpers/file-handle-file.d.ts.map +1 -0
  82. package/dist/slpk-extractor/helpers/{file-handle-provider.js → file-handle-file.js} +5 -5
  83. package/dist/slpk-extractor/slpk-extractor.js +5 -5
  84. package/dist/slpk-extractor.min.js +32 -32
  85. package/package.json +15 -14
  86. package/src/constants.ts +3 -0
  87. package/src/converter-cli.ts +9 -2
  88. package/src/i3s-converter/helpers/batch-ids-extensions.ts +39 -12
  89. package/src/i3s-converter/helpers/geometry-attributes.ts +15 -8
  90. package/src/i3s-converter/helpers/geometry-converter.ts +66 -35
  91. package/src/i3s-converter/helpers/preprocess-3d-tiles.ts +48 -18
  92. package/src/i3s-converter/i3s-converter.ts +54 -12
  93. package/src/i3s-converter/types.ts +4 -2
  94. package/src/i3s-server/controllers/slpk-controller.ts +2 -2
  95. package/src/index.ts +1 -1
  96. package/src/slpk-extractor/helpers/{file-handle-provider.ts → file-handle-file.ts} +5 -5
  97. package/src/slpk-extractor/slpk-extractor.ts +3 -3
  98. package/dist/es5/slpk-extractor/helpers/file-handle-provider.js.map +0 -1
  99. package/dist/esm/slpk-extractor/helpers/file-handle-provider.js.map +0 -1
  100. package/dist/slpk-extractor/helpers/file-handle-provider.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loaders.gl/tile-converter",
3
- "version": "4.0.0-alpha.19",
3
+ "version": "4.0.0-alpha.20",
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.19",
49
- "@loaders.gl/crypto": "4.0.0-alpha.19",
50
- "@loaders.gl/draco": "4.0.0-alpha.19",
51
- "@loaders.gl/gltf": "4.0.0-alpha.19",
52
- "@loaders.gl/i3s": "4.0.0-alpha.19",
53
- "@loaders.gl/images": "4.0.0-alpha.19",
54
- "@loaders.gl/loader-utils": "4.0.0-alpha.19",
55
- "@loaders.gl/polyfills": "4.0.0-alpha.19",
56
- "@loaders.gl/textures": "4.0.0-alpha.19",
57
- "@loaders.gl/tiles": "4.0.0-alpha.19",
58
- "@loaders.gl/worker-utils": "4.0.0-alpha.19",
59
- "@loaders.gl/zip": "4.0.0-alpha.19",
48
+ "@loaders.gl/3d-tiles": "4.0.0-alpha.20",
49
+ "@loaders.gl/crypto": "4.0.0-alpha.20",
50
+ "@loaders.gl/draco": "4.0.0-alpha.20",
51
+ "@loaders.gl/gltf": "4.0.0-alpha.20",
52
+ "@loaders.gl/i3s": "4.0.0-alpha.20",
53
+ "@loaders.gl/images": "4.0.0-alpha.20",
54
+ "@loaders.gl/loader-utils": "4.0.0-alpha.20",
55
+ "@loaders.gl/polyfills": "4.0.0-alpha.20",
56
+ "@loaders.gl/textures": "4.0.0-alpha.20",
57
+ "@loaders.gl/tiles": "4.0.0-alpha.20",
58
+ "@loaders.gl/worker-utils": "4.0.0-alpha.20",
59
+ "@loaders.gl/zip": "4.0.0-alpha.20",
60
60
  "@math.gl/core": "^3.5.1",
61
61
  "@math.gl/culling": "^3.5.1",
62
62
  "@math.gl/geoid": "^3.5.1",
@@ -66,6 +66,7 @@
66
66
  "crypt": "^0.0.2",
67
67
  "debug": "~4.3.4",
68
68
  "express": "~4.18.2",
69
+ "inquirer": "^8.0.0",
69
70
  "json-map-transform": "^1.2.6",
70
71
  "jszip": "^3.5.0",
71
72
  "md5": "^2.3.0",
@@ -79,7 +80,7 @@
79
80
  "join-images": "^1.1.3",
80
81
  "sharp": "^0.31.3"
81
82
  },
82
- "gitHead": "2ca50ec4e1d312c124eb7c93c60ab6fd17ee833e",
83
+ "gitHead": "ac122e83102657c38207d59c631a5ce4e7aa46bd",
83
84
  "devDependencies": {
84
85
  "@types/express": "^4.17.17",
85
86
  "@types/node": "^20.4.2"
package/src/constants.ts CHANGED
@@ -1,2 +1,5 @@
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';
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-console */
2
2
  import '@loaders.gl/polyfills';
3
3
  import {join} from 'path';
4
+ import inquirer from 'inquirer';
4
5
  import {I3SConverter, Tiles3DConverter} from '@loaders.gl/tile-converter';
5
6
  import {DepsInstaller} from './deps-installer/deps-installer';
6
7
  import {
@@ -49,6 +50,8 @@ type TileConversionOptions = {
49
50
  maxDepth?: number;
50
51
  /** 3DTiles->I3S only. Whether the converter generates *.slpk (Scene Layer Package) I3S output file */
51
52
  slpk: boolean;
53
+ /** Feature metadata class from EXT_FEATURE_METADATA or EXT_STRUCTURAL_METADATA extensions */
54
+ metadataClass?: string;
52
55
  };
53
56
 
54
57
  /* During validation we check that particular options are defined so they can't be undefined */
@@ -132,8 +135,9 @@ function printHelp(): void {
132
135
  console.log(
133
136
  '--generate-textures [Enable KTX2 textures generation if only one of (JPG, PNG) texture is provided or generate JPG texture if only KTX2 is provided]'
134
137
  );
138
+ console.log('--generate-bounding-volumes [Generate obb and mbs bounding volumes from geometry]');
135
139
  console.log(
136
- '--generate-bounding-volumes [Will generate obb and mbs bounding volumes from geometry]'
140
+ '--metadata-class [One of the list of feature metadata classes, detected by converter on "analyze" stage, default: not set]'
137
141
  );
138
142
  console.log('--validate [Enable validation]');
139
143
  process.exit(0); // eslint-disable-line
@@ -175,7 +179,8 @@ async function convert(options: ValidatedTileConversionOptions) {
175
179
  generateTextures: options.generateTextures,
176
180
  generateBoundingVolumes: options.generateBoundingVolumes,
177
181
  validate: options.validate,
178
- instantNodeWriting: options.instantNodeWriting
182
+ instantNodeWriting: options.instantNodeWriting,
183
+ inquirer
179
184
  });
180
185
  break;
181
186
  default:
@@ -292,6 +297,8 @@ function parseOptions(args: string[]): TileConversionOptions {
292
297
  case '--generate-bounding-volumes':
293
298
  opts.generateBoundingVolumes = getBooleanValue(index, args);
294
299
  break;
300
+ case '--metadata-class':
301
+ opts.metadataClass = getStringValue(index, args);
295
302
  case '--help':
296
303
  printHelp();
297
304
  break;
@@ -2,26 +2,54 @@ import {GLTFAccessorPostprocessed, GLTFMeshPrimitivePostprocessed} from '@loader
2
2
  import type {NumericArray} from '@loaders.gl/loader-utils';
3
3
  import type {
4
4
  GLTF_EXT_feature_metadata_FeatureIdTexture,
5
+ GLTF_EXT_feature_metadata_GLTF,
5
6
  GLTF_EXT_feature_metadata_Primitive
6
7
  } from '@loaders.gl/gltf';
7
8
  import {TypedArray} from '@math.gl/core';
8
9
  import {TextureImageProperties} from '../types';
10
+ import {EXT_FEATURE_METADATA, EXT_MESH_FEATURES} from '../../constants';
11
+ import {Tiles3DTileContent} from '@loaders.gl/3d-tiles';
9
12
 
10
- const EXT_MESH_FEATURES = 'EXT_mesh_features';
11
- const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
13
+ /**
14
+ * Get featureTexture by metadataClass
15
+ * @param tileContent - 3d tile content
16
+ * @param metadataClass - user selected feature metadata class name
17
+ * @returns featureTexture key
18
+ */
19
+ export function getTextureByMetadataClass(
20
+ tileContent: Tiles3DTileContent,
21
+ metadataClass?: string
22
+ ): string | null {
23
+ const extFeatureMetadata = tileContent.gltf?.extensions?.[
24
+ EXT_FEATURE_METADATA
25
+ ] as GLTF_EXT_feature_metadata_GLTF;
26
+ if (!extFeatureMetadata?.featureTextures) {
27
+ return null;
28
+ }
29
+ for (const textureKey in extFeatureMetadata.featureTextures) {
30
+ const texture = extFeatureMetadata.featureTextures[textureKey];
31
+ if (texture.class === metadataClass) {
32
+ return textureKey;
33
+ }
34
+ }
35
+ return null;
36
+ }
12
37
 
13
38
  /**
14
39
  * Getting batchIds from 3DTilesNext extensions.
15
40
  * @param attributes - gltf accessors
16
41
  * @param primitive - gltf primitive data
17
42
  * @param images - gltf texture images
43
+ * @param featureTexture - feature texture key
44
+ * @return array of batch IDs
18
45
  */
19
46
  export function handleBatchIdsExtensions(
20
47
  attributes: {
21
48
  [key: string]: GLTFAccessorPostprocessed;
22
49
  },
23
50
  primitive: GLTFMeshPrimitivePostprocessed,
24
- images: (TextureImageProperties | null)[]
51
+ images: (TextureImageProperties | null)[],
52
+ featureTexture: string | null
25
53
  ): NumericArray {
26
54
  const extensions = primitive?.extensions;
27
55
 
@@ -35,7 +63,8 @@ export function handleBatchIdsExtensions(
35
63
  return handleExtFeatureMetadataExtension(
36
64
  attributes,
37
65
  extensionData as GLTF_EXT_feature_metadata_Primitive,
38
- images
66
+ images,
67
+ featureTexture
39
68
  );
40
69
  case EXT_MESH_FEATURES:
41
70
  console.warn('EXT_mesh_features extension is not supported yet');
@@ -51,16 +80,18 @@ export function handleBatchIdsExtensions(
51
80
  /**
52
81
  * Get batchIds from EXT_feature_metadata extension.
53
82
  * Docs - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata
54
- * @param attributes
55
- * @param extFeatureMetadata
56
- * @param textures
83
+ * @param attributes - glTF attributes
84
+ * @param extFeatureMetadata - primitive-level EXT_FEATURE_METADATA extension data
85
+ * @param textures - texture images
86
+ * @param featureTexture - feature texture key
57
87
  */
58
88
  function handleExtFeatureMetadataExtension(
59
89
  attributes: {
60
90
  [key: string]: GLTFAccessorPostprocessed;
61
91
  },
62
92
  extFeatureMetadata: GLTF_EXT_feature_metadata_Primitive,
63
- images: (TextureImageProperties | null)[]
93
+ images: (TextureImageProperties | null)[],
94
+ featureTexture: string | null
64
95
  ): NumericArray {
65
96
  // Take only first extension object to get batchIds attribute name.
66
97
  const featureIdAttribute = extFeatureMetadata?.featureIdAttributes?.[0];
@@ -93,10 +124,6 @@ function handleExtFeatureMetadataExtension(
93
124
  return generateBatchIdsFromTexture(featureIdTexture, textureCoordinates, images);
94
125
  }
95
126
 
96
- // Take only first extension texture to get batchIds from the root EXT_feature_metadata object.
97
- const featureTexture =
98
- extFeatureMetadata?.featureTextures && extFeatureMetadata?.featureTextures[0];
99
-
100
127
  if (featureTexture) {
101
128
  const batchIdsAttribute = attributes[featureTexture];
102
129
  return batchIdsAttribute.value;
@@ -199,19 +199,26 @@ function unifyObjectsByFeatureId(
199
199
 
200
200
  for (let index = 0; index < sortedData.length; index++) {
201
201
  const currentObject = sortedData[index];
202
- const existedObject = uniqueObjects.find((obj) => obj.featureId === currentObject.featureId);
202
+ const existingObject = uniqueObjects.find((obj) => obj.featureId === currentObject.featureId);
203
203
 
204
- if (existedObject) {
205
- existedObject.positions = concatenateTypedArrays(
206
- existedObject.positions,
204
+ if (existingObject) {
205
+ existingObject.positions = concatenateTypedArrays(
206
+ existingObject.positions,
207
207
  currentObject.positions
208
208
  );
209
- existedObject.normals = concatenateTypedArrays(existedObject.normals, currentObject.normals);
210
- existedObject.colors = concatenateTypedArrays(existedObject.colors, currentObject.colors);
211
- existedObject.texCoords = concatenateTypedArrays(
212
- existedObject.texCoords,
209
+ existingObject.normals = concatenateTypedArrays(
210
+ existingObject.normals,
211
+ currentObject.normals
212
+ );
213
+ existingObject.colors = concatenateTypedArrays(existingObject.colors, currentObject.colors);
214
+ existingObject.texCoords = concatenateTypedArrays(
215
+ existingObject.texCoords,
213
216
  currentObject.texCoords
214
217
  );
218
+ existingObject.uvRegions = concatenateTypedArrays(
219
+ existingObject.uvRegions,
220
+ currentObject.uvRegions
221
+ );
215
222
  } else {
216
223
  uniqueObjects.push(currentObject);
217
224
  }
@@ -35,7 +35,7 @@ import {
35
35
  import {NumberArray, TypedArray} from '@loaders.gl/loader-utils';
36
36
  import {Geoid} from '@math.gl/geoid';
37
37
  import {prepareDataForAttributesConversion} from './gltf-attributes';
38
- import {handleBatchIdsExtensions} from './batch-ids-extensions';
38
+ import {getTextureByMetadataClass, handleBatchIdsExtensions} from './batch-ids-extensions';
39
39
  import {checkPropertiesLength, flattenPropertyTableByFeatureIds} from './feature-attributes';
40
40
  import {GL} from '@loaders.gl/math';
41
41
 
@@ -47,6 +47,7 @@ import {GL} from '@loaders.gl/math';
47
47
  import type {GLTFAttributesData, TextureImageProperties, TypedArrayConstructor} from '../types';
48
48
  import {generateSyntheticIndices} from '../../lib/utils/geometry-utils';
49
49
  import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling';
50
+ import {EXT_FEATURE_METADATA, EXT_MESH_FEATURES} from '../../constants';
50
51
 
51
52
  // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
52
53
  const DEFAULT_ROUGHNESS_FACTOR = 1;
@@ -67,9 +68,6 @@ const OBJECT_ID_TYPE = 'Oid32';
67
68
  */
68
69
  const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID'];
69
70
 
70
- const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
71
- const EXT_MESH_FEATURES = 'EXT_mesh_features';
72
-
73
71
  let scratchVector = new Vector3();
74
72
 
75
73
  /**
@@ -88,6 +86,7 @@ let scratchVector = new Vector3();
88
86
  * @param shouldMergeMaterials - Try to merge similar materials to be able to merge meshes into one node
89
87
  * @param geoidHeightModel - model to convert elevation from elipsoidal to geoid
90
88
  * @param libraries - dynamicaly loaded 3rd-party libraries
89
+ * @param metadataClass `- user selected feature metadata class name`
91
90
  * @returns Array of node resources to create one or more i3s nodes
92
91
  */
93
92
  export default async function convertB3dmToI3sGeometry(
@@ -102,7 +101,8 @@ export default async function convertB3dmToI3sGeometry(
102
101
  generateBoundingVolumes: boolean,
103
102
  shouldMergeMaterials: boolean,
104
103
  geoidHeightModel: Geoid,
105
- libraries: Record<string, string>
104
+ libraries: Record<string, string>,
105
+ metadataClass?: string
106
106
  ): Promise<I3SConvertedResources[] | null> {
107
107
  const useCartesianPositions = generateBoundingVolumes;
108
108
  const materialAndTextureList: I3SMaterialWithTexture[] = await convertMaterials(
@@ -115,10 +115,12 @@ export default async function convertB3dmToI3sGeometry(
115
115
  tileTransform,
116
116
  tileBoundingVolume
117
117
  );
118
+ const featureTexture = getTextureByMetadataClass(tileContent, metadataClass);
118
119
  const convertedAttributesMap: Map<string, ConvertedAttributes> = await convertAttributes(
119
120
  dataForAttributesConversion,
120
121
  materialAndTextureList,
121
- useCartesianPositions
122
+ useCartesianPositions,
123
+ featureTexture
122
124
  );
123
125
  /** Usage of worker here brings more overhead than advantage */
124
126
  // const convertedAttributesMap: Map<string, ConvertedAttributes> =
@@ -312,12 +314,14 @@ async function _makeNodeResources({
312
314
  * @param materialAndTextureList - array of data about materials and textures of the content
313
315
  * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
314
316
  * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
317
+ * @param featureTexture - feature texture key
315
318
  * @returns map of converted geometry attributes
316
319
  */
317
320
  export async function convertAttributes(
318
321
  attributesData: GLTFAttributesData,
319
322
  materialAndTextureList: I3SMaterialWithTexture[],
320
- useCartesianPositions: boolean
323
+ useCartesianPositions: boolean,
324
+ featureTexture: string | null
321
325
  ): Promise<Map<string, ConvertedAttributes>> {
322
326
  const {nodes, images, cartographicOrigin, cartesianModelMatrix} = attributesData;
323
327
  const attributesMap = new Map<string, ConvertedAttributes>();
@@ -345,7 +349,9 @@ export async function convertAttributes(
345
349
  cartographicOrigin,
346
350
  cartesianModelMatrix,
347
351
  attributesMap,
348
- useCartesianPositions
352
+ useCartesianPositions,
353
+ undefined,
354
+ featureTexture
349
355
  );
350
356
 
351
357
  for (const attrKey of attributesMap.keys()) {
@@ -379,6 +385,7 @@ export async function convertAttributes(
379
385
  * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
380
386
  * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
381
387
  * @param matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
388
+ * @param featureTexture - feature texture key
382
389
  * @returns {void}
383
390
  */
384
391
  function convertNodes(
@@ -388,7 +395,8 @@ function convertNodes(
388
395
  cartesianModelMatrix: Matrix4,
389
396
  attributesMap: Map<string, ConvertedAttributes>,
390
397
  useCartesianPositions: boolean,
391
- matrix: Matrix4 = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
398
+ matrix: Matrix4 = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
399
+ featureTexture: string | null
392
400
  ) {
393
401
  if (nodes) {
394
402
  for (const node of nodes) {
@@ -399,7 +407,8 @@ function convertNodes(
399
407
  cartesianModelMatrix,
400
408
  attributesMap,
401
409
  useCartesianPositions,
402
- matrix
410
+ matrix,
411
+ featureTexture
403
412
  );
404
413
  }
405
414
  }
@@ -441,11 +450,12 @@ function getCompositeTransformationMatrix(node: GLTFNodePostprocessed, matrix: M
441
450
  * @param images - gltf images array
442
451
  * @param cartographicOrigin - cartographic origin of bounding volume
443
452
  * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
444
- * @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
453
+ * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
445
454
  * attributes
446
455
  * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
447
456
  * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
448
- * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
457
+ * @param matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
458
+ * @param featureTexture - feature texture key
449
459
  */
450
460
  function convertNode(
451
461
  node: GLTFNodePostprocessed,
@@ -454,7 +464,8 @@ function convertNode(
454
464
  cartesianModelMatrix: Matrix4,
455
465
  attributesMap: Map<string, ConvertedAttributes>,
456
466
  useCartesianPositions,
457
- matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
467
+ matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
468
+ featureTexture: string | null
458
469
  ) {
459
470
  const transformationMatrix = getCompositeTransformationMatrix(node, matrix);
460
471
 
@@ -468,7 +479,8 @@ function convertNode(
468
479
  cartesianModelMatrix,
469
480
  attributesMap,
470
481
  useCartesianPositions,
471
- transformationMatrix
482
+ transformationMatrix,
483
+ featureTexture
472
484
  );
473
485
  }
474
486
 
@@ -479,7 +491,8 @@ function convertNode(
479
491
  cartesianModelMatrix,
480
492
  attributesMap,
481
493
  useCartesianPositions,
482
- transformationMatrix
494
+ transformationMatrix,
495
+ featureTexture
483
496
  );
484
497
  }
485
498
 
@@ -491,12 +504,12 @@ function convertNode(
491
504
  * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
492
505
  * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
493
506
  * attributes
494
- * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
507
+ * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
495
508
  * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
496
509
  * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
497
510
  * attributes
498
-
499
- * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
511
+ * @param matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
512
+ * @param featureTexture - feature texture key
500
513
  */
501
514
  function convertMesh(
502
515
  mesh: GLTFMeshPostprocessed,
@@ -505,7 +518,8 @@ function convertMesh(
505
518
  cartesianModelMatrix: Matrix4,
506
519
  attributesMap: Map<string, ConvertedAttributes>,
507
520
  useCartesianPositions = false,
508
- matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
521
+ matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
522
+ featureTexture: string | null
509
523
  ) {
510
524
  for (const primitive of mesh.primitives) {
511
525
  let outputAttributes: ConvertedAttributes | null | undefined = null;
@@ -576,7 +590,7 @@ function convertMesh(
576
590
 
577
591
  outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || [];
578
592
  outputAttributes.featureIndicesGroups.push(
579
- flattenBatchIds(getBatchIds(attributes, primitive, images), indices)
593
+ flattenBatchIds(getBatchIds(attributes, primitive, images, featureTexture), indices)
580
594
  );
581
595
  }
582
596
  }
@@ -803,15 +817,23 @@ function flattenBatchIds(batchedIds: NumberArray, indices: TypedArray): number[]
803
817
  * @param attributes - gltf accessors
804
818
  * @param primitive - gltf primitive data
805
819
  * @param images - gltf texture images
820
+ * @param featureTexture - feature texture key
821
+ * @return batch IDs
806
822
  */
807
823
  function getBatchIds(
808
824
  attributes: {
809
825
  [key: string]: GLTFAccessorPostprocessed;
810
826
  },
811
827
  primitive: GLTFMeshPrimitivePostprocessed,
812
- images: (TextureImageProperties | null)[]
828
+ images: (TextureImageProperties | null)[],
829
+ featureTexture: string | null
813
830
  ): NumberArray {
814
- const batchIds: NumberArray = handleBatchIdsExtensions(attributes, primitive, images);
831
+ const batchIds: NumberArray = handleBatchIdsExtensions(
832
+ attributes,
833
+ primitive,
834
+ images,
835
+ featureTexture
836
+ );
815
837
 
816
838
  if (batchIds.length) {
817
839
  return batchIds;
@@ -1555,9 +1577,13 @@ function generateFeatureIndexAttribute(
1555
1577
  * Find property table in tile
1556
1578
  * For example it can be batchTable for b3dm files or property table in gLTF extension.
1557
1579
  * @param tileContent - 3DTiles tile content
1580
+ * @param metadataClass - - user selected feature metadata class name
1558
1581
  * @return batch table from b3dm / feature properties from EXT_FEATURE_METADATA
1559
1582
  */
1560
- export function getPropertyTable(tileContent: Tiles3DTileContent | null): FeatureTableJson | null {
1583
+ export function getPropertyTable(
1584
+ tileContent: Tiles3DTileContent | null,
1585
+ metadataClass?: string
1586
+ ): FeatureTableJson | null {
1561
1587
  if (!tileContent) {
1562
1588
  return null;
1563
1589
  }
@@ -1576,7 +1602,10 @@ export function getPropertyTable(tileContent: Tiles3DTileContent | null): Featur
1576
1602
  return null;
1577
1603
  }
1578
1604
  case EXT_FEATURE_METADATA: {
1579
- return getPropertyTableFromExtFeatureMetadata(extension as GLTF_EXT_feature_metadata_GLTF);
1605
+ return getPropertyTableFromExtFeatureMetadata(
1606
+ extension as GLTF_EXT_feature_metadata_GLTF,
1607
+ metadataClass
1608
+ );
1580
1609
  }
1581
1610
  default:
1582
1611
  return null;
@@ -1619,10 +1648,11 @@ function getPropertyTableExtension(tileContent: Tiles3DTileContent): {
1619
1648
 
1620
1649
  /**
1621
1650
  * Handle EXT_feature_metadata to get property table
1622
- * @param extension
1651
+ * @param extension - global level of EXT_FEATURE_METADATA extension
1623
1652
  */
1624
1653
  function getPropertyTableFromExtFeatureMetadata(
1625
- extension: GLTF_EXT_feature_metadata_GLTF
1654
+ extension: GLTF_EXT_feature_metadata_GLTF,
1655
+ metadataClass?: string
1626
1656
  ): FeatureTableJson | null {
1627
1657
  if (extension?.featureTables) {
1628
1658
  /**
@@ -1646,15 +1676,16 @@ function getPropertyTableFromExtFeatureMetadata(
1646
1676
  }
1647
1677
 
1648
1678
  if (extension?.featureTextures) {
1649
- /**
1650
- * Take only first feature texture to generate attributes storage info object.
1651
- * TODO: Think about getting data from all feature textures?
1652
- * It can be tricky just because 3dTiles is able to have multiple featureTextures.
1653
- * In I3S we should decide which featureTextures will be passed to geometry data.
1654
- */
1655
- const firstTextureName = Object.keys(extension.featureTextures)?.[0];
1656
- if (firstTextureName) {
1657
- const featureTable = extension?.featureTextures[firstTextureName];
1679
+ let featureTexture: string | undefined;
1680
+ for (const textureKey in extension.featureTextures) {
1681
+ const texture = extension.featureTextures[textureKey];
1682
+ if (texture.class === metadataClass) {
1683
+ featureTexture = textureKey;
1684
+ }
1685
+ }
1686
+
1687
+ if (typeof featureTexture === 'string') {
1688
+ const featureTable = extension?.featureTextures[featureTexture];
1658
1689
  const propertyTable = {};
1659
1690
 
1660
1691
  for (const propertyName in featureTable.properties) {
@@ -1,20 +1,21 @@
1
1
  import {Tiles3DTileContent} from '@loaders.gl/3d-tiles';
2
- import {GltfPrimitiveModeString, PreprocessData} from '../types';
3
- import {GLTF, GLTFLoader} from '@loaders.gl/gltf';
2
+ import {GLTFPrimitiveModeString, PreprocessData} from '../types';
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
6
 
6
7
  /**
7
8
  * glTF primitive modes
8
9
  * @see https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode
9
10
  */
10
11
  export const GLTF_PRIMITIVE_MODES = [
11
- GltfPrimitiveModeString.POINTS, // 0
12
- GltfPrimitiveModeString.LINES, // 1
13
- GltfPrimitiveModeString.LINE_LOOP, // 2
14
- GltfPrimitiveModeString.LINE_STRIP, // 3
15
- GltfPrimitiveModeString.TRIANGLES, // 4
16
- GltfPrimitiveModeString.TRIANGLE_STRIP, // 5
17
- GltfPrimitiveModeString.TRIANGLE_FAN // 6
12
+ GLTFPrimitiveModeString.POINTS, // 0
13
+ GLTFPrimitiveModeString.LINES, // 1
14
+ GLTFPrimitiveModeString.LINE_LOOP, // 2
15
+ GLTFPrimitiveModeString.LINE_STRIP, // 3
16
+ GLTFPrimitiveModeString.TRIANGLES, // 4
17
+ GLTFPrimitiveModeString.TRIANGLE_STRIP, // 5
18
+ GLTFPrimitiveModeString.TRIANGLE_FAN // 6
18
19
  ];
19
20
 
20
21
  /**
@@ -26,11 +27,12 @@ export const GLTF_PRIMITIVE_MODES = [
26
27
  export const analyzeTileContent = async (
27
28
  tileContent: Tiles3DTileContent | null
28
29
  ): Promise<PreprocessData> => {
29
- const result: PreprocessData = {
30
- meshTopologyTypes: new Set()
30
+ const defaultResult = {
31
+ meshTopologyTypes: new Set<GLTFPrimitiveModeString>(),
32
+ metadataClasses: new Set<string>()
31
33
  };
32
34
  if (!tileContent?.gltfArrayBuffer) {
33
- return result;
35
+ return defaultResult;
34
36
  }
35
37
 
36
38
  const gltfData = await parse(tileContent.gltfArrayBuffer, GLTFLoader, {
@@ -39,11 +41,14 @@ export const analyzeTileContent = async (
39
41
  const gltf = gltfData.json;
40
42
 
41
43
  if (!gltf) {
42
- return result;
44
+ return defaultResult;
43
45
  }
44
- const meshTypes = getMeshTypesFromGltf(gltf);
45
- result.meshTopologyTypes = meshTypes;
46
- return result;
46
+ const meshTopologyTypes = getMeshTypesFromGltf(gltf);
47
+ const metadataClasses = getMetadataClassesFromGltf(gltf);
48
+ return {
49
+ meshTopologyTypes,
50
+ metadataClasses
51
+ };
47
52
  };
48
53
 
49
54
  /**
@@ -51,8 +56,8 @@ export const analyzeTileContent = async (
51
56
  * @param gltfJson - JSON part of GLB content
52
57
  * @returns array of mesh types found
53
58
  */
54
- const getMeshTypesFromGltf = (gltfJson: GLTF): Set<GltfPrimitiveModeString> => {
55
- const result: Set<GltfPrimitiveModeString> = new Set();
59
+ const getMeshTypesFromGltf = (gltfJson: GLTF): Set<GLTFPrimitiveModeString> => {
60
+ const result: Set<GLTFPrimitiveModeString> = new Set();
56
61
  for (const mesh of gltfJson.meshes || []) {
57
62
  for (const primitive of mesh.primitives) {
58
63
  let {mode} = primitive;
@@ -65,6 +70,26 @@ const getMeshTypesFromGltf = (gltfJson: GLTF): Set<GltfPrimitiveModeString> => {
65
70
  return result;
66
71
  };
67
72
 
73
+ /**
74
+ * Get feature metadata classes from glTF
75
+ * @param gltfJson - JSON part of GLB content
76
+ * @returns array of classes
77
+ */
78
+ const getMetadataClassesFromGltf = (gltfJson: GLTF): Set<string> => {
79
+ const result: Set<string> = new Set();
80
+
81
+ const classes = (gltfJson.extensions?.[EXT_FEATURE_METADATA] as GLTF_EXT_feature_metadata_GLTF)
82
+ ?.schema?.classes;
83
+
84
+ if (classes) {
85
+ for (const classKey of Object.keys(classes)) {
86
+ result.add(classKey);
87
+ }
88
+ }
89
+
90
+ return result;
91
+ };
92
+
68
93
  /**
69
94
  * Merge object2 into object1
70
95
  * @param object1
@@ -76,4 +101,9 @@ export const mergePreprocessData = (object1: PreprocessData, object2: Preprocess
76
101
  for (const type of object2.meshTopologyTypes) {
77
102
  object1.meshTopologyTypes.add(type);
78
103
  }
104
+
105
+ // Merge feature metadata classes
106
+ for (const metadataClass of object2.metadataClasses) {
107
+ object1.metadataClasses.add(metadataClass);
108
+ }
79
109
  };