@loaders.gl/tile-converter 4.0.4 → 4.1.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/constants.d.ts +1 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +1 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/converter-cli.js +41 -4
  6. package/dist/converter-cli.js.map +1 -1
  7. package/dist/converter.min.cjs +110 -110
  8. package/dist/deps-installer/deps-installer.d.ts.map +1 -1
  9. package/dist/deps-installer/deps-installer.js +4 -3
  10. package/dist/deps-installer/deps-installer.js.map +1 -1
  11. package/dist/i3s-converter/i3s-converter.d.ts +14 -0
  12. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  13. package/dist/i3s-converter/i3s-converter.js +71 -17
  14. package/dist/i3s-converter/i3s-converter.js.map +1 -1
  15. package/dist/i3s-converter/types.d.ts +7 -0
  16. package/dist/i3s-converter/types.d.ts.map +1 -1
  17. package/dist/i3s-converter/types.js +8 -0
  18. package/dist/i3s-converter/types.js.map +1 -1
  19. package/dist/i3s-server/bin/i3s-server.min.cjs +74 -74
  20. package/dist/i3s-server/routes/slpk-router.js +1 -1
  21. package/dist/i3s-server/routes/slpk-router.js.map +1 -1
  22. package/dist/index.cjs +347 -38
  23. package/dist/lib/utils/conversion-dump.d.ts +80 -0
  24. package/dist/lib/utils/conversion-dump.d.ts.map +1 -0
  25. package/dist/lib/utils/conversion-dump.js +127 -0
  26. package/dist/lib/utils/conversion-dump.js.map +1 -0
  27. package/dist/lib/utils/statistic-utills.d.ts +23 -6
  28. package/dist/lib/utils/write-queue.d.ts +6 -1
  29. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  30. package/dist/lib/utils/write-queue.js +15 -3
  31. package/dist/lib/utils/write-queue.js.map +1 -1
  32. package/dist/pgm-loader.js +1 -1
  33. package/dist/pgm-loader.js.map +1 -1
  34. package/dist/slpk-extractor.min.cjs +38 -38
  35. package/package.json +16 -16
  36. package/src/constants.ts +1 -0
  37. package/src/converter-cli.ts +58 -4
  38. package/src/deps-installer/deps-installer.ts +3 -2
  39. package/src/i3s-converter/i3s-converter.ts +189 -21
  40. package/src/i3s-converter/types.ts +8 -0
  41. package/src/i3s-server/routes/slpk-router.ts +1 -1
  42. package/src/lib/utils/conversion-dump.ts +198 -0
  43. package/src/lib/utils/write-queue.ts +15 -2
  44. package/dist/lib/utils/statistic-utills.d.js +0 -2
  45. package/dist/lib/utils/statistic-utills.d.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loaders.gl/tile-converter",
3
- "version": "4.0.4",
3
+ "version": "4.1.0-alpha.10",
4
4
  "description": "Converter",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,9 +21,9 @@
21
21
  "module": "dist/index.js",
22
22
  "exports": {
23
23
  ".": {
24
+ "types": "./dist/index.d.ts",
24
25
  "import": "./dist/index.js",
25
- "require": "./dist/index.cjs",
26
- "types": "./dist/index.d.ts"
26
+ "require": "./dist/index.cjs"
27
27
  }
28
28
  },
29
29
  "sideEffects": false,
@@ -52,18 +52,18 @@
52
52
  "build-i3s-server-bundle": "esbuild src/i3s-server/bin/www.ts --outfile=dist/i3s-server/bin/i3s-server.min.cjs --platform=node --target=esnext,node14 --minify --bundle --define:__VERSION__=\\\"$npm_package_version\\\""
53
53
  },
54
54
  "dependencies": {
55
- "@loaders.gl/3d-tiles": "4.0.4",
56
- "@loaders.gl/crypto": "4.0.4",
57
- "@loaders.gl/draco": "4.0.4",
58
- "@loaders.gl/gltf": "4.0.4",
59
- "@loaders.gl/i3s": "4.0.4",
60
- "@loaders.gl/images": "4.0.4",
61
- "@loaders.gl/loader-utils": "4.0.4",
62
- "@loaders.gl/polyfills": "4.0.4",
63
- "@loaders.gl/textures": "4.0.4",
64
- "@loaders.gl/tiles": "4.0.4",
65
- "@loaders.gl/worker-utils": "4.0.4",
66
- "@loaders.gl/zip": "4.0.4",
55
+ "@loaders.gl/3d-tiles": "4.1.0-alpha.10",
56
+ "@loaders.gl/crypto": "4.1.0-alpha.10",
57
+ "@loaders.gl/draco": "4.1.0-alpha.10",
58
+ "@loaders.gl/gltf": "4.1.0-alpha.10",
59
+ "@loaders.gl/i3s": "4.1.0-alpha.10",
60
+ "@loaders.gl/images": "4.1.0-alpha.10",
61
+ "@loaders.gl/loader-utils": "4.1.0-alpha.10",
62
+ "@loaders.gl/polyfills": "4.1.0-alpha.10",
63
+ "@loaders.gl/textures": "4.1.0-alpha.10",
64
+ "@loaders.gl/tiles": "4.1.0-alpha.10",
65
+ "@loaders.gl/worker-utils": "4.1.0-alpha.10",
66
+ "@loaders.gl/zip": "4.1.0-alpha.10",
67
67
  "@math.gl/core": "^4.0.0",
68
68
  "@math.gl/culling": "^4.0.0",
69
69
  "@math.gl/geoid": "^4.0.0",
@@ -87,7 +87,7 @@
87
87
  "join-images": "^1.1.3",
88
88
  "sharp": "^0.31.3"
89
89
  },
90
- "gitHead": "4dc810fa04bb400f4aedfef98a83c7ef882ed3d7",
90
+ "gitHead": "19f43c2d90d8b50860c3f8e487429779a386287d",
91
91
  "devDependencies": {
92
92
  "@types/express": "^4.17.17",
93
93
  "@types/node": "^20.4.2"
package/src/constants.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export const BROWSER_ERROR_MESSAGE =
2
2
  'Tile converter does not work in browser, only in node js environment';
3
+ export const DUMP_FILE_SUFFIX = '.dump.json';
@@ -11,6 +11,9 @@ import {
11
11
  getURLValue,
12
12
  validateOptionsWithEqual
13
13
  } from './lib/utils/cli-utils';
14
+ import {addOneFile, composeHashFile} from '@loaders.gl/zip';
15
+ import {FileHandleFile} from '@loaders.gl/loader-utils';
16
+ import {copyFile} from 'node:fs/promises';
14
17
 
15
18
  type TileConversionOptions = {
16
19
  /** "I3S" - for I3S to 3DTiles conversion, "3DTILES" for 3DTiles to I3S conversion */
@@ -50,6 +53,8 @@ type TileConversionOptions = {
50
53
  maxDepth?: number;
51
54
  /** 3DTiles->I3S only. Whether the converter generates *.slpk (Scene Layer Package) I3S output file */
52
55
  slpk: boolean;
56
+ /** adds hash file to the slpk if there's no one */
57
+ addHash: boolean;
53
58
  /** Feature metadata class from EXT_FEATURE_METADATA or EXT_STRUCTURAL_METADATA extensions */
54
59
  metadataClass?: string;
55
60
  /** With this options the tileset content will be analyzed without conversion */
@@ -92,6 +97,48 @@ async function main() {
92
97
  return;
93
98
  }
94
99
 
100
+ if (options.addHash) {
101
+ const validatedOptions = validateOptions(options, true);
102
+ let finalPath = validatedOptions.tileset;
103
+ if (validatedOptions.output === 'data') {
104
+ const nameWithoutExt = validatedOptions.tileset.substring(
105
+ 0,
106
+ validatedOptions.tileset.length - 5
107
+ );
108
+
109
+ const result = await inquirer.prompt<{isNewFileRequired: boolean}>([
110
+ {
111
+ name: 'isNewFileRequired',
112
+ type: 'list',
113
+ message: 'What would you like to do?',
114
+ choices: [
115
+ {
116
+ name: 'Add hash file to the current SLPK file',
117
+ value: false
118
+ },
119
+ {
120
+ name: `Create a new file ${nameWithoutExt}-hash.slpk with hash file inside`,
121
+ value: true
122
+ }
123
+ ]
124
+ }
125
+ ]);
126
+
127
+ if (result.isNewFileRequired) {
128
+ finalPath = `${nameWithoutExt}-hash.slpk`;
129
+ }
130
+ } else {
131
+ finalPath = validatedOptions.output;
132
+ }
133
+ if (finalPath !== validatedOptions.tileset) {
134
+ await copyFile(validatedOptions.tileset, finalPath);
135
+ }
136
+ const hashTable = await composeHashFile(new FileHandleFile(finalPath));
137
+ await addOneFile(finalPath, hashTable, '@specialIndexFileHASH128@');
138
+
139
+ return;
140
+ }
141
+
95
142
  const validatedOptions: ValidatedTileConversionOptions = validateOptions(options);
96
143
 
97
144
  await convert(validatedOptions);
@@ -199,7 +246,10 @@ async function convert(options: ValidatedTileConversionOptions) {
199
246
  * @param options - input options of the CLI command
200
247
  * @returns validated options
201
248
  */
202
- function validateOptions(options: TileConversionOptions): ValidatedTileConversionOptions {
249
+ function validateOptions(
250
+ options: TileConversionOptions,
251
+ addHash?: boolean
252
+ ): ValidatedTileConversionOptions {
203
253
  const mandatoryOptionsWithExceptions: {
204
254
  [key: string]: {
205
255
  getMessage: () => void;
@@ -208,7 +258,7 @@ function validateOptions(options: TileConversionOptions): ValidatedTileConversio
208
258
  } = {
209
259
  name: {
210
260
  getMessage: () => console.log('Missed: --name [Tileset name]'),
211
- condition: (value: any) => Boolean(value) || Boolean(options.analyze)
261
+ condition: (value: any) => addHash || Boolean(value) || Boolean(options.analyze)
212
262
  },
213
263
  output: {getMessage: () => console.log('Missed: --output [Output path name]')},
214
264
  sevenZipExe: {getMessage: () => console.log('Missed: --7zExe [7z archiver executable path]')},
@@ -218,7 +268,7 @@ function validateOptions(options: TileConversionOptions): ValidatedTileConversio
218
268
  getMessage: () =>
219
269
  console.log('Missed/Incorrect: --input-type [tileset input type: I3S or 3DTILES]'),
220
270
  condition: (value) =>
221
- Boolean(value) && Object.values(TILESET_TYPE).includes(value.toUpperCase())
271
+ addHash || (Boolean(value) && Object.values(TILESET_TYPE).includes(value.toUpperCase()))
222
272
  }
223
273
  };
224
274
  const exceptions: (() => void)[] = [];
@@ -256,7 +306,8 @@ function parseOptions(args: string[]): TileConversionOptions {
256
306
  generateTextures: false,
257
307
  generateBoundingVolumes: false,
258
308
  validate: false,
259
- slpk: false
309
+ slpk: false,
310
+ addHash: false
260
311
  };
261
312
 
262
313
  // eslint-disable-next-line complexity
@@ -287,6 +338,9 @@ function parseOptions(args: string[]): TileConversionOptions {
287
338
  case '--slpk':
288
339
  opts.slpk = getBooleanValue(index, args);
289
340
  break;
341
+ case '--add-hash':
342
+ opts.addHash = getBooleanValue(index, args);
343
+ break;
290
344
  case '--7zExe':
291
345
  opts.sevenZipExe = getStringValue(index, args);
292
346
  break;
@@ -1,7 +1,7 @@
1
1
  import {load, fetchFile} from '@loaders.gl/core';
2
2
  import {ZipLoader} from '@loaders.gl/zip';
3
3
  import {writeFile} from '../lib/utils/file-utils';
4
- import {join} from 'path';
4
+ import {join, dirname} from 'path';
5
5
  import {ChildProcessProxy} from '@loaders.gl/worker-utils';
6
6
  import {DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS} from '@loaders.gl/draco';
7
7
  import {BASIS_EXTERNAL_LIBRARIES} from '@loaders.gl/textures';
@@ -80,8 +80,9 @@ export class DepsInstaller {
80
80
 
81
81
  console.log('Installing "join-images" npm package');
82
82
  const childProcess = new ChildProcessProxy();
83
+ const nodeDir = dirname(process.execPath);
83
84
  await childProcess.start({
84
- command: process.platform === 'win32' ? 'npm.cmd' : 'npm',
85
+ command: `${nodeDir}/${process.platform === 'win32' ? 'npm.cmd' : 'npm'}`,
85
86
  // `npm install sharp join-images` works unstable. It fails because installed `sharp` version
86
87
  // may be different from the version required by `join-images`. Pointing to specific versions
87
88
  // resolve this issue
@@ -1,4 +1,5 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  import {AttributeMetadataInfo} from './helpers/attribute-metadata-info';
@@ -50,7 +51,7 @@ import {GEOMETRY_DEFINITION as geometryDefinitionTemlate} from './json-templates
50
51
  import {SHARED_RESOURCES as sharedResourcesTemplate} from './json-templates/shared-resources';
51
52
  import {validateNodeBoundingVolumes} from './helpers/node-debug';
52
53
  import {KTX2BasisWriterWorker} from '@loaders.gl/textures';
53
- import {LoaderWithParser} from '@loaders.gl/loader-utils';
54
+ import {FileHandleFile, LoaderWithParser} from '@loaders.gl/loader-utils';
54
55
  import {I3SMaterialDefinition, TextureSetDefinitionFormats} from '@loaders.gl/i3s';
55
56
  import {ImageWriter} from '@loaders.gl/images';
56
57
  import {GLTFImagePostprocessed} from '@loaders.gl/gltf';
@@ -58,6 +59,7 @@ import {
58
59
  GLTFPrimitiveModeString,
59
60
  I3SConvertedResources,
60
61
  PreprocessData,
62
+ ResourceType,
61
63
  SharedResourcesArrays
62
64
  } from './types';
63
65
  import {WorkerFarm} from '@loaders.gl/worker-utils';
@@ -80,6 +82,8 @@ import {createBoundingVolume} from '@loaders.gl/tiles';
80
82
  import {TraversalConversionProps, traverseDatasetWith} from './helpers/tileset-traversal';
81
83
  import {analyzeTileContent, mergePreprocessData} from './helpers/preprocess-3d-tiles';
82
84
  import {Progress} from './helpers/progress';
85
+ import {addOneFile, composeHashFile} from '@loaders.gl/zip';
86
+ import {ConversionDump, ConversionDumpOptions} from '../lib/utils/conversion-dump';
83
87
 
84
88
  const ION_DEFAULT_TOKEN = process.env?.IonToken;
85
89
  const HARDCODED_NODES_PER_PAGE = 64;
@@ -134,13 +138,14 @@ export default class I3SConverter {
134
138
  generateBoundingVolumes: boolean;
135
139
  layersHasTexture: boolean;
136
140
  workerSource: {[key: string]: string} = {};
137
- writeQueue: WriteQueue<WriteQueueItem> = new WriteQueue();
141
+ writeQueue: WriteQueue<WriteQueueItem> = new WriteQueue(new ConversionDump());
138
142
  compressList: string[] | null = null;
139
143
  preprocessData: PreprocessData = {
140
144
  meshTopologyTypes: new Set(),
141
145
  metadataClasses: new Set()
142
146
  };
143
147
  progresses: Record<string, Progress> = {};
148
+ conversionDump: ConversionDump;
144
149
 
145
150
  constructor() {
146
151
  this.attributeMetadataInfo = new AttributeMetadataInfo();
@@ -163,6 +168,7 @@ export default class I3SConverter {
163
168
  this.generateBoundingVolumes = false;
164
169
  this.layersHasTexture = false;
165
170
  this.compressList = null;
171
+ this.conversionDump = new ConversionDump();
166
172
  }
167
173
 
168
174
  /**
@@ -226,6 +232,8 @@ export default class I3SConverter {
226
232
  analyze = false
227
233
  } = options;
228
234
  this.options = {
235
+ outputPath,
236
+ tilesetName,
229
237
  maxDepth,
230
238
  slpk,
231
239
  sevenZipExe,
@@ -245,7 +253,7 @@ export default class I3SConverter {
245
253
  this.generateTextures = Boolean(generateTextures);
246
254
  this.generateBoundingVolumes = Boolean(generateBoundingVolumes);
247
255
 
248
- this.writeQueue = new WriteQueue();
256
+ this.writeQueue = new WriteQueue(this.conversionDump);
249
257
  this.writeQueue.startListening();
250
258
 
251
259
  console.log('Loading egm file...'); // eslint-disable-line
@@ -256,6 +264,9 @@ export default class I3SConverter {
256
264
  this.nodePages.useWriteFunction(writeFileForSlpk);
257
265
  }
258
266
 
267
+ //create a dump file with convertion options
268
+ await this.conversionDump.createDumpFile(options as ConversionDumpOptions);
269
+
259
270
  try {
260
271
  const preloadOptions = await this._fetchPreloadOptions();
261
272
  let tilesetUrl = inputUrl;
@@ -552,6 +563,7 @@ export default class I3SConverter {
552
563
  * @param tilesetPath - Path to save file
553
564
  */
554
565
  private async _createSlpk(tilesetPath: string): Promise<void> {
566
+ await this.conversionDump.deleteDumpFile();
555
567
  if (this.options.slpk) {
556
568
  const slpkTilesetPath = join(tilesetPath, 'SceneServer', 'layers', '0');
557
569
  const slpkFileName = `${tilesetPath}.slpk`;
@@ -563,6 +575,9 @@ export default class I3SConverter {
563
575
  this.options.sevenZipExe
564
576
  );
565
577
 
578
+ const hashTable = await composeHashFile(new FileHandleFile(slpkFileName));
579
+ await addOneFile(slpkFileName, hashTable, '@specialIndexFileHASH128@');
580
+
566
581
  // TODO: `addFileToZip` corrupts archive so it can't be validated with windows i3s_converter.exe
567
582
  // const fileHash128Path = `${tilesetPath}/@specialIndexFileHASH128@`;
568
583
  // try {
@@ -613,6 +628,7 @@ export default class I3SConverter {
613
628
  if (sourceTile.id) {
614
629
  console.log(`[convert]: ${sourceTile.id}`); // eslint-disable-line
615
630
  }
631
+
616
632
  const {parentNodes, transform} = traversalProps;
617
633
  let transformationMatrix: Matrix4 = transform.clone();
618
634
  if (sourceTile.transform) {
@@ -751,7 +767,13 @@ export default class I3SConverter {
751
767
  nodes.push(node);
752
768
 
753
769
  if (nodeInPage.mesh) {
754
- await this._writeResources(resources, node.id);
770
+ //update a record in a dump file
771
+ if (sourceTile.id) {
772
+ await this.conversionDump.addNode(sourceTile.id, nodeInPage.index);
773
+ }
774
+
775
+ //write resources
776
+ await this._writeResources(resources, node.id, sourceTile);
755
777
  }
756
778
 
757
779
  if (this.validate) {
@@ -900,9 +922,15 @@ export default class I3SConverter {
900
922
  * @param resources.texture - texture image
901
923
  * @param resources.sharedResources - shared resource data object
902
924
  * @param resources.attributes - feature attributes
925
+ * @param nodePath - node path
926
+ * @param sourceTile - source tile (3DTile)
903
927
  * @return {Promise<void>}
904
928
  */
905
- private async _writeResources(resources: I3SConvertedResources, nodePath: string): Promise<void> {
929
+ private async _writeResources(
930
+ resources: I3SConvertedResources,
931
+ nodePath: string,
932
+ sourceTile: Tiles3DTileJSONPostprocessed
933
+ ): Promise<void> {
906
934
  const {
907
935
  geometry: geometryBuffer,
908
936
  compressedGeometry,
@@ -913,10 +941,36 @@ export default class I3SConverter {
913
941
  const childPath = join(this.layers0Path, 'nodes', nodePath);
914
942
  const slpkChildPath = join('nodes', nodePath);
915
943
 
916
- await this._writeGeometries(geometryBuffer!, compressedGeometry!, childPath, slpkChildPath);
917
- await this._writeShared(sharedResources, childPath, slpkChildPath, nodePath);
918
- await this._writeTexture(texture, childPath, slpkChildPath);
919
- await this._writeAttributes(attributes, childPath, slpkChildPath);
944
+ await this._writeGeometries(
945
+ geometryBuffer!,
946
+ compressedGeometry!,
947
+ childPath,
948
+ slpkChildPath,
949
+ sourceTile.id || '',
950
+ parseInt(nodePath)
951
+ );
952
+ await this._writeShared(
953
+ sharedResources,
954
+ childPath,
955
+ slpkChildPath,
956
+ nodePath,
957
+ sourceTile.id || '',
958
+ parseInt(nodePath)
959
+ );
960
+ await this._writeTexture(
961
+ texture,
962
+ childPath,
963
+ slpkChildPath,
964
+ sourceTile.id || '',
965
+ parseInt(nodePath)
966
+ );
967
+ await this._writeAttributes(
968
+ attributes,
969
+ childPath,
970
+ slpkChildPath,
971
+ sourceTile.id || '',
972
+ parseInt(nodePath)
973
+ );
920
974
  }
921
975
 
922
976
  /**
@@ -925,37 +979,57 @@ export default class I3SConverter {
925
979
  * @param compressedGeometry - Uint8Array with compressed (draco) geometry
926
980
  * @param childPath - a child path to write resources
927
981
  * @param slpkChildPath - resource path inside *slpk file
982
+ * @param sourceId - source filename
983
+ * @param nodeId - nodeId of a converted node for the writing
928
984
  */
929
985
  private async _writeGeometries(
930
986
  geometryBuffer: ArrayBuffer,
931
987
  compressedGeometry: Promise<ArrayBuffer>,
932
988
  childPath: string,
933
- slpkChildPath: string
989
+ slpkChildPath: string,
990
+ sourceId: string,
991
+ nodeId: number
934
992
  ): Promise<void> {
993
+ this.conversionDump.updateDoneStatus(sourceId, nodeId, ResourceType.GEOMETRY, false);
994
+
935
995
  if (this.options.slpk) {
936
996
  const slpkGeometryPath = join(childPath, 'geometries');
937
997
  await this.writeQueue.enqueue({
938
998
  archiveKey: `${slpkChildPath}/geometries/0.bin.gz`,
999
+ sourceId,
1000
+ outputId: nodeId,
1001
+ resourceType: ResourceType.GEOMETRY,
939
1002
  writePromise: () => writeFileForSlpk(slpkGeometryPath, geometryBuffer, '0.bin')
940
1003
  });
941
1004
  } else {
942
1005
  const geometryPath = join(childPath, 'geometries/0/');
943
1006
  await this.writeQueue.enqueue({
1007
+ sourceId,
1008
+ outputId: nodeId,
1009
+ resourceType: ResourceType.GEOMETRY,
944
1010
  writePromise: () => writeFile(geometryPath, geometryBuffer, 'index.bin')
945
1011
  });
946
1012
  }
947
1013
 
948
1014
  if (this.options.draco) {
1015
+ this.conversionDump.updateDoneStatus(sourceId, nodeId, ResourceType.DRACO_GEOMETRY, false);
1016
+
949
1017
  if (this.options.slpk) {
950
1018
  const slpkCompressedGeometryPath = join(childPath, 'geometries');
951
1019
  await this.writeQueue.enqueue({
952
1020
  archiveKey: `${slpkChildPath}/geometries/1.bin.gz`,
1021
+ sourceId,
1022
+ outputId: nodeId,
1023
+ resourceType: ResourceType.DRACO_GEOMETRY,
953
1024
  writePromise: () =>
954
1025
  writeFileForSlpk(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
955
1026
  });
956
1027
  } else {
957
1028
  const compressedGeometryPath = join(childPath, 'geometries/1/');
958
1029
  await this.writeQueue.enqueue({
1030
+ sourceId,
1031
+ outputId: nodeId,
1032
+ resourceType: ResourceType.DRACO_GEOMETRY,
959
1033
  writePromise: () => writeFile(compressedGeometryPath, compressedGeometry, 'index.bin')
960
1034
  });
961
1035
  }
@@ -968,12 +1042,16 @@ export default class I3SConverter {
968
1042
  * @param childPath - a child path to write resources
969
1043
  * @param slpkChildPath - resource path inside *slpk file
970
1044
  * @param nodePath - a node path
1045
+ * @param sourceId - source filename
1046
+ * @param nodeId - nodeId of a converted node for the writing
971
1047
  */
972
1048
  private async _writeShared(
973
1049
  sharedResources: SharedResourcesArrays | null,
974
1050
  childPath: string,
975
1051
  slpkChildPath: string,
976
- nodePath: string
1052
+ nodePath: string,
1053
+ sourceId: string,
1054
+ nodeId: number
977
1055
  ): Promise<void> {
978
1056
  if (!sharedResources) {
979
1057
  return;
@@ -981,15 +1059,24 @@ export default class I3SConverter {
981
1059
  sharedResources.nodePath = nodePath;
982
1060
  const sharedData = transform(sharedResources, sharedResourcesTemplate());
983
1061
  const sharedDataStr = JSON.stringify(sharedData);
1062
+ this.conversionDump.updateDoneStatus(sourceId, nodeId, ResourceType.SHARED, false);
984
1063
  if (this.options.slpk) {
985
1064
  const slpkSharedPath = join(childPath, 'shared');
986
1065
  await this.writeQueue.enqueue({
987
1066
  archiveKey: `${slpkChildPath}/shared/sharedResource.json.gz`,
1067
+ sourceId,
1068
+ outputId: nodeId,
1069
+ resourceType: ResourceType.SHARED,
988
1070
  writePromise: () => writeFileForSlpk(slpkSharedPath, sharedDataStr, 'sharedResource.json')
989
1071
  });
990
1072
  } else {
991
1073
  const sharedPath = join(childPath, 'shared/');
992
- await this.writeQueue.enqueue({writePromise: () => writeFile(sharedPath, sharedDataStr)});
1074
+ await this.writeQueue.enqueue({
1075
+ sourceId,
1076
+ outputId: nodeId,
1077
+ resourceType: ResourceType.SHARED,
1078
+ writePromise: () => writeFile(sharedPath, sharedDataStr)
1079
+ });
993
1080
  }
994
1081
  }
995
1082
 
@@ -998,11 +1085,15 @@ export default class I3SConverter {
998
1085
  * @param texture - the texture image
999
1086
  * @param childPath - a child path to write resources
1000
1087
  * @param slpkChildPath - the resource path inside *slpk file
1088
+ * @param sourceId - source filename
1089
+ * @param nodeId - nodeId of a converted node for the writing
1001
1090
  */
1002
1091
  private async _writeTexture(
1003
1092
  texture: GLTFImagePostprocessed,
1004
1093
  childPath: string,
1005
- slpkChildPath: string
1094
+ slpkChildPath: string,
1095
+ sourceId: string,
1096
+ nodeId: number
1006
1097
  ): Promise<void> {
1007
1098
  if (texture) {
1008
1099
  const format = this._getFormatByMimeType(texture?.mimeType);
@@ -1013,7 +1104,21 @@ export default class I3SConverter {
1013
1104
  case 'jpg':
1014
1105
  case 'png': {
1015
1106
  formats.push({name: '0', format});
1016
- await this.writeTextureFile(textureData, '0', format, childPath, slpkChildPath);
1107
+ this.conversionDump.updateDoneStatus(
1108
+ sourceId,
1109
+ nodeId,
1110
+ `${ResourceType.TEXTURE}/${format}`,
1111
+ false
1112
+ );
1113
+ await this.writeTextureFile(
1114
+ textureData,
1115
+ '0',
1116
+ format,
1117
+ childPath,
1118
+ slpkChildPath,
1119
+ sourceId,
1120
+ nodeId
1121
+ );
1017
1122
 
1018
1123
  if (this.generateTextures) {
1019
1124
  formats.push({name: '1', format: 'ktx2'});
@@ -1022,6 +1127,7 @@ export default class I3SConverter {
1022
1127
  const arrayToEncode = new Uint8Array(copyArrayBuffer);
1023
1128
  const ktx2TextureData = encode(
1024
1129
  {...texture.image, data: arrayToEncode},
1130
+ // @ts-expect-error - Worker encoder typing is still WIP
1025
1131
  KTX2BasisWriterWorker,
1026
1132
  {
1027
1133
  ...KTX2BasisWriterWorker.options,
@@ -1035,7 +1141,22 @@ export default class I3SConverter {
1035
1141
  }
1036
1142
  );
1037
1143
 
1038
- await this.writeTextureFile(ktx2TextureData, '1', 'ktx2', childPath, slpkChildPath);
1144
+ this.conversionDump.updateDoneStatus(
1145
+ sourceId,
1146
+ nodeId,
1147
+ `${ResourceType.TEXTURE}/ktx2`,
1148
+ false
1149
+ );
1150
+
1151
+ await this.writeTextureFile(
1152
+ ktx2TextureData,
1153
+ '1',
1154
+ 'ktx2',
1155
+ childPath,
1156
+ slpkChildPath,
1157
+ sourceId,
1158
+ nodeId
1159
+ );
1039
1160
  }
1040
1161
 
1041
1162
  break;
@@ -1043,17 +1164,39 @@ export default class I3SConverter {
1043
1164
 
1044
1165
  case 'ktx2': {
1045
1166
  formats.push({name: '1', format});
1046
- await this.writeTextureFile(textureData, '1', format, childPath, slpkChildPath);
1167
+ this.conversionDump.updateDoneStatus(
1168
+ sourceId,
1169
+ nodeId,
1170
+ `${ResourceType.TEXTURE}/${format}`,
1171
+ false
1172
+ );
1173
+ await this.writeTextureFile(
1174
+ textureData,
1175
+ '1',
1176
+ format,
1177
+ childPath,
1178
+ slpkChildPath,
1179
+ sourceId,
1180
+ nodeId
1181
+ );
1047
1182
 
1048
1183
  if (this.generateTextures) {
1049
1184
  formats.push({name: '0', format: 'jpg'});
1050
1185
  const decodedFromKTX2TextureData = encode(texture.image!.data[0], ImageWriter);
1186
+ this.conversionDump.updateDoneStatus(
1187
+ sourceId,
1188
+ nodeId,
1189
+ `${ResourceType.TEXTURE}/jpg`,
1190
+ false
1191
+ );
1051
1192
  await this.writeTextureFile(
1052
1193
  decodedFromKTX2TextureData,
1053
1194
  '0',
1054
1195
  'jpg',
1055
1196
  childPath,
1056
- slpkChildPath
1197
+ slpkChildPath,
1198
+ sourceId,
1199
+ nodeId
1057
1200
  );
1058
1201
  }
1059
1202
  }
@@ -1073,13 +1216,17 @@ export default class I3SConverter {
1073
1216
  * @param format
1074
1217
  * @param childPath
1075
1218
  * @param slpkChildPath
1219
+ * @param sourceId
1220
+ * @param nodeId
1076
1221
  */
1077
1222
  private async writeTextureFile(
1078
1223
  textureData: Uint8Array | Promise<ArrayBuffer>,
1079
1224
  name: string,
1080
1225
  format: 'jpg' | 'png' | 'ktx2',
1081
1226
  childPath: string,
1082
- slpkChildPath: string
1227
+ slpkChildPath: string,
1228
+ sourceId: string,
1229
+ nodeId: number
1083
1230
  ): Promise<void> {
1084
1231
  if (this.options.slpk) {
1085
1232
  const slpkTexturePath = join(childPath, 'textures');
@@ -1087,12 +1234,18 @@ export default class I3SConverter {
1087
1234
 
1088
1235
  await this.writeQueue.enqueue({
1089
1236
  archiveKey: `${slpkChildPath}/textures/${name}.${format}`,
1237
+ sourceId,
1238
+ outputId: nodeId,
1239
+ resourceType: `${ResourceType.TEXTURE}/${format}`,
1090
1240
  writePromise: () =>
1091
1241
  writeFileForSlpk(slpkTexturePath, textureData, `${name}.${format}`, compress)
1092
1242
  });
1093
1243
  } else {
1094
1244
  const texturePath = join(childPath, `textures/${name}/`);
1095
1245
  await this.writeQueue.enqueue({
1246
+ sourceId,
1247
+ outputId: nodeId,
1248
+ resourceType: `${ResourceType.TEXTURE}/${format}`,
1096
1249
  writePromise: () => writeFile(texturePath, textureData, `index.${format}`)
1097
1250
  });
1098
1251
  }
@@ -1103,11 +1256,15 @@ export default class I3SConverter {
1103
1256
  * @param attributes - feature attributes
1104
1257
  * @param childPath - a child path to write resources
1105
1258
  * @param slpkChildPath - the resource path inside *slpk file
1259
+ * @param sourceId - source filename
1260
+ * @param nodeId - nodeId of a converted node for the writing
1106
1261
  */
1107
1262
  private async _writeAttributes(
1108
1263
  attributes: ArrayBuffer[] | null = [],
1109
1264
  childPath: string,
1110
- slpkChildPath: string
1265
+ slpkChildPath: string,
1266
+ sourceId: string,
1267
+ nodeId: number
1111
1268
  ): Promise<void> {
1112
1269
  if (attributes?.length && this.attributeMetadataInfo.attributeStorageInfo.length) {
1113
1270
  const minimumLength =
@@ -1118,16 +1275,27 @@ export default class I3SConverter {
1118
1275
  for (let index = 0; index < minimumLength; index++) {
1119
1276
  const folderName = this.attributeMetadataInfo.attributeStorageInfo[index].key;
1120
1277
  const fileBuffer = new Uint8Array(attributes[index]);
1121
-
1278
+ this.conversionDump.updateDoneStatus(
1279
+ sourceId,
1280
+ nodeId,
1281
+ `${ResourceType.ATTRIBUTES}/${folderName}`,
1282
+ false
1283
+ );
1122
1284
  if (this.options.slpk) {
1123
1285
  const slpkAttributesPath = join(childPath, 'attributes', folderName);
1124
1286
  await this.writeQueue.enqueue({
1125
1287
  archiveKey: `${slpkChildPath}/attributes/${folderName}.bin.gz`,
1288
+ sourceId,
1289
+ outputId: nodeId,
1290
+ resourceType: `${ResourceType.ATTRIBUTES}/${folderName}`,
1126
1291
  writePromise: () => writeFileForSlpk(slpkAttributesPath, fileBuffer, '0.bin')
1127
1292
  });
1128
1293
  } else {
1129
1294
  const attributesPath = join(childPath, `attributes/${folderName}/0`);
1130
1295
  await this.writeQueue.enqueue({
1296
+ sourceId,
1297
+ outputId: nodeId,
1298
+ resourceType: `${ResourceType.ATTRIBUTES}/${folderName}`,
1131
1299
  writePromise: () => writeFile(attributesPath, fileBuffer, 'index.bin')
1132
1300
  });
1133
1301
  }
@@ -241,3 +241,11 @@ export const AttributeType = {
241
241
  /** Integer data type name for feature attributes */
242
242
  SHORT_INT_TYPE: 'Int32'
243
243
  } as const;
244
+
245
+ export enum ResourceType {
246
+ ATTRIBUTES = 'ATTRIBUTES',
247
+ DRACO_GEOMETRY = 'DRACO_GEOMETRY',
248
+ GEOMETRY = 'GEOMETRY',
249
+ SHARED = 'SHARED',
250
+ TEXTURE = 'TEXTURE'
251
+ }