@loaders.gl/tile-converter 3.3.0-alpha.8 → 3.4.0-alpha.1

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 (125) hide show
  1. package/dist/3d-tiles-attributes-worker.js +1 -1
  2. package/dist/converter-cli.js +14 -2
  3. package/dist/converter.min.js +22 -22
  4. package/dist/deps-installer/deps-installer.d.ts.map +1 -1
  5. package/dist/deps-installer/deps-installer.js +8 -0
  6. package/dist/dist.min.js +1014 -693
  7. package/dist/es5/3d-tiles-attributes-worker.js +1 -1
  8. package/dist/es5/converter-cli.js +14 -2
  9. package/dist/es5/converter-cli.js.map +1 -1
  10. package/dist/es5/deps-installer/deps-installer.js +13 -2
  11. package/dist/es5/deps-installer/deps-installer.js.map +1 -1
  12. package/dist/es5/i3s-attributes-worker.js +1 -1
  13. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  14. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +16 -7
  15. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  16. package/dist/es5/i3s-converter/helpers/geometry-converter.js +363 -113
  17. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  18. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +6 -11
  19. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  20. package/dist/es5/i3s-converter/helpers/node-index-document.js +517 -0
  21. package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -0
  22. package/dist/es5/i3s-converter/helpers/node-pages.js +455 -173
  23. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
  24. package/dist/es5/i3s-converter/i3s-converter.js +549 -618
  25. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  26. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js +107 -0
  27. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  28. package/dist/es5/i3s-converter/json-templates/layers.js +2 -93
  29. package/dist/es5/i3s-converter/json-templates/layers.js.map +1 -1
  30. package/dist/es5/i3s-converter/json-templates/shared-resources.js +3 -3
  31. package/dist/es5/i3s-converter/json-templates/shared-resources.js.map +1 -1
  32. package/dist/es5/i3s-converter/types.js.map +1 -1
  33. package/dist/es5/lib/utils/file-utils.js +93 -9
  34. package/dist/es5/lib/utils/file-utils.js.map +1 -1
  35. package/dist/es5/lib/utils/write-queue.js +38 -25
  36. package/dist/es5/lib/utils/write-queue.js.map +1 -1
  37. package/dist/es5/pgm-loader.js +1 -1
  38. package/dist/es5/workers/i3s-attributes-worker.js +1 -1
  39. package/dist/es5/workers/i3s-attributes-worker.js.map +1 -1
  40. package/dist/esm/3d-tiles-attributes-worker.js +1 -1
  41. package/dist/esm/converter-cli.js +14 -2
  42. package/dist/esm/converter-cli.js.map +1 -1
  43. package/dist/esm/deps-installer/deps-installer.js +9 -1
  44. package/dist/esm/deps-installer/deps-installer.js.map +1 -1
  45. package/dist/esm/i3s-attributes-worker.js +1 -1
  46. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  47. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +16 -7
  48. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  49. package/dist/esm/i3s-converter/helpers/geometry-converter.js +150 -40
  50. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  51. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +6 -9
  52. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  53. package/dist/esm/i3s-converter/helpers/node-index-document.js +202 -0
  54. package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -0
  55. package/dist/esm/i3s-converter/helpers/node-pages.js +162 -76
  56. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
  57. package/dist/esm/i3s-converter/i3s-converter.js +115 -220
  58. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  59. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js +89 -0
  60. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  61. package/dist/esm/i3s-converter/json-templates/layers.js +2 -85
  62. package/dist/esm/i3s-converter/json-templates/layers.js.map +1 -1
  63. package/dist/esm/i3s-converter/json-templates/shared-resources.js +3 -3
  64. package/dist/esm/i3s-converter/json-templates/shared-resources.js.map +1 -1
  65. package/dist/esm/i3s-converter/types.js.map +1 -1
  66. package/dist/esm/lib/utils/file-utils.js +44 -3
  67. package/dist/esm/lib/utils/file-utils.js.map +1 -1
  68. package/dist/esm/lib/utils/write-queue.js +19 -10
  69. package/dist/esm/lib/utils/write-queue.js.map +1 -1
  70. package/dist/esm/pgm-loader.js +1 -1
  71. package/dist/esm/workers/i3s-attributes-worker.js +1 -1
  72. package/dist/esm/workers/i3s-attributes-worker.js.map +1 -1
  73. package/dist/i3s-attributes-worker.js +2 -2
  74. package/dist/i3s-attributes-worker.js.map +2 -2
  75. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +3 -3
  76. package/dist/i3s-converter/helpers/batch-ids-extensions.js +3 -3
  77. package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -1
  78. package/dist/i3s-converter/helpers/geometry-attributes.js +16 -10
  79. package/dist/i3s-converter/helpers/geometry-converter.d.ts +8 -4
  80. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  81. package/dist/i3s-converter/helpers/geometry-converter.js +200 -44
  82. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
  83. package/dist/i3s-converter/helpers/gltf-attributes.js +2 -3
  84. package/dist/i3s-converter/helpers/node-index-document.d.ts +95 -0
  85. package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -0
  86. package/dist/i3s-converter/helpers/node-index-document.js +250 -0
  87. package/dist/i3s-converter/helpers/node-pages.d.ts +78 -43
  88. package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -1
  89. package/dist/i3s-converter/helpers/node-pages.js +194 -93
  90. package/dist/i3s-converter/i3s-converter.d.ts +33 -58
  91. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  92. package/dist/i3s-converter/i3s-converter.js +122 -233
  93. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts +7 -0
  94. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts.map +1 -0
  95. package/dist/i3s-converter/json-templates/geometry-definitions.js +87 -0
  96. package/dist/i3s-converter/json-templates/layers.d.ts +1 -30
  97. package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -1
  98. package/dist/i3s-converter/json-templates/layers.js +2 -86
  99. package/dist/i3s-converter/json-templates/shared-resources.js +3 -3
  100. package/dist/i3s-converter/types.d.ts +28 -2
  101. package/dist/i3s-converter/types.d.ts.map +1 -1
  102. package/dist/lib/utils/file-utils.d.ts +17 -1
  103. package/dist/lib/utils/file-utils.d.ts.map +1 -1
  104. package/dist/lib/utils/file-utils.js +64 -7
  105. package/dist/lib/utils/write-queue.d.ts +18 -2
  106. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  107. package/dist/lib/utils/write-queue.js +18 -12
  108. package/dist/workers/i3s-attributes-worker.js +1 -1
  109. package/package.json +23 -19
  110. package/src/converter-cli.ts +22 -2
  111. package/src/deps-installer/deps-installer.ts +9 -0
  112. package/src/i3s-converter/helpers/batch-ids-extensions.ts +3 -3
  113. package/src/i3s-converter/helpers/geometry-attributes.ts +16 -11
  114. package/src/i3s-converter/helpers/geometry-converter.ts +217 -48
  115. package/src/i3s-converter/helpers/gltf-attributes.ts +2 -3
  116. package/src/i3s-converter/helpers/node-index-document.ts +315 -0
  117. package/src/i3s-converter/helpers/node-pages.ts +215 -110
  118. package/src/i3s-converter/i3s-converter.ts +170 -312
  119. package/src/i3s-converter/json-templates/geometry-definitions.ts +83 -0
  120. package/src/i3s-converter/json-templates/layers.ts +2 -91
  121. package/src/i3s-converter/json-templates/shared-resources.ts +3 -3
  122. package/src/i3s-converter/types.ts +29 -2
  123. package/src/lib/utils/file-utils.ts +62 -7
  124. package/src/lib/utils/write-queue.ts +36 -15
  125. package/src/workers/i3s-attributes-worker.ts +2 -1
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ // loaders.gl, MIT license
2
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
4
  if (k2 === undefined) k2 = k;
4
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -44,7 +45,7 @@ const create_scene_server_path_1 = require("./helpers/create-scene-server-path")
44
45
  const lod_conversion_utils_1 = require("../lib/utils/lod-conversion-utils");
45
46
  const pgm_loader_1 = require("../pgm-loader");
46
47
  const layers_1 = require("./json-templates/layers");
47
- const node_1 = require("./json-templates/node");
48
+ const geometry_definitions_1 = require("./json-templates/geometry-definitions");
48
49
  const shared_resources_1 = require("./json-templates/shared-resources");
49
50
  const node_debug_1 = require("./helpers/node-debug");
50
51
  const textures_1 = require("@loaders.gl/textures");
@@ -55,6 +56,7 @@ const write_queue_1 = __importDefault(require("../lib/utils/write-queue"));
55
56
  const i3s_attributes_worker_1 = require("../i3s-attributes-worker");
56
57
  const constants_1 = require("../constants");
57
58
  const feature_attributes_1 = require("./helpers/feature-attributes");
59
+ const node_index_document_1 = require("./helpers/node-index-document");
58
60
  const ION_DEFAULT_TOKEN = process_1.default.env?.IonToken || // eslint-disable-line
59
61
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ'; // eslint-disable-line
60
62
  const HARDCODED_NODES_PER_PAGE = 64;
@@ -76,11 +78,14 @@ class I3SConverter {
76
78
  this.Loader = _3d_tiles_1.Tiles3DLoader;
77
79
  this.workerSource = {};
78
80
  this.writeQueue = new write_queue_1.default();
79
- this.nodePages = new node_pages_1.default(file_utils_1.writeFile, HARDCODED_NODES_PER_PAGE);
81
+ this.compressList = null;
82
+ this.nodePages = new node_pages_1.default(file_utils_1.writeFile, HARDCODED_NODES_PER_PAGE, this);
80
83
  this.options = {};
81
84
  this.layers0Path = '';
82
85
  this.materialMap = new Map();
83
86
  this.materialDefinitions = [];
87
+ this.geometryMap = new Map();
88
+ this.geometryConfigs = [];
84
89
  this.vertexCounter = 0;
85
90
  this.layers0 = null;
86
91
  this.featuresHashArray = [];
@@ -92,6 +97,7 @@ class I3SConverter {
92
97
  this.generateTextures = false;
93
98
  this.generateBoundingVolumes = false;
94
99
  this.layersHasTexture = false;
100
+ this.compressList = null;
95
101
  }
96
102
  /**
97
103
  * Convert a 3d tileset
@@ -106,6 +112,9 @@ class I3SConverter {
106
112
  * @param options.token Token for Cesium ION tilesets authentication
107
113
  * @param options.draco Generate I3S 1.7 draco compressed geometries
108
114
  * @param options.validate -enable validation
115
+ * @param options.generateTextures - generate alternative type of textures (to have non-compressed jpeg/png and compressed ktx2)
116
+ * @param options.generateBoundingVolumes - generate bounding volumes from vertices coordinates instead of source tiles bounding volumes
117
+ * @param options.instantNodeWriting - Keep created 3DNodeIndexDocument files on disk instead of memory. This option reduce memory usage but decelerates conversion speed
109
118
  */
110
119
  async convert(options) {
111
120
  if (core_1.isBrowser) {
@@ -113,8 +122,19 @@ class I3SConverter {
113
122
  return constants_1.BROWSER_ERROR_MESSAGE;
114
123
  }
115
124
  this.conversionStartTime = process_1.default.hrtime();
116
- const { tilesetName, slpk, egmFilePath, inputUrl, validate, outputPath, draco, sevenZipExe, maxDepth, token, generateTextures, generateBoundingVolumes } = options;
117
- this.options = { maxDepth, slpk, sevenZipExe, egmFilePath, draco, token, inputUrl };
125
+ const { tilesetName, slpk, egmFilePath, inputUrl, validate, outputPath, draco = true, sevenZipExe, maxDepth, token, generateTextures, generateBoundingVolumes, instantNodeWriting = false, mergeMaterials = true } = options;
126
+ this.options = {
127
+ maxDepth,
128
+ slpk,
129
+ sevenZipExe,
130
+ egmFilePath,
131
+ draco,
132
+ token,
133
+ inputUrl,
134
+ instantNodeWriting,
135
+ mergeMaterials
136
+ };
137
+ this.compressList = (this.options.instantNodeWriting && []) || null;
118
138
  this.validate = Boolean(validate);
119
139
  this.Loader = inputUrl.indexOf(CESIUM_DATASET_PREFIX) !== -1 ? _3d_tiles_1.CesiumIonLoader : _3d_tiles_1.Tiles3DLoader;
120
140
  this.generateTextures = Boolean(generateTextures);
@@ -182,24 +202,30 @@ class I3SConverter {
182
202
  this.materialMap = new Map();
183
203
  const sourceRootTile = this.sourceTileset.root;
184
204
  const boundingVolumes = (0, coordinate_converter_1.createBoundingVolumes)(sourceRootTile, this.geoidHeightModel);
185
- const parentId = this.nodePages.push({
205
+ await this.nodePages.push({
186
206
  index: 0,
187
207
  lodThreshold: 0,
188
208
  obb: boundingVolumes.obb,
189
209
  children: []
190
210
  });
191
- const isCreateSlpk = this.options.slpk;
192
- const root0 = this._formRootNodeIndexDocument(boundingVolumes);
193
- await this._convertNodesTree(root0, sourceRootTile, parentId, boundingVolumes);
211
+ const rootNode = await node_index_document_1.NodeIndexDocument.createRootNode(boundingVolumes, this);
212
+ await this._convertNodesTree(rootNode, sourceRootTile);
194
213
  this.layers0.materialDefinitions = this.materialDefinitions;
214
+ // @ts-ignore
215
+ this.layers0.geometryDefinitions = (0, json_map_transform_1.default)(this.geometryConfigs.map((config) => ({
216
+ geometryConfig: { ...config, draco: this.options.draco }
217
+ })), (0, geometry_definitions_1.GEOMETRY_DEFINITION)());
195
218
  if (this.layersHasTexture === false) {
196
219
  this.layers0.store.defaultGeometrySchema.ordering =
197
220
  this.layers0.store.defaultGeometrySchema.ordering.filter((attribute) => attribute !== 'uv0');
198
221
  }
199
222
  await this._writeLayers0();
200
223
  (0, create_scene_server_path_1.createSceneServerPath)(tilesetName, this.layers0, tilesetPath);
201
- await this._writeNodeIndexDocument(root0, 'root', (0, path_1.join)(this.layers0Path, 'nodes', 'root'));
202
- await this.nodePages.save(this.layers0Path, this.writeQueue, isCreateSlpk);
224
+ for (const filePath of this.compressList || []) {
225
+ await (0, compress_util_1.compressFileWithGzip)(filePath);
226
+ await (0, file_utils_1.removeFile)(filePath);
227
+ }
228
+ await this.nodePages.save();
203
229
  await this.writeQueue.finalize();
204
230
  await this._createSlpk(tilesetPath);
205
231
  }
@@ -231,68 +257,29 @@ class I3SConverter {
231
257
  };
232
258
  this.layers0 = (0, json_map_transform_1.default)(layers0data, (0, layers_1.LAYERS)());
233
259
  }
234
- /**
235
- * Convert and save the layer and embedded tiles
236
- * @param boundingVolumes - mbs and obb data about node's bounding volume
237
- * @return 3DNodeIndexDocument data https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
238
- */
239
- _formRootNodeIndexDocument(boundingVolumes) {
240
- const root0data = {
241
- version: `{${(0, uuid_1.v4)().toUpperCase()}}`,
242
- id: 'root',
243
- level: 0,
244
- lodSelection: [
245
- {
246
- metricType: 'maxScreenThresholdSQ',
247
- maxError: 0
248
- },
249
- {
250
- metricType: 'maxScreenThreshold',
251
- maxError: 0
252
- }
253
- ],
254
- ...boundingVolumes,
255
- children: []
256
- };
257
- return (0, json_map_transform_1.default)(root0data, (0, node_1.NODE)());
258
- }
259
260
  /**
260
261
  * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
261
- * @param root0 - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
262
+ * @param rootNode - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
262
263
  * @param sourceRootTile - Source (3DTile) tile data
263
- * @param parentId - node id in node pages
264
- * @param boundingVolumes - mbs and obb data about node's bounding volume
265
264
  */
266
- async _convertNodesTree(root0, sourceRootTile, parentId, boundingVolumes) {
265
+ async _convertNodesTree(rootNode, sourceRootTile) {
267
266
  await this.sourceTileset._loadTile(sourceRootTile);
268
267
  if (this.isContentSupported(sourceRootTile)) {
269
- root0.children = root0.children || [];
270
- root0.children.push({
271
- id: '1',
272
- href: './1',
273
- ...boundingVolumes
274
- });
275
- const [child] = await this._createNode(root0, sourceRootTile, parentId, 0);
276
- const childPath = (0, path_1.join)(this.layers0Path, 'nodes', child.path);
277
- if (this.options.slpk) {
278
- await this.writeQueue.enqueue({
279
- archiveKey: 'nodes/1/3dNodeIndexDocument.json.gz',
280
- writePromise: (0, file_utils_1.writeFileForSlpk)(childPath, JSON.stringify(child), '3dNodeIndexDocument.json')
281
- });
282
- }
283
- else {
284
- await this.writeQueue.enqueue({ writePromise: (0, file_utils_1.writeFile)(childPath, JSON.stringify(child)) });
268
+ const childNodes = await this._createNode(rootNode, sourceRootTile, 0);
269
+ for (const childNode of childNodes) {
270
+ await childNode.save();
285
271
  }
272
+ await rootNode.addChildren(childNodes);
286
273
  }
287
274
  else {
288
275
  await this._addChildrenWithNeighborsAndWriteFile({
289
- parentNode: root0,
276
+ parentNode: rootNode,
290
277
  sourceTiles: sourceRootTile.children,
291
- parentId,
292
278
  level: 1
293
279
  });
294
280
  }
295
281
  await sourceRootTile.unloadContent();
282
+ await rootNode.save();
296
283
  }
297
284
  /**
298
285
  * Write 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md in file
@@ -301,29 +288,15 @@ class I3SConverter {
301
288
  if (this.options.slpk) {
302
289
  await this.writeQueue.enqueue({
303
290
  archiveKey: '3dSceneLayer.json.gz',
304
- writePromise: (0, file_utils_1.writeFileForSlpk)(this.layers0Path, JSON.stringify(this.layers0), '3dSceneLayer.json')
291
+ writePromise: () => (0, file_utils_1.writeFileForSlpk)(this.layers0Path, JSON.stringify(this.layers0), '3dSceneLayer.json')
305
292
  });
306
293
  }
307
294
  else {
308
295
  await this.writeQueue.enqueue({
309
- writePromise: (0, file_utils_1.writeFile)(this.layers0Path, JSON.stringify(this.layers0))
296
+ writePromise: () => (0, file_utils_1.writeFile)(this.layers0Path, JSON.stringify(this.layers0))
310
297
  });
311
298
  }
312
299
  }
313
- /**
314
- * Write 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md in file
315
- */
316
- async _writeNodeIndexDocument(root0, nodePath, rootPath) {
317
- if (this.options.slpk) {
318
- await this.writeQueue.enqueue({
319
- archiveKey: `nodes/${nodePath}/3dNodeIndexDocument.json.gz`,
320
- writePromise: (0, file_utils_1.writeFileForSlpk)(rootPath, JSON.stringify(root0), '3dNodeIndexDocument.json')
321
- });
322
- }
323
- else {
324
- await this.writeQueue.enqueue({ writePromise: (0, file_utils_1.writeFile)(rootPath, JSON.stringify(root0)) });
325
- }
326
- }
327
300
  /**
328
301
  * Pack files into *.slpk archive
329
302
  * @param tilesetPath - Path to save file
@@ -362,32 +335,26 @@ class I3SConverter {
362
335
  /**
363
336
  * Add child nodes recursively and write them to files
364
337
  * @param data - arguments
338
+ * @param data.parentNode - 3DNodeIndexDocument of parent node
365
339
  * @param data.sourceTiles - array of source child nodes
366
- * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
367
- * @param data.parentId - id of parent node in node pages
368
340
  * @param data.level - level of node (distanse to root node in the tree)
369
341
  */
370
342
  async _addChildrenWithNeighborsAndWriteFile(data) {
371
- const childNodes = [];
372
- await this._addChildren({ ...data, childNodes });
373
- await this._addNeighborsAndWriteFile(data.parentNode, childNodes);
343
+ await this._addChildren(data);
344
+ await data.parentNode.addNeighbors();
374
345
  }
375
346
  /**
376
347
  * Convert nested subtree of 3DTiles dataset
377
348
  * @param param0
349
+ * @param data.parentNode - 3DNodeIndexDocument of parent node
378
350
  * @param param0.sourceTile - source 3DTile data
379
- * @param param0.parentNode - parent I3S node
380
- * @param param0.childNodes - child I3S nodes
381
- * @param param0.parentId - parent node ID
382
351
  * @param param0.level - tree level
383
352
  */
384
- async convertNestedTileset({ sourceTile, parentNode, childNodes, parentId, level }) {
353
+ async convertNestedTileset({ parentNode, sourceTile, level }) {
385
354
  await this.sourceTileset._loadTile(sourceTile);
386
355
  await this._addChildren({
387
356
  parentNode,
388
357
  sourceTiles: sourceTile.children,
389
- childNodes,
390
- parentId,
391
358
  level: level + 1
392
359
  });
393
360
  await sourceTile.unloadContent();
@@ -395,92 +362,45 @@ class I3SConverter {
395
362
  /**
396
363
  * Convert 3DTiles tile to I3S node
397
364
  * @param param0
365
+ * @param param0.parentNode - 3DNodeIndexDocument of parent node
398
366
  * @param param0.sourceTile - source 3DTile data
399
- * @param param0.parentNode - parent I3S node
400
- * @param param0.childNodes - child I3S nodes
401
- * @param param0.parentId - parent node ID
402
367
  * @param param0.level - tree level
403
368
  */
404
- async convertNode({ sourceTile, parentNode, childNodes, parentId, level }) {
405
- const children = await this._createNode(parentNode, sourceTile, parentId, level);
406
- parentNode.children = parentNode.children || [];
407
- for (const child of children) {
408
- parentNode.children.push({
409
- id: child.id,
410
- href: `../${child.path}`,
411
- obb: child.obb,
412
- mbs: child.mbs
413
- });
414
- childNodes.push(child);
415
- }
369
+ async convertNode({ parentNode, sourceTile, level }) {
370
+ const childNodes = await this._createNode(parentNode, sourceTile, level);
371
+ await parentNode.addChildren(childNodes);
416
372
  }
417
373
  /**
418
374
  * Add child nodes recursively and write them to files
419
- * @param data - arguments
420
- * @param data.childNodes - array of target child nodes
421
- * @param data.sourceTiles - array of source child nodes
422
- * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
423
- * @param data.parentId - id of parent node in node pages
424
- * @param data.level - level of node (distanse to root node in the tree)
375
+ * @param param0 - arguments
376
+ * @param param0.parentNode - 3DNodeIndexDocument of parent node
377
+ * @param param0.sourceTile - source 3DTile data
378
+ * @param param0.level - tree level
425
379
  */
426
380
  async _addChildren(data) {
427
- const { childNodes, sourceTiles, parentNode, parentId, level } = data;
381
+ const { sourceTiles, parentNode, level } = data;
428
382
  if (this.options.maxDepth && level > this.options.maxDepth) {
429
383
  return;
430
384
  }
431
- const promises = [];
432
385
  for (const sourceTile of sourceTiles) {
433
386
  if (sourceTile.type === 'json') {
434
- promises.push(this.convertNestedTileset({ sourceTile, parentNode, childNodes, parentId, level }));
387
+ await this.convertNestedTileset({ parentNode, sourceTile, level });
435
388
  }
436
389
  else {
437
- promises.push(this.convertNode({ sourceTile, parentNode, childNodes, parentId, level }));
390
+ await this.convertNode({ parentNode, sourceTile, level });
438
391
  }
439
- await Promise.all(promises);
440
392
  if (sourceTile.id) {
441
393
  console.log(sourceTile.id); // eslint-disable-line
442
394
  }
443
395
  }
444
396
  }
445
- /**
446
- * Add neightbors to 3DNodeIndexDocument and write it in a file
447
- * @param parentNode - arguments
448
- * @param childNodes - array of target child nodes
449
- */
450
- async _addNeighborsAndWriteFile(parentNode, childNodes) {
451
- for (const node of childNodes) {
452
- const childPath = (0, path_1.join)(this.layers0Path, 'nodes', node.path);
453
- const nodePath = node.path;
454
- delete node.path;
455
- // Don't do large amount of "neightbors" to avoid big memory consumption
456
- if (Number(parentNode?.children?.length) < 1000) {
457
- for (const neighbor of parentNode.children || []) {
458
- // eslint-disable-next-line max-depth
459
- if (node.id === neighbor.id) {
460
- continue; // eslint-disable-line
461
- }
462
- if (node.neighbors) {
463
- node.neighbors.push({ ...neighbor });
464
- }
465
- }
466
- }
467
- else {
468
- // eslint-disable-next-line no-console, no-undef
469
- console.warn(`Node ${node.id}: neighbors attribute is omited because of large number of neigbors`);
470
- delete node.neighbors;
471
- }
472
- await this._writeNodeIndexDocument(node, nodePath, childPath);
473
- node.neighbors = [];
474
- }
475
- }
476
397
  /**
477
398
  * Convert tile to one or more I3S nodes
478
- * @param parentTile - parent 3DNodeIndexDocument
479
- * @param sourceTile - source tile (3DTile)
480
- * @param parentId - id of parent node in node pages
481
- * @param level - level of node (distanse to root node in the tree)
399
+ * @param parentNode - 3DNodeIndexDocument of parent node
400
+ * @param sourceTile - source 3DTile data
401
+ * @param level - tree level
482
402
  */
483
- async _createNode(parentTile, sourceTile, parentId, level) {
403
+ async _createNode(parentNode, sourceTile, level) {
484
404
  this._checkAddRefinementTypeForTile(sourceTile);
485
405
  await this._updateTilesetOptions();
486
406
  await this.sourceTileset._loadTile(sourceTile);
@@ -489,13 +409,15 @@ class I3SConverter {
489
409
  if (propertyTable && !this.layers0?.attributeStorageInfo?.length) {
490
410
  this._convertPropertyTableToNodeAttributes(propertyTable);
491
411
  }
492
- const resourcesData = await this._convertResources(sourceTile, parentId, propertyTable);
412
+ const resourcesData = await this._convertResources(sourceTile, parentNode.inPageId, propertyTable);
493
413
  const nodes = [];
414
+ const nodeIds = [];
494
415
  const nodesInPage = [];
495
416
  const emptyResources = {
496
417
  geometry: null,
497
418
  compressedGeometry: null,
498
419
  texture: null,
420
+ hasUvRegions: false,
499
421
  sharedResources: null,
500
422
  meshMaterial: null,
501
423
  vertexCount: null,
@@ -510,25 +432,26 @@ class I3SConverter {
510
432
  }
511
433
  const lodSelection = (0, lod_conversion_utils_1.convertGeometricErrorToScreenThreshold)(sourceTile, boundingVolumes);
512
434
  const maxScreenThresholdSQ = lodSelection.find((val) => val.metricType === 'maxScreenThresholdSQ') || { maxError: 0 };
513
- const nodeInPage = this._updateNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentId, resources);
514
- const node = this._createNodeIndexDocument(parentTile, boundingVolumes, lodSelection, nodeInPage, resources);
435
+ const nodeInPage = await this._updateNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentNode.inPageId, resources);
436
+ const nodeData = await node_index_document_1.NodeIndexDocument.createNodeIndexDocument(parentNode, boundingVolumes, lodSelection, nodeInPage, resources);
437
+ const node = await new node_index_document_1.NodeIndexDocument(nodeInPage.index, this).addData(nodeData);
438
+ nodes.push(node);
515
439
  if (nodeInPage.mesh) {
516
- await this._writeResources(resources, node.path);
440
+ await this._writeResources(resources, node.id);
517
441
  }
518
442
  if (this.validate) {
519
- this.boundingVolumeWarnings = (0, node_debug_1.validateNodeBoundingVolumes)(node);
443
+ this.boundingVolumeWarnings = (0, node_debug_1.validateNodeBoundingVolumes)(nodeData);
520
444
  if (this.boundingVolumeWarnings && this.boundingVolumeWarnings.length) {
521
445
  console.warn('Bounding Volume Warnings: ', ...this.boundingVolumeWarnings); //eslint-disable-line
522
446
  }
523
447
  }
524
- nodes.push(node);
448
+ nodeIds.push(nodeInPage.index);
525
449
  nodesInPage.push(nodeInPage);
526
450
  }
527
451
  sourceTile.unloadContent();
528
452
  await this._addChildrenWithNeighborsAndWriteFile({
529
453
  parentNode: nodes[0],
530
454
  sourceTiles: sourceTile.children,
531
- parentId: nodesInPage[0].index,
532
455
  level: level + 1
533
456
  });
534
457
  return nodes;
@@ -538,14 +461,7 @@ class I3SConverter {
538
461
  * @param sourceTile - source tile (3DTile)
539
462
  * @param parentId - id of parent node in node pages
540
463
  * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA
541
- * result.geometry - ArrayBuffer with geometry attributes
542
- * result.compressedGeometry - ArrayBuffer with compressed (draco) geometry
543
- * result.texture - texture image
544
- * result.sharedResources - shared resource data object
545
- * result.meshMaterial - PBR-like material object
546
- * result.vertexCount - number of vertices in geometry
547
- * result.attributes - feature attributes
548
- * result.featureCount - number of features
464
+ * @returns - converted node resources
549
465
  */
550
466
  async _convertResources(sourceTile, parentId, propertyTable) {
551
467
  if (!this.isContentSupported(sourceTile)) {
@@ -556,7 +472,7 @@ class I3SConverter {
556
472
  halfSize: [],
557
473
  quaternion: []
558
474
  };
559
- const resourcesData = await (0, geometry_converter_1.default)(sourceTile.content, () => this.nodePages.push({ index: 0, obb: draftObb }, parentId), propertyTable, this.featuresHashArray, this.layers0?.attributeStorageInfo, this.options.draco, this.generateBoundingVolumes, this.geoidHeightModel, this.workerSource);
475
+ const resourcesData = await (0, geometry_converter_1.default)(sourceTile.content, async () => (await this.nodePages.push({ index: 0, obb: draftObb }, parentId)).index, propertyTable, this.featuresHashArray, this.layers0?.attributeStorageInfo, this.options.draco, this.generateBoundingVolumes, this.options.mergeMaterials, this.geoidHeightModel, this.workerSource);
560
476
  return resourcesData;
561
477
  }
562
478
  /**
@@ -571,10 +487,11 @@ class I3SConverter {
571
487
  * @param resources.texture - texture image
572
488
  * @param resources.vertexCount - number of vertices in geometry
573
489
  * @param resources.featureCount - number of features
490
+ * @param resources.geometry - Uint8Array with geometry attributes
574
491
  * @return the node object in node pages
575
492
  */
576
- _updateNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentId, resources) {
577
- const { meshMaterial, texture, vertexCount, featureCount, geometry } = resources;
493
+ async _updateNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentId, resources) {
494
+ const { meshMaterial, texture, vertexCount, featureCount, geometry, hasUvRegions } = resources;
578
495
  const nodeInPage = {
579
496
  index: 0,
580
497
  lodThreshold: maxScreenThresholdSQ.maxError,
@@ -584,7 +501,7 @@ class I3SConverter {
584
501
  if (geometry && this.isContentSupported(sourceTile)) {
585
502
  nodeInPage.mesh = {
586
503
  geometry: {
587
- definition: texture ? 0 : 1,
504
+ definition: this.findOrCreateGeometryDefinition(Boolean(texture), hasUvRegions),
588
505
  resource: 0
589
506
  },
590
507
  attribute: {
@@ -596,76 +513,30 @@ class I3SConverter {
596
513
  };
597
514
  }
598
515
  let nodeId = resources.nodeId;
599
- if (nodeId) {
600
- this.nodePages.updateAll(nodeId, nodeInPage);
601
- const node = this.nodePages.getNodeById(nodeId);
602
- this.nodePages.updateResourceInMesh(node);
516
+ let node;
517
+ if (!nodeId) {
518
+ node = await this.nodePages.push(nodeInPage, parentId);
603
519
  }
604
520
  else {
605
- nodeId = this.nodePages.push(nodeInPage, parentId);
521
+ node = await this.nodePages.getNodeById(nodeId);
606
522
  }
523
+ node_pages_1.default.updateAll(node, nodeInPage);
607
524
  if (meshMaterial) {
608
- this.nodePages.updateMaterialByNodeId(nodeId, this._findOrCreateMaterial(meshMaterial));
525
+ node_pages_1.default.updateMaterialByNodeId(node, this._findOrCreateMaterial(meshMaterial));
609
526
  }
610
527
  if (texture) {
611
528
  const texelCountHint = texture.image.height * texture.image.width;
612
- this.nodePages.updateTexelCountHintByNodeId(nodeId, texelCountHint);
529
+ node_pages_1.default.updateTexelCountHintByNodeId(node, texelCountHint);
613
530
  }
614
531
  if (vertexCount) {
615
532
  this.vertexCounter += vertexCount;
616
- this.nodePages.updateVertexCountByNodeId(nodeId, vertexCount);
533
+ node_pages_1.default.updateVertexCountByNodeId(node, vertexCount);
617
534
  }
618
- this.nodePages.updateNodeAttributeByNodeId(nodeId);
535
+ node_pages_1.default.updateNodeAttributeByNodeId(node);
619
536
  if (featureCount) {
620
- this.nodePages.updateFeatureCountByNodeId(nodeId, featureCount);
621
- }
622
- return this.nodePages.getNodeById(nodeId);
623
- }
624
- /**
625
- * Create a new node page object in node pages
626
- * @param parentNode - 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object of the parent node
627
- * @param boundingVolumes - Bounding volumes
628
- * @param lodSelection - Level of Details (LOD) metrics
629
- * @param nodeInPage - corresponding node object in a node page
630
- * @param resources - the node resources data
631
- * @param resources.texture - texture image
632
- * @param resources.attributes - feature attributes
633
- * @return 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object
634
- */
635
- _createNodeIndexDocument(parentNode, boundingVolumes, lodSelection, nodeInPage, resources) {
636
- const { texture, attributes } = resources;
637
- const nodeId = nodeInPage.index;
638
- const nodeData = {
639
- version: parentNode.version,
640
- id: nodeId.toString(),
641
- path: nodeId.toString(),
642
- level: parentNode.level + 1,
643
- ...boundingVolumes,
644
- lodSelection,
645
- parentNode: {
646
- id: parentNode.id,
647
- href: `../${parentNode.id}`,
648
- mbs: parentNode.mbs,
649
- obb: parentNode.obb
650
- },
651
- children: [],
652
- neighbors: []
653
- };
654
- const node = (0, json_map_transform_1.default)(nodeData, (0, node_1.NODE)());
655
- if (nodeInPage.mesh) {
656
- node.geometryData = [{ href: './geometries/0' }];
657
- node.sharedResource = { href: './shared' };
658
- if (texture) {
659
- node.textureData = [{ href: './textures/0' }, { href: './textures/1' }];
660
- }
661
- if (attributes && attributes.length && this.layers0?.attributeStorageInfo?.length) {
662
- node.attributeData = [];
663
- for (let index = 0; index < attributes.length; index++) {
664
- const folderName = this.layers0.attributeStorageInfo[index].key;
665
- node.attributeData.push({ href: `./attributes/${folderName}/0` });
666
- }
667
- }
537
+ node_pages_1.default.updateFeatureCountByNodeId(node, featureCount);
668
538
  }
539
+ this.nodePages.saveNode(node);
669
540
  return node;
670
541
  }
671
542
  /**
@@ -699,13 +570,13 @@ class I3SConverter {
699
570
  const slpkGeometryPath = (0, path_1.join)(childPath, 'geometries');
700
571
  await this.writeQueue.enqueue({
701
572
  archiveKey: `${slpkChildPath}/geometries/0.bin.gz`,
702
- writePromise: (0, file_utils_1.writeFileForSlpk)(slpkGeometryPath, geometryBuffer, '0.bin')
573
+ writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkGeometryPath, geometryBuffer, '0.bin')
703
574
  });
704
575
  }
705
576
  else {
706
577
  const geometryPath = (0, path_1.join)(childPath, 'geometries/0/');
707
578
  await this.writeQueue.enqueue({
708
- writePromise: (0, file_utils_1.writeFile)(geometryPath, geometryBuffer, 'index.bin')
579
+ writePromise: () => (0, file_utils_1.writeFile)(geometryPath, geometryBuffer, 'index.bin')
709
580
  });
710
581
  }
711
582
  if (this.options.draco) {
@@ -713,13 +584,13 @@ class I3SConverter {
713
584
  const slpkCompressedGeometryPath = (0, path_1.join)(childPath, 'geometries');
714
585
  await this.writeQueue.enqueue({
715
586
  archiveKey: `${slpkChildPath}/geometries/1.bin.gz`,
716
- writePromise: (0, file_utils_1.writeFileForSlpk)(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
587
+ writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
717
588
  });
718
589
  }
719
590
  else {
720
591
  const compressedGeometryPath = (0, path_1.join)(childPath, 'geometries/1/');
721
592
  await this.writeQueue.enqueue({
722
- writePromise: (0, file_utils_1.writeFile)(compressedGeometryPath, compressedGeometry, 'index.bin')
593
+ writePromise: () => (0, file_utils_1.writeFile)(compressedGeometryPath, compressedGeometry, 'index.bin')
723
594
  });
724
595
  }
725
596
  }
@@ -742,12 +613,12 @@ class I3SConverter {
742
613
  const slpkSharedPath = (0, path_1.join)(childPath, 'shared');
743
614
  await this.writeQueue.enqueue({
744
615
  archiveKey: `${slpkChildPath}/shared/sharedResource.json.gz`,
745
- writePromise: (0, file_utils_1.writeFileForSlpk)(slpkSharedPath, sharedDataStr, 'sharedResource.json')
616
+ writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkSharedPath, sharedDataStr, 'sharedResource.json')
746
617
  });
747
618
  }
748
619
  else {
749
620
  const sharedPath = (0, path_1.join)(childPath, 'shared/');
750
- await this.writeQueue.enqueue({ writePromise: (0, file_utils_1.writeFile)(sharedPath, sharedDataStr) });
621
+ await this.writeQueue.enqueue({ writePromise: () => (0, file_utils_1.writeFile)(sharedPath, sharedDataStr) });
751
622
  }
752
623
  }
753
624
  /**
@@ -793,6 +664,7 @@ class I3SConverter {
793
664
  }
794
665
  if (!this.layers0.textureSetDefinitions.length) {
795
666
  this.layers0.textureSetDefinitions.push({ formats });
667
+ this.layers0.textureSetDefinitions.push({ formats, atlas: true });
796
668
  }
797
669
  }
798
670
  }
@@ -810,13 +682,13 @@ class I3SConverter {
810
682
  const compress = false;
811
683
  await this.writeQueue.enqueue({
812
684
  archiveKey: `${slpkChildPath}/textures/${name}.${format}`,
813
- writePromise: (0, file_utils_1.writeFileForSlpk)(slpkTexturePath, textureData, `${name}.${format}`, compress)
685
+ writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkTexturePath, textureData, `${name}.${format}`, compress)
814
686
  });
815
687
  }
816
688
  else {
817
689
  const texturePath = (0, path_1.join)(childPath, `textures/${name}/`);
818
690
  await this.writeQueue.enqueue({
819
- writePromise: (0, file_utils_1.writeFile)(texturePath, textureData, `index.${format}`)
691
+ writePromise: () => (0, file_utils_1.writeFile)(texturePath, textureData, `index.${format}`)
820
692
  });
821
693
  }
822
694
  }
@@ -835,13 +707,13 @@ class I3SConverter {
835
707
  const slpkAttributesPath = (0, path_1.join)(childPath, 'attributes', folderName);
836
708
  await this.writeQueue.enqueue({
837
709
  archiveKey: `${slpkChildPath}/attributes/${folderName}.bin.gz`,
838
- writePromise: (0, file_utils_1.writeFileForSlpk)(slpkAttributesPath, fileBuffer, '0.bin')
710
+ writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkAttributesPath, fileBuffer, '0.bin')
839
711
  });
840
712
  }
841
713
  else {
842
714
  const attributesPath = (0, path_1.join)(childPath, `attributes/${folderName}/0`);
843
715
  await this.writeQueue.enqueue({
844
- writePromise: (0, file_utils_1.writeFile)(attributesPath, fileBuffer, 'index.bin')
716
+ writePromise: () => (0, file_utils_1.writeFile)(attributesPath, fileBuffer, 'index.bin')
845
717
  });
846
718
  }
847
719
  }
@@ -871,12 +743,29 @@ class I3SConverter {
871
743
  _findOrCreateMaterial(material) {
872
744
  const hash = (0, md5_1.default)(JSON.stringify(material));
873
745
  if (this.materialMap.has(hash)) {
874
- return this.materialMap.get(hash);
746
+ return this.materialMap.get(hash) || 0;
875
747
  }
876
748
  const newMaterialId = this.materialDefinitions.push(material) - 1;
877
749
  this.materialMap.set(hash, newMaterialId);
878
750
  return newMaterialId;
879
751
  }
752
+ /**
753
+ * Get unique geometry configuration index
754
+ * In the end of conversion configurations will be transformed to geometryDefinitions array
755
+ * @param hasTexture
756
+ * @param hasUvRegions
757
+ * @returns
758
+ */
759
+ findOrCreateGeometryDefinition(hasTexture, hasUvRegions) {
760
+ const geometryConfig = { hasTexture, hasUvRegions };
761
+ const hash = (0, md5_1.default)(JSON.stringify(geometryConfig));
762
+ if (this.geometryMap.has(hash)) {
763
+ return this.geometryMap.get(hash) || 0;
764
+ }
765
+ const newGeometryId = this.geometryConfigs.push(geometryConfig) - 1;
766
+ this.geometryMap.set(hash, newGeometryId);
767
+ return newGeometryId;
768
+ }
880
769
  /**
881
770
  * Do conversion of 3DTiles property table to I3s node attributes.
882
771
  * @param propertyTable - Table with layer meta data.