@loaders.gl/tile-converter 3.3.0-alpha.7 → 3.3.0

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 (136) hide show
  1. package/dist/3d-tiles-attributes-worker.d.ts +2 -2
  2. package/dist/3d-tiles-attributes-worker.d.ts.map +1 -1
  3. package/dist/3d-tiles-attributes-worker.js +2 -2
  4. package/dist/3d-tiles-attributes-worker.js.map +3 -3
  5. package/dist/converter-cli.js +14 -2
  6. package/dist/converter.min.js +22 -22
  7. package/dist/deps-installer/deps-installer.d.ts.map +1 -1
  8. package/dist/deps-installer/deps-installer.js +8 -0
  9. package/dist/dist.min.js +1407 -1242
  10. package/dist/es5/3d-tiles-attributes-worker.js +1 -1
  11. package/dist/es5/3d-tiles-attributes-worker.js.map +1 -1
  12. package/dist/es5/converter-cli.js +14 -2
  13. package/dist/es5/converter-cli.js.map +1 -1
  14. package/dist/es5/deps-installer/deps-installer.js +13 -2
  15. package/dist/es5/deps-installer/deps-installer.js.map +1 -1
  16. package/dist/es5/i3s-attributes-worker.js +1 -1
  17. package/dist/es5/i3s-attributes-worker.js.map +1 -1
  18. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  19. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +16 -7
  20. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  21. package/dist/es5/i3s-converter/helpers/geometry-converter.js +363 -113
  22. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  23. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +6 -11
  24. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  25. package/dist/es5/i3s-converter/helpers/node-index-document.js +517 -0
  26. package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -0
  27. package/dist/es5/i3s-converter/helpers/node-pages.js +455 -173
  28. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
  29. package/dist/es5/i3s-converter/i3s-converter.js +549 -618
  30. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  31. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js +107 -0
  32. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  33. package/dist/es5/i3s-converter/json-templates/layers.js +2 -93
  34. package/dist/es5/i3s-converter/json-templates/layers.js.map +1 -1
  35. package/dist/es5/i3s-converter/json-templates/shared-resources.js +3 -3
  36. package/dist/es5/i3s-converter/json-templates/shared-resources.js.map +1 -1
  37. package/dist/es5/i3s-converter/types.js.map +1 -1
  38. package/dist/es5/lib/utils/file-utils.js +93 -9
  39. package/dist/es5/lib/utils/file-utils.js.map +1 -1
  40. package/dist/es5/lib/utils/write-queue.js +38 -25
  41. package/dist/es5/lib/utils/write-queue.js.map +1 -1
  42. package/dist/es5/pgm-loader.js +1 -1
  43. package/dist/es5/pgm-loader.js.map +1 -1
  44. package/dist/es5/workers/i3s-attributes-worker.js +1 -1
  45. package/dist/es5/workers/i3s-attributes-worker.js.map +1 -1
  46. package/dist/esm/3d-tiles-attributes-worker.js +1 -1
  47. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
  48. package/dist/esm/converter-cli.js +14 -2
  49. package/dist/esm/converter-cli.js.map +1 -1
  50. package/dist/esm/deps-installer/deps-installer.js +9 -1
  51. package/dist/esm/deps-installer/deps-installer.js.map +1 -1
  52. package/dist/esm/i3s-attributes-worker.js +1 -1
  53. package/dist/esm/i3s-attributes-worker.js.map +1 -1
  54. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  55. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +16 -7
  56. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  57. package/dist/esm/i3s-converter/helpers/geometry-converter.js +150 -40
  58. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  59. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +6 -9
  60. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  61. package/dist/esm/i3s-converter/helpers/node-index-document.js +202 -0
  62. package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -0
  63. package/dist/esm/i3s-converter/helpers/node-pages.js +162 -76
  64. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
  65. package/dist/esm/i3s-converter/i3s-converter.js +115 -220
  66. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  67. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js +89 -0
  68. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  69. package/dist/esm/i3s-converter/json-templates/layers.js +2 -85
  70. package/dist/esm/i3s-converter/json-templates/layers.js.map +1 -1
  71. package/dist/esm/i3s-converter/json-templates/shared-resources.js +3 -3
  72. package/dist/esm/i3s-converter/json-templates/shared-resources.js.map +1 -1
  73. package/dist/esm/i3s-converter/types.js.map +1 -1
  74. package/dist/esm/lib/utils/file-utils.js +44 -3
  75. package/dist/esm/lib/utils/file-utils.js.map +1 -1
  76. package/dist/esm/lib/utils/write-queue.js +19 -10
  77. package/dist/esm/lib/utils/write-queue.js.map +1 -1
  78. package/dist/esm/pgm-loader.js +1 -1
  79. package/dist/esm/pgm-loader.js.map +1 -1
  80. package/dist/esm/workers/i3s-attributes-worker.js +1 -1
  81. package/dist/esm/workers/i3s-attributes-worker.js.map +1 -1
  82. package/dist/i3s-attributes-worker.d.ts +2 -2
  83. package/dist/i3s-attributes-worker.d.ts.map +1 -1
  84. package/dist/i3s-attributes-worker.js +2 -2
  85. package/dist/i3s-attributes-worker.js.map +2 -2
  86. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +3 -3
  87. package/dist/i3s-converter/helpers/batch-ids-extensions.js +3 -3
  88. package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -1
  89. package/dist/i3s-converter/helpers/geometry-attributes.js +16 -10
  90. package/dist/i3s-converter/helpers/geometry-converter.d.ts +8 -4
  91. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  92. package/dist/i3s-converter/helpers/geometry-converter.js +200 -44
  93. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
  94. package/dist/i3s-converter/helpers/gltf-attributes.js +2 -3
  95. package/dist/i3s-converter/helpers/node-index-document.d.ts +95 -0
  96. package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -0
  97. package/dist/i3s-converter/helpers/node-index-document.js +250 -0
  98. package/dist/i3s-converter/helpers/node-pages.d.ts +78 -43
  99. package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -1
  100. package/dist/i3s-converter/helpers/node-pages.js +195 -94
  101. package/dist/i3s-converter/i3s-converter.d.ts +33 -58
  102. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  103. package/dist/i3s-converter/i3s-converter.js +122 -233
  104. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts +7 -0
  105. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts.map +1 -0
  106. package/dist/i3s-converter/json-templates/geometry-definitions.js +87 -0
  107. package/dist/i3s-converter/json-templates/layers.d.ts +1 -30
  108. package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -1
  109. package/dist/i3s-converter/json-templates/layers.js +2 -86
  110. package/dist/i3s-converter/json-templates/shared-resources.js +3 -3
  111. package/dist/i3s-converter/types.d.ts +34 -8
  112. package/dist/i3s-converter/types.d.ts.map +1 -1
  113. package/dist/lib/utils/file-utils.d.ts +17 -1
  114. package/dist/lib/utils/file-utils.d.ts.map +1 -1
  115. package/dist/lib/utils/file-utils.js +64 -7
  116. package/dist/lib/utils/write-queue.d.ts +19 -3
  117. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  118. package/dist/lib/utils/write-queue.js +18 -12
  119. package/dist/workers/i3s-attributes-worker.js +1 -1
  120. package/package.json +25 -20
  121. package/src/converter-cli.ts +22 -2
  122. package/src/deps-installer/deps-installer.ts +9 -0
  123. package/src/i3s-converter/helpers/batch-ids-extensions.ts +3 -3
  124. package/src/i3s-converter/helpers/geometry-attributes.ts +16 -11
  125. package/src/i3s-converter/helpers/geometry-converter.ts +217 -48
  126. package/src/i3s-converter/helpers/gltf-attributes.ts +2 -3
  127. package/src/i3s-converter/helpers/node-index-document.ts +315 -0
  128. package/src/i3s-converter/helpers/node-pages.ts +215 -110
  129. package/src/i3s-converter/i3s-converter.ts +170 -312
  130. package/src/i3s-converter/json-templates/geometry-definitions.ts +83 -0
  131. package/src/i3s-converter/json-templates/layers.ts +2 -91
  132. package/src/i3s-converter/json-templates/shared-resources.ts +3 -3
  133. package/src/i3s-converter/types.ts +29 -2
  134. package/src/lib/utils/file-utils.ts +62 -7
  135. package/src/lib/utils/write-queue.ts +36 -15
  136. package/src/workers/i3s-attributes-worker.ts +2 -1
@@ -1,14 +1,13 @@
1
- import type {Tile3D, Tileset3DProps} from '@loaders.gl/tiles';
1
+ // loaders.gl, MIT license
2
+
3
+ import type {Tileset3DProps} from '@loaders.gl/tiles';
2
4
  import type {FeatureTableJson} from '@loaders.gl/3d-tiles';
3
5
  import type {WriteQueueItem} from '../lib/utils/write-queue';
4
6
  import type {
5
7
  SceneLayer3D,
6
8
  BoundingVolumes,
7
- Node3DIndexDocument,
8
- NodeReference,
9
9
  MaxScreenThresholdSQ,
10
- NodeInPage,
11
- LodSelection
10
+ NodeInPage
12
11
  } from '@loaders.gl/i3s';
13
12
  import {load, encode, fetchFile, getLoaderOptions, isBrowser} from '@loaders.gl/core';
14
13
  import {Tileset3D} from '@loaders.gl/tiles';
@@ -21,8 +20,9 @@ import transform from 'json-map-transform';
21
20
  import md5 from 'md5';
22
21
 
23
22
  import NodePages from './helpers/node-pages';
24
- import {writeFile, removeDir, writeFileForSlpk} from '../lib/utils/file-utils';
23
+ import {writeFile, removeDir, writeFileForSlpk, removeFile} from '../lib/utils/file-utils';
25
24
  import {
25
+ compressFileWithGzip,
26
26
  compressWithChildProcess
27
27
  // generateHash128FromZip,
28
28
  // addFileToZip
@@ -38,10 +38,12 @@ import {convertGeometricErrorToScreenThreshold} from '../lib/utils/lod-conversio
38
38
  import {PGMLoader} from '../pgm-loader';
39
39
 
40
40
  import {LAYERS as layersTemplate} from './json-templates/layers';
41
- import {NODE as nodeTemplate} from './json-templates/node';
41
+ import {GEOMETRY_DEFINITION as geometryDefinitionTemlate} from './json-templates/geometry-definitions';
42
42
  import {SHARED_RESOURCES as sharedResourcesTemplate} from './json-templates/shared-resources';
43
43
  import {validateNodeBoundingVolumes} from './helpers/node-debug';
44
- import TileHeader from '@loaders.gl/tiles/src/tileset/tile-3d';
44
+ // loaders.gl, MIT license
45
+
46
+ import {Tile3D} from '@loaders.gl/tiles';
45
47
  import {KTX2BasisWriterWorker} from '@loaders.gl/textures';
46
48
  import {LoaderWithParser} from '@loaders.gl/loader-utils';
47
49
  import {I3SMaterialDefinition, TextureSetDefinitionFormats} from '@loaders.gl/i3s/src/types';
@@ -60,6 +62,7 @@ import {
60
62
  getAttributeType,
61
63
  getFieldAttributeType
62
64
  } from './helpers/feature-attributes';
65
+ import {NodeIndexDocument} from './helpers/node-index-document';
63
66
 
64
67
  const ION_DEFAULT_TOKEN =
65
68
  process.env?.IonToken || // eslint-disable-line
@@ -78,8 +81,10 @@ export default class I3SConverter {
78
81
  nodePages: NodePages;
79
82
  options: any;
80
83
  layers0Path: string;
81
- materialMap: Map<any, any>;
84
+ materialMap: Map<string, number>;
82
85
  materialDefinitions: I3SMaterialDefinition[];
86
+ geometryMap: Map<string, number>;
87
+ geometryConfigs: {hasTexture: boolean; hasUvRegions: boolean}[];
83
88
  vertexCounter: number;
84
89
  layers0: SceneLayer3D | null;
85
90
  featuresHashArray: string[];
@@ -99,13 +104,16 @@ export default class I3SConverter {
99
104
  layersHasTexture: boolean;
100
105
  workerSource: {[key: string]: string} = {};
101
106
  writeQueue: WriteQueue<WriteQueueItem> = new WriteQueue();
107
+ compressList: string[] | null = null;
102
108
 
103
109
  constructor() {
104
- this.nodePages = new NodePages(writeFile, HARDCODED_NODES_PER_PAGE);
110
+ this.nodePages = new NodePages(writeFile, HARDCODED_NODES_PER_PAGE, this);
105
111
  this.options = {};
106
112
  this.layers0Path = '';
107
113
  this.materialMap = new Map();
108
114
  this.materialDefinitions = [];
115
+ this.geometryMap = new Map();
116
+ this.geometryConfigs = [];
109
117
  this.vertexCounter = 0;
110
118
  this.layers0 = null;
111
119
  this.featuresHashArray = [];
@@ -117,6 +125,7 @@ export default class I3SConverter {
117
125
  this.generateTextures = false;
118
126
  this.generateBoundingVolumes = false;
119
127
  this.layersHasTexture = false;
128
+ this.compressList = null;
120
129
  }
121
130
 
122
131
  /**
@@ -132,6 +141,9 @@ export default class I3SConverter {
132
141
  * @param options.token Token for Cesium ION tilesets authentication
133
142
  * @param options.draco Generate I3S 1.7 draco compressed geometries
134
143
  * @param options.validate -enable validation
144
+ * @param options.generateTextures - generate alternative type of textures (to have non-compressed jpeg/png and compressed ktx2)
145
+ * @param options.generateBoundingVolumes - generate bounding volumes from vertices coordinates instead of source tiles bounding volumes
146
+ * @param options.instantNodeWriting - Keep created 3DNodeIndexDocument files on disk instead of memory. This option reduce memory usage but decelerates conversion speed
135
147
  */
136
148
  async convert(options: {
137
149
  inputUrl: string;
@@ -143,9 +155,11 @@ export default class I3SConverter {
143
155
  slpk?: boolean;
144
156
  token?: string;
145
157
  draco?: boolean;
158
+ mergeMaterials?: boolean;
146
159
  validate?: boolean;
147
160
  generateTextures?: boolean;
148
161
  generateBoundingVolumes?: boolean;
162
+ instantNodeWriting?: boolean;
149
163
  }): Promise<any> {
150
164
  if (isBrowser) {
151
165
  console.log(BROWSER_ERROR_MESSAGE);
@@ -159,14 +173,27 @@ export default class I3SConverter {
159
173
  inputUrl,
160
174
  validate,
161
175
  outputPath,
162
- draco,
176
+ draco = true,
163
177
  sevenZipExe,
164
178
  maxDepth,
165
179
  token,
166
180
  generateTextures,
167
- generateBoundingVolumes
181
+ generateBoundingVolumes,
182
+ instantNodeWriting = false,
183
+ mergeMaterials = true
168
184
  } = options;
169
- this.options = {maxDepth, slpk, sevenZipExe, egmFilePath, draco, token, inputUrl};
185
+ this.options = {
186
+ maxDepth,
187
+ slpk,
188
+ sevenZipExe,
189
+ egmFilePath,
190
+ draco,
191
+ token,
192
+ inputUrl,
193
+ instantNodeWriting,
194
+ mergeMaterials
195
+ };
196
+ this.compressList = (this.options.instantNodeWriting && []) || null;
170
197
  this.validate = Boolean(validate);
171
198
  this.Loader = inputUrl.indexOf(CESIUM_DATASET_PREFIX) !== -1 ? CesiumIonLoader : Tiles3DLoader;
172
199
  this.generateTextures = Boolean(generateTextures);
@@ -248,21 +275,26 @@ export default class I3SConverter {
248
275
  this.materialDefinitions = [];
249
276
  this.materialMap = new Map();
250
277
 
251
- const sourceRootTile: TileHeader = this.sourceTileset!.root!;
278
+ const sourceRootTile: Tile3D = this.sourceTileset!.root!;
252
279
  const boundingVolumes = createBoundingVolumes(sourceRootTile, this.geoidHeightModel!);
253
- const parentId = this.nodePages.push({
280
+ await this.nodePages.push({
254
281
  index: 0,
255
282
  lodThreshold: 0,
256
283
  obb: boundingVolumes.obb,
257
284
  children: []
258
285
  });
259
286
 
260
- const isCreateSlpk = this.options.slpk;
261
- const root0 = this._formRootNodeIndexDocument(boundingVolumes);
262
-
263
- await this._convertNodesTree(root0, sourceRootTile, parentId, boundingVolumes);
287
+ const rootNode = await NodeIndexDocument.createRootNode(boundingVolumes, this);
288
+ await this._convertNodesTree(rootNode, sourceRootTile);
264
289
 
265
290
  this.layers0!.materialDefinitions = this.materialDefinitions;
291
+ // @ts-ignore
292
+ this.layers0.geometryDefinitions = transform(
293
+ this.geometryConfigs.map((config) => ({
294
+ geometryConfig: {...config, draco: this.options.draco}
295
+ })),
296
+ geometryDefinitionTemlate()
297
+ );
266
298
 
267
299
  if (this.layersHasTexture === false) {
268
300
  this.layers0!.store.defaultGeometrySchema.ordering =
@@ -273,8 +305,11 @@ export default class I3SConverter {
273
305
 
274
306
  await this._writeLayers0();
275
307
  createSceneServerPath(tilesetName, this.layers0!, tilesetPath);
276
- await this._writeNodeIndexDocument(root0, 'root', join(this.layers0Path, 'nodes', 'root'));
277
- await this.nodePages.save(this.layers0Path, this.writeQueue, isCreateSlpk);
308
+ for (const filePath of this.compressList || []) {
309
+ await compressFileWithGzip(filePath);
310
+ await removeFile(filePath);
311
+ }
312
+ await this.nodePages.save();
278
313
  await this.writeQueue.finalize();
279
314
  await this._createSlpk(tilesetPath);
280
315
  }
@@ -310,77 +345,31 @@ export default class I3SConverter {
310
345
  this.layers0 = transform(layers0data, layersTemplate());
311
346
  }
312
347
 
313
- /**
314
- * Convert and save the layer and embedded tiles
315
- * @param boundingVolumes - mbs and obb data about node's bounding volume
316
- * @return 3DNodeIndexDocument data https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
317
- */
318
- private _formRootNodeIndexDocument(boundingVolumes: BoundingVolumes): Node3DIndexDocument {
319
- const root0data = {
320
- version: `{${uuidv4().toUpperCase()}}`,
321
- id: 'root',
322
- level: 0,
323
- lodSelection: [
324
- {
325
- metricType: 'maxScreenThresholdSQ',
326
- maxError: 0
327
- },
328
- {
329
- metricType: 'maxScreenThreshold',
330
- maxError: 0
331
- }
332
- ],
333
- ...boundingVolumes,
334
- children: []
335
- };
336
- return transform(root0data, nodeTemplate());
337
- }
338
-
339
348
  /**
340
349
  * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
341
- * @param root0 - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
350
+ * @param rootNode - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
342
351
  * @param sourceRootTile - Source (3DTile) tile data
343
- * @param parentId - node id in node pages
344
- * @param boundingVolumes - mbs and obb data about node's bounding volume
345
352
  */
346
353
  private async _convertNodesTree(
347
- root0: Node3DIndexDocument,
348
- sourceRootTile: TileHeader,
349
- parentId: number,
350
- boundingVolumes: BoundingVolumes
354
+ rootNode: NodeIndexDocument,
355
+ sourceRootTile: Tile3D
351
356
  ): Promise<void> {
352
357
  await this.sourceTileset!._loadTile(sourceRootTile);
353
358
  if (this.isContentSupported(sourceRootTile)) {
354
- root0.children = root0.children || [];
355
- root0.children.push({
356
- id: '1',
357
- href: './1',
358
- ...boundingVolumes
359
- });
360
- const [child] = await this._createNode(root0, sourceRootTile, parentId, 0);
361
- const childPath = join(this.layers0Path, 'nodes', child.path!);
362
-
363
- if (this.options.slpk) {
364
- await this.writeQueue.enqueue({
365
- archiveKey: 'nodes/1/3dNodeIndexDocument.json.gz',
366
- writePromise: writeFileForSlpk(
367
- childPath,
368
- JSON.stringify(child),
369
- '3dNodeIndexDocument.json'
370
- )
371
- });
372
- } else {
373
- await this.writeQueue.enqueue({writePromise: writeFile(childPath, JSON.stringify(child))});
359
+ const childNodes = await this._createNode(rootNode, sourceRootTile, 0);
360
+ for (const childNode of childNodes) {
361
+ await childNode.save();
374
362
  }
363
+ await rootNode.addChildren(childNodes);
375
364
  } else {
376
365
  await this._addChildrenWithNeighborsAndWriteFile({
377
- parentNode: root0,
366
+ parentNode: rootNode,
378
367
  sourceTiles: sourceRootTile.children,
379
- parentId,
380
368
  level: 1
381
369
  });
382
370
  }
383
371
  await sourceRootTile.unloadContent();
372
+ await rootNode.save();
384
373
  }
385
374
 
386
375
  /**
@@ -390,34 +379,13 @@ export default class I3SConverter {
390
379
  if (this.options.slpk) {
391
380
  await this.writeQueue.enqueue({
392
381
  archiveKey: '3dSceneLayer.json.gz',
393
- writePromise: writeFileForSlpk(
394
- this.layers0Path,
395
- JSON.stringify(this.layers0),
396
- '3dSceneLayer.json'
397
- )
382
+ writePromise: () =>
383
+ writeFileForSlpk(this.layers0Path, JSON.stringify(this.layers0), '3dSceneLayer.json')
398
384
  });
399
385
  } else {
400
386
  await this.writeQueue.enqueue({
401
- writePromise: writeFile(this.layers0Path, JSON.stringify(this.layers0))
402
- });
403
- }
404
- }
405
-
406
- /**
407
- * Write 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md in file
408
- */
409
- private async _writeNodeIndexDocument(
410
- root0: Node3DIndexDocument,
411
- nodePath: string,
412
- rootPath: string
413
- ): Promise<void> {
414
- if (this.options.slpk) {
415
- await this.writeQueue.enqueue({
416
- archiveKey: `nodes/${nodePath}/3dNodeIndexDocument.json.gz`,
417
- writePromise: writeFileForSlpk(rootPath, JSON.stringify(root0), '3dNodeIndexDocument.json')
387
+ writePromise: () => writeFile(this.layers0Path, JSON.stringify(this.layers0))
418
388
  });
419
- } else {
420
- await this.writeQueue.enqueue({writePromise: writeFile(rootPath, JSON.stringify(root0))});
421
389
  }
422
390
  }
423
391
 
@@ -466,50 +434,39 @@ export default class I3SConverter {
466
434
  /**
467
435
  * Add child nodes recursively and write them to files
468
436
  * @param data - arguments
437
+ * @param data.parentNode - 3DNodeIndexDocument of parent node
469
438
  * @param data.sourceTiles - array of source child nodes
470
- * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
471
- * @param data.parentId - id of parent node in node pages
472
439
  * @param data.level - level of node (distanse to root node in the tree)
473
440
  */
474
441
  private async _addChildrenWithNeighborsAndWriteFile(data: {
475
- parentNode: Node3DIndexDocument;
476
- sourceTiles: TileHeader[];
477
- parentId: number;
442
+ parentNode: NodeIndexDocument;
443
+ sourceTiles: Tile3D[];
478
444
  level: number;
479
445
  }): Promise<void> {
480
- const childNodes = [];
481
- await this._addChildren({...data, childNodes});
482
- await this._addNeighborsAndWriteFile(data.parentNode, childNodes);
446
+ await this._addChildren(data);
447
+ await data.parentNode.addNeighbors();
483
448
  }
484
449
 
485
450
  /**
486
451
  * Convert nested subtree of 3DTiles dataset
487
452
  * @param param0
453
+ * @param data.parentNode - 3DNodeIndexDocument of parent node
488
454
  * @param param0.sourceTile - source 3DTile data
489
- * @param param0.parentNode - parent I3S node
490
- * @param param0.childNodes - child I3S nodes
491
- * @param param0.parentId - parent node ID
492
455
  * @param param0.level - tree level
493
456
  */
494
457
  private async convertNestedTileset({
495
- sourceTile,
496
458
  parentNode,
497
- childNodes,
498
- parentId,
459
+ sourceTile,
499
460
  level
500
461
  }: {
501
- childNodes: NodeReference[];
502
- sourceTile: TileHeader;
503
- parentNode: Node3DIndexDocument;
504
- parentId: number;
462
+ parentNode: NodeIndexDocument;
463
+ sourceTile: Tile3D;
505
464
  level: number;
506
465
  }) {
507
466
  await this.sourceTileset!._loadTile(sourceTile);
508
467
  await this._addChildren({
509
468
  parentNode,
510
469
  sourceTiles: sourceTile.children,
511
- childNodes,
512
- parentId,
513
470
  level: level + 1
514
471
  });
515
472
  await sourceTile.unloadContent();
@@ -518,127 +475,62 @@ export default class I3SConverter {
518
475
  /**
519
476
  * Convert 3DTiles tile to I3S node
520
477
  * @param param0
478
+ * @param param0.parentNode - 3DNodeIndexDocument of parent node
521
479
  * @param param0.sourceTile - source 3DTile data
522
- * @param param0.parentNode - parent I3S node
523
- * @param param0.childNodes - child I3S nodes
524
- * @param param0.parentId - parent node ID
525
480
  * @param param0.level - tree level
526
481
  */
527
482
  private async convertNode({
528
- sourceTile,
529
483
  parentNode,
530
- childNodes,
531
- parentId,
484
+ sourceTile,
532
485
  level
533
486
  }: {
534
- childNodes: NodeReference[];
535
- sourceTile: TileHeader;
536
- parentNode: Node3DIndexDocument;
537
- parentId: number;
487
+ parentNode: NodeIndexDocument;
488
+ sourceTile: Tile3D;
538
489
  level: number;
539
490
  }) {
540
- const children = await this._createNode(parentNode, sourceTile, parentId, level);
541
- parentNode.children = parentNode.children || [];
542
- for (const child of children) {
543
- parentNode.children.push({
544
- id: child.id,
545
- href: `../${child.path}`,
546
- obb: child.obb,
547
- mbs: child.mbs
548
- });
549
- childNodes.push(child);
550
- }
491
+ const childNodes = await this._createNode(parentNode, sourceTile, level);
492
+ await parentNode.addChildren(childNodes);
551
493
  }
552
494
 
553
495
  /**
554
496
  * Add child nodes recursively and write them to files
555
- * @param data - arguments
556
- * @param data.childNodes - array of target child nodes
557
- * @param data.sourceTiles - array of source child nodes
558
- * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
559
- * @param data.parentId - id of parent node in node pages
560
- * @param data.level - level of node (distanse to root node in the tree)
497
+ * @param param0 - arguments
498
+ * @param param0.parentNode - 3DNodeIndexDocument of parent node
499
+ * @param param0.sourceTile - source 3DTile data
500
+ * @param param0.level - tree level
561
501
  */
562
502
  private async _addChildren(data: {
563
- childNodes: NodeReference[];
564
- sourceTiles: TileHeader[];
565
- parentNode: Node3DIndexDocument;
566
- parentId: number;
503
+ parentNode: NodeIndexDocument;
504
+ sourceTiles: Tile3D[];
567
505
  level: number;
568
506
  }): Promise<void> {
569
- const {childNodes, sourceTiles, parentNode, parentId, level} = data;
507
+ const {sourceTiles, parentNode, level} = data;
570
508
  if (this.options.maxDepth && level > this.options.maxDepth) {
571
509
  return;
572
510
  }
573
-
574
- const promises: Promise<void>[] = [];
575
-
576
511
  for (const sourceTile of sourceTiles) {
577
512
  if (sourceTile.type === 'json') {
578
- promises.push(
579
- this.convertNestedTileset({sourceTile, parentNode, childNodes, parentId, level})
580
- );
513
+ await this.convertNestedTileset({parentNode, sourceTile, level});
581
514
  } else {
582
- promises.push(this.convertNode({sourceTile, parentNode, childNodes, parentId, level}));
515
+ await this.convertNode({parentNode, sourceTile, level});
583
516
  }
584
- await Promise.all(promises);
585
517
  if (sourceTile.id) {
586
518
  console.log(sourceTile.id); // eslint-disable-line
587
519
  }
588
520
  }
589
521
  }
590
522
 
591
- /**
592
- * Add neightbors to 3DNodeIndexDocument and write it in a file
593
- * @param parentNode - arguments
594
- * @param childNodes - array of target child nodes
595
- */
596
- private async _addNeighborsAndWriteFile(
597
- parentNode: Node3DIndexDocument,
598
- childNodes: Node3DIndexDocument[]
599
- ): Promise<void> {
600
- for (const node of childNodes) {
601
- const childPath = join(this.layers0Path, 'nodes', node.path!);
602
- const nodePath = node.path;
603
- delete node.path;
604
-
605
- // Don't do large amount of "neightbors" to avoid big memory consumption
606
- if (Number(parentNode?.children?.length) < 1000) {
607
- for (const neighbor of parentNode.children || []) {
608
- // eslint-disable-next-line max-depth
609
- if (node.id === neighbor.id) {
610
- continue; // eslint-disable-line
611
- }
612
-
613
- if (node.neighbors) {
614
- node.neighbors.push({...neighbor});
615
- }
616
- }
617
- } else {
618
- // eslint-disable-next-line no-console, no-undef
619
- console.warn(
620
- `Node ${node.id}: neighbors attribute is omited because of large number of neigbors`
621
- );
622
- delete node.neighbors;
623
- }
624
- await this._writeNodeIndexDocument(node, nodePath!, childPath);
625
- node.neighbors = [];
626
- }
627
- }
628
-
629
523
  /**
630
524
  * Convert tile to one or more I3S nodes
631
- * @param parentTile - parent 3DNodeIndexDocument
632
- * @param sourceTile - source tile (3DTile)
633
- * @param parentId - id of parent node in node pages
634
- * @param level - level of node (distanse to root node in the tree)
525
+ * @param parentNode - 3DNodeIndexDocument of parent node
526
+ * @param sourceTile - source 3DTile data
527
+ * @param level - tree level
635
528
  */
636
529
  private async _createNode(
637
- parentTile: Node3DIndexDocument,
638
- sourceTile: TileHeader,
639
- parentId: number,
530
+ parentNode: NodeIndexDocument,
531
+ sourceTile: Tile3D,
640
532
  level: number
641
- ): Promise<Node3DIndexDocument[]> {
533
+ ): Promise<NodeIndexDocument[]> {
642
534
  this._checkAddRefinementTypeForTile(sourceTile);
643
535
 
644
536
  await this._updateTilesetOptions();
@@ -652,14 +544,20 @@ export default class I3SConverter {
652
544
  this._convertPropertyTableToNodeAttributes(propertyTable);
653
545
  }
654
546
 
655
- const resourcesData = await this._convertResources(sourceTile, parentId, propertyTable);
547
+ const resourcesData = await this._convertResources(
548
+ sourceTile,
549
+ parentNode.inPageId,
550
+ propertyTable
551
+ );
656
552
 
657
- const nodes: Node3DIndexDocument[] = [];
553
+ const nodes: NodeIndexDocument[] = [];
554
+ const nodeIds: number[] = [];
658
555
  const nodesInPage: NodeInPage[] = [];
659
556
  const emptyResources = {
660
557
  geometry: null,
661
558
  compressedGeometry: null,
662
559
  texture: null,
560
+ hasUvRegions: false,
663
561
  sharedResources: null,
664
562
  meshMaterial: null,
665
563
  vertexCount: null,
@@ -680,34 +578,37 @@ export default class I3SConverter {
680
578
  (val) => val.metricType === 'maxScreenThresholdSQ'
681
579
  ) || {maxError: 0};
682
580
 
683
- const nodeInPage = this._updateNodeInNodePages(
581
+ const nodeInPage = await this._updateNodeInNodePages(
684
582
  maxScreenThresholdSQ,
685
583
  boundingVolumes,
686
584
  sourceTile,
687
- parentId,
585
+ parentNode.inPageId,
688
586
  resources
689
587
  );
690
- const node = this._createNodeIndexDocument(
691
- parentTile,
588
+
589
+ const nodeData = await NodeIndexDocument.createNodeIndexDocument(
590
+ parentNode,
692
591
  boundingVolumes,
693
592
  lodSelection,
694
593
  nodeInPage,
695
594
  resources
696
595
  );
596
+ const node = await new NodeIndexDocument(nodeInPage.index, this).addData(nodeData);
597
+ nodes.push(node);
697
598
 
698
599
  if (nodeInPage.mesh) {
699
- await this._writeResources(resources, node.path!);
600
+ await this._writeResources(resources, node.id);
700
601
  }
701
602
 
702
603
  if (this.validate) {
703
- this.boundingVolumeWarnings = validateNodeBoundingVolumes(node);
604
+ this.boundingVolumeWarnings = validateNodeBoundingVolumes(nodeData);
704
605
 
705
606
  if (this.boundingVolumeWarnings && this.boundingVolumeWarnings.length) {
706
607
  console.warn('Bounding Volume Warnings: ', ...this.boundingVolumeWarnings); //eslint-disable-line
707
608
  }
708
609
  }
709
610
 
710
- nodes.push(node);
611
+ nodeIds.push(nodeInPage.index);
711
612
  nodesInPage.push(nodeInPage);
712
613
  }
713
614
 
@@ -716,7 +617,6 @@ export default class I3SConverter {
716
617
  await this._addChildrenWithNeighborsAndWriteFile({
717
618
  parentNode: nodes[0],
718
619
  sourceTiles: sourceTile.children,
719
- parentId: nodesInPage[0].index!,
720
620
  level: level + 1
721
621
  });
722
622
  return nodes;
@@ -727,17 +627,10 @@ export default class I3SConverter {
727
627
  * @param sourceTile - source tile (3DTile)
728
628
  * @param parentId - id of parent node in node pages
729
629
  * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA
730
- * result.geometry - ArrayBuffer with geometry attributes
731
- * result.compressedGeometry - ArrayBuffer with compressed (draco) geometry
732
- * result.texture - texture image
733
- * result.sharedResources - shared resource data object
734
- * result.meshMaterial - PBR-like material object
735
- * result.vertexCount - number of vertices in geometry
736
- * result.attributes - feature attributes
737
- * result.featureCount - number of features
630
+ * @returns - converted node resources
738
631
  */
739
632
  private async _convertResources(
740
- sourceTile: TileHeader,
633
+ sourceTile: Tile3D,
741
634
  parentId: number,
742
635
  propertyTable: FeatureTableJson | null
743
636
  ): Promise<I3SConvertedResources[] | null> {
@@ -751,12 +644,13 @@ export default class I3SConverter {
751
644
  };
752
645
  const resourcesData = await convertB3dmToI3sGeometry(
753
646
  sourceTile.content,
754
- () => this.nodePages.push({index: 0, obb: draftObb}, parentId),
647
+ async () => (await this.nodePages.push({index: 0, obb: draftObb}, parentId)).index,
755
648
  propertyTable,
756
649
  this.featuresHashArray,
757
650
  this.layers0?.attributeStorageInfo,
758
651
  this.options.draco,
759
652
  this.generateBoundingVolumes,
653
+ this.options.mergeMaterials,
760
654
  this.geoidHeightModel!,
761
655
  this.workerSource
762
656
  );
@@ -775,16 +669,17 @@ export default class I3SConverter {
775
669
  * @param resources.texture - texture image
776
670
  * @param resources.vertexCount - number of vertices in geometry
777
671
  * @param resources.featureCount - number of features
672
+ * @param resources.geometry - Uint8Array with geometry attributes
778
673
  * @return the node object in node pages
779
674
  */
780
- private _updateNodeInNodePages(
675
+ private async _updateNodeInNodePages(
781
676
  maxScreenThresholdSQ: MaxScreenThresholdSQ,
782
677
  boundingVolumes: BoundingVolumes,
783
- sourceTile: TileHeader,
678
+ sourceTile: Tile3D,
784
679
  parentId: number,
785
680
  resources: I3SConvertedResources
786
- ): NodeInPage {
787
- const {meshMaterial, texture, vertexCount, featureCount, geometry} = resources;
681
+ ): Promise<NodeInPage> {
682
+ const {meshMaterial, texture, vertexCount, featureCount, geometry, hasUvRegions} = resources;
788
683
  const nodeInPage: NodeInPage = {
789
684
  index: 0,
790
685
  lodThreshold: maxScreenThresholdSQ.maxError,
@@ -794,7 +689,7 @@ export default class I3SConverter {
794
689
  if (geometry && this.isContentSupported(sourceTile)) {
795
690
  nodeInPage.mesh = {
796
691
  geometry: {
797
- definition: texture ? 0 : 1,
692
+ definition: this.findOrCreateGeometryDefinition(Boolean(texture), hasUvRegions),
798
693
  resource: 0
799
694
  },
800
695
  attribute: {
@@ -807,89 +702,31 @@ export default class I3SConverter {
807
702
  }
808
703
 
809
704
  let nodeId = resources.nodeId;
810
- if (nodeId) {
811
- this.nodePages.updateAll(nodeId, nodeInPage);
812
- const node = this.nodePages.getNodeById(nodeId);
813
- this.nodePages.updateResourceInMesh(node);
705
+ let node;
706
+ if (!nodeId) {
707
+ node = await this.nodePages.push(nodeInPage, parentId);
814
708
  } else {
815
- nodeId = this.nodePages.push(nodeInPage, parentId);
709
+ node = await this.nodePages.getNodeById(nodeId);
816
710
  }
817
711
 
712
+ NodePages.updateAll(node, nodeInPage);
818
713
  if (meshMaterial) {
819
- this.nodePages.updateMaterialByNodeId(nodeId, this._findOrCreateMaterial(meshMaterial));
714
+ NodePages.updateMaterialByNodeId(node, this._findOrCreateMaterial(meshMaterial));
820
715
  }
821
-
822
716
  if (texture) {
823
717
  const texelCountHint = texture.image.height * texture.image.width;
824
- this.nodePages.updateTexelCountHintByNodeId(nodeId, texelCountHint);
718
+ NodePages.updateTexelCountHintByNodeId(node, texelCountHint);
825
719
  }
826
-
827
720
  if (vertexCount) {
828
721
  this.vertexCounter += vertexCount;
829
- this.nodePages.updateVertexCountByNodeId(nodeId, vertexCount);
722
+ NodePages.updateVertexCountByNodeId(node, vertexCount);
830
723
  }
831
- this.nodePages.updateNodeAttributeByNodeId(nodeId);
724
+ NodePages.updateNodeAttributeByNodeId(node);
832
725
  if (featureCount) {
833
- this.nodePages.updateFeatureCountByNodeId(nodeId, featureCount);
726
+ NodePages.updateFeatureCountByNodeId(node, featureCount);
834
727
  }
835
728
 
836
- return this.nodePages.getNodeById(nodeId);
837
- }
838
-
839
- /**
840
- * Create a new node page object in node pages
841
- * @param parentNode - 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object of the parent node
842
- * @param boundingVolumes - Bounding volumes
843
- * @param lodSelection - Level of Details (LOD) metrics
844
- * @param nodeInPage - corresponding node object in a node page
845
- * @param resources - the node resources data
846
- * @param resources.texture - texture image
847
- * @param resources.attributes - feature attributes
848
- * @return 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object
849
- */
850
- private _createNodeIndexDocument(
851
- parentNode: Node3DIndexDocument,
852
- boundingVolumes: BoundingVolumes,
853
- lodSelection: LodSelection[],
854
- nodeInPage: NodeInPage,
855
- resources: I3SConvertedResources
856
- ): Node3DIndexDocument {
857
- const {texture, attributes} = resources;
858
- const nodeId = nodeInPage.index!;
859
- const nodeData = {
860
- version: parentNode.version,
861
- id: nodeId.toString(),
862
- path: nodeId.toString(),
863
- level: parentNode.level! + 1,
864
- ...boundingVolumes,
865
- lodSelection,
866
- parentNode: {
867
- id: parentNode.id,
868
- href: `../${parentNode.id}`,
869
- mbs: parentNode.mbs,
870
- obb: parentNode.obb
871
- },
872
- children: [],
873
- neighbors: []
874
- };
875
- const node = transform(nodeData, nodeTemplate());
876
-
877
- if (nodeInPage.mesh) {
878
- node.geometryData = [{href: './geometries/0'}];
879
- node.sharedResource = {href: './shared'};
880
-
881
- if (texture) {
882
- node.textureData = [{href: './textures/0'}, {href: './textures/1'}];
883
- }
884
-
885
- if (attributes && attributes.length && this.layers0?.attributeStorageInfo?.length) {
886
- node.attributeData = [];
887
- for (let index = 0; index < attributes.length; index++) {
888
- const folderName = this.layers0.attributeStorageInfo[index].key;
889
- node.attributeData.push({href: `./attributes/${folderName}/0`});
890
- }
891
- }
892
- }
729
+ this.nodePages.saveNode(node);
893
730
 
894
731
  return node;
895
732
  }
@@ -938,12 +775,12 @@ export default class I3SConverter {
938
775
  const slpkGeometryPath = join(childPath, 'geometries');
939
776
  await this.writeQueue.enqueue({
940
777
  archiveKey: `${slpkChildPath}/geometries/0.bin.gz`,
941
- writePromise: writeFileForSlpk(slpkGeometryPath, geometryBuffer, '0.bin')
778
+ writePromise: () => writeFileForSlpk(slpkGeometryPath, geometryBuffer, '0.bin')
942
779
  });
943
780
  } else {
944
781
  const geometryPath = join(childPath, 'geometries/0/');
945
782
  await this.writeQueue.enqueue({
946
- writePromise: writeFile(geometryPath, geometryBuffer, 'index.bin')
783
+ writePromise: () => writeFile(geometryPath, geometryBuffer, 'index.bin')
947
784
  });
948
785
  }
949
786
 
@@ -952,12 +789,13 @@ export default class I3SConverter {
952
789
  const slpkCompressedGeometryPath = join(childPath, 'geometries');
953
790
  await this.writeQueue.enqueue({
954
791
  archiveKey: `${slpkChildPath}/geometries/1.bin.gz`,
955
- writePromise: writeFileForSlpk(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
792
+ writePromise: () =>
793
+ writeFileForSlpk(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
956
794
  });
957
795
  } else {
958
796
  const compressedGeometryPath = join(childPath, 'geometries/1/');
959
797
  await this.writeQueue.enqueue({
960
- writePromise: writeFile(compressedGeometryPath, compressedGeometry, 'index.bin')
798
+ writePromise: () => writeFile(compressedGeometryPath, compressedGeometry, 'index.bin')
961
799
  });
962
800
  }
963
801
  }
@@ -986,11 +824,11 @@ export default class I3SConverter {
986
824
  const slpkSharedPath = join(childPath, 'shared');
987
825
  await this.writeQueue.enqueue({
988
826
  archiveKey: `${slpkChildPath}/shared/sharedResource.json.gz`,
989
- writePromise: writeFileForSlpk(slpkSharedPath, sharedDataStr, 'sharedResource.json')
827
+ writePromise: () => writeFileForSlpk(slpkSharedPath, sharedDataStr, 'sharedResource.json')
990
828
  });
991
829
  } else {
992
830
  const sharedPath = join(childPath, 'shared/');
993
- await this.writeQueue.enqueue({writePromise: writeFile(sharedPath, sharedDataStr)});
831
+ await this.writeQueue.enqueue({writePromise: () => writeFile(sharedPath, sharedDataStr)});
994
832
  }
995
833
  }
996
834
 
@@ -1058,6 +896,7 @@ export default class I3SConverter {
1058
896
 
1059
897
  if (!this.layers0!.textureSetDefinitions!.length) {
1060
898
  this.layers0!.textureSetDefinitions!.push({formats});
899
+ this.layers0!.textureSetDefinitions!.push({formats, atlas: true});
1061
900
  }
1062
901
  }
1063
902
  }
@@ -1083,12 +922,13 @@ export default class I3SConverter {
1083
922
 
1084
923
  await this.writeQueue.enqueue({
1085
924
  archiveKey: `${slpkChildPath}/textures/${name}.${format}`,
1086
- writePromise: writeFileForSlpk(slpkTexturePath, textureData, `${name}.${format}`, compress)
925
+ writePromise: () =>
926
+ writeFileForSlpk(slpkTexturePath, textureData, `${name}.${format}`, compress)
1087
927
  });
1088
928
  } else {
1089
929
  const texturePath = join(childPath, `textures/${name}/`);
1090
930
  await this.writeQueue.enqueue({
1091
- writePromise: writeFile(texturePath, textureData, `index.${format}`)
931
+ writePromise: () => writeFile(texturePath, textureData, `index.${format}`)
1092
932
  });
1093
933
  }
1094
934
  }
@@ -1113,12 +953,12 @@ export default class I3SConverter {
1113
953
  const slpkAttributesPath = join(childPath, 'attributes', folderName);
1114
954
  await this.writeQueue.enqueue({
1115
955
  archiveKey: `${slpkChildPath}/attributes/${folderName}.bin.gz`,
1116
- writePromise: writeFileForSlpk(slpkAttributesPath, fileBuffer, '0.bin')
956
+ writePromise: () => writeFileForSlpk(slpkAttributesPath, fileBuffer, '0.bin')
1117
957
  });
1118
958
  } else {
1119
959
  const attributesPath = join(childPath, `attributes/${folderName}/0`);
1120
960
  await this.writeQueue.enqueue({
1121
- writePromise: writeFile(attributesPath, fileBuffer, 'index.bin')
961
+ writePromise: () => writeFile(attributesPath, fileBuffer, 'index.bin')
1122
962
  });
1123
963
  }
1124
964
  }
@@ -1150,13 +990,31 @@ export default class I3SConverter {
1150
990
  private _findOrCreateMaterial(material: I3SMaterialDefinition): number {
1151
991
  const hash = md5(JSON.stringify(material));
1152
992
  if (this.materialMap.has(hash)) {
1153
- return this.materialMap.get(hash);
993
+ return this.materialMap.get(hash) || 0;
1154
994
  }
1155
995
  const newMaterialId = this.materialDefinitions.push(material) - 1;
1156
996
  this.materialMap.set(hash, newMaterialId);
1157
997
  return newMaterialId;
1158
998
  }
1159
999
 
1000
+ /**
1001
+ * Get unique geometry configuration index
1002
+ * In the end of conversion configurations will be transformed to geometryDefinitions array
1003
+ * @param hasTexture
1004
+ * @param hasUvRegions
1005
+ * @returns
1006
+ */
1007
+ private findOrCreateGeometryDefinition(hasTexture: boolean, hasUvRegions: boolean): number {
1008
+ const geometryConfig = {hasTexture, hasUvRegions};
1009
+ const hash = md5(JSON.stringify(geometryConfig));
1010
+ if (this.geometryMap.has(hash)) {
1011
+ return this.geometryMap.get(hash) || 0;
1012
+ }
1013
+ const newGeometryId = this.geometryConfigs.push(geometryConfig) - 1;
1014
+ this.geometryMap.set(hash, newGeometryId);
1015
+ return newGeometryId;
1016
+ }
1017
+
1160
1018
  /**
1161
1019
  * Do conversion of 3DTiles property table to I3s node attributes.
1162
1020
  * @param propertyTable - Table with layer meta data.
@@ -1250,7 +1108,7 @@ export default class I3SConverter {
1250
1108
  /** Do calculations of all tiles and tiles with "ADD" type of refinement.
1251
1109
  * @param tile
1252
1110
  */
1253
- private _checkAddRefinementTypeForTile(tile: TileHeader): void {
1111
+ private _checkAddRefinementTypeForTile(tile: Tile3D): void {
1254
1112
  const ADD_TILE_REFINEMENT = 1;
1255
1113
 
1256
1114
  if (tile.refine === ADD_TILE_REFINEMENT) {