@loaders.gl/tile-converter 3.4.6 → 4.0.0-alpha.10

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 (179) hide show
  1. package/bin/converter.js +1 -1
  2. package/dist/3d-tiles-attributes-worker.js +2 -2
  3. package/dist/3d-tiles-attributes-worker.js.map +3 -3
  4. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -1
  5. package/dist/3d-tiles-converter/3d-tiles-converter.js +4 -3
  6. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts +0 -8
  7. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts.map +1 -1
  8. package/dist/3d-tiles-converter/helpers/b3dm-converter.js +2 -15
  9. package/dist/converter.min.js +75 -76
  10. package/dist/deps-installer/deps-installer.js +4 -4
  11. package/dist/dist.min.js +2101 -2120
  12. package/dist/es5/3d-tiles-attributes-worker.js +1 -1
  13. package/dist/es5/3d-tiles-attributes-worker.js.map +1 -1
  14. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js +0 -3
  15. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  16. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js +2 -10
  17. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  18. package/dist/es5/deps-installer/deps-installer.js +4 -4
  19. package/dist/es5/deps-installer/deps-installer.js.map +1 -1
  20. package/dist/es5/i3s-attributes-worker.js +1 -1
  21. package/dist/es5/i3s-attributes-worker.js.map +1 -1
  22. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +4 -4
  23. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  24. package/dist/es5/i3s-converter/helpers/coordinate-converter.js +6 -7
  25. package/dist/es5/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  26. package/dist/es5/i3s-converter/helpers/geometry-converter.js +49 -30
  27. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  28. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +46 -16
  29. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  30. package/dist/es5/i3s-converter/helpers/load-3d-tiles.js +82 -0
  31. package/dist/es5/i3s-converter/helpers/load-3d-tiles.js.map +1 -0
  32. package/dist/es5/i3s-converter/helpers/node-index-document.js +74 -45
  33. package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -1
  34. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js +111 -0
  35. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -0
  36. package/dist/es5/i3s-converter/helpers/tileset-traversal.js +82 -0
  37. package/dist/es5/i3s-converter/helpers/tileset-traversal.js.map +1 -0
  38. package/dist/es5/i3s-converter/i3s-converter.js +545 -523
  39. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  40. package/dist/es5/i3s-converter/types.js +16 -0
  41. package/dist/es5/i3s-converter/types.js.map +1 -1
  42. package/dist/es5/i3s-server/README.md +19 -0
  43. package/dist/es5/i3s-server/app.js +10 -1
  44. package/dist/es5/i3s-server/app.js.map +1 -1
  45. package/dist/es5/i3s-server/controllers/slpk-controller.js +84 -0
  46. package/dist/es5/i3s-server/controllers/slpk-controller.js.map +1 -0
  47. package/dist/es5/i3s-server/routes/slpk-router.js +71 -0
  48. package/dist/es5/i3s-server/routes/slpk-router.js.map +1 -0
  49. package/dist/es5/i3s-server/utils/create-scene-server.js +17 -0
  50. package/dist/es5/i3s-server/utils/create-scene-server.js.map +1 -0
  51. package/dist/es5/lib/utils/file-utils.js +1 -1
  52. package/dist/es5/lib/utils/file-utils.js.map +1 -1
  53. package/dist/es5/lib/utils/geometry-utils.js +15 -0
  54. package/dist/es5/lib/utils/geometry-utils.js.map +1 -0
  55. package/dist/es5/lib/utils/lod-conversion-utils.js.map +1 -1
  56. package/dist/es5/pgm-loader.js +1 -1
  57. package/dist/es5/pgm-loader.js.map +1 -1
  58. package/dist/esm/3d-tiles-attributes-worker.js +1 -1
  59. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
  60. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js +0 -3
  61. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  62. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js +2 -8
  63. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  64. package/dist/esm/deps-installer/deps-installer.js +4 -4
  65. package/dist/esm/deps-installer/deps-installer.js.map +1 -1
  66. package/dist/esm/i3s-attributes-worker.js +1 -1
  67. package/dist/esm/i3s-attributes-worker.js.map +1 -1
  68. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +4 -4
  69. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  70. package/dist/esm/i3s-converter/helpers/coordinate-converter.js +6 -7
  71. package/dist/esm/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  72. package/dist/esm/i3s-converter/helpers/geometry-converter.js +37 -18
  73. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  74. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +50 -16
  75. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  76. package/dist/esm/i3s-converter/helpers/load-3d-tiles.js +35 -0
  77. package/dist/esm/i3s-converter/helpers/load-3d-tiles.js.map +1 -0
  78. package/dist/esm/i3s-converter/helpers/node-index-document.js +14 -1
  79. package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -1
  80. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js +48 -0
  81. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -0
  82. package/dist/esm/i3s-converter/helpers/tileset-traversal.js +14 -0
  83. package/dist/esm/i3s-converter/helpers/tileset-traversal.js.map +1 -0
  84. package/dist/esm/i3s-converter/i3s-converter.js +134 -127
  85. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  86. package/dist/esm/i3s-converter/types.js +10 -1
  87. package/dist/esm/i3s-converter/types.js.map +1 -1
  88. package/dist/esm/i3s-server/README.md +19 -0
  89. package/dist/esm/i3s-server/app.js +11 -1
  90. package/dist/esm/i3s-server/app.js.map +1 -1
  91. package/dist/esm/i3s-server/controllers/slpk-controller.js +36 -0
  92. package/dist/esm/i3s-server/controllers/slpk-controller.js.map +1 -0
  93. package/dist/esm/i3s-server/routes/slpk-router.js +33 -0
  94. package/dist/esm/i3s-server/routes/slpk-router.js.map +1 -0
  95. package/dist/esm/i3s-server/utils/create-scene-server.js +16 -0
  96. package/dist/esm/i3s-server/utils/create-scene-server.js.map +1 -0
  97. package/dist/esm/lib/utils/file-utils.js +1 -1
  98. package/dist/esm/lib/utils/file-utils.js.map +1 -1
  99. package/dist/esm/lib/utils/geometry-utils.js +8 -0
  100. package/dist/esm/lib/utils/geometry-utils.js.map +1 -0
  101. package/dist/esm/lib/utils/lod-conversion-utils.js.map +1 -1
  102. package/dist/esm/pgm-loader.js +1 -1
  103. package/dist/esm/pgm-loader.js.map +1 -1
  104. package/dist/i3s-attributes-worker.d.ts +10 -2
  105. package/dist/i3s-attributes-worker.d.ts.map +1 -1
  106. package/dist/i3s-attributes-worker.js +2 -2
  107. package/dist/i3s-attributes-worker.js.map +3 -3
  108. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +4 -2
  109. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -1
  110. package/dist/i3s-converter/helpers/batch-ids-extensions.js +4 -7
  111. package/dist/i3s-converter/helpers/coordinate-converter.d.ts +3 -4
  112. package/dist/i3s-converter/helpers/coordinate-converter.d.ts.map +1 -1
  113. package/dist/i3s-converter/helpers/coordinate-converter.js +8 -9
  114. package/dist/i3s-converter/helpers/geometry-converter.d.ts +9 -4
  115. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  116. package/dist/i3s-converter/helpers/geometry-converter.js +84 -54
  117. package/dist/i3s-converter/helpers/gltf-attributes.d.ts +22 -3
  118. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
  119. package/dist/i3s-converter/helpers/gltf-attributes.js +62 -22
  120. package/dist/i3s-converter/helpers/load-3d-tiles.d.ts +18 -0
  121. package/dist/i3s-converter/helpers/load-3d-tiles.d.ts.map +1 -0
  122. package/dist/i3s-converter/helpers/load-3d-tiles.js +53 -0
  123. package/dist/i3s-converter/helpers/node-index-document.d.ts +8 -0
  124. package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -1
  125. package/dist/i3s-converter/helpers/node-index-document.js +20 -2
  126. package/dist/i3s-converter/helpers/preprocess-3d-tiles.d.ts +23 -0
  127. package/dist/i3s-converter/helpers/preprocess-3d-tiles.d.ts.map +1 -0
  128. package/dist/i3s-converter/helpers/preprocess-3d-tiles.js +76 -0
  129. package/dist/i3s-converter/helpers/tileset-traversal.d.ts +25 -0
  130. package/dist/i3s-converter/helpers/tileset-traversal.d.ts.map +1 -0
  131. package/dist/i3s-converter/helpers/tileset-traversal.js +29 -0
  132. package/dist/i3s-converter/i3s-converter.d.ts +40 -40
  133. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  134. package/dist/i3s-converter/i3s-converter.js +150 -125
  135. package/dist/i3s-converter/types.d.ts +18 -0
  136. package/dist/i3s-converter/types.d.ts.map +1 -1
  137. package/dist/i3s-converter/types.js +15 -0
  138. package/dist/i3s-server/app.d.ts.map +1 -1
  139. package/dist/i3s-server/app.js +9 -1
  140. package/dist/i3s-server/controllers/slpk-controller.d.ts +3 -0
  141. package/dist/i3s-server/controllers/slpk-controller.d.ts.map +1 -0
  142. package/dist/i3s-server/controllers/slpk-controller.js +32 -0
  143. package/dist/i3s-server/routes/slpk-router.d.ts +3 -0
  144. package/dist/i3s-server/routes/slpk-router.d.ts.map +1 -0
  145. package/dist/i3s-server/routes/slpk-router.js +33 -0
  146. package/dist/i3s-server/utils/create-scene-server.d.ts +11 -0
  147. package/dist/i3s-server/utils/create-scene-server.d.ts.map +1 -0
  148. package/dist/i3s-server/utils/create-scene-server.js +14 -0
  149. package/dist/lib/utils/file-utils.d.ts.map +1 -1
  150. package/dist/lib/utils/file-utils.js +2 -1
  151. package/dist/lib/utils/geometry-utils.d.ts +9 -0
  152. package/dist/lib/utils/geometry-utils.d.ts.map +1 -0
  153. package/dist/lib/utils/geometry-utils.js +18 -0
  154. package/dist/lib/utils/lod-conversion-utils.d.ts +3 -2
  155. package/dist/lib/utils/lod-conversion-utils.d.ts.map +1 -1
  156. package/dist/lib/utils/lod-conversion-utils.js +1 -1
  157. package/package.json +15 -16
  158. package/src/3d-tiles-converter/3d-tiles-converter.ts +9 -8
  159. package/src/3d-tiles-converter/helpers/b3dm-converter.ts +2 -16
  160. package/src/deps-installer/deps-installer.ts +4 -4
  161. package/src/i3s-attributes-worker.ts +11 -2
  162. package/src/i3s-converter/helpers/batch-ids-extensions.ts +15 -19
  163. package/src/i3s-converter/helpers/coordinate-converter.ts +11 -10
  164. package/src/i3s-converter/helpers/geometry-converter.ts +154 -95
  165. package/src/i3s-converter/helpers/gltf-attributes.ts +85 -25
  166. package/src/i3s-converter/helpers/load-3d-tiles.ts +68 -0
  167. package/src/i3s-converter/helpers/node-index-document.ts +22 -2
  168. package/src/i3s-converter/helpers/preprocess-3d-tiles.ts +81 -0
  169. package/src/i3s-converter/helpers/tileset-traversal.ts +51 -0
  170. package/src/i3s-converter/i3s-converter.ts +229 -178
  171. package/src/i3s-converter/types.ts +20 -0
  172. package/src/i3s-server/README.md +19 -0
  173. package/src/i3s-server/app.js +8 -1
  174. package/src/i3s-server/controllers/slpk-controller.js +38 -0
  175. package/src/i3s-server/routes/slpk-router.js +33 -0
  176. package/src/i3s-server/utils/create-scene-server.js +15 -0
  177. package/src/lib/utils/file-utils.ts +2 -1
  178. package/src/lib/utils/geometry-utils.ts +14 -0
  179. package/src/lib/utils/lod-conversion-utils.ts +6 -2
@@ -1,7 +1,12 @@
1
1
  // loaders.gl, MIT license
2
2
 
3
- import type {Tileset3DProps} from '@loaders.gl/tiles';
4
- import type {FeatureTableJson} from '@loaders.gl/3d-tiles';
3
+ import type {
4
+ FeatureTableJson,
5
+ Tiles3DLoaderOptions,
6
+ Tiles3DTileContent,
7
+ Tiles3DTileJSONPostprocessed,
8
+ Tiles3DTilesetJSONPostprocessed
9
+ } from '@loaders.gl/3d-tiles';
5
10
  import type {WriteQueueItem} from '../lib/utils/write-queue';
6
11
  import type {
7
12
  SceneLayer3D,
@@ -10,7 +15,6 @@ import type {
10
15
  NodeInPage
11
16
  } from '@loaders.gl/i3s';
12
17
  import {load, encode, fetchFile, getLoaderOptions, isBrowser} from '@loaders.gl/core';
13
- import {Tileset3D} from '@loaders.gl/tiles';
14
18
  import {CesiumIonLoader, Tiles3DLoader} from '@loaders.gl/3d-tiles';
15
19
  import {Geoid} from '@math.gl/geoid';
16
20
  import {join} from 'path';
@@ -41,15 +45,17 @@ import {LAYERS as layersTemplate} from './json-templates/layers';
41
45
  import {GEOMETRY_DEFINITION as geometryDefinitionTemlate} from './json-templates/geometry-definitions';
42
46
  import {SHARED_RESOURCES as sharedResourcesTemplate} from './json-templates/shared-resources';
43
47
  import {validateNodeBoundingVolumes} from './helpers/node-debug';
44
- // loaders.gl, MIT license
45
-
46
- import {Tile3D} from '@loaders.gl/tiles';
47
48
  import {KTX2BasisWriterWorker} from '@loaders.gl/textures';
48
49
  import {LoaderWithParser} from '@loaders.gl/loader-utils';
49
- import {I3SMaterialDefinition, TextureSetDefinitionFormats} from '@loaders.gl/i3s/src/types';
50
+ import {I3SMaterialDefinition, TextureSetDefinitionFormats} from '@loaders.gl/i3s';
50
51
  import {ImageWriter} from '@loaders.gl/images';
51
52
  import {GLTFImagePostprocessed} from '@loaders.gl/gltf';
52
- import {I3SConvertedResources, SharedResourcesArrays} from './types';
53
+ import {
54
+ GltfPrimitiveModeString,
55
+ I3SConvertedResources,
56
+ PreprocessData,
57
+ SharedResourcesArrays
58
+ } from './types';
53
59
  import {getWorkerURL, WorkerFarm} from '@loaders.gl/worker-utils';
54
60
  import {DracoWriterWorker} from '@loaders.gl/draco';
55
61
  import WriteQueue from '../lib/utils/write-queue';
@@ -63,6 +69,12 @@ import {
63
69
  getFieldAttributeType
64
70
  } from './helpers/feature-attributes';
65
71
  import {NodeIndexDocument} from './helpers/node-index-document';
72
+ import {loadNestedTileset, loadTile3DContent} from './helpers/load-3d-tiles';
73
+ import {Matrix4} from '@math.gl/core';
74
+ import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling';
75
+ import {createBoundingVolume} from '@loaders.gl/tiles';
76
+ import {TraversalConversionProps, traverseDatasetWith} from './helpers/tileset-traversal';
77
+ import {analyzeTileContent, mergePreprocessData} from './helpers/preprocess-3d-tiles';
66
78
 
67
79
  const ION_DEFAULT_TOKEN =
68
80
  process.env?.IonToken || // eslint-disable-line
@@ -96,7 +108,19 @@ export default class I3SConverter {
96
108
  boundingVolumeWarnings?: string[] = [];
97
109
  conversionStartTime: [number, number] = [0, 0];
98
110
  refreshTokenTime: [number, number] = [0, 0];
99
- sourceTileset: Tileset3D | null = null;
111
+ sourceTileset: Tiles3DTilesetJSONPostprocessed | null = null;
112
+ loadOptions: Tiles3DLoaderOptions = {
113
+ _nodeWorkers: true,
114
+ reuseWorkers: true,
115
+ basis: {
116
+ format: 'rgba32',
117
+ // We need to load local fs workers because nodejs can't load workers from the Internet
118
+ workerUrl: './modules/textures/dist/basis-worker-node.js'
119
+ },
120
+ // We need to load local fs workers because nodejs can't load workers from the Internet
121
+ draco: {workerUrl: './modules/draco/dist/draco-worker-node.js'},
122
+ fetch: {}
123
+ };
100
124
  geoidHeightModel: Geoid | null = null;
101
125
  Loader: LoaderWithParser = Tiles3DLoader;
102
126
  generateTextures: boolean;
@@ -105,6 +129,9 @@ export default class I3SConverter {
105
129
  workerSource: {[key: string]: string} = {};
106
130
  writeQueue: WriteQueue<WriteQueueItem> = new WriteQueue();
107
131
  compressList: string[] | null = null;
132
+ preprocessData: PreprocessData = {
133
+ meshTopologyTypes: new Set()
134
+ };
108
135
 
109
136
  constructor() {
110
137
  this.nodePages = new NodePages(writeFile, HARDCODED_NODES_PER_PAGE, this);
@@ -160,7 +187,7 @@ export default class I3SConverter {
160
187
  generateTextures?: boolean;
161
188
  generateBoundingVolumes?: boolean;
162
189
  instantNodeWriting?: boolean;
163
- }): Promise<any> {
190
+ }): Promise<string> {
164
191
  if (isBrowser) {
165
192
  console.log(BROWSER_ERROR_MESSAGE);
166
193
  return BROWSER_ERROR_MESSAGE;
@@ -214,40 +241,86 @@ export default class I3SConverter {
214
241
 
215
242
  try {
216
243
  const preloadOptions = await this._fetchPreloadOptions();
217
- const tilesetOptions: Tileset3DProps = {
218
- loadOptions: {
219
- _nodeWorkers: true,
220
- reuseWorkers: true,
221
- basis: {format: 'rgba32'},
222
- 'basis-nodejs': {
223
- format: 'rgba32',
224
- workerUrl: './modules/textures/dist/basis-nodejs-worker.js'
225
- },
226
- 'draco-nodejs': {workerUrl: './modules/draco/dist/draco-nodejs-worker.js'}
227
- }
228
- };
229
244
  if (preloadOptions.headers) {
230
- tilesetOptions.loadOptions!.fetch = {headers: preloadOptions.headers};
245
+ this.loadOptions.fetch = {headers: preloadOptions.headers};
246
+ }
247
+ this.sourceTileset = await load(inputUrl, this.Loader, this.loadOptions);
248
+
249
+ const preprocessResult = await this.preprocessConversion();
250
+
251
+ if (preprocessResult) {
252
+ await this._createAndSaveTileset(outputPath, tilesetName);
253
+ await this._finishConversion({slpk: Boolean(slpk), outputPath, tilesetName});
231
254
  }
232
- Object.assign(tilesetOptions, preloadOptions);
233
- const sourceTilesetJson = await load(inputUrl, this.Loader, tilesetOptions.loadOptions);
234
- // console.log(tilesetJson); // eslint-disable-line
235
- this.sourceTileset = new Tileset3D(sourceTilesetJson, tilesetOptions);
236
-
237
- await this._createAndSaveTileset(
238
- outputPath,
239
- tilesetName,
240
- sourceTilesetJson?.root?.boundingVolume?.region
241
- );
242
- await this._finishConversion({slpk: Boolean(slpk), outputPath, tilesetName});
243
- return sourceTilesetJson;
244
255
  } catch (error) {
245
256
  throw error;
246
257
  } finally {
258
+ await this.writeQueue.finalize();
247
259
  // Clean up worker pools
248
260
  const workerFarm = WorkerFarm.getWorkerFarm({});
249
261
  workerFarm.destroy();
250
262
  }
263
+ return 'success';
264
+ }
265
+
266
+ /**
267
+ * Preprocess stage of the tile converter. Traverse all the tiles tree and
268
+ * check a tile content to be sure that the data is supported
269
+ * @returns true - the conversion is possible, false - the tileset's content is not supported
270
+ */
271
+ private async preprocessConversion(): Promise<boolean> {
272
+ console.log(`Analyze source tileset`);
273
+ const sourceRootTile: Tiles3DTileJSONPostprocessed = this.sourceTileset!.root!;
274
+ await traverseDatasetWith<null>(
275
+ sourceRootTile,
276
+ null,
277
+ this.analyzeTile.bind(this),
278
+ undefined,
279
+ this.options.maxDepth
280
+ );
281
+ const {meshTopologyTypes} = this.preprocessData;
282
+ console.log(`------------------------------------------------`);
283
+ console.log(`Preprocess results:`);
284
+ console.log(`glTF mesh topology types: ${Array.from(meshTopologyTypes).join(', ')}`);
285
+ console.log(`------------------------------------------------`);
286
+ if (
287
+ !meshTopologyTypes.has(GltfPrimitiveModeString.TRIANGLES) &&
288
+ !meshTopologyTypes.has(GltfPrimitiveModeString.TRIANGLE_STRIP)
289
+ ) {
290
+ console.log(
291
+ 'The tileset is of unsupported mesh topology types. The conversion will be interrupted.'
292
+ );
293
+ console.log(`------------------------------------------------`);
294
+ return false;
295
+ }
296
+ return true;
297
+ }
298
+
299
+ /**
300
+ * Analyze a tile content. The callback for preprocess stage.
301
+ * @param sourceTile - 3DTiles tile JSON metadata
302
+ * @param traversalProps - mandatory argument but it is not used for the preprocess stage
303
+ * @returns - nothing
304
+ */
305
+ private async analyzeTile(
306
+ sourceTile: Tiles3DTileJSONPostprocessed,
307
+ traversalProps: null
308
+ ): Promise<null> {
309
+ if (sourceTile.type === 'json') {
310
+ await loadNestedTileset(this.sourceTileset, sourceTile, this.loadOptions);
311
+ return null;
312
+ }
313
+ if (sourceTile.id) {
314
+ console.log(`[analyze]: ${sourceTile.id}`); // eslint-disable-line
315
+ }
316
+ const tileContent = await loadTile3DContent(this.sourceTileset, sourceTile, {
317
+ ...this.loadOptions,
318
+ '3d-tiles': {...this.loadOptions['3d-tiles'], loadGLTF: false}
319
+ });
320
+ const tilePreprocessData = await analyzeTileContent(sourceTile, tileContent);
321
+ mergePreprocessData(this.preprocessData, tilePreprocessData);
322
+
323
+ return null;
251
324
  }
252
325
 
253
326
  /**
@@ -255,11 +328,7 @@ export default class I3SConverter {
255
328
  * @param outputPath - path to save output data
256
329
  * @param tilesetName - new tileset path
257
330
  */
258
- private async _createAndSaveTileset(
259
- outputPath: string,
260
- tilesetName: string,
261
- boundingVolumeRegion?: number[]
262
- ): Promise<void> {
331
+ private async _createAndSaveTileset(outputPath: string, tilesetName: string): Promise<void> {
263
332
  const tilesetPath = join(`${outputPath}`, `${tilesetName}`);
264
333
  // Removing the tilesetPath needed to exclude erroneous files after conversion
265
334
  try {
@@ -270,13 +339,24 @@ export default class I3SConverter {
270
339
 
271
340
  this.layers0Path = join(tilesetPath, 'SceneServer', 'layers', '0');
272
341
 
273
- this._formLayers0(tilesetName, boundingVolumeRegion);
274
-
275
342
  this.materialDefinitions = [];
276
343
  this.materialMap = new Map();
277
344
 
278
- const sourceRootTile: Tile3D = this.sourceTileset!.root!;
279
- const boundingVolumes = createBoundingVolumes(sourceRootTile, this.geoidHeightModel!);
345
+ const sourceRootTile: Tiles3DTileJSONPostprocessed = this.sourceTileset!.root!;
346
+ const sourceBoundingVolume = createBoundingVolume(
347
+ sourceRootTile.boundingVolume,
348
+ new Matrix4(sourceRootTile.transform),
349
+ null
350
+ );
351
+
352
+ this._formLayers0(
353
+ tilesetName,
354
+ sourceBoundingVolume,
355
+ this.sourceTileset?.root?.boundingVolume?.region
356
+ );
357
+
358
+ const boundingVolumes = createBoundingVolumes(sourceBoundingVolume, this.geoidHeightModel!);
359
+
280
360
  await this.nodePages.push({
281
361
  index: 0,
282
362
  lodThreshold: 0,
@@ -285,7 +365,16 @@ export default class I3SConverter {
285
365
  });
286
366
 
287
367
  const rootNode = await NodeIndexDocument.createRootNode(boundingVolumes, this);
288
- await this._convertNodesTree(rootNode, sourceRootTile);
368
+ await traverseDatasetWith<TraversalConversionProps>(
369
+ sourceRootTile,
370
+ {
371
+ transform: new Matrix4(sourceRootTile.transform),
372
+ parentNodes: [rootNode]
373
+ },
374
+ this.convertTile.bind(this),
375
+ this.finalizeTile.bind(this),
376
+ this.options.maxDepth
377
+ );
289
378
 
290
379
  this.layers0!.materialDefinitions = this.materialDefinitions;
291
380
  // @ts-ignore
@@ -316,12 +405,19 @@ export default class I3SConverter {
316
405
 
317
406
  /**
318
407
  * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
319
- * @param tilesetName - Name of layer
408
+ * @param tilesetName - Name of layer
409
+ * @param sourceBoundingVolume - initialized bounding volume of the source root tile
410
+ * @param boundingVolumeRegion - region bounding volume of the source root tile
320
411
  */
321
- private _formLayers0(tilesetName: string, boundingVolumeRegion?: number[]): void {
322
- const fullExtent = convertBoundingVolumeToI3SFullExtent(
323
- this.sourceTileset?.boundingVolume || this.sourceTileset?.root?.boundingVolume
324
- );
412
+ private _formLayers0(
413
+ tilesetName: string,
414
+ sourceBoundingVolume: OrientedBoundingBox | BoundingSphere,
415
+ boundingVolumeRegion?: number[]
416
+ ): void {
417
+ if (!this.sourceTileset?.root) {
418
+ return;
419
+ }
420
+ const fullExtent = convertBoundingVolumeToI3SFullExtent(sourceBoundingVolume);
325
421
  if (boundingVolumeRegion) {
326
422
  fullExtent.zmin = boundingVolumeRegion[4];
327
423
  fullExtent.zmax = boundingVolumeRegion[5];
@@ -345,33 +441,6 @@ export default class I3SConverter {
345
441
  this.layers0 = transform(layers0data, layersTemplate());
346
442
  }
347
443
 
348
- /**
349
- * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
350
- * @param rootNode - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
351
- * @param sourceRootTile - Source (3DTile) tile data
352
- */
353
- private async _convertNodesTree(
354
- rootNode: NodeIndexDocument,
355
- sourceRootTile: Tile3D
356
- ): Promise<void> {
357
- await this.sourceTileset!._loadTile(sourceRootTile);
358
- if (this.isContentSupported(sourceRootTile)) {
359
- const childNodes = await this._createNode(rootNode, sourceRootTile, 0);
360
- for (const childNode of childNodes) {
361
- await childNode.save();
362
- }
363
- await rootNode.addChildren(childNodes);
364
- } else {
365
- await this._addChildrenWithNeighborsAndWriteFile({
366
- parentNode: rootNode,
367
- sourceTiles: sourceRootTile.children,
368
- level: 1
369
- });
370
- }
371
- await sourceRootTile.unloadContent();
372
- await rootNode.save();
373
- }
374
-
375
444
  /**
376
445
  * Write 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md in file
377
446
  */
@@ -432,113 +501,90 @@ export default class I3SConverter {
432
501
  }
433
502
 
434
503
  /**
435
- * Add child nodes recursively and write them to files
436
- * @param data - arguments
437
- * @param data.parentNode - 3DNodeIndexDocument of parent node
438
- * @param data.sourceTiles - array of source child nodes
439
- * @param data.level - level of node (distanse to root node in the tree)
440
- */
441
- private async _addChildrenWithNeighborsAndWriteFile(data: {
442
- parentNode: NodeIndexDocument;
443
- sourceTiles: Tile3D[];
444
- level: number;
445
- }): Promise<void> {
446
- await this._addChildren(data);
447
- await data.parentNode.addNeighbors();
448
- }
449
-
450
- /**
451
- * Convert nested subtree of 3DTiles dataset
452
- * @param param0
453
- * @param data.parentNode - 3DNodeIndexDocument of parent node
454
- * @param param0.sourceTile - source 3DTile data
455
- * @param param0.level - tree level
504
+ * Convert the specific 3DTiles tile to I3S nodes.
505
+ * This is callback function for the traversal generic function
506
+ * @param sourceTile - current 3DTiles tile JSON metadata
507
+ * @param traversalProps - traversal properties calculated recursively
508
+ * @returns - traversal properties for the child tiles
456
509
  */
457
- private async convertNestedTileset({
458
- parentNode,
459
- sourceTile,
460
- level
461
- }: {
462
- parentNode: NodeIndexDocument;
463
- sourceTile: Tile3D;
464
- level: number;
465
- }) {
466
- await this.sourceTileset!._loadTile(sourceTile);
467
- await this._addChildren({
468
- parentNode,
469
- sourceTiles: sourceTile.children,
470
- level: level + 1
471
- });
472
- await sourceTile.unloadContent();
473
- }
510
+ private async convertTile(
511
+ sourceTile: Tiles3DTileJSONPostprocessed,
512
+ traversalProps: TraversalConversionProps
513
+ ): Promise<TraversalConversionProps> {
514
+ if (sourceTile.type === 'json' || sourceTile.type === 'empty') {
515
+ if (sourceTile.type === 'json') {
516
+ if (sourceTile.id) {
517
+ console.log(`[load]: ${sourceTile.id}`); // eslint-disable-line
518
+ }
519
+ await loadNestedTileset(this.sourceTileset, sourceTile, this.loadOptions);
520
+ }
521
+ return traversalProps;
522
+ }
523
+ if (sourceTile.id) {
524
+ console.log(`[convert]: ${sourceTile.id}`); // eslint-disable-line
525
+ }
474
526
 
475
- /**
476
- * Convert 3DTiles tile to I3S node
477
- * @param param0
478
- * @param param0.parentNode - 3DNodeIndexDocument of parent node
479
- * @param param0.sourceTile - source 3DTile data
480
- * @param param0.level - tree level
481
- */
482
- private async convertNode({
483
- parentNode,
484
- sourceTile,
485
- level
486
- }: {
487
- parentNode: NodeIndexDocument;
488
- sourceTile: Tile3D;
489
- level: number;
490
- }) {
491
- const childNodes = await this._createNode(parentNode, sourceTile, level);
527
+ const {parentNodes, transform} = traversalProps;
528
+ let transformationMatrix: Matrix4 = transform.clone();
529
+ if (sourceTile.transform) {
530
+ transformationMatrix = transformationMatrix.multiplyRight(sourceTile.transform);
531
+ }
532
+ const parentNode = parentNodes[0];
533
+ const childNodes = await this._createNode(parentNode, sourceTile, transformationMatrix);
492
534
  await parentNode.addChildren(childNodes);
535
+
536
+ const newTraversalProps: TraversalConversionProps = {
537
+ transform: transformationMatrix,
538
+ parentNodes: childNodes
539
+ };
540
+ return newTraversalProps;
493
541
  }
494
542
 
495
543
  /**
496
- * Add child nodes recursively and write them to files
497
- * @param param0 - arguments
498
- * @param param0.parentNode - 3DNodeIndexDocument of parent node
499
- * @param param0.sourceTile - source 3DTile data
500
- * @param param0.level - tree level
544
+ * Do final action with nodes after the current node and all child nodes been converted.
545
+ * @param conversionResults - array of conversion results of the current node
546
+ * @param currentTraversalProps - traversal properties of the current node
501
547
  */
502
- private async _addChildren(data: {
503
- parentNode: NodeIndexDocument;
504
- sourceTiles: Tile3D[];
505
- level: number;
506
- }): Promise<void> {
507
- const {sourceTiles, parentNode, level} = data;
508
- if (this.options.maxDepth && level > this.options.maxDepth) {
509
- return;
510
- }
511
- for (const sourceTile of sourceTiles) {
512
- if (sourceTile.type === 'json') {
513
- await this.convertNestedTileset({parentNode, sourceTile, level});
514
- } else {
515
- await this.convertNode({parentNode, sourceTile, level});
516
- }
517
- if (sourceTile.id) {
518
- console.log(sourceTile.id); // eslint-disable-line
548
+ private async finalizeTile(
549
+ conversionResults: TraversalConversionProps[],
550
+ currentTraversalProps: TraversalConversionProps
551
+ ): Promise<void> {
552
+ for (const result of conversionResults) {
553
+ for (const node of result.parentNodes) {
554
+ await node.addNeighbors();
519
555
  }
520
556
  }
557
+ for (const node of currentTraversalProps.parentNodes) {
558
+ await node.save();
559
+ }
521
560
  }
522
561
 
523
562
  /**
524
563
  * Convert tile to one or more I3S nodes
525
564
  * @param parentNode - 3DNodeIndexDocument of parent node
526
565
  * @param sourceTile - source 3DTile data
566
+ * @param transformationMatrix - transformation matrix of the current tile, calculated recursively multiplying
567
+ * transform of all parent tiles and transform of the current tile
527
568
  * @param level - tree level
528
569
  */
529
570
  private async _createNode(
530
571
  parentNode: NodeIndexDocument,
531
- sourceTile: Tile3D,
532
- level: number
572
+ sourceTile: Tiles3DTileJSONPostprocessed,
573
+ transformationMatrix: Matrix4
533
574
  ): Promise<NodeIndexDocument[]> {
534
575
  this._checkAddRefinementTypeForTile(sourceTile);
535
576
 
536
577
  await this._updateTilesetOptions();
537
- await this.sourceTileset!._loadTile(sourceTile);
538
578
 
539
- let boundingVolumes = createBoundingVolumes(sourceTile, this.geoidHeightModel!);
579
+ const tileContent = await loadTile3DContent(this.sourceTileset, sourceTile, this.loadOptions);
580
+ const sourceBoundingVolume = createBoundingVolume(
581
+ sourceTile.boundingVolume,
582
+ transformationMatrix,
583
+ null
584
+ );
585
+ let boundingVolumes = createBoundingVolumes(sourceBoundingVolume, this.geoidHeightModel!);
540
586
 
541
- const propertyTable = getPropertyTable(sourceTile.content);
587
+ const propertyTable = getPropertyTable(tileContent);
542
588
 
543
589
  if (propertyTable && !this.layers0?.attributeStorageInfo?.length) {
544
590
  this._convertPropertyTableToNodeAttributes(propertyTable);
@@ -546,6 +592,9 @@ export default class I3SConverter {
546
592
 
547
593
  const resourcesData = await this._convertResources(
548
594
  sourceTile,
595
+ transformationMatrix,
596
+ sourceBoundingVolume,
597
+ tileContent,
549
598
  parentNode.inPageId,
550
599
  propertyTable
551
600
  );
@@ -612,29 +661,29 @@ export default class I3SConverter {
612
661
  nodesInPage.push(nodeInPage);
613
662
  }
614
663
 
615
- sourceTile.unloadContent();
616
-
617
- await this._addChildrenWithNeighborsAndWriteFile({
618
- parentNode: nodes[0],
619
- sourceTiles: sourceTile.children,
620
- level: level + 1
621
- });
622
664
  return nodes;
623
665
  }
624
666
 
625
667
  /**
626
668
  * Convert tile to one or more I3S nodes
627
669
  * @param sourceTile - source tile (3DTile)
670
+ * @param transformationMatrix - transformation matrix of the current tile, calculated recursively multiplying
671
+ * transform of all parent tiles and transform of the current tile
672
+ * @param boundingVolume - initialized bounding volume of the source tile
673
+ * @param tileContent - content of the source tile
628
674
  * @param parentId - id of parent node in node pages
629
675
  * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA
630
676
  * @returns - converted node resources
631
677
  */
632
678
  private async _convertResources(
633
- sourceTile: Tile3D,
679
+ sourceTile: Tiles3DTileJSONPostprocessed,
680
+ transformationMatrix: Matrix4,
681
+ boundingVolume: OrientedBoundingBox | BoundingSphere,
682
+ tileContent: Tiles3DTileContent | null,
634
683
  parentId: number,
635
684
  propertyTable: FeatureTableJson | null
636
685
  ): Promise<I3SConvertedResources[] | null> {
637
- if (!this.isContentSupported(sourceTile)) {
686
+ if (!this.isContentSupported(sourceTile) || !tileContent) {
638
687
  return null;
639
688
  }
640
689
  const draftObb = {
@@ -643,7 +692,9 @@ export default class I3SConverter {
643
692
  quaternion: []
644
693
  };
645
694
  const resourcesData = await convertB3dmToI3sGeometry(
646
- sourceTile.content,
695
+ tileContent,
696
+ transformationMatrix,
697
+ boundingVolume,
647
698
  async () => (await this.nodePages.push({index: 0, obb: draftObb}, parentId)).index,
648
699
  propertyTable,
649
700
  this.featuresHashArray,
@@ -675,7 +726,7 @@ export default class I3SConverter {
675
726
  private async _updateNodeInNodePages(
676
727
  maxScreenThresholdSQ: MaxScreenThresholdSQ,
677
728
  boundingVolumes: BoundingVolumes,
678
- sourceTile: Tile3D,
729
+ sourceTile: Tiles3DTileJSONPostprocessed,
679
730
  parentId: number,
680
731
  resources: I3SConvertedResources
681
732
  ): Promise<NodeInPage> {
@@ -910,7 +961,7 @@ export default class I3SConverter {
910
961
  * @param slpkChildPath
911
962
  */
912
963
  private async writeTextureFile(
913
- textureData: Promise<ArrayBuffer>,
964
+ textureData: Uint8Array | Promise<ArrayBuffer>,
914
965
  name: string,
915
966
  format: 'jpg' | 'png' | 'ktx2',
916
967
  childPath: string,
@@ -1095,10 +1146,9 @@ export default class I3SConverter {
1095
1146
  this.refreshTokenTime = process.hrtime();
1096
1147
 
1097
1148
  const preloadOptions = await this._fetchPreloadOptions();
1098
- this.sourceTileset!.options = {...this.sourceTileset!.options, ...preloadOptions};
1099
1149
  if (preloadOptions.headers) {
1100
- this.sourceTileset!.loadOptions.fetch = {
1101
- ...this.sourceTileset!.loadOptions.fetch,
1150
+ this.loadOptions.fetch = {
1151
+ ...this.loadOptions.fetch,
1102
1152
  headers: preloadOptions.headers
1103
1153
  };
1104
1154
  console.log('Authorization Bearer token has been updated'); // eslint-disable-line no-undef, no-console
@@ -1108,7 +1158,7 @@ export default class I3SConverter {
1108
1158
  /** Do calculations of all tiles and tiles with "ADD" type of refinement.
1109
1159
  * @param tile
1110
1160
  */
1111
- private _checkAddRefinementTypeForTile(tile: Tile3D): void {
1161
+ private _checkAddRefinementTypeForTile(tile: Tiles3DTileJSONPostprocessed): void {
1112
1162
  const ADD_TILE_REFINEMENT = 1;
1113
1163
 
1114
1164
  if (tile.refine === ADD_TILE_REFINEMENT) {
@@ -1118,13 +1168,14 @@ export default class I3SConverter {
1118
1168
 
1119
1169
  this.refinementCounter.tilesCount += 1;
1120
1170
  }
1171
+
1121
1172
  /**
1122
1173
  * Check if the tile's content format is supported by the converter
1123
- * @param sourceRootTile
1174
+ * @param sourceTile
1124
1175
  * @returns
1125
1176
  */
1126
- private isContentSupported(sourceRootTile: Tile3D): boolean {
1127
- return ['b3dm', 'glTF'].includes(sourceRootTile?.content?.type);
1177
+ private isContentSupported(sourceTile: Tiles3DTileJSONPostprocessed): boolean {
1178
+ return ['b3dm', 'glTF', 'scenegraph'].includes(sourceTile.type || '');
1128
1179
  }
1129
1180
 
1130
1181
  private async loadWorkers(): Promise<void> {
@@ -163,3 +163,23 @@ export type TypedArrayConstructor =
163
163
  | Uint32ArrayConstructor
164
164
  | Float32ArrayConstructor
165
165
  | Float64ArrayConstructor;
166
+
167
+ /**
168
+ * glTF primitive modes (mesh topology types)
169
+ * @see https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode
170
+ */
171
+ export enum GltfPrimitiveModeString {
172
+ POINTS = 'POINTS',
173
+ LINES = 'LINES',
174
+ LINE_LOOP = 'LINE_LOOP',
175
+ LINE_STRIP = 'LINE_STRIP',
176
+ TRIANGLES = 'TRIANGLES',
177
+ TRIANGLE_STRIP = 'TRIANGLE_STRIP',
178
+ TRIANGLE_FAN = 'TRIANGLE_FAN'
179
+ }
180
+
181
+ /** Preprocessed data gathered from child tiles binary content */
182
+ export type PreprocessData = {
183
+ /** Mesh topology types used in gltf primitives of the tileset */
184
+ meshTopologyTypes: Set<GltfPrimitiveModeString>;
185
+ };
@@ -0,0 +1,19 @@
1
+ # I3S server
2
+
3
+ The http server run on NodeJS ExpressJS framework.
4
+ The server provides I3S Rest endpoints per specification https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3Dobject_ReadMe.md#http-api-overview-17
5
+
6
+ ## Usage
7
+
8
+ ### Serve 3DTiles to I3S converted dataset
9
+
10
+ - Convert data set from 3DTiles to I3S without `--slpk` option
11
+ - Serve output folder `I3sLayerPath="./data/BatchedTextured" DEBUG=i3s-server:* npx i3s-server`
12
+
13
+ ### Serve SLPK
14
+
15
+ - Serve slpk file `I3sLayerPath="../datasets/Rancho_Mesh_mesh_v17_1.slpk" DEBUG=i3s-server:* npx i3s-server`
16
+
17
+ ## ENV variables
18
+
19
+ - `I3sLayerPath` - path to converted data or SLPK file.
@@ -4,7 +4,9 @@ const logger = require('morgan');
4
4
  const cors = require('cors');
5
5
 
6
6
  const indexRouter = require('./routes/index');
7
+ const {sceneServerRouter, router} = require('./routes/slpk-router');
7
8
 
9
+ const I3S_LAYER_PATH = process.env.I3sLayerPath || ''; // eslint-disable-line no-process-env, no-undef
8
10
  const app = express();
9
11
 
10
12
  app.use(logger('dev'));
@@ -13,6 +15,11 @@ app.use(express.urlencoded({extended: false}));
13
15
  app.use(express.static(path.join(__dirname, 'public')));
14
16
  app.use(cors());
15
17
 
16
- app.use('/', indexRouter);
18
+ if (/\.slpk$/.test(I3S_LAYER_PATH)) {
19
+ app.use('/SceneServer/layers/0', router);
20
+ app.use('/SceneServer', sceneServerRouter);
21
+ } else {
22
+ app.use('/', indexRouter);
23
+ }
17
24
 
18
25
  module.exports = app;