@loaders.gl/tile-converter 3.4.10 → 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.
- package/dist/3d-tiles-attributes-worker.js +1 -1
- package/dist/converter.min.js +58 -58
- package/dist/dist.min.js +12 -27
- package/dist/es5/3d-tiles-attributes-worker.js +1 -1
- package/dist/es5/deps-installer/deps-installer.js +1 -1
- package/dist/es5/i3s-attributes-worker.js +1 -1
- package/dist/es5/pgm-loader.js +1 -1
- package/dist/esm/3d-tiles-attributes-worker.js +1 -1
- package/dist/esm/deps-installer/deps-installer.js +1 -1
- package/dist/esm/i3s-attributes-worker.js +1 -1
- package/dist/esm/pgm-loader.js +1 -1
- package/package.json +15 -15
- package/dist/3d-tiles-converter/3d-tiles-converter.js +0 -287
- package/dist/3d-tiles-converter/helpers/b3dm-converter.js +0 -282
- package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +0 -23
- package/dist/3d-tiles-converter/helpers/texture-atlas.js +0 -52
- package/dist/3d-tiles-converter/json-templates/tileset.js +0 -43
- package/dist/bundle.js +0 -5
- package/dist/constants.js +0 -4
- package/dist/converter-cli.js +0 -280
- package/dist/deps-installer/deps-installer.js +0 -63
- package/dist/i3s-converter/helpers/batch-ids-extensions.js +0 -141
- package/dist/i3s-converter/helpers/coordinate-converter.js +0 -124
- package/dist/i3s-converter/helpers/create-scene-server-path.js +0 -28
- package/dist/i3s-converter/helpers/feature-attributes.js +0 -216
- package/dist/i3s-converter/helpers/geometry-attributes.js +0 -202
- package/dist/i3s-converter/helpers/geometry-converter.js +0 -1195
- package/dist/i3s-converter/helpers/gltf-attributes.js +0 -88
- package/dist/i3s-converter/helpers/node-debug.js +0 -120
- package/dist/i3s-converter/helpers/node-index-document.js +0 -250
- package/dist/i3s-converter/helpers/node-pages.js +0 -316
- package/dist/i3s-converter/i3s-converter.js +0 -890
- package/dist/i3s-converter/json-templates/geometry-definitions.js +0 -87
- package/dist/i3s-converter/json-templates/layers.js +0 -139
- package/dist/i3s-converter/json-templates/metadata.js +0 -25
- package/dist/i3s-converter/json-templates/node.js +0 -89
- package/dist/i3s-converter/json-templates/scene-server.js +0 -31
- package/dist/i3s-converter/json-templates/shared-resources.js +0 -129
- package/dist/i3s-converter/json-templates/store.js +0 -103
- package/dist/i3s-converter/types.js +0 -2
- package/dist/i3s-server/app.js +0 -14
- package/dist/i3s-server/controllers/index-controller.js +0 -23
- package/dist/i3s-server/routes/index.js +0 -16
- package/dist/index.js +0 -10
- package/dist/lib/utils/compress-util.js +0 -257
- package/dist/lib/utils/file-utils.js +0 -138
- package/dist/lib/utils/lod-conversion-utils.js +0 -76
- package/dist/lib/utils/queue.js +0 -18
- package/dist/lib/utils/statistic-utills.js +0 -64
- package/dist/lib/utils/write-queue.js +0 -80
- package/dist/pgm-loader.js +0 -24
- package/dist/workers/3d-tiles-attributes-worker.js +0 -9
- 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;
|