@loaders.gl/tile-converter 3.4.11 → 3.4.12

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 (53) hide show
  1. package/dist/3d-tiles-attributes-worker.js +1 -1
  2. package/dist/converter.min.js +58 -58
  3. package/dist/dist.min.js +11 -26
  4. package/dist/es5/3d-tiles-attributes-worker.js +1 -1
  5. package/dist/es5/deps-installer/deps-installer.js +1 -1
  6. package/dist/es5/i3s-attributes-worker.js +1 -1
  7. package/dist/es5/pgm-loader.js +1 -1
  8. package/dist/esm/3d-tiles-attributes-worker.js +1 -1
  9. package/dist/esm/deps-installer/deps-installer.js +1 -1
  10. package/dist/esm/i3s-attributes-worker.js +1 -1
  11. package/dist/esm/pgm-loader.js +1 -1
  12. package/package.json +15 -15
  13. package/dist/3d-tiles-converter/3d-tiles-converter.js +0 -287
  14. package/dist/3d-tiles-converter/helpers/b3dm-converter.js +0 -282
  15. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +0 -23
  16. package/dist/3d-tiles-converter/helpers/texture-atlas.js +0 -52
  17. package/dist/3d-tiles-converter/json-templates/tileset.js +0 -43
  18. package/dist/bundle.js +0 -5
  19. package/dist/constants.js +0 -4
  20. package/dist/converter-cli.js +0 -280
  21. package/dist/deps-installer/deps-installer.js +0 -63
  22. package/dist/i3s-converter/helpers/batch-ids-extensions.js +0 -141
  23. package/dist/i3s-converter/helpers/coordinate-converter.js +0 -124
  24. package/dist/i3s-converter/helpers/create-scene-server-path.js +0 -28
  25. package/dist/i3s-converter/helpers/feature-attributes.js +0 -216
  26. package/dist/i3s-converter/helpers/geometry-attributes.js +0 -202
  27. package/dist/i3s-converter/helpers/geometry-converter.js +0 -1195
  28. package/dist/i3s-converter/helpers/gltf-attributes.js +0 -88
  29. package/dist/i3s-converter/helpers/node-debug.js +0 -120
  30. package/dist/i3s-converter/helpers/node-index-document.js +0 -250
  31. package/dist/i3s-converter/helpers/node-pages.js +0 -316
  32. package/dist/i3s-converter/i3s-converter.js +0 -890
  33. package/dist/i3s-converter/json-templates/geometry-definitions.js +0 -87
  34. package/dist/i3s-converter/json-templates/layers.js +0 -139
  35. package/dist/i3s-converter/json-templates/metadata.js +0 -25
  36. package/dist/i3s-converter/json-templates/node.js +0 -89
  37. package/dist/i3s-converter/json-templates/scene-server.js +0 -31
  38. package/dist/i3s-converter/json-templates/shared-resources.js +0 -129
  39. package/dist/i3s-converter/json-templates/store.js +0 -103
  40. package/dist/i3s-converter/types.js +0 -2
  41. package/dist/i3s-server/app.js +0 -14
  42. package/dist/i3s-server/controllers/index-controller.js +0 -23
  43. package/dist/i3s-server/routes/index.js +0 -16
  44. package/dist/index.js +0 -10
  45. package/dist/lib/utils/compress-util.js +0 -257
  46. package/dist/lib/utils/file-utils.js +0 -138
  47. package/dist/lib/utils/lod-conversion-utils.js +0 -76
  48. package/dist/lib/utils/queue.js +0 -18
  49. package/dist/lib/utils/statistic-utills.js +0 -64
  50. package/dist/lib/utils/write-queue.js +0 -80
  51. package/dist/pgm-loader.js +0 -24
  52. package/dist/workers/3d-tiles-attributes-worker.js +0 -9
  53. package/dist/workers/i3s-attributes-worker.js +0 -5
@@ -1,890 +0,0 @@
1
- "use strict";
2
- // loaders.gl, MIT license
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || function (mod) {
20
- if (mod && mod.__esModule) return mod;
21
- var result = {};
22
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
- __setModuleDefault(result, mod);
24
- return result;
25
- };
26
- var __importDefault = (this && this.__importDefault) || function (mod) {
27
- return (mod && mod.__esModule) ? mod : { "default": mod };
28
- };
29
- Object.defineProperty(exports, "__esModule", { value: true });
30
- const core_1 = require("@loaders.gl/core");
31
- const tiles_1 = require("@loaders.gl/tiles");
32
- const _3d_tiles_1 = require("@loaders.gl/3d-tiles");
33
- const path_1 = require("path");
34
- const uuid_1 = require("uuid");
35
- const process_1 = __importDefault(require("process"));
36
- const json_map_transform_1 = __importDefault(require("json-map-transform"));
37
- const md5_1 = __importDefault(require("md5"));
38
- const node_pages_1 = __importDefault(require("./helpers/node-pages"));
39
- const file_utils_1 = require("../lib/utils/file-utils");
40
- const compress_util_1 = require("../lib/utils/compress-util");
41
- const statistic_utills_1 = require("../lib/utils/statistic-utills");
42
- const geometry_converter_1 = __importStar(require("./helpers/geometry-converter"));
43
- const coordinate_converter_1 = require("./helpers/coordinate-converter");
44
- const create_scene_server_path_1 = require("./helpers/create-scene-server-path");
45
- const lod_conversion_utils_1 = require("../lib/utils/lod-conversion-utils");
46
- const pgm_loader_1 = require("../pgm-loader");
47
- const layers_1 = require("./json-templates/layers");
48
- const geometry_definitions_1 = require("./json-templates/geometry-definitions");
49
- const shared_resources_1 = require("./json-templates/shared-resources");
50
- const node_debug_1 = require("./helpers/node-debug");
51
- const textures_1 = require("@loaders.gl/textures");
52
- const images_1 = require("@loaders.gl/images");
53
- const worker_utils_1 = require("@loaders.gl/worker-utils");
54
- const draco_1 = require("@loaders.gl/draco");
55
- const write_queue_1 = __importDefault(require("../lib/utils/write-queue"));
56
- const i3s_attributes_worker_1 = require("../i3s-attributes-worker");
57
- const constants_1 = require("../constants");
58
- const feature_attributes_1 = require("./helpers/feature-attributes");
59
- const node_index_document_1 = require("./helpers/node-index-document");
60
- const ION_DEFAULT_TOKEN = process_1.default.env?.IonToken || // eslint-disable-line
61
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ'; // eslint-disable-line
62
- const HARDCODED_NODES_PER_PAGE = 64;
63
- const _3D_TILES = '3DTILES';
64
- const _3D_OBJECT_LAYER_TYPE = '3DObject';
65
- const REFRESH_TOKEN_TIMEOUT = 1800; // 30 minutes in seconds
66
- const CESIUM_DATASET_PREFIX = 'https://';
67
- // const FS_FILE_TOO_LARGE = 'ERR_FS_FILE_TOO_LARGE';
68
- /**
69
- * Converter from 3d-tiles tileset to i3s layer
70
- */
71
- class I3SConverter {
72
- constructor() {
73
- this.boundingVolumeWarnings = [];
74
- this.conversionStartTime = [0, 0];
75
- this.refreshTokenTime = [0, 0];
76
- this.sourceTileset = null;
77
- this.geoidHeightModel = null;
78
- this.Loader = _3d_tiles_1.Tiles3DLoader;
79
- this.workerSource = {};
80
- this.writeQueue = new write_queue_1.default();
81
- this.compressList = null;
82
- this.nodePages = new node_pages_1.default(file_utils_1.writeFile, HARDCODED_NODES_PER_PAGE, this);
83
- this.options = {};
84
- this.layers0Path = '';
85
- this.materialMap = new Map();
86
- this.materialDefinitions = [];
87
- this.geometryMap = new Map();
88
- this.geometryConfigs = [];
89
- this.vertexCounter = 0;
90
- this.layers0 = null;
91
- this.featuresHashArray = [];
92
- this.refinementCounter = {
93
- tilesCount: 0,
94
- tilesWithAddRefineCount: 0
95
- };
96
- this.validate = false;
97
- this.generateTextures = false;
98
- this.generateBoundingVolumes = false;
99
- this.layersHasTexture = false;
100
- this.compressList = null;
101
- }
102
- /**
103
- * Convert a 3d tileset
104
- * @param options
105
- * @param options.inputUrl the url to read the tileset from
106
- * @param options.outputPath the output filename
107
- * @param options.tilesetName the output name of the tileset
108
- * @param options.maxDepth The max tree depth of conversion
109
- * @param options.slpk Generate slpk (Scene Layer Packages) output file
110
- * @param options.sevenZipExe Location of 7z.exe archiver to create slpk on Windows
111
- * @param options.egmFilePath location of *.pgm file to convert heights from ellipsoidal to gravity-related format
112
- * @param options.token Token for Cesium ION tilesets authentication
113
- * @param options.draco Generate I3S 1.7 draco compressed geometries
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
118
- */
119
- async convert(options) {
120
- if (core_1.isBrowser) {
121
- console.log(constants_1.BROWSER_ERROR_MESSAGE);
122
- return constants_1.BROWSER_ERROR_MESSAGE;
123
- }
124
- this.conversionStartTime = process_1.default.hrtime();
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;
138
- this.validate = Boolean(validate);
139
- this.Loader = inputUrl.indexOf(CESIUM_DATASET_PREFIX) !== -1 ? _3d_tiles_1.CesiumIonLoader : _3d_tiles_1.Tiles3DLoader;
140
- this.generateTextures = Boolean(generateTextures);
141
- this.generateBoundingVolumes = Boolean(generateBoundingVolumes);
142
- this.writeQueue = new write_queue_1.default();
143
- this.writeQueue.startListening();
144
- console.log('Loading egm file...'); // eslint-disable-line
145
- this.geoidHeightModel = await (0, core_1.load)(egmFilePath, pgm_loader_1.PGMLoader);
146
- console.log('Loading egm file completed!'); // eslint-disable-line
147
- if (slpk) {
148
- this.nodePages.useWriteFunction(file_utils_1.writeFileForSlpk);
149
- }
150
- await this.loadWorkers();
151
- try {
152
- const preloadOptions = await this._fetchPreloadOptions();
153
- const tilesetOptions = {
154
- loadOptions: {
155
- _nodeWorkers: true,
156
- reuseWorkers: true,
157
- basis: { format: 'rgba32' },
158
- 'basis-nodejs': {
159
- format: 'rgba32',
160
- workerUrl: './modules/textures/dist/basis-nodejs-worker.js'
161
- },
162
- 'draco-nodejs': { workerUrl: './modules/draco/dist/draco-nodejs-worker.js' }
163
- }
164
- };
165
- let tilesetUrl = inputUrl;
166
- if (preloadOptions.url) {
167
- tilesetUrl = preloadOptions.url;
168
- }
169
- if (preloadOptions.headers) {
170
- tilesetOptions.loadOptions.fetch = { headers: preloadOptions.headers };
171
- }
172
- Object.assign(tilesetOptions, preloadOptions);
173
- const sourceTilesetJson = await (0, core_1.load)(tilesetUrl, this.Loader, tilesetOptions.loadOptions);
174
- // console.log(tilesetJson); // eslint-disable-line
175
- this.sourceTileset = new tiles_1.Tileset3D(sourceTilesetJson, tilesetOptions);
176
- await this._createAndSaveTileset(outputPath, tilesetName, sourceTilesetJson?.root?.boundingVolume?.region);
177
- await this._finishConversion({ slpk: Boolean(slpk), outputPath, tilesetName });
178
- return sourceTilesetJson;
179
- }
180
- catch (error) {
181
- throw error;
182
- }
183
- finally {
184
- // Clean up worker pools
185
- const workerFarm = worker_utils_1.WorkerFarm.getWorkerFarm({});
186
- workerFarm.destroy();
187
- }
188
- }
189
- /**
190
- * Convert and save the layer and embedded tiles
191
- * @param outputPath - path to save output data
192
- * @param tilesetName - new tileset path
193
- */
194
- async _createAndSaveTileset(outputPath, tilesetName, boundingVolumeRegion) {
195
- const tilesetPath = (0, path_1.join)(`${outputPath}`, `${tilesetName}`);
196
- // Removing the tilesetPath needed to exclude erroneous files after conversion
197
- try {
198
- await (0, file_utils_1.removeDir)(tilesetPath);
199
- }
200
- catch (e) {
201
- // do nothing
202
- }
203
- this.layers0Path = (0, path_1.join)(tilesetPath, 'SceneServer', 'layers', '0');
204
- this._formLayers0(tilesetName, boundingVolumeRegion);
205
- this.materialDefinitions = [];
206
- this.materialMap = new Map();
207
- const sourceRootTile = this.sourceTileset.root;
208
- const boundingVolumes = (0, coordinate_converter_1.createBoundingVolumes)(sourceRootTile, this.geoidHeightModel);
209
- await this.nodePages.push({
210
- index: 0,
211
- lodThreshold: 0,
212
- obb: boundingVolumes.obb,
213
- children: []
214
- });
215
- const rootNode = await node_index_document_1.NodeIndexDocument.createRootNode(boundingVolumes, this);
216
- await this._convertNodesTree(rootNode, sourceRootTile);
217
- this.layers0.materialDefinitions = this.materialDefinitions;
218
- // @ts-ignore
219
- this.layers0.geometryDefinitions = (0, json_map_transform_1.default)(this.geometryConfigs.map((config) => ({
220
- geometryConfig: { ...config, draco: this.options.draco }
221
- })), (0, geometry_definitions_1.GEOMETRY_DEFINITION)());
222
- if (this.layersHasTexture === false) {
223
- this.layers0.store.defaultGeometrySchema.ordering =
224
- this.layers0.store.defaultGeometrySchema.ordering.filter((attribute) => attribute !== 'uv0');
225
- }
226
- await this._writeLayers0();
227
- (0, create_scene_server_path_1.createSceneServerPath)(tilesetName, this.layers0, tilesetPath);
228
- for (const filePath of this.compressList || []) {
229
- await (0, compress_util_1.compressFileWithGzip)(filePath);
230
- await (0, file_utils_1.removeFile)(filePath);
231
- }
232
- await this.nodePages.save();
233
- await this.writeQueue.finalize();
234
- await this._createSlpk(tilesetPath);
235
- }
236
- /**
237
- * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
238
- * @param tilesetName - Name of layer
239
- */
240
- _formLayers0(tilesetName, boundingVolumeRegion) {
241
- const fullExtent = (0, coordinate_converter_1.convertBoundingVolumeToI3SFullExtent)(this.sourceTileset?.boundingVolume || this.sourceTileset?.root?.boundingVolume);
242
- if (boundingVolumeRegion) {
243
- fullExtent.zmin = boundingVolumeRegion[4];
244
- fullExtent.zmax = boundingVolumeRegion[5];
245
- }
246
- const extent = [fullExtent.xmin, fullExtent.ymin, fullExtent.xmax, fullExtent.ymax];
247
- const layers0data = {
248
- version: `{${(0, uuid_1.v4)().toUpperCase()}}`,
249
- id: 0,
250
- name: tilesetName,
251
- href: './layers/0',
252
- store: {
253
- id: `{${(0, uuid_1.v4)().toUpperCase()}}`,
254
- extent
255
- },
256
- nodePages: {
257
- nodesPerPage: HARDCODED_NODES_PER_PAGE
258
- },
259
- compressGeometry: this.options.draco,
260
- fullExtent
261
- };
262
- this.layers0 = (0, json_map_transform_1.default)(layers0data, (0, layers_1.LAYERS)());
263
- }
264
- /**
265
- * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
266
- * @param rootNode - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
267
- * @param sourceRootTile - Source (3DTile) tile data
268
- */
269
- async _convertNodesTree(rootNode, sourceRootTile) {
270
- await this.sourceTileset._loadTile(sourceRootTile);
271
- if (this.isContentSupported(sourceRootTile)) {
272
- const childNodes = await this._createNode(rootNode, sourceRootTile, 0);
273
- for (const childNode of childNodes) {
274
- await childNode.save();
275
- }
276
- await rootNode.addChildren(childNodes);
277
- }
278
- else {
279
- await this._addChildrenWithNeighborsAndWriteFile({
280
- parentNode: rootNode,
281
- sourceTiles: sourceRootTile.children,
282
- level: 1
283
- });
284
- }
285
- await sourceRootTile.unloadContent();
286
- await rootNode.save();
287
- }
288
- /**
289
- * Write 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md in file
290
- */
291
- async _writeLayers0() {
292
- if (this.options.slpk) {
293
- await this.writeQueue.enqueue({
294
- archiveKey: '3dSceneLayer.json.gz',
295
- writePromise: () => (0, file_utils_1.writeFileForSlpk)(this.layers0Path, JSON.stringify(this.layers0), '3dSceneLayer.json')
296
- });
297
- }
298
- else {
299
- await this.writeQueue.enqueue({
300
- writePromise: () => (0, file_utils_1.writeFile)(this.layers0Path, JSON.stringify(this.layers0))
301
- });
302
- }
303
- }
304
- /**
305
- * Pack files into *.slpk archive
306
- * @param tilesetPath - Path to save file
307
- */
308
- async _createSlpk(tilesetPath) {
309
- if (this.options.slpk) {
310
- const slpkTilesetPath = (0, path_1.join)(tilesetPath, 'SceneServer', 'layers', '0');
311
- const slpkFileName = `${tilesetPath}.slpk`;
312
- await (0, compress_util_1.compressWithChildProcess)(slpkTilesetPath, slpkFileName, 0, '.', this.options.sevenZipExe);
313
- // TODO: `addFileToZip` corrupts archive so it can't be validated with windows i3s_converter.exe
314
- // const fileHash128Path = `${tilesetPath}/@specialIndexFileHASH128@`;
315
- // try {
316
- // await generateHash128FromZip(slpkFileName, fileHash128Path);
317
- // await addFileToZip(
318
- // tilesetPath,
319
- // '@specialIndexFileHASH128@',
320
- // slpkFileName,
321
- // this.options.sevenZipExe
322
- // );
323
- // } catch (error) {
324
- // if (error.code === FS_FILE_TOO_LARGE) {
325
- // console.warn(`${slpkFileName} file is too big to generate a hash`); // eslint-disable-line
326
- // } else {
327
- // console.error(error); // eslint-disable-line
328
- // }
329
- // }
330
- // All converted files are contained in slpk now they can be deleted
331
- try {
332
- await (0, file_utils_1.removeDir)(tilesetPath);
333
- }
334
- catch (e) {
335
- // do nothing
336
- }
337
- }
338
- }
339
- /**
340
- * Add child nodes recursively and write them to files
341
- * @param data - arguments
342
- * @param data.parentNode - 3DNodeIndexDocument of parent node
343
- * @param data.sourceTiles - array of source child nodes
344
- * @param data.level - level of node (distanse to root node in the tree)
345
- */
346
- async _addChildrenWithNeighborsAndWriteFile(data) {
347
- await this._addChildren(data);
348
- await data.parentNode.addNeighbors();
349
- }
350
- /**
351
- * Convert nested subtree of 3DTiles dataset
352
- * @param param0
353
- * @param data.parentNode - 3DNodeIndexDocument of parent node
354
- * @param param0.sourceTile - source 3DTile data
355
- * @param param0.level - tree level
356
- */
357
- async convertNestedTileset({ parentNode, sourceTile, level }) {
358
- await this.sourceTileset._loadTile(sourceTile);
359
- await this._addChildren({
360
- parentNode,
361
- sourceTiles: sourceTile.children,
362
- level: level + 1
363
- });
364
- await sourceTile.unloadContent();
365
- }
366
- /**
367
- * Convert 3DTiles tile to I3S node
368
- * @param param0
369
- * @param param0.parentNode - 3DNodeIndexDocument of parent node
370
- * @param param0.sourceTile - source 3DTile data
371
- * @param param0.level - tree level
372
- */
373
- async convertNode({ parentNode, sourceTile, level }) {
374
- const childNodes = await this._createNode(parentNode, sourceTile, level);
375
- await parentNode.addChildren(childNodes);
376
- }
377
- /**
378
- * Add child nodes recursively and write them to files
379
- * @param param0 - arguments
380
- * @param param0.parentNode - 3DNodeIndexDocument of parent node
381
- * @param param0.sourceTile - source 3DTile data
382
- * @param param0.level - tree level
383
- */
384
- async _addChildren(data) {
385
- const { sourceTiles, parentNode, level } = data;
386
- if (this.options.maxDepth && level > this.options.maxDepth) {
387
- return;
388
- }
389
- for (const sourceTile of sourceTiles) {
390
- if (sourceTile.type === 'json') {
391
- await this.convertNestedTileset({ parentNode, sourceTile, level });
392
- }
393
- else {
394
- await this.convertNode({ parentNode, sourceTile, level });
395
- }
396
- if (sourceTile.id) {
397
- console.log(sourceTile.id); // eslint-disable-line
398
- }
399
- }
400
- }
401
- /**
402
- * Convert tile to one or more I3S nodes
403
- * @param parentNode - 3DNodeIndexDocument of parent node
404
- * @param sourceTile - source 3DTile data
405
- * @param level - tree level
406
- */
407
- async _createNode(parentNode, sourceTile, level) {
408
- this._checkAddRefinementTypeForTile(sourceTile);
409
- await this._updateTilesetOptions();
410
- await this.sourceTileset._loadTile(sourceTile);
411
- let boundingVolumes = (0, coordinate_converter_1.createBoundingVolumes)(sourceTile, this.geoidHeightModel);
412
- const propertyTable = (0, geometry_converter_1.getPropertyTable)(sourceTile.content);
413
- if (propertyTable && !this.layers0?.attributeStorageInfo?.length) {
414
- this._convertPropertyTableToNodeAttributes(propertyTable);
415
- }
416
- const resourcesData = await this._convertResources(sourceTile, parentNode.inPageId, propertyTable);
417
- const nodes = [];
418
- const nodeIds = [];
419
- const nodesInPage = [];
420
- const emptyResources = {
421
- geometry: null,
422
- compressedGeometry: null,
423
- texture: null,
424
- hasUvRegions: false,
425
- sharedResources: null,
426
- meshMaterial: null,
427
- vertexCount: null,
428
- attributes: null,
429
- featureCount: null,
430
- boundingVolumes: null
431
- };
432
- for (const resources of resourcesData || [emptyResources]) {
433
- this.layersHasTexture = this.layersHasTexture || Boolean(resources.texture);
434
- if (this.generateBoundingVolumes && resources.boundingVolumes) {
435
- boundingVolumes = resources.boundingVolumes;
436
- }
437
- const lodSelection = (0, lod_conversion_utils_1.convertGeometricErrorToScreenThreshold)(sourceTile, boundingVolumes);
438
- const maxScreenThresholdSQ = lodSelection.find((val) => val.metricType === 'maxScreenThresholdSQ') || { maxError: 0 };
439
- const nodeInPage = await this._updateNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentNode.inPageId, resources);
440
- const nodeData = await node_index_document_1.NodeIndexDocument.createNodeIndexDocument(parentNode, boundingVolumes, lodSelection, nodeInPage, resources);
441
- const node = await new node_index_document_1.NodeIndexDocument(nodeInPage.index, this).addData(nodeData);
442
- nodes.push(node);
443
- if (nodeInPage.mesh) {
444
- await this._writeResources(resources, node.id);
445
- }
446
- if (this.validate) {
447
- this.boundingVolumeWarnings = (0, node_debug_1.validateNodeBoundingVolumes)(nodeData);
448
- if (this.boundingVolumeWarnings && this.boundingVolumeWarnings.length) {
449
- console.warn('Bounding Volume Warnings: ', ...this.boundingVolumeWarnings); //eslint-disable-line
450
- }
451
- }
452
- nodeIds.push(nodeInPage.index);
453
- nodesInPage.push(nodeInPage);
454
- }
455
- sourceTile.unloadContent();
456
- await this._addChildrenWithNeighborsAndWriteFile({
457
- parentNode: nodes[0],
458
- sourceTiles: sourceTile.children,
459
- level: level + 1
460
- });
461
- return nodes;
462
- }
463
- /**
464
- * Convert tile to one or more I3S nodes
465
- * @param sourceTile - source tile (3DTile)
466
- * @param parentId - id of parent node in node pages
467
- * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA
468
- * @returns - converted node resources
469
- */
470
- async _convertResources(sourceTile, parentId, propertyTable) {
471
- if (!this.isContentSupported(sourceTile)) {
472
- return null;
473
- }
474
- const draftObb = {
475
- center: [],
476
- halfSize: [],
477
- quaternion: []
478
- };
479
- 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);
480
- return resourcesData;
481
- }
482
- /**
483
- * Update node object (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/node.cmn.md)
484
- * in node pages (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/nodePage.cmn.md)
485
- * @param maxScreenThresholdSQ - Level of Details (LOD) metric
486
- * @param boundingVolumes - Bounding volumes
487
- * @param sourceTile - source tile (3DTile)
488
- * @param parentId - id of parent node in node pages
489
- * @param resources - the node resources data
490
- * @param resources.meshMaterial - PBR-like material object
491
- * @param resources.texture - texture image
492
- * @param resources.vertexCount - number of vertices in geometry
493
- * @param resources.featureCount - number of features
494
- * @param resources.geometry - Uint8Array with geometry attributes
495
- * @return the node object in node pages
496
- */
497
- async _updateNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentId, resources) {
498
- const { meshMaterial, texture, vertexCount, featureCount, geometry, hasUvRegions } = resources;
499
- const nodeInPage = {
500
- index: 0,
501
- lodThreshold: maxScreenThresholdSQ.maxError,
502
- obb: boundingVolumes.obb,
503
- children: []
504
- };
505
- if (geometry && this.isContentSupported(sourceTile)) {
506
- nodeInPage.mesh = {
507
- geometry: {
508
- definition: this.findOrCreateGeometryDefinition(Boolean(texture), hasUvRegions),
509
- resource: 0
510
- },
511
- attribute: {
512
- resource: 0
513
- },
514
- material: {
515
- definition: 0
516
- }
517
- };
518
- }
519
- let nodeId = resources.nodeId;
520
- let node;
521
- if (!nodeId) {
522
- node = await this.nodePages.push(nodeInPage, parentId);
523
- }
524
- else {
525
- node = await this.nodePages.getNodeById(nodeId);
526
- }
527
- node_pages_1.default.updateAll(node, nodeInPage);
528
- if (meshMaterial) {
529
- node_pages_1.default.updateMaterialByNodeId(node, this._findOrCreateMaterial(meshMaterial));
530
- }
531
- if (texture) {
532
- const texelCountHint = texture.image.height * texture.image.width;
533
- node_pages_1.default.updateTexelCountHintByNodeId(node, texelCountHint);
534
- }
535
- if (vertexCount) {
536
- this.vertexCounter += vertexCount;
537
- node_pages_1.default.updateVertexCountByNodeId(node, vertexCount);
538
- }
539
- node_pages_1.default.updateNodeAttributeByNodeId(node);
540
- if (featureCount) {
541
- node_pages_1.default.updateFeatureCountByNodeId(node, featureCount);
542
- }
543
- this.nodePages.saveNode(node);
544
- return node;
545
- }
546
- /**
547
- * Write node resources in files
548
- * @param resources - source tile (3DTile)
549
- * @param resources.geometry - Uint8Array with geometry attributes
550
- * @param resources.compressedGeometry - Uint8Array with compressed (draco) geometry
551
- * @param resources.texture - texture image
552
- * @param resources.sharedResources - shared resource data object
553
- * @param resources.attributes - feature attributes
554
- * @return {Promise<void>}
555
- */
556
- async _writeResources(resources, nodePath) {
557
- const { geometry: geometryBuffer, compressedGeometry, texture, sharedResources, attributes } = resources;
558
- const childPath = (0, path_1.join)(this.layers0Path, 'nodes', nodePath);
559
- const slpkChildPath = (0, path_1.join)('nodes', nodePath);
560
- await this._writeGeometries(geometryBuffer, compressedGeometry, childPath, slpkChildPath);
561
- await this._writeShared(sharedResources, childPath, slpkChildPath, nodePath);
562
- await this._writeTexture(texture, childPath, slpkChildPath);
563
- await this._writeAttributes(attributes, childPath, slpkChildPath);
564
- }
565
- /**
566
- * Write non-compressed and compressed geometries in files
567
- * @param geometryBuffer - Uint8Array with geometry attributes
568
- * @param compressedGeometry - Uint8Array with compressed (draco) geometry
569
- * @param childPath - a child path to write resources
570
- * @param slpkChildPath - resource path inside *slpk file
571
- */
572
- async _writeGeometries(geometryBuffer, compressedGeometry, childPath, slpkChildPath) {
573
- if (this.options.slpk) {
574
- const slpkGeometryPath = (0, path_1.join)(childPath, 'geometries');
575
- await this.writeQueue.enqueue({
576
- archiveKey: `${slpkChildPath}/geometries/0.bin.gz`,
577
- writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkGeometryPath, geometryBuffer, '0.bin')
578
- });
579
- }
580
- else {
581
- const geometryPath = (0, path_1.join)(childPath, 'geometries/0/');
582
- await this.writeQueue.enqueue({
583
- writePromise: () => (0, file_utils_1.writeFile)(geometryPath, geometryBuffer, 'index.bin')
584
- });
585
- }
586
- if (this.options.draco) {
587
- if (this.options.slpk) {
588
- const slpkCompressedGeometryPath = (0, path_1.join)(childPath, 'geometries');
589
- await this.writeQueue.enqueue({
590
- archiveKey: `${slpkChildPath}/geometries/1.bin.gz`,
591
- writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
592
- });
593
- }
594
- else {
595
- const compressedGeometryPath = (0, path_1.join)(childPath, 'geometries/1/');
596
- await this.writeQueue.enqueue({
597
- writePromise: () => (0, file_utils_1.writeFile)(compressedGeometryPath, compressedGeometry, 'index.bin')
598
- });
599
- }
600
- }
601
- }
602
- /**
603
- * Write shared resources in a file
604
- * @param sharedResources - shared resource data object
605
- * @param childPath - a child path to write resources
606
- * @param slpkChildPath - resource path inside *slpk file
607
- * @param nodePath - a node path
608
- */
609
- async _writeShared(sharedResources, childPath, slpkChildPath, nodePath) {
610
- if (!sharedResources) {
611
- return;
612
- }
613
- sharedResources.nodePath = nodePath;
614
- const sharedData = (0, json_map_transform_1.default)(sharedResources, (0, shared_resources_1.SHARED_RESOURCES)());
615
- const sharedDataStr = JSON.stringify(sharedData);
616
- if (this.options.slpk) {
617
- const slpkSharedPath = (0, path_1.join)(childPath, 'shared');
618
- await this.writeQueue.enqueue({
619
- archiveKey: `${slpkChildPath}/shared/sharedResource.json.gz`,
620
- writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkSharedPath, sharedDataStr, 'sharedResource.json')
621
- });
622
- }
623
- else {
624
- const sharedPath = (0, path_1.join)(childPath, 'shared/');
625
- await this.writeQueue.enqueue({ writePromise: () => (0, file_utils_1.writeFile)(sharedPath, sharedDataStr) });
626
- }
627
- }
628
- /**
629
- * Generates textures based on texture mime type and fill in textureSetDefinitions data.
630
- * @param texture - the texture image
631
- * @param childPath - a child path to write resources
632
- * @param slpkChildPath - the resource path inside *slpk file
633
- */
634
- async _writeTexture(texture, childPath, slpkChildPath) {
635
- if (texture) {
636
- const format = this._getFormatByMimeType(texture?.mimeType);
637
- const formats = [];
638
- const textureData = texture.bufferView.data;
639
- switch (format) {
640
- case 'jpg':
641
- case 'png': {
642
- formats.push({ name: '0', format });
643
- await this.writeTextureFile(textureData, '0', format, childPath, slpkChildPath);
644
- if (this.generateTextures) {
645
- formats.push({ name: '1', format: 'ktx2' });
646
- // For Node.js texture.image.data is type of Buffer
647
- const copyArrayBuffer = texture.image.data.subarray();
648
- const arrayToEncode = new Uint8Array(copyArrayBuffer);
649
- const ktx2TextureData = (0, core_1.encode)({ ...texture.image, data: arrayToEncode }, textures_1.KTX2BasisWriterWorker, {
650
- ...textures_1.KTX2BasisWriterWorker.options,
651
- source: this.workerSource.ktx2,
652
- reuseWorkers: true,
653
- _nodeWorkers: true
654
- });
655
- await this.writeTextureFile(ktx2TextureData, '1', 'ktx2', childPath, slpkChildPath);
656
- }
657
- break;
658
- }
659
- case 'ktx2': {
660
- formats.push({ name: '1', format });
661
- await this.writeTextureFile(textureData, '1', format, childPath, slpkChildPath);
662
- if (this.generateTextures) {
663
- formats.push({ name: '0', format: 'jpg' });
664
- const decodedFromKTX2TextureData = (0, core_1.encode)(texture.image.data[0], images_1.ImageWriter);
665
- await this.writeTextureFile(decodedFromKTX2TextureData, '0', 'jpg', childPath, slpkChildPath);
666
- }
667
- }
668
- }
669
- if (!this.layers0.textureSetDefinitions.length) {
670
- this.layers0.textureSetDefinitions.push({ formats });
671
- this.layers0.textureSetDefinitions.push({ formats, atlas: true });
672
- }
673
- }
674
- }
675
- /**
676
- * Write the texture image in a file
677
- * @param textureData
678
- * @param name
679
- * @param format
680
- * @param childPath
681
- * @param slpkChildPath
682
- */
683
- async writeTextureFile(textureData, name, format, childPath, slpkChildPath) {
684
- if (this.options.slpk) {
685
- const slpkTexturePath = (0, path_1.join)(childPath, 'textures');
686
- const compress = false;
687
- await this.writeQueue.enqueue({
688
- archiveKey: `${slpkChildPath}/textures/${name}.${format}`,
689
- writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkTexturePath, textureData, `${name}.${format}`, compress)
690
- });
691
- }
692
- else {
693
- const texturePath = (0, path_1.join)(childPath, `textures/${name}/`);
694
- await this.writeQueue.enqueue({
695
- writePromise: () => (0, file_utils_1.writeFile)(texturePath, textureData, `index.${format}`)
696
- });
697
- }
698
- }
699
- /**
700
- * Write feature attributes in files
701
- * @param attributes - feature attributes
702
- * @param childPath - a child path to write resources
703
- * @param slpkChildPath - the resource path inside *slpk file
704
- */
705
- async _writeAttributes(attributes = [], childPath, slpkChildPath) {
706
- if (attributes?.length && this.layers0?.attributeStorageInfo?.length) {
707
- for (let index = 0; index < attributes.length; index++) {
708
- const folderName = this.layers0.attributeStorageInfo[index].key;
709
- const fileBuffer = new Uint8Array(attributes[index]);
710
- if (this.options.slpk) {
711
- const slpkAttributesPath = (0, path_1.join)(childPath, 'attributes', folderName);
712
- await this.writeQueue.enqueue({
713
- archiveKey: `${slpkChildPath}/attributes/${folderName}.bin.gz`,
714
- writePromise: () => (0, file_utils_1.writeFileForSlpk)(slpkAttributesPath, fileBuffer, '0.bin')
715
- });
716
- }
717
- else {
718
- const attributesPath = (0, path_1.join)(childPath, `attributes/${folderName}/0`);
719
- await this.writeQueue.enqueue({
720
- writePromise: () => (0, file_utils_1.writeFile)(attributesPath, fileBuffer, 'index.bin')
721
- });
722
- }
723
- }
724
- }
725
- }
726
- /**
727
- * Return file format by its MIME type
728
- * @param mimeType - feature attributes
729
- */
730
- _getFormatByMimeType(mimeType) {
731
- switch (mimeType) {
732
- case 'image/jpeg':
733
- return 'jpg';
734
- case 'image/png':
735
- return 'png';
736
- case 'image/ktx2':
737
- return 'ktx2';
738
- default:
739
- return 'jpg';
740
- }
741
- }
742
- /**
743
- * Find or create material in materialDefinitions array
744
- * @param material - end-to-end index of the node
745
- * @return material id
746
- */
747
- _findOrCreateMaterial(material) {
748
- const hash = (0, md5_1.default)(JSON.stringify(material));
749
- if (this.materialMap.has(hash)) {
750
- return this.materialMap.get(hash) || 0;
751
- }
752
- const newMaterialId = this.materialDefinitions.push(material) - 1;
753
- this.materialMap.set(hash, newMaterialId);
754
- return newMaterialId;
755
- }
756
- /**
757
- * Get unique geometry configuration index
758
- * In the end of conversion configurations will be transformed to geometryDefinitions array
759
- * @param hasTexture
760
- * @param hasUvRegions
761
- * @returns
762
- */
763
- findOrCreateGeometryDefinition(hasTexture, hasUvRegions) {
764
- const geometryConfig = { hasTexture, hasUvRegions };
765
- const hash = (0, md5_1.default)(JSON.stringify(geometryConfig));
766
- if (this.geometryMap.has(hash)) {
767
- return this.geometryMap.get(hash) || 0;
768
- }
769
- const newGeometryId = this.geometryConfigs.push(geometryConfig) - 1;
770
- this.geometryMap.set(hash, newGeometryId);
771
- return newGeometryId;
772
- }
773
- /**
774
- * Do conversion of 3DTiles property table to I3s node attributes.
775
- * @param propertyTable - Table with layer meta data.
776
- */
777
- _convertPropertyTableToNodeAttributes(propertyTable) {
778
- let attributeIndex = 0;
779
- const propertyTableWithObjectId = {
780
- OBJECTID: [0],
781
- ...propertyTable
782
- };
783
- for (const key in propertyTableWithObjectId) {
784
- const firstAttribute = propertyTableWithObjectId[key][0];
785
- const attributeType = (0, feature_attributes_1.getAttributeType)(key, firstAttribute);
786
- const storageAttribute = (0, feature_attributes_1.createdStorageAttribute)(attributeIndex, key, attributeType);
787
- const fieldAttributeType = (0, feature_attributes_1.getFieldAttributeType)(attributeType);
788
- const fieldAttribute = (0, feature_attributes_1.createFieldAttribute)(key, fieldAttributeType);
789
- const popupInfo = (0, feature_attributes_1.createPopupInfo)(propertyTableWithObjectId);
790
- this.layers0.attributeStorageInfo.push(storageAttribute);
791
- this.layers0.fields.push(fieldAttribute);
792
- this.layers0.popupInfo = popupInfo;
793
- this.layers0.layerType = _3D_OBJECT_LAYER_TYPE;
794
- attributeIndex += 1;
795
- }
796
- }
797
- /**
798
- * Print statistics in the end of conversion
799
- * @param params - output files data
800
- */
801
- async _finishConversion(params) {
802
- const { tilesCount, tilesWithAddRefineCount } = this.refinementCounter;
803
- const addRefinementPercentage = tilesWithAddRefineCount
804
- ? (tilesWithAddRefineCount / tilesCount) * 100
805
- : 0;
806
- const filesSize = await (0, statistic_utills_1.calculateFilesSize)(params);
807
- const diff = process_1.default.hrtime(this.conversionStartTime);
808
- const conversionTime = (0, statistic_utills_1.timeConverter)(diff);
809
- console.log(`------------------------------------------------`); // eslint-disable-line no-undef, no-console
810
- console.log(`Finishing conversion of ${_3D_TILES}`); // eslint-disable-line no-undef, no-console
811
- console.log(`Total conversion time: ${conversionTime}`); // eslint-disable-line no-undef, no-console
812
- console.log(`Vertex count: `, this.vertexCounter); // eslint-disable-line no-undef, no-console
813
- console.log(`File(s) size: `, filesSize, ' bytes'); // eslint-disable-line no-undef, no-console
814
- console.log(`Percentage of tiles with "ADD" refinement type:`, addRefinementPercentage, '%'); // eslint-disable-line no-undef, no-console
815
- console.log(`------------------------------------------------`); // eslint-disable-line no-undef, no-console
816
- }
817
- /**
818
- * Fetch preload options for ION tileset
819
- */
820
- async _fetchPreloadOptions() {
821
- if (!this.Loader.preload) {
822
- return {};
823
- }
824
- const options = {
825
- 'cesium-ion': { accessToken: this.options.token || ION_DEFAULT_TOKEN }
826
- };
827
- const preloadOptions = await this.Loader.preload(this.options.inputUrl, options);
828
- this.refreshTokenTime = process_1.default.hrtime();
829
- return { ...options, ...preloadOptions };
830
- }
831
- /**
832
- * Update options of source tileset
833
- */
834
- async _updateTilesetOptions() {
835
- const diff = process_1.default.hrtime(this.refreshTokenTime);
836
- if (diff[0] < REFRESH_TOKEN_TIMEOUT) {
837
- return;
838
- }
839
- this.refreshTokenTime = process_1.default.hrtime();
840
- const preloadOptions = await this._fetchPreloadOptions();
841
- this.sourceTileset.options = { ...this.sourceTileset.options, ...preloadOptions };
842
- if (preloadOptions.headers) {
843
- this.sourceTileset.loadOptions.fetch = {
844
- ...this.sourceTileset.loadOptions.fetch,
845
- headers: preloadOptions.headers
846
- };
847
- console.log('Authorization Bearer token has been updated'); // eslint-disable-line no-undef, no-console
848
- }
849
- }
850
- /** Do calculations of all tiles and tiles with "ADD" type of refinement.
851
- * @param tile
852
- */
853
- _checkAddRefinementTypeForTile(tile) {
854
- const ADD_TILE_REFINEMENT = 1;
855
- if (tile.refine === ADD_TILE_REFINEMENT) {
856
- this.refinementCounter.tilesWithAddRefineCount += 1;
857
- console.warn('This tile uses "ADD" type of refinement'); // eslint-disable-line
858
- }
859
- this.refinementCounter.tilesCount += 1;
860
- }
861
- /**
862
- * Check if the tile's content format is supported by the converter
863
- * @param sourceRootTile
864
- * @returns
865
- */
866
- isContentSupported(sourceRootTile) {
867
- return ['b3dm', 'glTF'].includes(sourceRootTile?.content?.type);
868
- }
869
- async loadWorkers() {
870
- console.log(`Loading workers source...`); // eslint-disable-line no-undef, no-console
871
- if (this.options.draco) {
872
- const url = (0, worker_utils_1.getWorkerURL)(draco_1.DracoWriterWorker, { ...(0, core_1.getLoaderOptions)() });
873
- const sourceResponse = await (0, core_1.fetchFile)(url);
874
- const source = await sourceResponse.text();
875
- this.workerSource.draco = source;
876
- }
877
- if (this.generateTextures) {
878
- const url = (0, worker_utils_1.getWorkerURL)(textures_1.KTX2BasisWriterWorker, { ...(0, core_1.getLoaderOptions)() });
879
- const sourceResponse = await (0, core_1.fetchFile)(url);
880
- const source = await sourceResponse.text();
881
- this.workerSource.ktx2 = source;
882
- }
883
- const i3sAttributesWorkerUrl = (0, worker_utils_1.getWorkerURL)(i3s_attributes_worker_1.I3SAttributesWorker, { ...(0, core_1.getLoaderOptions)() });
884
- const sourceResponse = await (0, core_1.fetchFile)(i3sAttributesWorkerUrl);
885
- const source = await sourceResponse.text();
886
- this.workerSource.I3SAttributes = source;
887
- console.log(`Loading workers source completed!`); // eslint-disable-line no-undef, no-console
888
- }
889
- }
890
- exports.default = I3SConverter;