@loaders.gl/tile-converter 3.2.12 → 3.3.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 (251) hide show
  1. package/dist/3d-tiles-attributes-worker.d.ts +3 -3
  2. package/dist/3d-tiles-attributes-worker.d.ts.map +1 -1
  3. package/dist/3d-tiles-attributes-worker.js +2 -3
  4. package/dist/3d-tiles-attributes-worker.js.map +3 -3
  5. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts +8 -0
  6. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -1
  7. package/dist/3d-tiles-converter/3d-tiles-converter.js +57 -43
  8. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts +5 -5
  9. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts.map +1 -1
  10. package/dist/3d-tiles-converter/helpers/b3dm-converter.js +21 -17
  11. package/dist/converter-cli.js +43 -8
  12. package/dist/converter.min.js +24 -21
  13. package/dist/deps-installer/deps-installer.d.ts +5 -1
  14. package/dist/deps-installer/deps-installer.d.ts.map +1 -1
  15. package/dist/deps-installer/deps-installer.js +29 -1
  16. package/dist/dist.min.js +58405 -61237
  17. package/dist/es5/3d-tiles-attributes-worker.js +4 -7
  18. package/dist/es5/3d-tiles-attributes-worker.js.map +1 -1
  19. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js +125 -210
  20. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  21. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js +53 -85
  22. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  23. package/dist/es5/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +0 -8
  24. package/dist/es5/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js.map +1 -1
  25. package/dist/es5/3d-tiles-converter/helpers/texture-atlas.js +0 -5
  26. package/dist/es5/3d-tiles-converter/helpers/texture-atlas.js.map +1 -1
  27. package/dist/es5/3d-tiles-converter/json-templates/tileset.js +0 -6
  28. package/dist/es5/3d-tiles-converter/json-templates/tileset.js.map +1 -1
  29. package/dist/es5/bundle.js +0 -1
  30. package/dist/es5/bundle.js.map +1 -1
  31. package/dist/es5/constants.js.map +1 -1
  32. package/dist/es5/converter-cli.js +50 -60
  33. package/dist/es5/converter-cli.js.map +1 -1
  34. package/dist/es5/deps-installer/deps-installer.js +73 -28
  35. package/dist/es5/deps-installer/deps-installer.js.map +1 -1
  36. package/dist/es5/i3s-attributes-worker.js +3 -6
  37. package/dist/es5/i3s-attributes-worker.js.map +1 -1
  38. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +124 -0
  39. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  40. package/dist/es5/i3s-converter/helpers/coordinate-converter.js +2 -19
  41. package/dist/es5/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  42. package/dist/es5/i3s-converter/helpers/create-scene-server-path.js +0 -11
  43. package/dist/es5/i3s-converter/helpers/create-scene-server-path.js.map +1 -1
  44. package/dist/es5/i3s-converter/helpers/feature-attributes.js +184 -0
  45. package/dist/es5/i3s-converter/helpers/feature-attributes.js.map +1 -0
  46. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +60 -51
  47. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  48. package/dist/es5/i3s-converter/helpers/geometry-converter.js +516 -356
  49. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  50. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +57 -43
  51. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  52. package/dist/es5/i3s-converter/helpers/node-debug.js +4 -23
  53. package/dist/es5/i3s-converter/helpers/node-debug.js.map +1 -1
  54. package/dist/es5/i3s-converter/helpers/node-index-document.js +507 -0
  55. package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -0
  56. package/dist/es5/i3s-converter/helpers/node-pages.js +462 -208
  57. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
  58. package/dist/es5/i3s-converter/i3s-converter.js +722 -1153
  59. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  60. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js +107 -0
  61. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  62. package/dist/es5/i3s-converter/json-templates/layers.js +2 -107
  63. package/dist/es5/i3s-converter/json-templates/layers.js.map +1 -1
  64. package/dist/es5/i3s-converter/json-templates/metadata.js +0 -2
  65. package/dist/es5/i3s-converter/json-templates/metadata.js.map +1 -1
  66. package/dist/es5/i3s-converter/json-templates/node.js +2 -12
  67. package/dist/es5/i3s-converter/json-templates/node.js.map +1 -1
  68. package/dist/es5/i3s-converter/json-templates/scene-server.js +0 -2
  69. package/dist/es5/i3s-converter/json-templates/scene-server.js.map +1 -1
  70. package/dist/es5/i3s-converter/json-templates/shared-resources.js +9 -32
  71. package/dist/es5/i3s-converter/json-templates/shared-resources.js.map +1 -1
  72. package/dist/es5/i3s-converter/json-templates/store.js.map +1 -1
  73. package/dist/es5/i3s-converter/types.js.map +1 -1
  74. package/dist/es5/i3s-server/app.js +0 -5
  75. package/dist/es5/i3s-server/app.js.map +1 -1
  76. package/dist/es5/i3s-server/controllers/index-controller.js +0 -16
  77. package/dist/es5/i3s-server/controllers/index-controller.js.map +1 -1
  78. package/dist/es5/i3s-server/routes/index.js +1 -10
  79. package/dist/es5/i3s-server/routes/index.js.map +1 -1
  80. package/dist/es5/index.js +0 -3
  81. package/dist/es5/index.js.map +1 -1
  82. package/dist/es5/lib/utils/compress-util.js +19 -74
  83. package/dist/es5/lib/utils/compress-util.js.map +1 -1
  84. package/dist/es5/lib/utils/file-utils.js +103 -47
  85. package/dist/es5/lib/utils/file-utils.js.map +1 -1
  86. package/dist/es5/lib/utils/lod-conversion-utils.js +0 -7
  87. package/dist/es5/lib/utils/lod-conversion-utils.js.map +1 -1
  88. package/dist/es5/lib/utils/queue.js +0 -14
  89. package/dist/es5/lib/utils/queue.js.map +1 -1
  90. package/dist/es5/lib/utils/statistic-utills.js +1 -46
  91. package/dist/es5/lib/utils/statistic-utills.js.map +1 -1
  92. package/dist/es5/lib/utils/write-queue.js +41 -82
  93. package/dist/es5/lib/utils/write-queue.js.map +1 -1
  94. package/dist/es5/pgm-loader.js +1 -8
  95. package/dist/es5/pgm-loader.js.map +1 -1
  96. package/dist/es5/workers/3d-tiles-attributes-worker.js +2 -9
  97. package/dist/es5/workers/3d-tiles-attributes-worker.js.map +1 -1
  98. package/dist/es5/workers/i3s-attributes-worker.js +2 -10
  99. package/dist/es5/workers/i3s-attributes-worker.js.map +1 -1
  100. package/dist/esm/3d-tiles-attributes-worker.js +4 -2
  101. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
  102. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js +60 -77
  103. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  104. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js +29 -50
  105. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  106. package/dist/esm/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +1 -0
  107. package/dist/esm/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js.map +1 -1
  108. package/dist/esm/3d-tiles-converter/helpers/texture-atlas.js +1 -4
  109. package/dist/esm/3d-tiles-converter/helpers/texture-atlas.js.map +1 -1
  110. package/dist/esm/3d-tiles-converter/json-templates/tileset.js +0 -3
  111. package/dist/esm/3d-tiles-converter/json-templates/tileset.js.map +1 -1
  112. package/dist/esm/bundle.js +1 -1
  113. package/dist/esm/bundle.js.map +1 -1
  114. package/dist/esm/constants.js.map +1 -1
  115. package/dist/esm/converter-cli.js +46 -40
  116. package/dist/esm/converter-cli.js.map +1 -1
  117. package/dist/esm/deps-installer/deps-installer.js +30 -4
  118. package/dist/esm/deps-installer/deps-installer.js.map +1 -1
  119. package/dist/esm/i3s-attributes-worker.js +3 -1
  120. package/dist/esm/i3s-attributes-worker.js.map +1 -1
  121. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +113 -0
  122. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  123. package/dist/esm/i3s-converter/helpers/coordinate-converter.js +5 -6
  124. package/dist/esm/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  125. package/dist/esm/i3s-converter/helpers/create-scene-server-path.js +1 -0
  126. package/dist/esm/i3s-converter/helpers/create-scene-server-path.js.map +1 -1
  127. package/dist/esm/i3s-converter/helpers/feature-attributes.js +158 -0
  128. package/dist/esm/i3s-converter/helpers/feature-attributes.js.map +1 -0
  129. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +39 -33
  130. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  131. package/dist/esm/i3s-converter/helpers/geometry-converter.js +295 -196
  132. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  133. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +59 -34
  134. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  135. package/dist/esm/i3s-converter/helpers/node-debug.js +3 -13
  136. package/dist/esm/i3s-converter/helpers/node-debug.js.map +1 -1
  137. package/dist/esm/i3s-converter/helpers/node-index-document.js +197 -0
  138. package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -0
  139. package/dist/esm/i3s-converter/helpers/node-pages.js +161 -87
  140. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
  141. package/dist/esm/i3s-converter/i3s-converter.js +216 -491
  142. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  143. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js +89 -0
  144. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  145. package/dist/esm/i3s-converter/json-templates/layers.js +2 -95
  146. package/dist/esm/i3s-converter/json-templates/layers.js.map +1 -1
  147. package/dist/esm/i3s-converter/json-templates/metadata.js.map +1 -1
  148. package/dist/esm/i3s-converter/json-templates/node.js +0 -4
  149. package/dist/esm/i3s-converter/json-templates/node.js.map +1 -1
  150. package/dist/esm/i3s-converter/json-templates/scene-server.js.map +1 -1
  151. package/dist/esm/i3s-converter/json-templates/shared-resources.js +3 -15
  152. package/dist/esm/i3s-converter/json-templates/shared-resources.js.map +1 -1
  153. package/dist/esm/i3s-converter/json-templates/store.js.map +1 -1
  154. package/dist/esm/i3s-converter/types.js.map +1 -1
  155. package/dist/esm/i3s-server/app.js +0 -5
  156. package/dist/esm/i3s-server/app.js.map +1 -1
  157. package/dist/esm/i3s-server/controllers/index-controller.js +0 -5
  158. package/dist/esm/i3s-server/controllers/index-controller.js.map +1 -1
  159. package/dist/esm/i3s-server/routes/index.js +0 -3
  160. package/dist/esm/i3s-server/routes/index.js.map +1 -1
  161. package/dist/esm/index.js.map +1 -1
  162. package/dist/esm/lib/utils/compress-util.js +19 -12
  163. package/dist/esm/lib/utils/compress-util.js.map +1 -1
  164. package/dist/esm/lib/utils/file-utils.js +54 -11
  165. package/dist/esm/lib/utils/file-utils.js.map +1 -1
  166. package/dist/esm/lib/utils/lod-conversion-utils.js +2 -6
  167. package/dist/esm/lib/utils/lod-conversion-utils.js.map +1 -1
  168. package/dist/esm/lib/utils/queue.js +0 -4
  169. package/dist/esm/lib/utils/queue.js.map +1 -1
  170. package/dist/esm/lib/utils/statistic-utills.js +0 -11
  171. package/dist/esm/lib/utils/statistic-utills.js.map +1 -1
  172. package/dist/esm/lib/utils/write-queue.js +27 -38
  173. package/dist/esm/lib/utils/write-queue.js.map +1 -1
  174. package/dist/esm/pgm-loader.js +3 -1
  175. package/dist/esm/pgm-loader.js.map +1 -1
  176. package/dist/esm/workers/3d-tiles-attributes-worker.js +4 -1
  177. package/dist/esm/workers/3d-tiles-attributes-worker.js.map +1 -1
  178. package/dist/esm/workers/i3s-attributes-worker.js +4 -1
  179. package/dist/esm/workers/i3s-attributes-worker.js.map +1 -1
  180. package/dist/i3s-attributes-worker.d.ts +7 -3
  181. package/dist/i3s-attributes-worker.d.ts.map +1 -1
  182. package/dist/i3s-attributes-worker.js +2 -3
  183. package/dist/i3s-attributes-worker.js.map +3 -3
  184. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +11 -0
  185. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -0
  186. package/dist/i3s-converter/helpers/batch-ids-extensions.js +141 -0
  187. package/dist/i3s-converter/helpers/coordinate-converter.d.ts +2 -2
  188. package/dist/i3s-converter/helpers/coordinate-converter.d.ts.map +1 -1
  189. package/dist/i3s-converter/helpers/feature-attributes.d.ts +56 -0
  190. package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -0
  191. package/dist/i3s-converter/helpers/feature-attributes.js +216 -0
  192. package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -1
  193. package/dist/i3s-converter/helpers/geometry-attributes.js +42 -17
  194. package/dist/i3s-converter/helpers/geometry-converter.d.ts +18 -6
  195. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  196. package/dist/i3s-converter/helpers/geometry-converter.js +349 -99
  197. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
  198. package/dist/i3s-converter/helpers/gltf-attributes.js +53 -21
  199. package/dist/i3s-converter/helpers/node-index-document.d.ts +91 -0
  200. package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -0
  201. package/dist/i3s-converter/helpers/node-index-document.js +242 -0
  202. package/dist/i3s-converter/helpers/node-pages.d.ts +81 -42
  203. package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -1
  204. package/dist/i3s-converter/helpers/node-pages.js +200 -92
  205. package/dist/i3s-converter/i3s-converter.d.ts +52 -108
  206. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  207. package/dist/i3s-converter/i3s-converter.js +218 -403
  208. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts +7 -0
  209. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts.map +1 -0
  210. package/dist/i3s-converter/json-templates/geometry-definitions.js +87 -0
  211. package/dist/i3s-converter/json-templates/layers.d.ts +1 -30
  212. package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -1
  213. package/dist/i3s-converter/json-templates/layers.js +2 -86
  214. package/dist/i3s-converter/json-templates/shared-resources.js +3 -3
  215. package/dist/i3s-converter/types.d.ts +38 -8
  216. package/dist/i3s-converter/types.d.ts.map +1 -1
  217. package/dist/lib/utils/file-utils.d.ts +17 -1
  218. package/dist/lib/utils/file-utils.d.ts.map +1 -1
  219. package/dist/lib/utils/file-utils.js +64 -7
  220. package/dist/lib/utils/write-queue.d.ts +19 -3
  221. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  222. package/dist/lib/utils/write-queue.js +21 -16
  223. package/dist/pgm-loader.d.ts.map +1 -1
  224. package/dist/pgm-loader.js +2 -1
  225. package/dist/workers/3d-tiles-attributes-worker.js +1 -1
  226. package/dist/workers/i3s-attributes-worker.js +1 -1
  227. package/package.json +18 -16
  228. package/src/3d-tiles-attributes-worker.ts +1 -1
  229. package/src/3d-tiles-converter/3d-tiles-converter.ts +71 -55
  230. package/src/3d-tiles-converter/helpers/b3dm-converter.ts +25 -18
  231. package/src/converter-cli.ts +54 -8
  232. package/src/deps-installer/deps-installer.ts +38 -2
  233. package/src/i3s-attributes-worker.ts +5 -1
  234. package/src/i3s-converter/helpers/batch-ids-extensions.ts +206 -0
  235. package/src/i3s-converter/helpers/coordinate-converter.ts +2 -2
  236. package/src/i3s-converter/helpers/feature-attributes.ts +247 -0
  237. package/src/i3s-converter/helpers/geometry-attributes.ts +46 -18
  238. package/src/i3s-converter/helpers/geometry-converter.ts +423 -111
  239. package/src/i3s-converter/helpers/gltf-attributes.ts +59 -24
  240. package/src/i3s-converter/helpers/node-index-document.ts +306 -0
  241. package/src/i3s-converter/helpers/node-pages.ts +222 -109
  242. package/src/i3s-converter/i3s-converter.ts +264 -487
  243. package/src/i3s-converter/json-templates/geometry-definitions.ts +83 -0
  244. package/src/i3s-converter/json-templates/layers.ts +2 -91
  245. package/src/i3s-converter/json-templates/shared-resources.ts +3 -3
  246. package/src/i3s-converter/types.ts +33 -2
  247. package/src/lib/utils/file-utils.ts +62 -7
  248. package/src/lib/utils/write-queue.ts +42 -19
  249. package/src/pgm-loader.ts +2 -2
  250. package/src/workers/3d-tiles-attributes-worker.ts +1 -1
  251. package/src/workers/i3s-attributes-worker.ts +2 -1
@@ -1,20 +1,11 @@
1
1
  import type {Tile3D, Tileset3DProps} from '@loaders.gl/tiles';
2
- import type {BatchTableJson, B3DMContent} from '@loaders.gl/3d-tiles';
2
+ import type {FeatureTableJson} from '@loaders.gl/3d-tiles';
3
3
  import type {WriteQueueItem} from '../lib/utils/write-queue';
4
4
  import type {
5
- AttributeStorageInfo,
6
5
  SceneLayer3D,
7
6
  BoundingVolumes,
8
- Node3DIndexDocument,
9
- NodeReference,
10
7
  MaxScreenThresholdSQ,
11
- NodeInPage,
12
- LodSelection,
13
- Attribute,
14
- ESRIField,
15
- Field,
16
- PopupInfo,
17
- FieldInfo
8
+ NodeInPage
18
9
  } from '@loaders.gl/i3s';
19
10
  import {load, encode, fetchFile, getLoaderOptions, isBrowser} from '@loaders.gl/core';
20
11
  import {Tileset3D} from '@loaders.gl/tiles';
@@ -27,14 +18,15 @@ import transform from 'json-map-transform';
27
18
  import md5 from 'md5';
28
19
 
29
20
  import NodePages from './helpers/node-pages';
30
- import {writeFile, removeDir, writeFileForSlpk} from '../lib/utils/file-utils';
21
+ import {writeFile, removeDir, writeFileForSlpk, removeFile} from '../lib/utils/file-utils';
31
22
  import {
23
+ compressFileWithGzip,
32
24
  compressWithChildProcess
33
25
  // generateHash128FromZip,
34
26
  // addFileToZip
35
27
  } from '../lib/utils/compress-util';
36
28
  import {calculateFilesSize, timeConverter} from '../lib/utils/statistic-utills';
37
- import convertB3dmToI3sGeometry from './helpers/geometry-converter';
29
+ import convertB3dmToI3sGeometry, {getPropertyTable} from './helpers/geometry-converter';
38
30
  import {
39
31
  createBoundingVolumes,
40
32
  convertBoundingVolumeToI3SFullExtent
@@ -44,7 +36,7 @@ import {convertGeometricErrorToScreenThreshold} from '../lib/utils/lod-conversio
44
36
  import {PGMLoader} from '../pgm-loader';
45
37
 
46
38
  import {LAYERS as layersTemplate} from './json-templates/layers';
47
- import {NODE as nodeTemplate} from './json-templates/node';
39
+ import {GEOMETRY_DEFINITION as geometryDefinitionTemlate} from './json-templates/geometry-definitions';
48
40
  import {SHARED_RESOURCES as sharedResourcesTemplate} from './json-templates/shared-resources';
49
41
  import {validateNodeBoundingVolumes} from './helpers/node-debug';
50
42
  import TileHeader from '@loaders.gl/tiles/src/tileset/tile-3d';
@@ -59,6 +51,14 @@ import {DracoWriterWorker} from '@loaders.gl/draco';
59
51
  import WriteQueue from '../lib/utils/write-queue';
60
52
  import {I3SAttributesWorker} from '../i3s-attributes-worker';
61
53
  import {BROWSER_ERROR_MESSAGE} from '../constants';
54
+ import {
55
+ createdStorageAttribute,
56
+ createFieldAttribute,
57
+ createPopupInfo,
58
+ getAttributeType,
59
+ getFieldAttributeType
60
+ } from './helpers/feature-attributes';
61
+ import {NodeIndexDocument} from './helpers/node-index-document';
62
62
 
63
63
  const ION_DEFAULT_TOKEN =
64
64
  process.env?.IonToken || // eslint-disable-line
@@ -66,10 +66,6 @@ const ION_DEFAULT_TOKEN =
66
66
  const HARDCODED_NODES_PER_PAGE = 64;
67
67
  const _3D_TILES = '3DTILES';
68
68
  const _3D_OBJECT_LAYER_TYPE = '3DObject';
69
- const STRING_TYPE = 'string';
70
- const SHORT_INT_TYPE = 'Int32';
71
- const DOUBLE_TYPE = 'double';
72
- const OBJECT_ID_TYPE = 'OBJECTID';
73
69
  const REFRESH_TOKEN_TIMEOUT = 1800; // 30 minutes in seconds
74
70
  const CESIUM_DATASET_PREFIX = 'https://';
75
71
  // const FS_FILE_TOO_LARGE = 'ERR_FS_FILE_TOO_LARGE';
@@ -81,8 +77,10 @@ export default class I3SConverter {
81
77
  nodePages: NodePages;
82
78
  options: any;
83
79
  layers0Path: string;
84
- materialMap: Map<any, any>;
80
+ materialMap: Map<string, number>;
85
81
  materialDefinitions: I3SMaterialDefinition[];
82
+ geometryMap: Map<string, number>;
83
+ geometryConfigs: {hasTexture: boolean; hasUvRegions: boolean}[];
86
84
  vertexCounter: number;
87
85
  layers0: SceneLayer3D | null;
88
86
  featuresHashArray: string[];
@@ -102,13 +100,16 @@ export default class I3SConverter {
102
100
  layersHasTexture: boolean;
103
101
  workerSource: {[key: string]: string} = {};
104
102
  writeQueue: WriteQueue<WriteQueueItem> = new WriteQueue();
103
+ compressList: string[] | null = null;
105
104
 
106
105
  constructor() {
107
- this.nodePages = new NodePages(writeFile, HARDCODED_NODES_PER_PAGE);
106
+ this.nodePages = new NodePages(writeFile, HARDCODED_NODES_PER_PAGE, this);
108
107
  this.options = {};
109
108
  this.layers0Path = '';
110
109
  this.materialMap = new Map();
111
110
  this.materialDefinitions = [];
111
+ this.geometryMap = new Map();
112
+ this.geometryConfigs = [];
112
113
  this.vertexCounter = 0;
113
114
  this.layers0 = null;
114
115
  this.featuresHashArray = [];
@@ -120,6 +121,7 @@ export default class I3SConverter {
120
121
  this.generateTextures = false;
121
122
  this.generateBoundingVolumes = false;
122
123
  this.layersHasTexture = false;
124
+ this.compressList = null;
123
125
  }
124
126
 
125
127
  /**
@@ -135,6 +137,9 @@ export default class I3SConverter {
135
137
  * @param options.token Token for Cesium ION tilesets authentication
136
138
  * @param options.draco Generate I3S 1.7 draco compressed geometries
137
139
  * @param options.validate -enable validation
140
+ * @param options.generateTextures - generate alternative type of textures (to have non-compressed jpeg/png and compressed ktx2)
141
+ * @param options.generateBoundingVolumes - generate bounding volumes from vertices coordinates instead of source tiles bounding volumes
142
+ * @param options.instantNodeWriting - Keep created 3DNodeIndexDocument files on disk instead of memory. This option reduce memory usage but decelerates conversion speed
138
143
  */
139
144
  async convert(options: {
140
145
  inputUrl: string;
@@ -146,9 +151,11 @@ export default class I3SConverter {
146
151
  slpk?: boolean;
147
152
  token?: string;
148
153
  draco?: boolean;
154
+ mergeMaterials?: boolean;
149
155
  validate?: boolean;
150
156
  generateTextures?: boolean;
151
157
  generateBoundingVolumes?: boolean;
158
+ instantNodeWriting?: boolean;
152
159
  }): Promise<any> {
153
160
  if (isBrowser) {
154
161
  console.log(BROWSER_ERROR_MESSAGE);
@@ -162,14 +169,27 @@ export default class I3SConverter {
162
169
  inputUrl,
163
170
  validate,
164
171
  outputPath,
165
- draco,
172
+ draco = true,
166
173
  sevenZipExe,
167
174
  maxDepth,
168
175
  token,
169
176
  generateTextures,
170
- generateBoundingVolumes
177
+ generateBoundingVolumes,
178
+ instantNodeWriting = false,
179
+ mergeMaterials = true
171
180
  } = options;
172
- this.options = {maxDepth, slpk, sevenZipExe, egmFilePath, draco, token, inputUrl};
181
+ this.options = {
182
+ maxDepth,
183
+ slpk,
184
+ sevenZipExe,
185
+ egmFilePath,
186
+ draco,
187
+ token,
188
+ inputUrl,
189
+ instantNodeWriting,
190
+ mergeMaterials
191
+ };
192
+ this.compressList = (this.options.instantNodeWriting && []) || null;
173
193
  this.validate = Boolean(validate);
174
194
  this.Loader = inputUrl.indexOf(CESIUM_DATASET_PREFIX) !== -1 ? CesiumIonLoader : Tiles3DLoader;
175
195
  this.generateTextures = Boolean(generateTextures);
@@ -190,7 +210,18 @@ export default class I3SConverter {
190
210
 
191
211
  try {
192
212
  const preloadOptions = await this._fetchPreloadOptions();
193
- const tilesetOptions: Tileset3DProps = {loadOptions: {basis: {format: 'rgba32'}}};
213
+ const tilesetOptions: Tileset3DProps = {
214
+ loadOptions: {
215
+ _nodeWorkers: true,
216
+ reuseWorkers: true,
217
+ basis: {format: 'rgba32'},
218
+ 'basis-nodejs': {
219
+ format: 'rgba32',
220
+ workerUrl: './modules/textures/dist/basis-nodejs-worker.js'
221
+ },
222
+ 'draco-nodejs': {workerUrl: './modules/draco/dist/draco-nodejs-worker.js'}
223
+ }
224
+ };
194
225
  if (preloadOptions.headers) {
195
226
  tilesetOptions.loadOptions!.fetch = {headers: preloadOptions.headers};
196
227
  }
@@ -242,19 +273,24 @@ export default class I3SConverter {
242
273
 
243
274
  const sourceRootTile: TileHeader = this.sourceTileset!.root!;
244
275
  const boundingVolumes = createBoundingVolumes(sourceRootTile, this.geoidHeightModel!);
245
- const parentId = this.nodePages.push({
276
+ await this.nodePages.push({
246
277
  index: 0,
247
278
  lodThreshold: 0,
248
279
  obb: boundingVolumes.obb,
249
280
  children: []
250
281
  });
251
282
 
252
- const isCreateSlpk = this.options.slpk;
253
- const root0 = this._formRootNodeIndexDocument(boundingVolumes);
254
-
255
- await this._convertNodesTree(root0, sourceRootTile, parentId, boundingVolumes);
283
+ const rootNode = await NodeIndexDocument.createRootNode(boundingVolumes, this);
284
+ await this._convertNodesTree(rootNode, sourceRootTile);
256
285
 
257
286
  this.layers0!.materialDefinitions = this.materialDefinitions;
287
+ // @ts-ignore
288
+ this.layers0.geometryDefinitions = transform(
289
+ this.geometryConfigs.map((config) => ({
290
+ geometryConfig: {...config, draco: this.options.draco}
291
+ })),
292
+ geometryDefinitionTemlate()
293
+ );
258
294
 
259
295
  if (this.layersHasTexture === false) {
260
296
  this.layers0!.store.defaultGeometrySchema.ordering =
@@ -265,8 +301,11 @@ export default class I3SConverter {
265
301
 
266
302
  await this._writeLayers0();
267
303
  createSceneServerPath(tilesetName, this.layers0!, tilesetPath);
268
- await this._writeNodeIndexDocument(root0, 'root', join(this.layers0Path, 'nodes', 'root'));
269
- await this.nodePages.save(this.layers0Path, this.writeQueue, isCreateSlpk);
304
+ for (const filePath of this.compressList || []) {
305
+ await compressFileWithGzip(filePath);
306
+ await removeFile(filePath);
307
+ }
308
+ await this.nodePages.save();
270
309
  await this.writeQueue.finalize();
271
310
  await this._createSlpk(tilesetPath);
272
311
  }
@@ -302,77 +341,31 @@ export default class I3SConverter {
302
341
  this.layers0 = transform(layers0data, layersTemplate());
303
342
  }
304
343
 
305
- /**
306
- * Convert and save the layer and embedded tiles
307
- * @param boundingVolumes - mbs and obb data about node's bounding volume
308
- * @return 3DNodeIndexDocument data https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
309
- */
310
- private _formRootNodeIndexDocument(boundingVolumes: BoundingVolumes): Node3DIndexDocument {
311
- const root0data = {
312
- version: `{${uuidv4().toUpperCase()}}`,
313
- id: 'root',
314
- level: 0,
315
- lodSelection: [
316
- {
317
- metricType: 'maxScreenThresholdSQ',
318
- maxError: 0
319
- },
320
- {
321
- metricType: 'maxScreenThreshold',
322
- maxError: 0
323
- }
324
- ],
325
- ...boundingVolumes,
326
- children: []
327
- };
328
- return transform(root0data, nodeTemplate());
329
- }
330
-
331
344
  /**
332
345
  * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
333
- * @param root0 - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
346
+ * @param rootNode - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
334
347
  * @param sourceRootTile - Source (3DTile) tile data
335
- * @param parentId - node id in node pages
336
- * @param boundingVolumes - mbs and obb data about node's bounding volume
337
348
  */
338
349
  private async _convertNodesTree(
339
- root0: Node3DIndexDocument,
340
- sourceRootTile: TileHeader,
341
- parentId: number,
342
- boundingVolumes: BoundingVolumes
350
+ rootNode: NodeIndexDocument,
351
+ sourceRootTile: TileHeader
343
352
  ): Promise<void> {
344
353
  await this.sourceTileset!._loadTile(sourceRootTile);
345
354
  if (this.isContentSupported(sourceRootTile)) {
346
- root0.children = root0.children || [];
347
- root0.children.push({
348
- id: '1',
349
- href: './1',
350
- ...boundingVolumes
351
- });
352
- const [child] = await this._createNode(root0, sourceRootTile, parentId, 0);
353
- const childPath = join(this.layers0Path, 'nodes', child.path!);
354
-
355
- if (this.options.slpk) {
356
- await this.writeQueue.enqueue({
357
- archiveKey: 'nodes/1/3dNodeIndexDocument.json.gz',
358
- writePromise: writeFileForSlpk(
359
- childPath,
360
- JSON.stringify(child),
361
- '3dNodeIndexDocument.json'
362
- )
363
- });
364
- } else {
365
- await this.writeQueue.enqueue({writePromise: writeFile(childPath, JSON.stringify(child))});
355
+ const childNodes = await this._createNode(rootNode, sourceRootTile, 0);
356
+ for (const childNode of childNodes) {
357
+ await childNode.save();
366
358
  }
359
+ await rootNode.addChildren(childNodes);
367
360
  } else {
368
361
  await this._addChildrenWithNeighborsAndWriteFile({
369
- parentNode: root0,
362
+ parentNode: rootNode,
370
363
  sourceTiles: sourceRootTile.children,
371
- parentId,
372
364
  level: 1
373
365
  });
374
366
  }
375
367
  await sourceRootTile.unloadContent();
368
+ await rootNode.save();
376
369
  }
377
370
 
378
371
  /**
@@ -382,37 +375,16 @@ export default class I3SConverter {
382
375
  if (this.options.slpk) {
383
376
  await this.writeQueue.enqueue({
384
377
  archiveKey: '3dSceneLayer.json.gz',
385
- writePromise: writeFileForSlpk(
386
- this.layers0Path,
387
- JSON.stringify(this.layers0),
388
- '3dSceneLayer.json'
389
- )
378
+ writePromise: () =>
379
+ writeFileForSlpk(this.layers0Path, JSON.stringify(this.layers0), '3dSceneLayer.json')
390
380
  });
391
381
  } else {
392
382
  await this.writeQueue.enqueue({
393
- writePromise: writeFile(this.layers0Path, JSON.stringify(this.layers0))
383
+ writePromise: () => writeFile(this.layers0Path, JSON.stringify(this.layers0))
394
384
  });
395
385
  }
396
386
  }
397
387
 
398
- /**
399
- * Write 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md in file
400
- */
401
- private async _writeNodeIndexDocument(
402
- root0: Node3DIndexDocument,
403
- nodePath: string,
404
- rootPath: string
405
- ): Promise<void> {
406
- if (this.options.slpk) {
407
- await this.writeQueue.enqueue({
408
- archiveKey: `nodes/${nodePath}/3dNodeIndexDocument.json.gz`,
409
- writePromise: writeFileForSlpk(rootPath, JSON.stringify(root0), '3dNodeIndexDocument.json')
410
- });
411
- } else {
412
- await this.writeQueue.enqueue({writePromise: writeFile(rootPath, JSON.stringify(root0))});
413
- }
414
- }
415
-
416
388
  /**
417
389
  * Pack files into *.slpk archive
418
390
  * @param tilesetPath - Path to save file
@@ -458,123 +430,114 @@ export default class I3SConverter {
458
430
  /**
459
431
  * Add child nodes recursively and write them to files
460
432
  * @param data - arguments
433
+ * @param data.parentNode - 3DNodeIndexDocument of parent node
461
434
  * @param data.sourceTiles - array of source child nodes
462
- * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
463
- * @param data.parentId - id of parent node in node pages
464
435
  * @param data.level - level of node (distanse to root node in the tree)
465
436
  */
466
437
  private async _addChildrenWithNeighborsAndWriteFile(data: {
467
- parentNode: Node3DIndexDocument;
438
+ parentNode: NodeIndexDocument;
468
439
  sourceTiles: TileHeader[];
469
- parentId: number;
470
440
  level: number;
471
441
  }): Promise<void> {
472
- const childNodes = [];
473
- await this._addChildren({...data, childNodes});
474
- await this._addNeighborsAndWriteFile(data.parentNode, childNodes);
442
+ await this._addChildren(data);
443
+ await data.parentNode.addNeighbors();
444
+ }
445
+
446
+ /**
447
+ * Convert nested subtree of 3DTiles dataset
448
+ * @param param0
449
+ * @param data.parentNode - 3DNodeIndexDocument of parent node
450
+ * @param param0.sourceTile - source 3DTile data
451
+ * @param param0.level - tree level
452
+ */
453
+ private async convertNestedTileset({
454
+ parentNode,
455
+ sourceTile,
456
+ level
457
+ }: {
458
+ parentNode: NodeIndexDocument;
459
+ sourceTile: TileHeader;
460
+ level: number;
461
+ }) {
462
+ await this.sourceTileset!._loadTile(sourceTile);
463
+ await this._addChildren({
464
+ parentNode,
465
+ sourceTiles: sourceTile.children,
466
+ level: level + 1
467
+ });
468
+ await sourceTile.unloadContent();
469
+ }
470
+
471
+ /**
472
+ * Convert 3DTiles tile to I3S node
473
+ * @param param0
474
+ * @param param0.parentNode - 3DNodeIndexDocument of parent node
475
+ * @param param0.sourceTile - source 3DTile data
476
+ * @param param0.level - tree level
477
+ */
478
+ private async convertNode({
479
+ parentNode,
480
+ sourceTile,
481
+ level
482
+ }: {
483
+ parentNode: NodeIndexDocument;
484
+ sourceTile: TileHeader;
485
+ level: number;
486
+ }) {
487
+ const childNodes = await this._createNode(parentNode, sourceTile, level);
488
+ await parentNode.addChildren(childNodes);
475
489
  }
476
490
 
477
491
  /**
478
492
  * Add child nodes recursively and write them to files
479
- * @param data - arguments
480
- * @param data.childNodes - array of target child nodes
481
- * @param data.sourceTiles - array of source child nodes
482
- * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
483
- * @param data.parentId - id of parent node in node pages
484
- * @param data.level - level of node (distanse to root node in the tree)
493
+ * @param param0 - arguments
494
+ * @param param0.parentNode - 3DNodeIndexDocument of parent node
495
+ * @param param0.sourceTile - source 3DTile data
496
+ * @param param0.level - tree level
485
497
  */
486
498
  private async _addChildren(data: {
487
- childNodes: NodeReference[];
499
+ parentNode: NodeIndexDocument;
488
500
  sourceTiles: TileHeader[];
489
- parentNode: Node3DIndexDocument;
490
- parentId: number;
491
501
  level: number;
492
502
  }): Promise<void> {
493
- const {childNodes, sourceTiles, parentNode, parentId, level} = data;
503
+ const {sourceTiles, parentNode, level} = data;
494
504
  if (this.options.maxDepth && level > this.options.maxDepth) {
495
505
  return;
496
506
  }
507
+
508
+ const promises: Promise<void>[] = [];
497
509
  for (const sourceTile of sourceTiles) {
498
510
  if (sourceTile.type === 'json') {
499
- await this.sourceTileset!._loadTile(sourceTile);
500
- await this._addChildren({
501
- parentNode,
502
- sourceTiles: sourceTile.children,
503
- childNodes,
504
- parentId,
505
- level: level + 1
506
- });
507
- await sourceTile.unloadContent();
511
+ if (this.options.instantNodeWriting) {
512
+ await this.convertNestedTileset({parentNode, sourceTile, level});
513
+ } else {
514
+ promises.push(this.convertNestedTileset({parentNode, sourceTile, level}));
515
+ }
508
516
  } else {
509
- const children = await this._createNode(parentNode, sourceTile, parentId, level);
510
- parentNode.children = parentNode.children || [];
511
- for (const child of children) {
512
- parentNode.children.push({
513
- id: child.id,
514
- href: `../${child.path}`,
515
- obb: child.obb,
516
- mbs: child.mbs
517
- });
518
- childNodes.push(child);
517
+ if (this.options.instantNodeWriting) {
518
+ await this.convertNode({parentNode, sourceTile, level});
519
+ } else {
520
+ promises.push(this.convertNode({parentNode, sourceTile, level}));
519
521
  }
520
522
  }
521
523
  if (sourceTile.id) {
522
524
  console.log(sourceTile.id); // eslint-disable-line
523
525
  }
524
526
  }
525
- }
526
-
527
- /**
528
- * Add neightbors to 3DNodeIndexDocument and write it in a file
529
- * @param parentNode - arguments
530
- * @param childNodes - array of target child nodes
531
- */
532
- private async _addNeighborsAndWriteFile(
533
- parentNode: Node3DIndexDocument,
534
- childNodes: Node3DIndexDocument[]
535
- ): Promise<void> {
536
- for (const node of childNodes) {
537
- const childPath = join(this.layers0Path, 'nodes', node.path!);
538
- const nodePath = node.path;
539
- delete node.path;
540
-
541
- // Don't do large amount of "neightbors" to avoid big memory consumption
542
- if (Number(parentNode?.children?.length) < 1000) {
543
- for (const neighbor of parentNode.children || []) {
544
- // eslint-disable-next-line max-depth
545
- if (node.id === neighbor.id) {
546
- continue; // eslint-disable-line
547
- }
548
-
549
- if (node.neighbors) {
550
- node.neighbors.push({...neighbor});
551
- }
552
- }
553
- } else {
554
- // eslint-disable-next-line no-console, no-undef
555
- console.warn(
556
- `Node ${node.id}: neighbors attribute is omited because of large number of neigbors`
557
- );
558
- delete node.neighbors;
559
- }
560
- await this._writeNodeIndexDocument(node, nodePath!, childPath);
561
- node.neighbors = [];
562
- }
527
+ await Promise.all(promises);
563
528
  }
564
529
 
565
530
  /**
566
531
  * Convert tile to one or more I3S nodes
567
- * @param parentTile - parent 3DNodeIndexDocument
568
- * @param sourceTile - source tile (3DTile)
569
- * @param parentId - id of parent node in node pages
570
- * @param level - level of node (distanse to root node in the tree)
532
+ * @param parentNode - 3DNodeIndexDocument of parent node
533
+ * @param sourceTile - source 3DTile data
534
+ * @param level - tree level
571
535
  */
572
536
  private async _createNode(
573
- parentTile: Node3DIndexDocument,
537
+ parentNode: NodeIndexDocument,
574
538
  sourceTile: TileHeader,
575
- parentId: number,
576
539
  level: number
577
- ): Promise<Node3DIndexDocument[]> {
540
+ ): Promise<NodeIndexDocument[]> {
578
541
  this._checkAddRefinementTypeForTile(sourceTile);
579
542
 
580
543
  await this._updateTilesetOptions();
@@ -582,20 +545,26 @@ export default class I3SConverter {
582
545
 
583
546
  let boundingVolumes = createBoundingVolumes(sourceTile, this.geoidHeightModel!);
584
547
 
585
- const batchTable = sourceTile?.content?.batchTableJson;
548
+ const propertyTable = getPropertyTable(sourceTile.content);
586
549
 
587
- if (batchTable) {
588
- this._convertAttributeStorageInfo(sourceTile.content);
550
+ if (propertyTable && !this.layers0?.attributeStorageInfo?.length) {
551
+ this._convertPropertyTableToNodeAttributes(propertyTable);
589
552
  }
590
553
 
591
- const resourcesData = await this._convertResources(sourceTile);
554
+ const resourcesData = await this._convertResources(
555
+ sourceTile,
556
+ parentNode.inPageId,
557
+ propertyTable
558
+ );
592
559
 
593
- const nodes: Node3DIndexDocument[] = [];
560
+ const nodes: NodeIndexDocument[] = [];
561
+ const nodeIds: number[] = [];
594
562
  const nodesInPage: NodeInPage[] = [];
595
563
  const emptyResources = {
596
564
  geometry: null,
597
565
  compressedGeometry: null,
598
566
  texture: null,
567
+ hasUvRegions: false,
599
568
  sharedResources: null,
600
569
  meshMaterial: null,
601
570
  vertexCount: null,
@@ -616,34 +585,37 @@ export default class I3SConverter {
616
585
  (val) => val.metricType === 'maxScreenThresholdSQ'
617
586
  ) || {maxError: 0};
618
587
 
619
- const nodeInPage = this._createNodeInNodePages(
588
+ const nodeInPage = await this._updateNodeInNodePages(
620
589
  maxScreenThresholdSQ,
621
590
  boundingVolumes,
622
591
  sourceTile,
623
- parentId,
592
+ parentNode.inPageId,
624
593
  resources
625
594
  );
626
- const node = this._createNodeIndexDocument(
627
- parentTile,
595
+
596
+ const nodeData = await NodeIndexDocument.createNodeIndexDocument(
597
+ parentNode,
628
598
  boundingVolumes,
629
599
  lodSelection,
630
600
  nodeInPage,
631
601
  resources
632
602
  );
603
+ const node = await new NodeIndexDocument(nodeInPage.index, this).addData(nodeData);
604
+ nodes.push(node);
633
605
 
634
606
  if (nodeInPage.mesh) {
635
- await this._writeResources(resources, node.path!);
607
+ await this._writeResources(resources, node.id);
636
608
  }
637
609
 
638
610
  if (this.validate) {
639
- this.boundingVolumeWarnings = validateNodeBoundingVolumes(node);
611
+ this.boundingVolumeWarnings = validateNodeBoundingVolumes(nodeData);
640
612
 
641
613
  if (this.boundingVolumeWarnings && this.boundingVolumeWarnings.length) {
642
614
  console.warn('Bounding Volume Warnings: ', ...this.boundingVolumeWarnings); //eslint-disable-line
643
615
  }
644
616
  }
645
617
 
646
- nodes.push(node);
618
+ nodeIds.push(nodeInPage.index);
647
619
  nodesInPage.push(nodeInPage);
648
620
  }
649
621
 
@@ -652,49 +624,40 @@ export default class I3SConverter {
652
624
  await this._addChildrenWithNeighborsAndWriteFile({
653
625
  parentNode: nodes[0],
654
626
  sourceTiles: sourceTile.children,
655
- parentId: nodesInPage[0].index!,
656
627
  level: level + 1
657
628
  });
658
629
  return nodes;
659
630
  }
660
631
 
661
- /**
662
- * Convert attributesStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
663
- * from B3DM batch table
664
- * @param sourceTileContent - tile content of 3DTile
665
- * @return {void}
666
- */
667
- private _convertAttributeStorageInfo(sourceTileContent: B3DMContent): void {
668
- // In legacy b3dm files sometimes sourceTileContent is null.
669
- const batchTable = sourceTileContent && sourceTileContent.batchTableJson;
670
- if (batchTable && !this.layers0?.attributeStorageInfo?.length) {
671
- this._convertBatchTableInfoToNodeAttributes(batchTable);
672
- }
673
- }
674
-
675
632
  /**
676
633
  * Convert tile to one or more I3S nodes
677
634
  * @param sourceTile - source tile (3DTile)
678
- * result.geometry - ArrayBuffer with geometry attributes
679
- * result.compressedGeometry - ArrayBuffer with compressed (draco) geometry
680
- * result.texture - texture image
681
- * result.sharedResources - shared resource data object
682
- * result.meshMaterial - PBR-like material object
683
- * result.vertexCount - number of vertices in geometry
684
- * result.attributes - feature attributes
685
- * result.featureCount - number of features
635
+ * @param parentId - id of parent node in node pages
636
+ * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA
637
+ * @returns - converted node resources
686
638
  */
687
- private async _convertResources(sourceTile: TileHeader): Promise<I3SConvertedResources[] | null> {
639
+ private async _convertResources(
640
+ sourceTile: TileHeader,
641
+ parentId: number,
642
+ propertyTable: FeatureTableJson | null
643
+ ): Promise<I3SConvertedResources[] | null> {
688
644
  if (!this.isContentSupported(sourceTile)) {
689
645
  return null;
690
646
  }
647
+ const draftObb = {
648
+ center: [],
649
+ halfSize: [],
650
+ quaternion: []
651
+ };
691
652
  const resourcesData = await convertB3dmToI3sGeometry(
692
653
  sourceTile.content,
693
- Number(this.nodePages.nodesCounter),
654
+ async () => (await this.nodePages.push({index: 0, obb: draftObb}, parentId)).index,
655
+ propertyTable,
694
656
  this.featuresHashArray,
695
657
  this.layers0?.attributeStorageInfo,
696
658
  this.options.draco,
697
659
  this.generateBoundingVolumes,
660
+ this.options.mergeMaterials,
698
661
  this.geoidHeightModel!,
699
662
  this.workerSource
700
663
  );
@@ -702,7 +665,7 @@ export default class I3SConverter {
702
665
  }
703
666
 
704
667
  /**
705
- * Create a new node object (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/node.cmn.md)
668
+ * Update node object (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/node.cmn.md)
706
669
  * in node pages (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/nodePage.cmn.md)
707
670
  * @param maxScreenThresholdSQ - Level of Details (LOD) metric
708
671
  * @param boundingVolumes - Bounding volumes
@@ -713,16 +676,17 @@ export default class I3SConverter {
713
676
  * @param resources.texture - texture image
714
677
  * @param resources.vertexCount - number of vertices in geometry
715
678
  * @param resources.featureCount - number of features
679
+ * @param resources.geometry - Uint8Array with geometry attributes
716
680
  * @return the node object in node pages
717
681
  */
718
- private _createNodeInNodePages(
682
+ private async _updateNodeInNodePages(
719
683
  maxScreenThresholdSQ: MaxScreenThresholdSQ,
720
684
  boundingVolumes: BoundingVolumes,
721
685
  sourceTile: TileHeader,
722
686
  parentId: number,
723
687
  resources: I3SConvertedResources
724
- ): NodeInPage {
725
- const {meshMaterial, texture, vertexCount, featureCount, geometry} = resources;
688
+ ): Promise<NodeInPage> {
689
+ const {meshMaterial, texture, vertexCount, featureCount, geometry, hasUvRegions} = resources;
726
690
  const nodeInPage: NodeInPage = {
727
691
  index: 0,
728
692
  lodThreshold: maxScreenThresholdSQ.maxError,
@@ -732,7 +696,7 @@ export default class I3SConverter {
732
696
  if (geometry && this.isContentSupported(sourceTile)) {
733
697
  nodeInPage.mesh = {
734
698
  geometry: {
735
- definition: texture ? 0 : 1,
699
+ definition: this.findOrCreateGeometryDefinition(Boolean(texture), hasUvRegions),
736
700
  resource: 0
737
701
  },
738
702
  attribute: {
@@ -743,83 +707,33 @@ export default class I3SConverter {
743
707
  }
744
708
  };
745
709
  }
746
- const nodeId = this.nodePages.push(nodeInPage, parentId);
747
710
 
748
- if (meshMaterial) {
749
- this.nodePages.updateMaterialByNodeId(nodeId, this._findOrCreateMaterial(meshMaterial));
711
+ let nodeId = resources.nodeId;
712
+ let node;
713
+ if (!nodeId) {
714
+ node = await this.nodePages.push(nodeInPage, parentId);
715
+ } else {
716
+ node = await this.nodePages.getNodeById(nodeId);
750
717
  }
751
718
 
719
+ NodePages.updateAll(node, nodeInPage);
720
+ if (meshMaterial) {
721
+ NodePages.updateMaterialByNodeId(node, this._findOrCreateMaterial(meshMaterial));
722
+ }
752
723
  if (texture) {
753
724
  const texelCountHint = texture.image.height * texture.image.width;
754
- this.nodePages.updateTexelCountHintByNodeId(nodeId, texelCountHint);
725
+ NodePages.updateTexelCountHintByNodeId(node, texelCountHint);
755
726
  }
756
-
757
727
  if (vertexCount) {
758
728
  this.vertexCounter += vertexCount;
759
- this.nodePages.updateVertexCountByNodeId(nodeId, vertexCount);
729
+ NodePages.updateVertexCountByNodeId(node, vertexCount);
760
730
  }
761
- this.nodePages.updateNodeAttributeByNodeId(nodeId);
731
+ NodePages.updateNodeAttributeByNodeId(node);
762
732
  if (featureCount) {
763
- this.nodePages.updateFeatureCountByNodeId(nodeId, featureCount);
733
+ NodePages.updateFeatureCountByNodeId(node, featureCount);
764
734
  }
765
735
 
766
- return nodeInPage;
767
- }
768
-
769
- /**
770
- * Create a new node page object in node pages
771
- * @param parentNode - 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object of the parent node
772
- * @param boundingVolumes - Bounding volumes
773
- * @param lodSelection - Level of Details (LOD) metrics
774
- * @param nodeInPage - corresponding node object in a node page
775
- * @param resources - the node resources data
776
- * @param resources.texture - texture image
777
- * @param resources.attributes - feature attributes
778
- * @return 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object
779
- */
780
- private _createNodeIndexDocument(
781
- parentNode: Node3DIndexDocument,
782
- boundingVolumes: BoundingVolumes,
783
- lodSelection: LodSelection[],
784
- nodeInPage: NodeInPage,
785
- resources: I3SConvertedResources
786
- ): Node3DIndexDocument {
787
- const {texture, attributes} = resources;
788
- const nodeId = nodeInPage.index!;
789
- const nodeData = {
790
- version: parentNode.version,
791
- id: nodeId.toString(),
792
- path: nodeId.toString(),
793
- level: parentNode.level! + 1,
794
- ...boundingVolumes,
795
- lodSelection,
796
- parentNode: {
797
- id: parentNode.id,
798
- href: `../${parentNode.id}`,
799
- mbs: parentNode.mbs,
800
- obb: parentNode.obb
801
- },
802
- children: [],
803
- neighbors: []
804
- };
805
- const node = transform(nodeData, nodeTemplate());
806
-
807
- if (nodeInPage.mesh) {
808
- node.geometryData = [{href: './geometries/0'}];
809
- node.sharedResource = {href: './shared'};
810
-
811
- if (texture) {
812
- node.textureData = [{href: './textures/0'}, {href: './textures/1'}];
813
- }
814
-
815
- if (attributes && attributes.length && this.layers0?.attributeStorageInfo?.length) {
816
- node.attributeData = [];
817
- for (let index = 0; index < attributes.length; index++) {
818
- const folderName = this.layers0.attributeStorageInfo[index].key;
819
- node.attributeData.push({href: `./attributes/${folderName}/0`});
820
- }
821
- }
822
- }
736
+ this.nodePages.saveNode(node);
823
737
 
824
738
  return node;
825
739
  }
@@ -868,12 +782,12 @@ export default class I3SConverter {
868
782
  const slpkGeometryPath = join(childPath, 'geometries');
869
783
  await this.writeQueue.enqueue({
870
784
  archiveKey: `${slpkChildPath}/geometries/0.bin.gz`,
871
- writePromise: writeFileForSlpk(slpkGeometryPath, geometryBuffer, '0.bin')
785
+ writePromise: () => writeFileForSlpk(slpkGeometryPath, geometryBuffer, '0.bin')
872
786
  });
873
787
  } else {
874
788
  const geometryPath = join(childPath, 'geometries/0/');
875
789
  await this.writeQueue.enqueue({
876
- writePromise: writeFile(geometryPath, geometryBuffer, 'index.bin')
790
+ writePromise: () => writeFile(geometryPath, geometryBuffer, 'index.bin')
877
791
  });
878
792
  }
879
793
 
@@ -882,12 +796,13 @@ export default class I3SConverter {
882
796
  const slpkCompressedGeometryPath = join(childPath, 'geometries');
883
797
  await this.writeQueue.enqueue({
884
798
  archiveKey: `${slpkChildPath}/geometries/1.bin.gz`,
885
- writePromise: writeFileForSlpk(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
799
+ writePromise: () =>
800
+ writeFileForSlpk(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
886
801
  });
887
802
  } else {
888
803
  const compressedGeometryPath = join(childPath, 'geometries/1/');
889
804
  await this.writeQueue.enqueue({
890
- writePromise: writeFile(compressedGeometryPath, compressedGeometry, 'index.bin')
805
+ writePromise: () => writeFile(compressedGeometryPath, compressedGeometry, 'index.bin')
891
806
  });
892
807
  }
893
808
  }
@@ -916,11 +831,11 @@ export default class I3SConverter {
916
831
  const slpkSharedPath = join(childPath, 'shared');
917
832
  await this.writeQueue.enqueue({
918
833
  archiveKey: `${slpkChildPath}/shared/sharedResource.json.gz`,
919
- writePromise: writeFileForSlpk(slpkSharedPath, sharedDataStr, 'sharedResource.json')
834
+ writePromise: () => writeFileForSlpk(slpkSharedPath, sharedDataStr, 'sharedResource.json')
920
835
  });
921
836
  } else {
922
837
  const sharedPath = join(childPath, 'shared/');
923
- await this.writeQueue.enqueue({writePromise: writeFile(sharedPath, sharedDataStr)});
838
+ await this.writeQueue.enqueue({writePromise: () => writeFile(sharedPath, sharedDataStr)});
924
839
  }
925
840
  }
926
841
 
@@ -948,12 +863,19 @@ export default class I3SConverter {
948
863
 
949
864
  if (this.generateTextures) {
950
865
  formats.push({name: '1', format: 'ktx2'});
951
- const ktx2TextureData = encode(texture.image, KTX2BasisWriterWorker, {
952
- ...KTX2BasisWriterWorker.options,
953
- source: this.workerSource.ktx2,
954
- reuseWorkers: true,
955
- _nodeWorkers: true
956
- });
866
+ // For Node.js texture.image.data is type of Buffer
867
+ const copyArrayBuffer = texture.image.data.subarray();
868
+ const arrayToEncode = new Uint8Array(copyArrayBuffer);
869
+ const ktx2TextureData = encode(
870
+ {...texture.image, data: arrayToEncode},
871
+ KTX2BasisWriterWorker,
872
+ {
873
+ ...KTX2BasisWriterWorker.options,
874
+ source: this.workerSource.ktx2,
875
+ reuseWorkers: true,
876
+ _nodeWorkers: true
877
+ }
878
+ );
957
879
 
958
880
  await this.writeTextureFile(ktx2TextureData, '1', 'ktx2', childPath, slpkChildPath);
959
881
  }
@@ -981,6 +903,7 @@ export default class I3SConverter {
981
903
 
982
904
  if (!this.layers0!.textureSetDefinitions!.length) {
983
905
  this.layers0!.textureSetDefinitions!.push({formats});
906
+ this.layers0!.textureSetDefinitions!.push({formats, atlas: true});
984
907
  }
985
908
  }
986
909
  }
@@ -1006,12 +929,13 @@ export default class I3SConverter {
1006
929
 
1007
930
  await this.writeQueue.enqueue({
1008
931
  archiveKey: `${slpkChildPath}/textures/${name}.${format}`,
1009
- writePromise: writeFileForSlpk(slpkTexturePath, textureData, `${name}.${format}`, compress)
932
+ writePromise: () =>
933
+ writeFileForSlpk(slpkTexturePath, textureData, `${name}.${format}`, compress)
1010
934
  });
1011
935
  } else {
1012
936
  const texturePath = join(childPath, `textures/${name}/`);
1013
937
  await this.writeQueue.enqueue({
1014
- writePromise: writeFile(texturePath, textureData, `index.${format}`)
938
+ writePromise: () => writeFile(texturePath, textureData, `index.${format}`)
1015
939
  });
1016
940
  }
1017
941
  }
@@ -1036,12 +960,12 @@ export default class I3SConverter {
1036
960
  const slpkAttributesPath = join(childPath, 'attributes', folderName);
1037
961
  await this.writeQueue.enqueue({
1038
962
  archiveKey: `${slpkChildPath}/attributes/${folderName}.bin.gz`,
1039
- writePromise: writeFileForSlpk(slpkAttributesPath, fileBuffer, '0.bin')
963
+ writePromise: () => writeFileForSlpk(slpkAttributesPath, fileBuffer, '0.bin')
1040
964
  });
1041
965
  } else {
1042
966
  const attributesPath = join(childPath, `attributes/${folderName}/0`);
1043
967
  await this.writeQueue.enqueue({
1044
- writePromise: writeFile(attributesPath, fileBuffer, 'index.bin')
968
+ writePromise: () => writeFile(attributesPath, fileBuffer, 'index.bin')
1045
969
  });
1046
970
  }
1047
971
  }
@@ -1073,7 +997,7 @@ export default class I3SConverter {
1073
997
  private _findOrCreateMaterial(material: I3SMaterialDefinition): number {
1074
998
  const hash = md5(JSON.stringify(material));
1075
999
  if (this.materialMap.has(hash)) {
1076
- return this.materialMap.get(hash);
1000
+ return this.materialMap.get(hash) || 0;
1077
1001
  }
1078
1002
  const newMaterialId = this.materialDefinitions.push(material) - 1;
1079
1003
  this.materialMap.set(hash, newMaterialId);
@@ -1081,133 +1005,42 @@ export default class I3SConverter {
1081
1005
  }
1082
1006
 
1083
1007
  /**
1084
- * Generate storage attribute for map segmentation.
1085
- * @param attributeIndex - order index of attribute (f_0, f_1 ...).
1086
- * @param key - attribute key from batch table.\
1087
- * @param attributeType - attribute type.
1088
- * @return Updated storageAttribute.
1089
- */
1090
- private _createdStorageAttribute(
1091
- attributeIndex: number,
1092
- key: string,
1093
- attributeType: Attribute
1094
- ): AttributeStorageInfo {
1095
- const storageAttribute = {
1096
- key: `f_${attributeIndex}`,
1097
- name: key,
1098
- ordering: ['attributeValues'],
1099
- header: [{property: 'count', valueType: 'UInt32'}],
1100
- attributeValues: {valueType: 'Int32', valuesPerElement: 1}
1101
- };
1102
-
1103
- switch (attributeType) {
1104
- case OBJECT_ID_TYPE:
1105
- this._setupIdAttribute(storageAttribute);
1106
- break;
1107
- case STRING_TYPE:
1108
- this._setupStringAttribute(storageAttribute);
1109
- break;
1110
- case DOUBLE_TYPE:
1111
- this._setupDoubleAttribute(storageAttribute);
1112
- break;
1113
- case SHORT_INT_TYPE:
1114
- break;
1115
- default:
1116
- this._setupStringAttribute(storageAttribute);
1117
- }
1118
-
1119
- return storageAttribute;
1120
- }
1121
-
1122
- /**
1123
- * Get the attribute type for attributeStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
1124
- * @param key - attribute's key
1125
- * @param attribute - attribute's type in batchTable
1008
+ * Get unique geometry configuration index
1009
+ * In the end of conversion configurations will be transformed to geometryDefinitions array
1010
+ * @param hasTexture
1011
+ * @param hasUvRegions
1012
+ * @returns
1126
1013
  */
1127
- private getAttributeType(key: string, attribute: string): string {
1128
- if (key === OBJECT_ID_TYPE) {
1129
- return OBJECT_ID_TYPE;
1130
- }
1131
- if (typeof attribute === STRING_TYPE) {
1132
- return STRING_TYPE;
1133
- } else if (typeof attribute === 'number') {
1134
- return Number.isInteger(attribute) ? SHORT_INT_TYPE : DOUBLE_TYPE;
1014
+ private findOrCreateGeometryDefinition(hasTexture: boolean, hasUvRegions: boolean): number {
1015
+ const geometryConfig = {hasTexture, hasUvRegions};
1016
+ const hash = md5(JSON.stringify(geometryConfig));
1017
+ if (this.geometryMap.has(hash)) {
1018
+ return this.geometryMap.get(hash) || 0;
1135
1019
  }
1136
- return STRING_TYPE;
1137
- }
1138
-
1139
- /**
1140
- * Setup storage attribute as string.
1141
- * @param storageAttribute - attribute for map segmentation.
1142
- */
1143
- private _setupStringAttribute(storageAttribute: AttributeStorageInfo): void {
1144
- storageAttribute.ordering!.unshift('attributeByteCounts');
1145
- storageAttribute.header.push({property: 'attributeValuesByteCount', valueType: 'UInt32'});
1146
- storageAttribute.attributeValues = {
1147
- valueType: 'String',
1148
- encoding: 'UTF-8',
1149
- valuesPerElement: 1
1150
- };
1151
- storageAttribute.attributeByteCounts = {
1152
- valueType: 'UInt32',
1153
- valuesPerElement: 1
1154
- };
1155
- }
1156
-
1157
- /**
1158
- * Setup Id attribute for map segmentation.
1159
- * @param storageAttribute - attribute for map segmentation .
1160
- */
1161
- private _setupIdAttribute(storageAttribute: AttributeStorageInfo): void {
1162
- storageAttribute.attributeValues = {
1163
- valueType: 'Oid32',
1164
- valuesPerElement: 1
1165
- };
1166
- }
1167
-
1168
- /**
1169
- * Setup double attribute for map segmentation.
1170
- * @param storageAttribute - attribute for map segmentation .
1171
- */
1172
- private _setupDoubleAttribute(storageAttribute: AttributeStorageInfo): void {
1173
- storageAttribute.attributeValues = {
1174
- valueType: 'Float64',
1175
- valuesPerElement: 1
1176
- };
1020
+ const newGeometryId = this.geometryConfigs.push(geometryConfig) - 1;
1021
+ this.geometryMap.set(hash, newGeometryId);
1022
+ return newGeometryId;
1177
1023
  }
1178
1024
 
1179
1025
  /**
1180
- * Setup field attribute for map segmentation.
1181
- * @param key - attribute for map segmentation.
1182
- * @param fieldAttributeType - esri attribute type ('esriFieldTypeString' or 'esriFieldTypeOID').
1026
+ * Do conversion of 3DTiles property table to I3s node attributes.
1027
+ * @param propertyTable - Table with layer meta data.
1183
1028
  */
1184
- private _createFieldAttribute(key: string, fieldAttributeType: ESRIField): Field {
1185
- return {
1186
- name: key,
1187
- type: fieldAttributeType,
1188
- alias: key
1189
- };
1190
- }
1191
-
1192
- /**
1193
- * Do conversion of 3DTiles batch table to I3s node attributes.
1194
- * @param batchTable - Table with layer meta data.
1195
- */
1196
- private _convertBatchTableInfoToNodeAttributes(batchTable: BatchTableJson): void {
1029
+ private _convertPropertyTableToNodeAttributes(propertyTable: FeatureTableJson): void {
1197
1030
  let attributeIndex = 0;
1198
- const batchTableWithObjectId = {
1031
+ const propertyTableWithObjectId = {
1199
1032
  OBJECTID: [0],
1200
- ...batchTable
1033
+ ...propertyTable
1201
1034
  };
1202
1035
 
1203
- for (const key in batchTableWithObjectId) {
1204
- const firstAttribute = batchTableWithObjectId[key][0];
1205
- const attributeType = this.getAttributeType(key, firstAttribute);
1036
+ for (const key in propertyTableWithObjectId) {
1037
+ const firstAttribute = propertyTableWithObjectId[key][0];
1038
+ const attributeType = getAttributeType(key, firstAttribute);
1206
1039
 
1207
- const storageAttribute = this._createdStorageAttribute(attributeIndex, key, attributeType);
1208
- const fieldAttributeType = this._getFieldAttributeType(attributeType);
1209
- const fieldAttribute = this._createFieldAttribute(key, fieldAttributeType);
1210
- const popupInfo = this._createPopupInfo(batchTableWithObjectId);
1040
+ const storageAttribute = createdStorageAttribute(attributeIndex, key, attributeType);
1041
+ const fieldAttributeType = getFieldAttributeType(attributeType);
1042
+ const fieldAttribute = createFieldAttribute(key, fieldAttributeType);
1043
+ const popupInfo = createPopupInfo(propertyTableWithObjectId);
1211
1044
 
1212
1045
  this.layers0!.attributeStorageInfo!.push(storageAttribute);
1213
1046
  this.layers0!.fields!.push(fieldAttribute);
@@ -1218,62 +1051,6 @@ export default class I3SConverter {
1218
1051
  }
1219
1052
  }
1220
1053
 
1221
- /**
1222
- * Find and return attribute type based on key form Batch table.
1223
- * @param attributeType
1224
- */
1225
- private _getFieldAttributeType(attributeType: Attribute): ESRIField {
1226
- switch (attributeType) {
1227
- case OBJECT_ID_TYPE:
1228
- return 'esriFieldTypeOID';
1229
- case STRING_TYPE:
1230
- return 'esriFieldTypeString';
1231
- case SHORT_INT_TYPE:
1232
- return 'esriFieldTypeInteger';
1233
- case DOUBLE_TYPE:
1234
- return 'esriFieldTypeDouble';
1235
- default:
1236
- return 'esriFieldTypeString';
1237
- }
1238
- }
1239
-
1240
- /**
1241
- * Generate popup info to show metadata on the map.
1242
- * @param batchTable - Batch table data with OBJECTID.
1243
- * @return data for correct rendering of popup.
1244
- */
1245
- private _createPopupInfo(batchTable: BatchTableJson): PopupInfo {
1246
- const title = '{OBJECTID}';
1247
- const mediaInfos = [];
1248
- const fieldInfos: FieldInfo[] = [];
1249
- const popupElements: {
1250
- fieldInfos: FieldInfo[];
1251
- type: string;
1252
- }[] = [];
1253
- const expressionInfos = [];
1254
-
1255
- for (const key in batchTable) {
1256
- fieldInfos.push({
1257
- fieldName: key,
1258
- visible: true,
1259
- isEditable: false,
1260
- label: key
1261
- });
1262
- }
1263
- popupElements.push({
1264
- fieldInfos,
1265
- type: 'fields'
1266
- });
1267
-
1268
- return {
1269
- title,
1270
- mediaInfos,
1271
- popupElements,
1272
- fieldInfos,
1273
- expressionInfos
1274
- };
1275
- }
1276
-
1277
1054
  /**
1278
1055
  * Print statistics in the end of conversion
1279
1056
  * @param params - output files data