@loaders.gl/tile-converter 4.0.0-alpha.1 → 4.0.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 (468) hide show
  1. package/bin/converter.js +1 -1
  2. package/dist/3d-tiles-attributes-worker.d.ts +28 -0
  3. package/dist/3d-tiles-attributes-worker.d.ts.map +1 -0
  4. package/dist/3d-tiles-attributes-worker.js +3 -0
  5. package/dist/3d-tiles-attributes-worker.js.map +7 -0
  6. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts +90 -0
  7. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -0
  8. package/dist/3d-tiles-converter/3d-tiles-converter.js +275 -226
  9. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts +71 -18
  10. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts.map +1 -0
  11. package/dist/3d-tiles-converter/helpers/b3dm-converter.js +256 -236
  12. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.d.ts +4 -7
  13. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.d.ts.map +1 -0
  14. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +22 -9
  15. package/dist/3d-tiles-converter/helpers/texture-atlas.d.ts +9 -0
  16. package/dist/3d-tiles-converter/helpers/texture-atlas.d.ts.map +1 -0
  17. package/dist/3d-tiles-converter/helpers/texture-atlas.js +47 -28
  18. package/dist/3d-tiles-converter/json-templates/tileset.d.ts +15 -0
  19. package/dist/3d-tiles-converter/json-templates/tileset.d.ts.map +1 -0
  20. package/dist/3d-tiles-converter/json-templates/tileset.js +42 -36
  21. package/dist/bundle.d.ts +2 -0
  22. package/dist/bundle.d.ts.map +1 -0
  23. package/dist/bundle.js +2 -2
  24. package/dist/constants.d.ts +2 -0
  25. package/dist/constants.d.ts.map +1 -0
  26. package/dist/constants.js +4 -0
  27. package/dist/converter-cli.d.ts +2 -0
  28. package/dist/converter-cli.d.ts.map +1 -0
  29. package/dist/converter-cli.js +280 -0
  30. package/dist/converter.min.js +185 -190
  31. package/dist/deps-installer/deps-installer.d.ts +11 -3
  32. package/dist/deps-installer/deps-installer.d.ts.map +1 -0
  33. package/dist/deps-installer/deps-installer.js +61 -23
  34. package/dist/dist.min.js +64143 -0
  35. package/dist/es5/3d-tiles-attributes-worker.js +25 -0
  36. package/dist/es5/3d-tiles-attributes-worker.js.map +1 -0
  37. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js +467 -0
  38. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js.map +1 -0
  39. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js +293 -0
  40. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -0
  41. package/dist/es5/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +18 -0
  42. package/dist/es5/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js.map +1 -0
  43. package/dist/es5/3d-tiles-converter/helpers/texture-atlas.js +33 -0
  44. package/dist/es5/3d-tiles-converter/helpers/texture-atlas.js.map +1 -0
  45. package/dist/es5/3d-tiles-converter/json-templates/tileset.js +61 -0
  46. package/dist/es5/3d-tiles-converter/json-templates/tileset.js.map +1 -0
  47. package/dist/es5/bundle.js +6 -0
  48. package/dist/es5/bundle.js.map +1 -0
  49. package/dist/es5/constants.js +9 -0
  50. package/dist/es5/constants.js.map +1 -0
  51. package/dist/es5/converter-cli.js +289 -0
  52. package/dist/es5/converter-cli.js.map +1 -0
  53. package/dist/es5/deps-installer/deps-installer.js +124 -0
  54. package/dist/es5/deps-installer/deps-installer.js.map +1 -0
  55. package/dist/es5/i3s-attributes-worker.js +25 -0
  56. package/dist/es5/i3s-attributes-worker.js.map +1 -0
  57. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +116 -0
  58. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  59. package/dist/es5/i3s-converter/helpers/coordinate-converter.js +89 -0
  60. package/dist/es5/i3s-converter/helpers/coordinate-converter.js.map +1 -0
  61. package/dist/es5/i3s-converter/helpers/create-scene-server-path.js +41 -0
  62. package/dist/es5/i3s-converter/helpers/create-scene-server-path.js.map +1 -0
  63. package/dist/es5/i3s-converter/helpers/feature-attributes.js +174 -0
  64. package/dist/es5/i3s-converter/helpers/feature-attributes.js.map +1 -0
  65. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +213 -0
  66. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -0
  67. package/dist/es5/i3s-converter/helpers/geometry-converter.js +1192 -0
  68. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -0
  69. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +113 -0
  70. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -0
  71. package/dist/es5/i3s-converter/helpers/load-3d-tiles.js +82 -0
  72. package/dist/es5/i3s-converter/helpers/load-3d-tiles.js.map +1 -0
  73. package/dist/es5/i3s-converter/helpers/node-debug.js +76 -0
  74. package/dist/es5/i3s-converter/helpers/node-debug.js.map +1 -0
  75. package/dist/es5/i3s-converter/helpers/node-index-document.js +521 -0
  76. package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -0
  77. package/dist/es5/i3s-converter/helpers/node-pages.js +519 -0
  78. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -0
  79. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js +111 -0
  80. package/dist/es5/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -0
  81. package/dist/es5/i3s-converter/helpers/tileset-traversal.js +82 -0
  82. package/dist/es5/i3s-converter/helpers/tileset-traversal.js.map +1 -0
  83. package/dist/es5/i3s-converter/i3s-converter.js +1541 -0
  84. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -0
  85. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js +107 -0
  86. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  87. package/dist/es5/i3s-converter/json-templates/layers.js +163 -0
  88. package/dist/es5/i3s-converter/json-templates/layers.js.map +1 -0
  89. package/dist/es5/i3s-converter/json-templates/metadata.js +31 -0
  90. package/dist/es5/i3s-converter/json-templates/metadata.js.map +1 -0
  91. package/dist/es5/i3s-converter/json-templates/node.js +99 -0
  92. package/dist/es5/i3s-converter/json-templates/node.js.map +1 -0
  93. package/dist/es5/i3s-converter/json-templates/scene-server.js +39 -0
  94. package/dist/es5/i3s-converter/json-templates/scene-server.js.map +1 -0
  95. package/dist/es5/i3s-converter/json-templates/shared-resources.js +173 -0
  96. package/dist/es5/i3s-converter/json-templates/shared-resources.js.map +1 -0
  97. package/dist/es5/i3s-converter/json-templates/store.js +107 -0
  98. package/dist/es5/i3s-converter/json-templates/store.js.map +1 -0
  99. package/dist/es5/i3s-converter/types.js +18 -0
  100. package/dist/es5/i3s-converter/types.js.map +1 -0
  101. package/dist/es5/i3s-server/README.md +19 -0
  102. package/dist/es5/i3s-server/app.js +27 -0
  103. package/dist/es5/i3s-server/app.js.map +1 -0
  104. package/dist/es5/i3s-server/controllers/index-controller.js +55 -0
  105. package/dist/es5/i3s-server/controllers/index-controller.js.map +1 -0
  106. package/dist/es5/i3s-server/controllers/slpk-controller.js +84 -0
  107. package/dist/es5/i3s-server/controllers/slpk-controller.js.map +1 -0
  108. package/dist/es5/i3s-server/routes/index.js +37 -0
  109. package/dist/es5/i3s-server/routes/index.js.map +1 -0
  110. package/dist/es5/i3s-server/routes/slpk-router.js +71 -0
  111. package/dist/es5/i3s-server/routes/slpk-router.js.map +1 -0
  112. package/dist/es5/i3s-server/utils/create-scene-server.js +17 -0
  113. package/dist/es5/i3s-server/utils/create-scene-server.js.map +1 -0
  114. package/dist/es5/index.js +21 -0
  115. package/dist/es5/index.js.map +1 -0
  116. package/dist/es5/lib/utils/compress-util.js +346 -0
  117. package/dist/es5/lib/utils/compress-util.js.map +1 -0
  118. package/dist/es5/lib/utils/file-utils.js +208 -0
  119. package/dist/es5/lib/utils/file-utils.js.map +1 -0
  120. package/dist/es5/lib/utils/geometry-utils.js +15 -0
  121. package/dist/es5/lib/utils/geometry-utils.js.map +1 -0
  122. package/dist/es5/lib/utils/lod-conversion-utils.js +44 -0
  123. package/dist/es5/lib/utils/lod-conversion-utils.js.map +1 -0
  124. package/dist/es5/lib/utils/queue.js +47 -0
  125. package/dist/es5/lib/utils/queue.js.map +1 -0
  126. package/dist/es5/lib/utils/statistic-utills.d.ts +25 -0
  127. package/dist/es5/lib/utils/statistic-utills.js +147 -0
  128. package/dist/es5/lib/utils/statistic-utills.js.map +1 -0
  129. package/dist/es5/lib/utils/write-queue.js +214 -0
  130. package/dist/es5/lib/utils/write-queue.js.map +1 -0
  131. package/dist/es5/pgm-loader.js +41 -0
  132. package/dist/es5/pgm-loader.js.map +1 -0
  133. package/dist/es5/workers/3d-tiles-attributes-worker.js +28 -0
  134. package/dist/es5/workers/3d-tiles-attributes-worker.js.map +1 -0
  135. package/dist/es5/workers/i3s-attributes-worker.js +30 -0
  136. package/dist/es5/workers/i3s-attributes-worker.js.map +1 -0
  137. package/dist/esm/3d-tiles-attributes-worker.js +16 -0
  138. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -0
  139. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js +245 -0
  140. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js.map +1 -0
  141. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js +218 -0
  142. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -0
  143. package/dist/esm/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +10 -0
  144. package/dist/esm/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js.map +1 -0
  145. package/dist/esm/3d-tiles-converter/helpers/texture-atlas.js +27 -0
  146. package/dist/esm/3d-tiles-converter/helpers/texture-atlas.js.map +1 -0
  147. package/dist/esm/3d-tiles-converter/json-templates/tileset.js +37 -0
  148. package/dist/esm/3d-tiles-converter/json-templates/tileset.js.map +1 -0
  149. package/dist/esm/bundle.js +4 -0
  150. package/dist/esm/bundle.js.map +1 -0
  151. package/dist/esm/constants.js +2 -0
  152. package/dist/esm/constants.js.map +1 -0
  153. package/dist/esm/converter-cli.js +232 -0
  154. package/dist/esm/converter-cli.js.map +1 -0
  155. package/dist/esm/deps-installer/deps-installer.js +45 -0
  156. package/dist/esm/deps-installer/deps-installer.js.map +1 -0
  157. package/dist/esm/i3s-attributes-worker.js +16 -0
  158. package/dist/esm/i3s-attributes-worker.js.map +1 -0
  159. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +105 -0
  160. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  161. package/dist/esm/i3s-converter/helpers/coordinate-converter.js +79 -0
  162. package/dist/esm/i3s-converter/helpers/coordinate-converter.js.map +1 -0
  163. package/dist/esm/i3s-converter/helpers/create-scene-server-path.js +16 -0
  164. package/dist/esm/i3s-converter/helpers/create-scene-server-path.js.map +1 -0
  165. package/dist/esm/i3s-converter/helpers/feature-attributes.js +147 -0
  166. package/dist/esm/i3s-converter/helpers/feature-attributes.js.map +1 -0
  167. package/{src → dist/esm}/i3s-converter/helpers/geometry-attributes.js +80 -106
  168. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -0
  169. package/dist/esm/i3s-converter/helpers/geometry-converter.js +906 -0
  170. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -0
  171. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +110 -0
  172. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -0
  173. package/dist/esm/i3s-converter/helpers/load-3d-tiles.js +35 -0
  174. package/dist/esm/i3s-converter/helpers/load-3d-tiles.js.map +1 -0
  175. package/{src → dist/esm}/i3s-converter/helpers/node-debug.js +20 -41
  176. package/dist/esm/i3s-converter/helpers/node-debug.js.map +1 -0
  177. package/dist/esm/i3s-converter/helpers/node-index-document.js +201 -0
  178. package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -0
  179. package/dist/esm/i3s-converter/helpers/node-pages.js +206 -0
  180. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -0
  181. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js +48 -0
  182. package/dist/esm/i3s-converter/helpers/preprocess-3d-tiles.js.map +1 -0
  183. package/dist/esm/i3s-converter/helpers/tileset-traversal.js +14 -0
  184. package/dist/esm/i3s-converter/helpers/tileset-traversal.js.map +1 -0
  185. package/dist/esm/i3s-converter/i3s-converter.js +778 -0
  186. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -0
  187. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js +89 -0
  188. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  189. package/dist/esm/i3s-converter/json-templates/layers.js +133 -0
  190. package/dist/esm/i3s-converter/json-templates/layers.js.map +1 -0
  191. package/dist/esm/i3s-converter/json-templates/metadata.js +22 -0
  192. package/dist/esm/i3s-converter/json-templates/metadata.js.map +1 -0
  193. package/dist/esm/i3s-converter/json-templates/node.js +80 -0
  194. package/dist/esm/i3s-converter/json-templates/node.js.map +1 -0
  195. package/dist/esm/i3s-converter/json-templates/scene-server.js +28 -0
  196. package/dist/esm/i3s-converter/json-templates/scene-server.js.map +1 -0
  197. package/dist/esm/i3s-converter/json-templates/shared-resources.js +123 -0
  198. package/dist/esm/i3s-converter/json-templates/shared-resources.js.map +1 -0
  199. package/dist/esm/i3s-converter/json-templates/store.js +98 -0
  200. package/dist/esm/i3s-converter/json-templates/store.js.map +1 -0
  201. package/dist/esm/i3s-converter/types.js +11 -0
  202. package/dist/esm/i3s-converter/types.js.map +1 -0
  203. package/dist/esm/i3s-server/README.md +19 -0
  204. package/dist/esm/i3s-server/app.js +26 -0
  205. package/dist/esm/i3s-server/app.js.map +1 -0
  206. package/dist/esm/i3s-server/bin/www +102 -0
  207. package/dist/esm/i3s-server/certs/cert.pem +19 -0
  208. package/dist/esm/i3s-server/certs/key.pem +27 -0
  209. package/dist/esm/i3s-server/controllers/index-controller.js +24 -0
  210. package/dist/esm/i3s-server/controllers/index-controller.js.map +1 -0
  211. package/dist/esm/i3s-server/controllers/slpk-controller.js +36 -0
  212. package/dist/esm/i3s-server/controllers/slpk-controller.js.map +1 -0
  213. package/dist/esm/i3s-server/routes/index.js +16 -0
  214. package/dist/esm/i3s-server/routes/index.js.map +1 -0
  215. package/dist/esm/i3s-server/routes/slpk-router.js +33 -0
  216. package/dist/esm/i3s-server/routes/slpk-router.js.map +1 -0
  217. package/dist/esm/i3s-server/utils/create-scene-server.js +16 -0
  218. package/dist/esm/i3s-server/utils/create-scene-server.js.map +1 -0
  219. package/dist/esm/index.js +3 -0
  220. package/dist/esm/index.js.map +1 -0
  221. package/dist/esm/lib/utils/compress-util.js +168 -0
  222. package/dist/esm/lib/utils/compress-util.js.map +1 -0
  223. package/dist/esm/lib/utils/file-utils.js +87 -0
  224. package/dist/esm/lib/utils/file-utils.js.map +1 -0
  225. package/dist/esm/lib/utils/geometry-utils.js +8 -0
  226. package/dist/esm/lib/utils/geometry-utils.js.map +1 -0
  227. package/dist/esm/lib/utils/lod-conversion-utils.js +37 -0
  228. package/dist/esm/lib/utils/lod-conversion-utils.js.map +1 -0
  229. package/dist/esm/lib/utils/queue.js +15 -0
  230. package/dist/esm/lib/utils/queue.js.map +1 -0
  231. package/dist/esm/lib/utils/statistic-utills.d.ts +25 -0
  232. package/dist/esm/lib/utils/statistic-utills.js +62 -0
  233. package/dist/esm/lib/utils/statistic-utills.js.map +1 -0
  234. package/dist/esm/lib/utils/write-queue.js +85 -0
  235. package/dist/esm/lib/utils/write-queue.js.map +1 -0
  236. package/dist/esm/pgm-loader.js +15 -0
  237. package/dist/esm/pgm-loader.js.map +1 -0
  238. package/dist/esm/workers/3d-tiles-attributes-worker.js +8 -0
  239. package/dist/esm/workers/3d-tiles-attributes-worker.js.map +1 -0
  240. package/dist/esm/workers/i3s-attributes-worker.js +7 -0
  241. package/dist/esm/workers/i3s-attributes-worker.js.map +1 -0
  242. package/dist/i3s-attributes-worker.d.ts +45 -0
  243. package/dist/i3s-attributes-worker.d.ts.map +1 -0
  244. package/dist/i3s-attributes-worker.js +9 -0
  245. package/dist/i3s-attributes-worker.js.map +7 -0
  246. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +13 -0
  247. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -0
  248. package/dist/i3s-converter/helpers/batch-ids-extensions.js +138 -0
  249. package/dist/i3s-converter/helpers/coordinate-converter.d.ts +40 -0
  250. package/dist/i3s-converter/helpers/coordinate-converter.d.ts.map +1 -0
  251. package/dist/i3s-converter/helpers/coordinate-converter.js +119 -42
  252. package/dist/i3s-converter/helpers/create-scene-server-path.d.ts +9 -0
  253. package/dist/i3s-converter/helpers/create-scene-server-path.d.ts.map +1 -0
  254. package/dist/i3s-converter/helpers/create-scene-server-path.js +27 -15
  255. package/dist/i3s-converter/helpers/feature-attributes.d.ts +56 -0
  256. package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -0
  257. package/dist/i3s-converter/helpers/feature-attributes.js +216 -0
  258. package/dist/i3s-converter/helpers/geometry-attributes.d.ts +8 -0
  259. package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -0
  260. package/dist/i3s-converter/helpers/geometry-attributes.js +188 -185
  261. package/dist/i3s-converter/helpers/geometry-converter.d.ts +41 -35
  262. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -0
  263. package/dist/i3s-converter/helpers/geometry-converter.js +1179 -650
  264. package/dist/i3s-converter/helpers/gltf-attributes.d.ts +28 -0
  265. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -0
  266. package/dist/i3s-converter/helpers/gltf-attributes.js +128 -0
  267. package/dist/i3s-converter/helpers/load-3d-tiles.d.ts +18 -0
  268. package/dist/i3s-converter/helpers/load-3d-tiles.d.ts.map +1 -0
  269. package/dist/i3s-converter/helpers/load-3d-tiles.js +53 -0
  270. package/dist/i3s-converter/helpers/node-debug.d.ts +8 -0
  271. package/dist/i3s-converter/helpers/node-debug.d.ts.map +1 -0
  272. package/dist/i3s-converter/helpers/node-debug.js +106 -74
  273. package/dist/i3s-converter/helpers/node-index-document.d.ts +103 -0
  274. package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -0
  275. package/dist/i3s-converter/helpers/node-index-document.js +268 -0
  276. package/dist/i3s-converter/helpers/node-pages.d.ts +125 -113
  277. package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -0
  278. package/dist/i3s-converter/helpers/node-pages.js +313 -133
  279. package/dist/i3s-converter/helpers/preprocess-3d-tiles.d.ts +23 -0
  280. package/dist/i3s-converter/helpers/preprocess-3d-tiles.d.ts.map +1 -0
  281. package/dist/i3s-converter/helpers/preprocess-3d-tiles.js +76 -0
  282. package/dist/i3s-converter/helpers/tileset-traversal.d.ts +25 -0
  283. package/dist/i3s-converter/helpers/tileset-traversal.d.ts.map +1 -0
  284. package/dist/i3s-converter/helpers/tileset-traversal.js +29 -0
  285. package/dist/i3s-converter/i3s-converter.d.ts +267 -0
  286. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -0
  287. package/dist/i3s-converter/i3s-converter.js +891 -867
  288. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts +7 -0
  289. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts.map +1 -0
  290. package/dist/i3s-converter/json-templates/geometry-definitions.js +87 -0
  291. package/dist/i3s-converter/json-templates/layers.d.ts +70 -0
  292. package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -0
  293. package/dist/i3s-converter/json-templates/layers.js +138 -190
  294. package/dist/i3s-converter/json-templates/metadata.d.ts +22 -0
  295. package/dist/i3s-converter/json-templates/metadata.d.ts.map +1 -0
  296. package/dist/i3s-converter/json-templates/metadata.js +25 -22
  297. package/dist/i3s-converter/json-templates/node.d.ts +61 -0
  298. package/dist/i3s-converter/json-templates/node.d.ts.map +1 -0
  299. package/dist/i3s-converter/json-templates/node.js +88 -79
  300. package/dist/i3s-converter/json-templates/scene-server.d.ts +28 -0
  301. package/dist/i3s-converter/json-templates/scene-server.d.ts.map +1 -0
  302. package/dist/i3s-converter/json-templates/scene-server.js +31 -28
  303. package/dist/i3s-converter/json-templates/shared-resources.d.ts +14 -0
  304. package/dist/i3s-converter/json-templates/shared-resources.d.ts.map +1 -0
  305. package/dist/i3s-converter/json-templates/shared-resources.js +124 -125
  306. package/dist/i3s-converter/json-templates/store.d.ts +95 -0
  307. package/dist/i3s-converter/json-templates/store.d.ts.map +1 -0
  308. package/dist/i3s-converter/json-templates/store.js +100 -95
  309. package/dist/i3s-converter/types.d.ts +163 -0
  310. package/dist/i3s-converter/types.d.ts.map +1 -0
  311. package/dist/i3s-converter/types.js +17 -0
  312. package/dist/i3s-server/app.d.ts +3 -0
  313. package/dist/i3s-server/app.d.ts.map +1 -0
  314. package/dist/i3s-server/app.js +11 -10
  315. package/dist/i3s-server/controllers/index-controller.d.ts +2 -0
  316. package/dist/i3s-server/controllers/index-controller.d.ts.map +1 -0
  317. package/dist/i3s-server/controllers/index-controller.js +16 -24
  318. package/dist/i3s-server/controllers/slpk-controller.d.ts +3 -0
  319. package/dist/i3s-server/controllers/slpk-controller.d.ts.map +1 -0
  320. package/dist/i3s-server/controllers/slpk-controller.js +32 -0
  321. package/dist/i3s-server/routes/index.d.ts +3 -0
  322. package/dist/i3s-server/routes/index.d.ts.map +1 -0
  323. package/dist/i3s-server/routes/index.js +11 -15
  324. package/dist/i3s-server/routes/slpk-router.d.ts +3 -0
  325. package/dist/i3s-server/routes/slpk-router.d.ts.map +1 -0
  326. package/dist/i3s-server/routes/slpk-router.js +33 -0
  327. package/dist/i3s-server/utils/create-scene-server.d.ts +11 -0
  328. package/dist/i3s-server/utils/create-scene-server.d.ts.map +1 -0
  329. package/dist/i3s-server/utils/create-scene-server.js +14 -0
  330. package/dist/index.d.ts +3 -0
  331. package/dist/index.d.ts.map +1 -0
  332. package/dist/index.js +10 -5
  333. package/dist/lib/utils/compress-util.d.ts +45 -0
  334. package/dist/lib/utils/compress-util.d.ts.map +1 -0
  335. package/dist/lib/utils/compress-util.js +238 -160
  336. package/dist/lib/utils/file-utils.d.ts +22 -14
  337. package/dist/lib/utils/file-utils.d.ts.map +1 -0
  338. package/dist/lib/utils/file-utils.js +134 -36
  339. package/dist/lib/utils/geometry-utils.d.ts +9 -0
  340. package/dist/lib/utils/geometry-utils.d.ts.map +1 -0
  341. package/dist/lib/utils/geometry-utils.js +18 -0
  342. package/dist/lib/utils/lod-conversion-utils.d.ts +23 -13
  343. package/dist/lib/utils/lod-conversion-utils.d.ts.map +1 -0
  344. package/dist/lib/utils/lod-conversion-utils.js +72 -39
  345. package/dist/lib/utils/queue.d.ts +7 -0
  346. package/dist/lib/utils/queue.d.ts.map +1 -0
  347. package/dist/lib/utils/queue.js +18 -0
  348. package/dist/lib/utils/statistic-utills.d.ts +3 -25
  349. package/dist/lib/utils/statistic-utills.d.ts.map +1 -0
  350. package/dist/lib/utils/statistic-utills.js +58 -67
  351. package/dist/lib/utils/write-queue.d.ts +39 -0
  352. package/dist/lib/utils/write-queue.d.ts.map +1 -0
  353. package/dist/lib/utils/write-queue.js +80 -0
  354. package/dist/pgm-loader.d.ts +6 -0
  355. package/dist/pgm-loader.d.ts.map +1 -0
  356. package/dist/pgm-loader.js +23 -14
  357. package/dist/workers/3d-tiles-attributes-worker.d.ts +2 -0
  358. package/dist/workers/3d-tiles-attributes-worker.d.ts.map +1 -0
  359. package/dist/workers/3d-tiles-attributes-worker.js +9 -0
  360. package/dist/workers/i3s-attributes-worker.d.ts +2 -0
  361. package/dist/workers/i3s-attributes-worker.d.ts.map +1 -0
  362. package/dist/workers/i3s-attributes-worker.js +5 -0
  363. package/package.json +34 -24
  364. package/src/3d-tiles-attributes-worker.ts +43 -0
  365. package/src/3d-tiles-converter/3d-tiles-converter.ts +131 -63
  366. package/src/3d-tiles-converter/helpers/{b3dm-converter.js → b3dm-converter.ts} +72 -53
  367. package/src/3d-tiles-converter/helpers/{i3s-obb-to-3d-tiles-obb.js → i3s-obb-to-3d-tiles-obb.ts} +16 -1
  368. package/src/3d-tiles-converter/helpers/texture-atlas.ts +4 -4
  369. package/src/3d-tiles-converter/json-templates/{tileset.js → tileset.ts} +9 -9
  370. package/src/constants.ts +2 -0
  371. package/src/converter-cli.ts +370 -0
  372. package/src/deps-installer/deps-installer.ts +72 -0
  373. package/src/i3s-attributes-worker.ts +59 -0
  374. package/src/i3s-converter/helpers/batch-ids-extensions.ts +202 -0
  375. package/src/i3s-converter/helpers/coordinate-converter.ts +94 -33
  376. package/src/i3s-converter/helpers/create-scene-server-path.ts +29 -0
  377. package/src/i3s-converter/helpers/feature-attributes.ts +247 -0
  378. package/src/i3s-converter/helpers/geometry-attributes.ts +267 -0
  379. package/src/i3s-converter/helpers/geometry-converter.ts +1667 -0
  380. package/src/i3s-converter/helpers/gltf-attributes.ts +163 -0
  381. package/src/i3s-converter/helpers/load-3d-tiles.ts +68 -0
  382. package/src/i3s-converter/helpers/node-debug.ts +146 -0
  383. package/src/i3s-converter/helpers/node-index-document.ts +335 -0
  384. package/src/i3s-converter/helpers/node-pages.ts +344 -0
  385. package/src/i3s-converter/helpers/preprocess-3d-tiles.ts +81 -0
  386. package/src/i3s-converter/helpers/tileset-traversal.ts +51 -0
  387. package/src/i3s-converter/i3s-converter.ts +650 -669
  388. package/src/i3s-converter/json-templates/geometry-definitions.ts +83 -0
  389. package/src/i3s-converter/json-templates/layers.ts +137 -0
  390. package/src/i3s-converter/json-templates/{metadata.js → metadata.ts} +2 -2
  391. package/src/i3s-converter/json-templates/{node.js → node.ts} +12 -12
  392. package/src/i3s-converter/json-templates/{scene-server.js → scene-server.ts} +2 -2
  393. package/src/i3s-converter/json-templates/{shared-resources.js → shared-resources.ts} +17 -17
  394. package/src/i3s-converter/types.ts +185 -0
  395. package/src/i3s-server/README.md +19 -0
  396. package/src/i3s-server/app.js +8 -1
  397. package/src/i3s-server/controllers/slpk-controller.js +38 -0
  398. package/src/i3s-server/routes/slpk-router.js +33 -0
  399. package/src/i3s-server/utils/create-scene-server.js +15 -0
  400. package/src/index.ts +0 -4
  401. package/src/lib/utils/{compress-util.js → compress-util.ts} +105 -18
  402. package/src/lib/utils/file-utils.ts +140 -0
  403. package/src/lib/utils/geometry-utils.ts +14 -0
  404. package/src/lib/utils/{lod-conversion-utils.js → lod-conversion-utils.ts} +31 -5
  405. package/src/lib/utils/queue.ts +17 -0
  406. package/src/lib/utils/write-queue.ts +110 -0
  407. package/src/pgm-loader.ts +3 -3
  408. package/src/workers/3d-tiles-attributes-worker.ts +6 -0
  409. package/src/workers/i3s-attributes-worker.ts +7 -0
  410. package/dist/3d-tiles-converter/3d-tiles-converter.js.map +0 -1
  411. package/dist/3d-tiles-converter/helpers/b3dm-converter.js.map +0 -1
  412. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js.map +0 -1
  413. package/dist/3d-tiles-converter/helpers/texture-atlas.js.map +0 -1
  414. package/dist/3d-tiles-converter/json-templates/tileset.js.map +0 -1
  415. package/dist/bundle.js.map +0 -1
  416. package/dist/deps-installer/deps-installer.js.map +0 -1
  417. package/dist/i3s-converter/helpers/coordinate-converter.js.map +0 -1
  418. package/dist/i3s-converter/helpers/create-scene-server-path.js.map +0 -1
  419. package/dist/i3s-converter/helpers/geometry-attributes.js.map +0 -1
  420. package/dist/i3s-converter/helpers/geometry-converter.js.map +0 -1
  421. package/dist/i3s-converter/helpers/node-debug.js.map +0 -1
  422. package/dist/i3s-converter/helpers/node-pages.js.map +0 -1
  423. package/dist/i3s-converter/i3s-converter.js.map +0 -1
  424. package/dist/i3s-converter/json-templates/layers.js.map +0 -1
  425. package/dist/i3s-converter/json-templates/metadata.js.map +0 -1
  426. package/dist/i3s-converter/json-templates/node.js.map +0 -1
  427. package/dist/i3s-converter/json-templates/scene-server.js.map +0 -1
  428. package/dist/i3s-converter/json-templates/shared-resources.js.map +0 -1
  429. package/dist/i3s-converter/json-templates/store.js.map +0 -1
  430. package/dist/i3s-server/app.js.map +0 -1
  431. package/dist/i3s-server/controllers/index-controller.js.map +0 -1
  432. package/dist/i3s-server/routes/index.js.map +0 -1
  433. package/dist/index.js.map +0 -1
  434. package/dist/lib/geoid-height-model.d.ts +0 -41
  435. package/dist/lib/geoid-height-model.js +0 -140
  436. package/dist/lib/geoid-height-model.js.map +0 -1
  437. package/dist/lib/pgm-parser.d.ts +0 -14
  438. package/dist/lib/pgm-parser.js +0 -183
  439. package/dist/lib/pgm-parser.js.map +0 -1
  440. package/dist/lib/utils/compress-util.js.map +0 -1
  441. package/dist/lib/utils/compress-utils.d.ts +0 -53
  442. package/dist/lib/utils/file-utils.js.map +0 -1
  443. package/dist/lib/utils/lod-conversion-utils.js.map +0 -1
  444. package/dist/lib/utils/statistic-utills.js.map +0 -1
  445. package/dist/pgm-loader.js.map +0 -1
  446. package/src/3d-tiles-converter/helpers/b3dm-converter.d.ts +0 -23
  447. package/src/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.d.ts +0 -16
  448. package/src/deps-installer/deps-installer.d.ts +0 -10
  449. package/src/deps-installer/deps-installer.js +0 -22
  450. package/src/i3s-converter/helpers/create-scene-server-path.js +0 -25
  451. package/src/i3s-converter/helpers/geometry-converter.d.ts +0 -40
  452. package/src/i3s-converter/helpers/geometry-converter.js +0 -915
  453. package/src/i3s-converter/helpers/node-pages.d.ts +0 -144
  454. package/src/i3s-converter/helpers/node-pages.js +0 -208
  455. package/src/i3s-converter/json-templates/layers.js +0 -199
  456. package/src/lib/geoid-height-model.d.ts +0 -41
  457. package/src/lib/geoid-height-model.js +0 -239
  458. package/src/lib/pgm-parser.d.ts +0 -14
  459. package/src/lib/pgm-parser.js +0 -179
  460. package/src/lib/utils/compress-utils.d.ts +0 -53
  461. package/src/lib/utils/file-utils.d.ts +0 -43
  462. package/src/lib/utils/file-utils.js +0 -38
  463. package/src/lib/utils/lod-conversion-utils.d.ts +0 -32
  464. /package/dist/{i3s-server → es5/i3s-server}/bin/www +0 -0
  465. /package/dist/{i3s-server → es5/i3s-server}/certs/cert.pem +0 -0
  466. /package/dist/{i3s-server → es5/i3s-server}/certs/key.pem +0 -0
  467. /package/src/i3s-converter/json-templates/{store.js → store.ts} +0 -0
  468. /package/src/lib/utils/{statistic-utills.js → statistic-utills.ts} +0 -0
@@ -0,0 +1,1667 @@
1
+ import type {FeatureTableJson, Tiles3DTileContent} from '@loaders.gl/3d-tiles';
2
+ import type {
3
+ GLTF_EXT_feature_metadata,
4
+ GLTF_EXT_mesh_features,
5
+ GLTFAccessorPostprocessed,
6
+ GLTFMaterialPostprocessed,
7
+ GLTFNodePostprocessed,
8
+ GLTFMeshPrimitivePostprocessed,
9
+ GLTFMeshPostprocessed,
10
+ GLTFTexturePostprocessed
11
+ } from '@loaders.gl/gltf';
12
+
13
+ import {Vector3, Matrix4, Vector4} from '@math.gl/core';
14
+ import {Ellipsoid} from '@math.gl/geospatial';
15
+
16
+ import {DracoWriterWorker} from '@loaders.gl/draco';
17
+ import {assert, encode} from '@loaders.gl/core';
18
+ import {concatenateArrayBuffers, concatenateTypedArrays} from '@loaders.gl/loader-utils';
19
+ import md5 from 'md5';
20
+ import {v4 as uuidv4} from 'uuid';
21
+ import {generateAttributes} from './geometry-attributes';
22
+ import {createBoundingVolumesFromGeometry} from './coordinate-converter';
23
+ import {
24
+ ConvertedAttributes,
25
+ I3SConvertedResources,
26
+ I3SMaterialWithTexture,
27
+ MergedMaterial,
28
+ SharedResourcesArrays
29
+ } from '../types';
30
+ import {
31
+ AttributeStorageInfo,
32
+ I3SMaterialDefinition,
33
+ MaterialDefinitionInfo,
34
+ TextureDefinitionInfo
35
+ } from '@loaders.gl/i3s';
36
+ import {NumberArray, TypedArray} from '@loaders.gl/loader-utils';
37
+ import {Geoid} from '@math.gl/geoid';
38
+ /** Usage of worker here brings more overhead than advantage */
39
+ import {
40
+ B3DMAttributesData /*, transformI3SAttributesOnWorker*/,
41
+ TextureImageProperties
42
+ } from '../../i3s-attributes-worker';
43
+ import {prepareDataForAttributesConversion} from './gltf-attributes';
44
+ import {handleBatchIdsExtensions} from './batch-ids-extensions';
45
+ import {checkPropertiesLength, flattenPropertyTableByFeatureIds} from './feature-attributes';
46
+ import {GL} from '@loaders.gl/math';
47
+
48
+ /*
49
+ At the moment of writing the type TypedArrayConstructor is not exported in '@math.gl/types'.
50
+ So the following import is replaced with the local import
51
+ import type {TypedArrayConstructor} from '@math.gl/types';
52
+ */
53
+ import type {TypedArrayConstructor} from '../types';
54
+ import {generateSyntheticIndices} from '../../lib/utils/geometry-utils';
55
+ import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling';
56
+
57
+ // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
58
+ const DEFAULT_ROUGHNESS_FACTOR = 1;
59
+ const DEFAULT_METALLIC_FACTOR = 1;
60
+
61
+ const VALUES_PER_VERTEX = 3;
62
+ const VALUES_PER_TEX_COORD = 2;
63
+ const VALUES_PER_COLOR_ELEMENT = 4;
64
+
65
+ const STRING_TYPE = 'string';
66
+ const SHORT_INT_TYPE = 'Int32';
67
+ const DOUBLE_TYPE = 'Float64';
68
+ const OBJECT_ID_TYPE = 'Oid32';
69
+ /*
70
+ * 'CUSTOM_ATTRIBUTE_2' - Attribute name which includes batch info and used by New York map.
71
+ * _BATCHID - Default attribute name which includes batch info.
72
+ * BATCHID - Legacy attribute name which includes batch info.
73
+ */
74
+ const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID'];
75
+
76
+ const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
77
+ const EXT_MESH_FEATURES = 'EXT_mesh_features';
78
+
79
+ let scratchVector = new Vector3();
80
+
81
+ /**
82
+ * Convert binary data from b3dm file to i3s resources
83
+ *
84
+ * @param tileContent - 3d tile content
85
+ * @param tileTransform - transformation matrix of the tile, calculated recursively multiplying
86
+ * transform of all parent tiles and transform of the current tile
87
+ * @param tileBoundingVolume - initialized bounding volume of the source tile
88
+ * @param addNodeToNodePage - function to add new node to node pages
89
+ * @param propertyTable - batch table (corresponding to feature attributes data)
90
+ * @param featuresHashArray - hash array of features that is needed to not to mix up same features in parent and child nodes
91
+ * @param attributeStorageInfo - attributes metadata from 3DSceneLayer json
92
+ * @param draco - is converter should create draco compressed geometry
93
+ * @param generateBoundingVolumes - is converter should create accurate bounding voulmes from geometry attributes
94
+ * @param shouldMergeMaterials - Try to merge similar materials to be able to merge meshes into one node
95
+ * @param geoidHeightModel - model to convert elevation from elipsoidal to geoid
96
+ * @param workerSource - source code of used workers
97
+ * @returns Array of node resources to create one or more i3s nodes
98
+ */
99
+ export default async function convertB3dmToI3sGeometry(
100
+ tileContent: Tiles3DTileContent,
101
+ tileTransform: Matrix4,
102
+ tileBoundingVolume: OrientedBoundingBox | BoundingSphere,
103
+ addNodeToNodePage: () => Promise<number>,
104
+ propertyTable: FeatureTableJson | null,
105
+ featuresHashArray: string[],
106
+ attributeStorageInfo: AttributeStorageInfo[] | undefined,
107
+ draco: boolean,
108
+ generateBoundingVolumes: boolean,
109
+ shouldMergeMaterials: boolean,
110
+ geoidHeightModel: Geoid,
111
+ workerSource: {[key: string]: string}
112
+ ): Promise<I3SConvertedResources[] | null> {
113
+ const useCartesianPositions = generateBoundingVolumes;
114
+ const materialAndTextureList: I3SMaterialWithTexture[] = await convertMaterials(
115
+ tileContent.gltf?.materials,
116
+ shouldMergeMaterials
117
+ );
118
+
119
+ const dataForAttributesConversion = prepareDataForAttributesConversion(
120
+ tileContent,
121
+ tileTransform,
122
+ tileBoundingVolume
123
+ );
124
+ const convertedAttributesMap: Map<string, ConvertedAttributes> = await convertAttributes(
125
+ dataForAttributesConversion,
126
+ materialAndTextureList,
127
+ useCartesianPositions
128
+ );
129
+ /** Usage of worker here brings more overhead than advantage */
130
+ // const convertedAttributesMap: Map<string, ConvertedAttributes> =
131
+ // await transformI3SAttributesOnWorker(dataForAttributesConversion, {
132
+ // reuseWorkers: true,
133
+ // _nodeWorkers: true,
134
+ // useCartesianPositions,
135
+ // source: workerSource.I3SAttributes
136
+ // });
137
+
138
+ if (generateBoundingVolumes) {
139
+ _generateBoundingVolumesFromGeometry(convertedAttributesMap, geoidHeightModel);
140
+ }
141
+
142
+ const result: I3SConvertedResources[] = [];
143
+ for (const materialAndTexture of materialAndTextureList) {
144
+ const originarMaterialId = materialAndTexture.mergedMaterials[0].originalMaterialId;
145
+ if (!convertedAttributesMap.has(originarMaterialId)) {
146
+ continue; // eslint-disable-line no-continue
147
+ }
148
+ const convertedAttributes = convertedAttributesMap.get(originarMaterialId);
149
+ if (!convertedAttributes) {
150
+ continue;
151
+ }
152
+ const {material, texture} = materialAndTexture;
153
+ const nodeId = await addNodeToNodePage();
154
+ result.push(
155
+ await _makeNodeResources({
156
+ convertedAttributes,
157
+ material,
158
+ texture,
159
+ tileContent,
160
+ nodeId,
161
+ featuresHashArray,
162
+ propertyTable,
163
+ attributeStorageInfo,
164
+ draco,
165
+ workerSource
166
+ })
167
+ );
168
+ }
169
+
170
+ if (!result.length) {
171
+ return null;
172
+ }
173
+ return result;
174
+ }
175
+
176
+ /**
177
+ * Create bounding volumes based on positions
178
+ * @param convertedAttributesMap - geometry attributes map
179
+ * @param geoidHeightModel - geoid height model to convert elevation from elipsoidal to geoid
180
+ */
181
+ function _generateBoundingVolumesFromGeometry(
182
+ convertedAttributesMap: Map<string, ConvertedAttributes>,
183
+ geoidHeightModel: Geoid
184
+ ) {
185
+ for (const attributes of convertedAttributesMap.values()) {
186
+ const boundingVolumes = createBoundingVolumesFromGeometry(
187
+ attributes.positions,
188
+ geoidHeightModel
189
+ );
190
+
191
+ attributes.boundingVolumes = boundingVolumes;
192
+ const cartographicOrigin = boundingVolumes.obb.center;
193
+
194
+ for (let index = 0; index < attributes.positions.length; index += VALUES_PER_VERTEX) {
195
+ const vertex = attributes.positions.subarray(index, index + VALUES_PER_VERTEX);
196
+ Ellipsoid.WGS84.cartesianToCartographic(Array.from(vertex), scratchVector);
197
+ scratchVector[2] =
198
+ scratchVector[2] - geoidHeightModel.getHeight(scratchVector[1], scratchVector[0]);
199
+ scratchVector = scratchVector.subtract(cartographicOrigin);
200
+ attributes.positions.set(scratchVector, index);
201
+ }
202
+ }
203
+ }
204
+
205
+ /**
206
+ *
207
+ * @param params
208
+ * @param params.convertedAttributes - Converted geometry attributes
209
+ * @param params.material - I3S PBR-like material definition
210
+ * @param params.texture - texture content
211
+ * @param params.tileContent - 3DTiles decoded content
212
+ * @param params.nodeId - new node ID
213
+ * @param params.featuresHashArray - hash array of features that is needed to not to mix up same features in parent and child nodes
214
+ * @param params.propertyTable - batch table (corresponding to feature attributes data)
215
+ * @param params.attributeStorageInfo - attributes metadata from 3DSceneLayer json
216
+ * @param params.draco - is converter should create draco compressed geometry
217
+ * @param params.workerSource - source code of used workers
218
+ * @returns Array of I3S node resources
219
+ */
220
+ async function _makeNodeResources({
221
+ convertedAttributes,
222
+ material,
223
+ texture,
224
+ tileContent,
225
+ nodeId,
226
+ featuresHashArray,
227
+ propertyTable,
228
+ attributeStorageInfo,
229
+ draco,
230
+ workerSource
231
+ }: {
232
+ convertedAttributes: ConvertedAttributes;
233
+ material: I3SMaterialDefinition;
234
+ texture?: {};
235
+ tileContent: Tiles3DTileContent;
236
+ nodeId: number;
237
+ featuresHashArray: string[];
238
+ propertyTable: FeatureTableJson | null;
239
+ attributeStorageInfo?: AttributeStorageInfo[];
240
+ draco: boolean;
241
+ workerSource: {[key: string]: string};
242
+ }): Promise<I3SConvertedResources> {
243
+ const boundingVolumes = convertedAttributes.boundingVolumes;
244
+ const vertexCount = convertedAttributes.positions.length / VALUES_PER_VERTEX;
245
+ const {faceRange, featureIds, positions, normals, colors, uvRegions, texCoords, featureCount} =
246
+ generateAttributes(convertedAttributes);
247
+
248
+ if (tileContent.batchTableJson) {
249
+ makeFeatureIdsUnique(
250
+ featureIds,
251
+ convertedAttributes.featureIndices,
252
+ featuresHashArray,
253
+ tileContent.batchTableJson
254
+ );
255
+ }
256
+
257
+ const header = new Uint32Array(2);
258
+ const typedFeatureIds = generateBigUint64Array(featureIds);
259
+
260
+ header.set([vertexCount, featureCount], 0);
261
+ const fileBuffer = new Uint8Array(
262
+ concatenateArrayBuffers(
263
+ header.buffer,
264
+ positions.buffer,
265
+ normals.buffer,
266
+ texture ? texCoords.buffer : new ArrayBuffer(0),
267
+ colors.buffer,
268
+ uvRegions,
269
+ typedFeatureIds.buffer,
270
+ faceRange.buffer
271
+ )
272
+ );
273
+ const compressedGeometry = draco
274
+ ? generateCompressedGeometry(
275
+ vertexCount,
276
+ convertedAttributes,
277
+ {
278
+ positions,
279
+ normals,
280
+ texCoords: texture ? texCoords : new Float32Array(0),
281
+ colors,
282
+ uvRegions,
283
+ featureIds,
284
+ faceRange
285
+ },
286
+ workerSource.draco
287
+ )
288
+ : null;
289
+
290
+ let attributes: ArrayBuffer[] = [];
291
+
292
+ if (attributeStorageInfo && propertyTable) {
293
+ attributes = convertPropertyTableToAttributeBuffers(
294
+ featureIds,
295
+ propertyTable,
296
+ attributeStorageInfo
297
+ );
298
+ }
299
+
300
+ return {
301
+ nodeId,
302
+ geometry: fileBuffer,
303
+ compressedGeometry,
304
+ texture,
305
+ hasUvRegions: Boolean(uvRegions.length),
306
+ sharedResources: getSharedResources(tileContent.gltf?.materials || [], nodeId),
307
+ meshMaterial: material,
308
+ vertexCount,
309
+ attributes,
310
+ featureCount,
311
+ boundingVolumes
312
+ };
313
+ }
314
+
315
+ /**
316
+ * Convert attributes from the gltf nodes tree to i3s plain geometry
317
+ * @param attributesData - geometry attributes from gltf
318
+ * @param materialAndTextureList - array of data about materials and textures of the content
319
+ * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
320
+ * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
321
+ * @returns map of converted geometry attributes
322
+ */
323
+ export async function convertAttributes(
324
+ attributesData: B3DMAttributesData,
325
+ materialAndTextureList: I3SMaterialWithTexture[],
326
+ useCartesianPositions: boolean
327
+ ): Promise<Map<string, ConvertedAttributes>> {
328
+ const {nodes, images, cartographicOrigin, cartesianModelMatrix} = attributesData;
329
+ const attributesMap = new Map<string, ConvertedAttributes>();
330
+
331
+ for (const materialAndTexture of materialAndTextureList) {
332
+ const attributes = {
333
+ positions: new Float32Array(0),
334
+ normals: new Float32Array(0),
335
+ texCoords: new Float32Array(0),
336
+ colors: new Uint8Array(0),
337
+ uvRegions: new Uint16Array(0),
338
+ featureIndicesGroups: [],
339
+ featureIndices: [],
340
+ boundingVolumes: null,
341
+ mergedMaterials: materialAndTexture.mergedMaterials
342
+ };
343
+ for (const mergedMaterial of materialAndTexture.mergedMaterials) {
344
+ attributesMap.set(mergedMaterial.originalMaterialId, attributes);
345
+ }
346
+ }
347
+
348
+ convertNodes(
349
+ nodes,
350
+ images,
351
+ cartographicOrigin,
352
+ cartesianModelMatrix,
353
+ attributesMap,
354
+ useCartesianPositions
355
+ );
356
+
357
+ for (const attrKey of attributesMap.keys()) {
358
+ const attributes = attributesMap.get(attrKey);
359
+ if (!attributes) {
360
+ continue;
361
+ }
362
+ if (attributes.positions.length === 0) {
363
+ attributesMap.delete(attrKey);
364
+ continue; // eslint-disable-line no-continue
365
+ }
366
+ if (attributes.featureIndicesGroups) {
367
+ attributes.featureIndices = attributes.featureIndicesGroups.reduce((acc, value) =>
368
+ acc.concat(value)
369
+ );
370
+ delete attributes.featureIndicesGroups;
371
+ }
372
+ }
373
+
374
+ return attributesMap;
375
+ }
376
+
377
+ /**
378
+ * Gltf has hierarchical structure of nodes. This function converts nodes starting from those which are in gltf scene object.
379
+ * The goal is applying tranformation matrix for all children. Functions "convertNodes" and "convertNode" work together recursively.
380
+ * @param nodes - gltf nodes array
381
+ * @param images - gltf images array
382
+ * @param cartographicOrigin - cartographic origin of bounding volume
383
+ * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
384
+ * @param attributesMap - for recursive concatenation of attributes
385
+ * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
386
+ * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
387
+ * @param matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
388
+ * @returns {void}
389
+ */
390
+ function convertNodes(
391
+ nodes: GLTFNodePostprocessed[],
392
+ images: (TextureImageProperties | null)[],
393
+ cartographicOrigin: Vector3,
394
+ cartesianModelMatrix: Matrix4,
395
+ attributesMap: Map<string, ConvertedAttributes>,
396
+ useCartesianPositions: boolean,
397
+ matrix: Matrix4 = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
398
+ ) {
399
+ if (nodes) {
400
+ for (const node of nodes) {
401
+ convertNode(
402
+ node,
403
+ images,
404
+ cartographicOrigin,
405
+ cartesianModelMatrix,
406
+ attributesMap,
407
+ useCartesianPositions,
408
+ matrix
409
+ );
410
+ }
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Generate transformation matrix for node
416
+ * Aapply all gltf transformations to initial transformation matrix.
417
+ * @param node
418
+ * @param matrix
419
+ */
420
+ function getCompositeTransformationMatrix(node: GLTFNodePostprocessed, matrix: Matrix4) {
421
+ let transformationMatrix = matrix;
422
+
423
+ const {matrix: nodeMatrix, rotation, scale, translation} = node;
424
+
425
+ if (nodeMatrix) {
426
+ transformationMatrix = matrix.multiplyRight(nodeMatrix);
427
+ }
428
+
429
+ if (translation) {
430
+ transformationMatrix = transformationMatrix.translate(translation);
431
+ }
432
+
433
+ if (rotation) {
434
+ transformationMatrix = transformationMatrix.rotateXYZ(rotation);
435
+ }
436
+
437
+ if (scale) {
438
+ transformationMatrix = transformationMatrix.scale(scale);
439
+ }
440
+
441
+ return transformationMatrix;
442
+ }
443
+
444
+ /**
445
+ * Convert all primitives of node and all children nodes
446
+ * @param node - gltf node
447
+ * @param images - gltf images array
448
+ * @param cartographicOrigin - cartographic origin of bounding volume
449
+ * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
450
+ * @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
451
+ * attributes
452
+ * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
453
+ * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
454
+ * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
455
+ */
456
+ function convertNode(
457
+ node: GLTFNodePostprocessed,
458
+ images: (TextureImageProperties | null)[],
459
+ cartographicOrigin: Vector3,
460
+ cartesianModelMatrix: Matrix4,
461
+ attributesMap: Map<string, ConvertedAttributes>,
462
+ useCartesianPositions,
463
+ matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
464
+ ) {
465
+ const transformationMatrix = getCompositeTransformationMatrix(node, matrix);
466
+
467
+ const mesh = node.mesh;
468
+
469
+ if (mesh) {
470
+ convertMesh(
471
+ mesh,
472
+ images,
473
+ cartographicOrigin,
474
+ cartesianModelMatrix,
475
+ attributesMap,
476
+ useCartesianPositions,
477
+ transformationMatrix
478
+ );
479
+ }
480
+
481
+ convertNodes(
482
+ node.children || [],
483
+ images,
484
+ cartographicOrigin,
485
+ cartesianModelMatrix,
486
+ attributesMap,
487
+ useCartesianPositions,
488
+ transformationMatrix
489
+ );
490
+ }
491
+
492
+ /**
493
+ * Convert all primitives of the mesh
494
+ * @param mesh - gltf mesh data
495
+ * @param images - gltf images array
496
+ * @param cartographicOrigin - cartographic origin of bounding volume
497
+ * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
498
+ * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
499
+ * attributes
500
+ * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
501
+ * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
502
+ * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
503
+ * attributes
504
+
505
+ * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
506
+ */
507
+ function convertMesh(
508
+ mesh: GLTFMeshPostprocessed,
509
+ images: (TextureImageProperties | null)[],
510
+ cartographicOrigin: Vector3,
511
+ cartesianModelMatrix: Matrix4,
512
+ attributesMap: Map<string, ConvertedAttributes>,
513
+ useCartesianPositions = false,
514
+ matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
515
+ ) {
516
+ for (const primitive of mesh.primitives) {
517
+ let outputAttributes: ConvertedAttributes | null | undefined = null;
518
+ let materialUvRegion: Uint16Array | undefined;
519
+ if (primitive.material) {
520
+ outputAttributes = attributesMap.get(primitive.material.id);
521
+ materialUvRegion = outputAttributes?.mergedMaterials.find(
522
+ ({originalMaterialId}) => originalMaterialId === primitive.material?.id
523
+ )?.uvRegion;
524
+ } else if (attributesMap.has('default')) {
525
+ outputAttributes = attributesMap.get('default');
526
+ }
527
+ assert(outputAttributes !== null, 'Primitive - material mapping failed');
528
+ // Per the spec https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode
529
+ // GL.TRIANGLES is default. So in case `mode` is `undefined`, it is 'TRIANGLES'
530
+ assert(
531
+ primitive.mode === undefined ||
532
+ primitive.mode === GL.TRIANGLES ||
533
+ primitive.mode === GL.TRIANGLE_STRIP,
534
+ `Primitive - unsupported mode ${primitive.mode}`
535
+ );
536
+ const attributes = primitive.attributes;
537
+ if (!outputAttributes) {
538
+ continue;
539
+ }
540
+
541
+ const indices = normalizeIndices(primitive);
542
+ outputAttributes.positions = concatenateTypedArrays(
543
+ outputAttributes.positions,
544
+ transformVertexArray({
545
+ vertices: attributes.POSITION.value,
546
+ cartographicOrigin,
547
+ cartesianModelMatrix,
548
+ nodeMatrix: matrix,
549
+ indices,
550
+ attributeSpecificTransformation: transformVertexPositions,
551
+ useCartesianPositions
552
+ })
553
+ );
554
+ outputAttributes.normals = concatenateTypedArrays(
555
+ outputAttributes.normals,
556
+ transformVertexArray({
557
+ vertices: attributes.NORMAL && attributes.NORMAL.value,
558
+ cartographicOrigin,
559
+ cartesianModelMatrix,
560
+ nodeMatrix: matrix,
561
+ indices,
562
+ attributeSpecificTransformation: transformVertexNormals,
563
+ useCartesianPositions: false
564
+ })
565
+ );
566
+ outputAttributes.texCoords = concatenateTypedArrays(
567
+ outputAttributes.texCoords,
568
+ flattenTexCoords(attributes.TEXCOORD_0 && attributes.TEXCOORD_0.value, indices)
569
+ );
570
+
571
+ outputAttributes.colors = concatenateTypedArrays(
572
+ outputAttributes.colors,
573
+ flattenColors(attributes.COLOR_0, indices)
574
+ );
575
+
576
+ if (materialUvRegion) {
577
+ outputAttributes.uvRegions = concatenateTypedArrays(
578
+ outputAttributes.uvRegions,
579
+ createUvRegion(materialUvRegion, indices)
580
+ );
581
+ }
582
+
583
+ outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || [];
584
+ outputAttributes.featureIndicesGroups.push(
585
+ flattenBatchIds(getBatchIds(attributes, primitive, images), indices)
586
+ );
587
+ }
588
+ }
589
+ /**
590
+ * Converts TRIANGLE-STRIPS to independent TRIANGLES
591
+ * @param primitive - the primitive to get the indices from
592
+ * @returns indices of vertices of the independent triangles
593
+ */
594
+ function normalizeIndices(primitive: GLTFMeshPrimitivePostprocessed): TypedArray {
595
+ let indices: TypedArray | undefined = primitive.indices?.value;
596
+ if (!indices) {
597
+ const positions = primitive.attributes.POSITION.value;
598
+ return generateSyntheticIndices(positions.length / VALUES_PER_VERTEX);
599
+ }
600
+
601
+ if (indices && primitive.mode === GL.TRIANGLE_STRIP) {
602
+ /*
603
+ TRIANGLE_STRIP geometry contains n+2 vertices for n triangles;
604
+ TRIANGLE geometry contains n*3 vertices for n triangles.
605
+ The conversion from TRIANGLE_STRIP to TRIANGLE implies duplicating adjacent vertices.
606
+ */
607
+ const TypedArrayConstructor = indices.constructor as TypedArrayConstructor;
608
+ const newIndices = new TypedArrayConstructor((indices.length - 2) * 3);
609
+
610
+ // Copy the first triangle indices with no modification like [i0, i1, i2, ...] -> [i0, i1, i2, ...]
611
+ let triangleIndex = 0;
612
+ let currentTriangle = indices.slice(0, 3);
613
+ newIndices.set(currentTriangle, 0);
614
+
615
+ // The rest triangle indices are being taken from strips using the following logic:
616
+ // [i1, i2, i3, i4, i5, i6, ...] -> [i3, i2, i1, i2, i3, i4, i5, i4, i3, i4, i5, i6, ...]
617
+ for (let i = 1; i + 2 < indices.length; i++) {
618
+ triangleIndex += 3;
619
+ currentTriangle = indices.slice(i, i + 3);
620
+ if (i % 2 === 0) {
621
+ newIndices.set(currentTriangle, triangleIndex);
622
+ } else {
623
+ // The following "reverce" is necessary to calculate normals correctly
624
+ newIndices.set(currentTriangle.reverse(), triangleIndex);
625
+ }
626
+ }
627
+ indices = newIndices;
628
+ }
629
+ return indices as TypedArray;
630
+ }
631
+
632
+ /**
633
+ * Convert vertices attributes (POSITIONS or NORMALS) to i3s compatible format
634
+ * @param args
635
+ * @param args.vertices - gltf primitive POSITION or NORMAL attribute
636
+ * @param args.cartographicOrigin - cartographic origin coordinates
637
+ * @param args.cartesianModelMatrix - a cartesian model matrix to transform coordnates from cartesian to cartographic format
638
+ * @param args.nodeMatrix - a gltf node transformation matrix - cumulative transformation matrix formed from all parent node matrices
639
+ * @param args.indices - gltf primitive indices
640
+ * @param args.attributeSpecificTransformation - function to do attribute - specific transformations
641
+ * @param args.useCartesianPositions - use coordinates as it is.
642
+ * @returns
643
+ */
644
+ function transformVertexArray(args: {
645
+ vertices: TypedArray;
646
+ cartographicOrigin: number[];
647
+ cartesianModelMatrix: number[];
648
+ nodeMatrix: Matrix4;
649
+ indices: TypedArray;
650
+ attributeSpecificTransformation: Function;
651
+ useCartesianPositions: boolean;
652
+ }): Float32Array {
653
+ const {vertices, indices, attributeSpecificTransformation} = args;
654
+ const newVertices = new Float32Array(indices.length * VALUES_PER_VERTEX);
655
+ if (!vertices) {
656
+ return newVertices;
657
+ }
658
+ for (let i = 0; i < indices.length; i++) {
659
+ const coordIndex = indices[i] * VALUES_PER_VERTEX;
660
+ const vertex = vertices.subarray(coordIndex, coordIndex + VALUES_PER_VERTEX);
661
+ let vertexVector = new Vector3(Array.from(vertex));
662
+
663
+ vertexVector = attributeSpecificTransformation(vertexVector, args);
664
+
665
+ newVertices[i * VALUES_PER_VERTEX] = vertexVector.x;
666
+ newVertices[i * VALUES_PER_VERTEX + 1] = vertexVector.y;
667
+ newVertices[i * VALUES_PER_VERTEX + 2] = vertexVector.z;
668
+ }
669
+ return newVertices;
670
+ }
671
+
672
+ /**
673
+ * Trasform positions vector with the attribute specific transformations
674
+ * @param vertexVector - source positions vector to transform
675
+ * @param calleeArgs
676
+ * @param calleeArgs.cartesianModelMatrix - a cartesian model matrix to transform coordnates from cartesian to cartographic format
677
+ * @param calleeArgs.cartographicOrigin - cartographic origin coordinates
678
+ * @param calleeArgs.nodeMatrix - a gltf node transformation matrix - cumulative transformation matrix formed from all parent node matrices
679
+ * @param calleeArgs.useCartesianPositions - use coordinates as it is.
680
+ * @returns transformed positions vector
681
+ */
682
+ function transformVertexPositions(vertexVector, calleeArgs): number[] {
683
+ const {cartesianModelMatrix, cartographicOrigin, nodeMatrix, useCartesianPositions} = calleeArgs;
684
+
685
+ if (nodeMatrix) {
686
+ vertexVector = vertexVector.transform(nodeMatrix);
687
+ }
688
+
689
+ vertexVector = vertexVector.transform(cartesianModelMatrix);
690
+
691
+ if (useCartesianPositions) {
692
+ return vertexVector;
693
+ }
694
+
695
+ Ellipsoid.WGS84.cartesianToCartographic(
696
+ [vertexVector[0], vertexVector[1], vertexVector[2]],
697
+ vertexVector
698
+ );
699
+ vertexVector = vertexVector.subtract(cartographicOrigin);
700
+ return vertexVector;
701
+ }
702
+
703
+ /**
704
+ * Trasform normals vector with the attribute specific transformations
705
+ * @param vertexVector - source normals vector to transform
706
+ * @param calleeArgs
707
+ * @param calleeArgs.cartesianModelMatrix - a cartesian model matrix to transform coordnates from cartesian to cartographic format
708
+ * @param calleeArgs.nodeMatrix - a gltf node transformation matrix - cumulative transformation matrix formed from all parent node matrices
709
+ * @returns transformed normals vector
710
+ */
711
+ function transformVertexNormals(vertexVector, calleeArgs): number[] {
712
+ const {cartesianModelMatrix, nodeMatrix} = calleeArgs;
713
+
714
+ if (nodeMatrix) {
715
+ vertexVector = vertexVector.transformAsVector(nodeMatrix);
716
+ }
717
+
718
+ vertexVector = vertexVector.transformAsVector(cartesianModelMatrix);
719
+ return vertexVector;
720
+ }
721
+
722
+ /**
723
+ * Convert uv0 (texture coordinates) from coords based on indices to plain arrays, compatible with i3s
724
+ * @param texCoords - gltf primitive TEXCOORD_0 attribute
725
+ * @param indices - gltf primitive indices
726
+ * @returns flattened texture coordinates
727
+ */
728
+ function flattenTexCoords(texCoords: TypedArray, indices: TypedArray): Float32Array {
729
+ const newTexCoords = new Float32Array(indices.length * VALUES_PER_TEX_COORD);
730
+ if (!texCoords) {
731
+ // We need dummy UV0s because it is required in 1.6
732
+ // https://github.com/Esri/i3s-spec/blob/master/docs/1.6/vertexAttribute.cmn.md
733
+ newTexCoords.fill(1);
734
+ return newTexCoords;
735
+ }
736
+ for (let i = 0; i < indices.length; i++) {
737
+ const coordIndex = indices[i] * VALUES_PER_TEX_COORD;
738
+ const texCoord = texCoords.subarray(coordIndex, coordIndex + VALUES_PER_TEX_COORD);
739
+ newTexCoords[i * VALUES_PER_TEX_COORD] = texCoord[0];
740
+ newTexCoords[i * VALUES_PER_TEX_COORD + 1] = texCoord[1];
741
+ }
742
+ return newTexCoords;
743
+ }
744
+
745
+ /**
746
+ * Convert color from COLOR_0 based on indices to plain arrays, compatible with i3s
747
+ * @param colorsAttribute - gltf primitive COLOR_0 attribute
748
+ * @param indices - gltf primitive indices
749
+ * @returns flattened colors attribute
750
+ */
751
+ function flattenColors(
752
+ colorsAttribute: GLTFAccessorPostprocessed,
753
+ indices: TypedArray
754
+ ): Uint8Array {
755
+ const components = colorsAttribute?.components || VALUES_PER_COLOR_ELEMENT;
756
+ const newColors = new Uint8Array(indices.length * components);
757
+ if (!colorsAttribute) {
758
+ // Vertex color multiplies by material color so it must be normalized 1 by default
759
+ newColors.fill(255);
760
+ return newColors;
761
+ }
762
+ const colors = colorsAttribute.value;
763
+ for (let i = 0; i < indices.length; i++) {
764
+ const colorIndex = indices[i] * components;
765
+ const color = colors.subarray(colorIndex, colorIndex + components);
766
+ const colorUint8 = new Uint8Array(components);
767
+ for (let j = 0; j < color.length; j++) {
768
+ colorUint8[j] = color[j] * 255;
769
+ }
770
+ newColors.set(colorUint8, i * components);
771
+ }
772
+ return newColors;
773
+ }
774
+
775
+ /**
776
+ * Create per-vertex uv-region array
777
+ * @param materialUvRegion - uv-region fragment for a single vertex
778
+ * @param indices - geometry indices data
779
+ * @returns - uv-region array
780
+ */
781
+ function createUvRegion(materialUvRegion: Uint16Array, indices: TypedArray): Uint16Array {
782
+ const result = new Uint16Array(indices.length * 4);
783
+ for (let i = 0; i < result.length; i += 4) {
784
+ result.set(materialUvRegion, i);
785
+ }
786
+ return result;
787
+ }
788
+
789
+ /**
790
+ * Flatten batchedIds list based on indices to right ordered array, compatible with i3s
791
+ * @param batchedIds - gltf primitive
792
+ * @param indices - gltf primitive indices
793
+ * @returns flattened batch ids
794
+ */
795
+ function flattenBatchIds(batchedIds: NumberArray, indices: TypedArray): number[] {
796
+ if (!batchedIds.length || !indices.length) {
797
+ return [];
798
+ }
799
+ const newBatchIds: number[] = [];
800
+ for (let i = 0; i < indices.length; i++) {
801
+ const coordIndex = indices[i];
802
+ newBatchIds.push(batchedIds[coordIndex]);
803
+ }
804
+ return newBatchIds;
805
+ }
806
+
807
+ /**
808
+ * Get batchIds for featureIds creation
809
+ * @param attributes - gltf accessors
810
+ * @param primitive - gltf primitive data
811
+ * @param images - gltf texture images
812
+ */
813
+ function getBatchIds(
814
+ attributes: {
815
+ [key: string]: GLTFAccessorPostprocessed;
816
+ },
817
+ primitive: GLTFMeshPrimitivePostprocessed,
818
+ images: (TextureImageProperties | null)[]
819
+ ): NumberArray {
820
+ const batchIds: NumberArray = handleBatchIdsExtensions(attributes, primitive, images);
821
+
822
+ if (batchIds.length) {
823
+ return batchIds;
824
+ }
825
+
826
+ for (let index = 0; index < BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES.length; index++) {
827
+ const possibleBatchIdAttributeName = BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES[index];
828
+ if (
829
+ attributes[possibleBatchIdAttributeName] &&
830
+ attributes[possibleBatchIdAttributeName].value
831
+ ) {
832
+ return attributes[possibleBatchIdAttributeName].value;
833
+ }
834
+ }
835
+
836
+ return [];
837
+ }
838
+
839
+ /**
840
+ * Convert GLTF material to I3S material definitions and textures
841
+ * @param sourceMaterials Source GLTF materials
842
+ * @param shouldMergeMaterials - if true - the converter will try to merge similar materials
843
+ * to be able to merge primitives having those materials
844
+ * @returns Array of Couples I3SMaterialDefinition + texture content
845
+ */
846
+ async function convertMaterials(
847
+ sourceMaterials: GLTFMaterialPostprocessed[] = [],
848
+ shouldMergeMaterials: boolean
849
+ ): Promise<I3SMaterialWithTexture[]> {
850
+ let materials: I3SMaterialWithTexture[] = [];
851
+ for (const sourceMaterial of sourceMaterials) {
852
+ materials.push(convertMaterial(sourceMaterial));
853
+ }
854
+
855
+ if (shouldMergeMaterials) {
856
+ materials = await mergeAllMaterials(materials);
857
+ }
858
+
859
+ return materials;
860
+ }
861
+
862
+ /**
863
+ * Merge materials when possible
864
+ * @param materials materials array
865
+ * @returns merged materials array
866
+ */
867
+ async function mergeAllMaterials(
868
+ materials: I3SMaterialWithTexture[]
869
+ ): Promise<I3SMaterialWithTexture[]> {
870
+ const result: I3SMaterialWithTexture[] = [];
871
+ while (materials.length > 0) {
872
+ let newMaterial = materials.splice(0, 1)[0];
873
+ const mergedIndices: number[] = [];
874
+ for (let i = 0; i < materials.length; i++) {
875
+ const material = materials[i];
876
+ if (
877
+ (newMaterial.texture && material.texture) ||
878
+ (!newMaterial.texture && !material.texture)
879
+ ) {
880
+ newMaterial = await mergeMaterials(newMaterial, material);
881
+ mergedIndices.push(i);
882
+ }
883
+ }
884
+ if (newMaterial.texture && mergedIndices.length) {
885
+ const newWidth = newMaterial.mergedMaterials?.reduce(
886
+ (accum, {textureSize}) => accum + (textureSize?.width || 0),
887
+ 0
888
+ );
889
+ const newHeight = newMaterial.mergedMaterials?.reduce(
890
+ (accum, {textureSize}) => Math.max(accum, textureSize?.height || 0),
891
+ 0
892
+ );
893
+ let currentX = -1;
894
+ for (const aTextureMetadata of newMaterial.mergedMaterials) {
895
+ if (aTextureMetadata.textureSize) {
896
+ const newX =
897
+ currentX +
898
+ 1 +
899
+ (aTextureMetadata.textureSize.width / newWidth) *
900
+ 2 ** (Uint16Array.BYTES_PER_ELEMENT * 8) -
901
+ 1;
902
+ aTextureMetadata.uvRegion = new Uint16Array([
903
+ currentX + 1,
904
+ 0,
905
+ newX,
906
+ (aTextureMetadata.textureSize.height / newHeight) *
907
+ 2 ** (Uint16Array.BYTES_PER_ELEMENT * 8) -
908
+ 1
909
+ ]);
910
+ currentX = newX;
911
+ }
912
+ }
913
+
914
+ newMaterial.texture.image.width = newWidth;
915
+ newMaterial.texture.image.height = newHeight;
916
+ }
917
+ for (const index of mergedIndices.reverse()) {
918
+ materials.splice(index, 1);
919
+ }
920
+ result.push(newMaterial);
921
+ }
922
+
923
+ if (!result.length) {
924
+ result.push({
925
+ material: getDefaultMaterial(),
926
+ mergedMaterials: [{originalMaterialId: 'default'}]
927
+ });
928
+ }
929
+ return result;
930
+ }
931
+
932
+ /**
933
+ * Merge 2 materials including texture
934
+ * @param material1
935
+ * @param material2
936
+ * @returns
937
+ */
938
+ async function mergeMaterials(
939
+ material1: I3SMaterialWithTexture,
940
+ material2: I3SMaterialWithTexture
941
+ ): Promise<I3SMaterialWithTexture> {
942
+ if (
943
+ material1.texture?.bufferView &&
944
+ material2.texture?.bufferView &&
945
+ material1.mergedMaterials &&
946
+ material2.mergedMaterials
947
+ ) {
948
+ const buffer1 = Buffer.from(material1.texture.bufferView.data);
949
+ const buffer2 = Buffer.from(material2.texture.bufferView.data);
950
+ try {
951
+ // @ts-ignore
952
+ const {joinImages} = await import('join-images');
953
+ const sharpData = await joinImages([buffer1, buffer2], {direction: 'horizontal'});
954
+ material1.texture.bufferView.data = await sharpData
955
+ .toFormat(material1.texture.mimeType === 'image/png' ? 'png' : 'jpeg')
956
+ .toBuffer();
957
+ } catch (error) {
958
+ console.log(
959
+ 'Join images into a texture atlas has failed. Consider usage `--split-nodes` option. (See documentation https://loaders.gl/modules/tile-converter/docs/cli-reference/tile-converter)'
960
+ );
961
+ throw error;
962
+ }
963
+ // @ts-ignore
964
+ material1.material.pbrMetallicRoughness.baseColorTexture.textureSetDefinitionId = 1;
965
+ }
966
+ material1.mergedMaterials = material1.mergedMaterials.concat(material2.mergedMaterials);
967
+ return material1;
968
+ }
969
+
970
+ /**
971
+ * Convert texture and material from gltf 2.0 material object
972
+ * @param sourceMaterial - material object
973
+ * @returns I3S material definition and texture
974
+ */
975
+ function convertMaterial(sourceMaterial: GLTFMaterialPostprocessed): I3SMaterialWithTexture {
976
+ const material: I3SMaterialDefinition = {
977
+ doubleSided: sourceMaterial.doubleSided,
978
+ emissiveFactor: sourceMaterial.emissiveFactor?.map((c) => Math.round(c * 255)) as [
979
+ number,
980
+ number,
981
+ number
982
+ ],
983
+ // It is in upper case in GLTF: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#alpha-coverage
984
+ // But it is in lower case in I3S: https://github.com/Esri/i3s-spec/blob/master/docs/1.7/materialDefinitions.cmn.md
985
+ alphaMode: convertAlphaMode(sourceMaterial.alphaMode),
986
+ pbrMetallicRoughness: {
987
+ roughnessFactor:
988
+ sourceMaterial?.pbrMetallicRoughness?.roughnessFactor || DEFAULT_ROUGHNESS_FACTOR,
989
+ metallicFactor:
990
+ sourceMaterial?.pbrMetallicRoughness?.metallicFactor || DEFAULT_METALLIC_FACTOR
991
+ }
992
+ };
993
+
994
+ let texture;
995
+ if (sourceMaterial?.pbrMetallicRoughness?.baseColorTexture) {
996
+ texture = sourceMaterial.pbrMetallicRoughness.baseColorTexture.texture.source;
997
+ material.pbrMetallicRoughness.baseColorTexture = {
998
+ textureSetDefinitionId: 0
999
+ };
1000
+ } else if (sourceMaterial.emissiveTexture) {
1001
+ texture = sourceMaterial.emissiveTexture.texture.source;
1002
+ // ArcGIS webscene doesn't show emissiveTexture but shows baseColorTexture
1003
+ material.pbrMetallicRoughness.baseColorTexture = {
1004
+ textureSetDefinitionId: 0
1005
+ };
1006
+ }
1007
+
1008
+ sourceMaterial.id = Number.isFinite(sourceMaterial.id) ? sourceMaterial.id : uuidv4();
1009
+ let mergedMaterials: MergedMaterial[] = [{originalMaterialId: sourceMaterial.id}];
1010
+ if (!texture) {
1011
+ // Should use default baseColorFactor if it is not present in source material
1012
+ // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-pbrmetallicroughness
1013
+ const baseColorFactor = sourceMaterial?.pbrMetallicRoughness?.baseColorFactor;
1014
+ material.pbrMetallicRoughness.baseColorFactor =
1015
+ ((baseColorFactor && baseColorFactor.map((c) => Math.round(c * 255))) as [
1016
+ number,
1017
+ number,
1018
+ number,
1019
+ number
1020
+ ]) || undefined;
1021
+ } else {
1022
+ mergedMaterials[0].textureSize = {width: texture.image.width, height: texture.image.height};
1023
+ }
1024
+
1025
+ return {material, texture, mergedMaterials};
1026
+ }
1027
+
1028
+ /**
1029
+ * Converts from `alphaMode` material property from GLTF to I3S format
1030
+ * @param gltfAlphaMode Gltf material `alphaMode` property
1031
+ * @returns I3SMaterialDefinition.alphaMode property
1032
+ */
1033
+ function convertAlphaMode(
1034
+ gltfAlphaMode?: 'OPAQUE' | 'MASK' | 'BLEND' | string
1035
+ ): 'opaque' | 'mask' | 'blend' {
1036
+ switch (gltfAlphaMode) {
1037
+ case 'OPAQUE':
1038
+ return 'opaque';
1039
+ case 'MASK':
1040
+ return 'mask';
1041
+ case 'BLEND':
1042
+ return 'blend';
1043
+ default:
1044
+ return 'opaque';
1045
+ }
1046
+ }
1047
+
1048
+ /**
1049
+ * Form default I3SMaterialDefinition
1050
+ * @returns I3S material definition
1051
+ */
1052
+ function getDefaultMaterial(): I3SMaterialDefinition {
1053
+ return {
1054
+ alphaMode: 'opaque',
1055
+ pbrMetallicRoughness: {
1056
+ metallicFactor: 1,
1057
+ roughnessFactor: 1
1058
+ }
1059
+ };
1060
+ }
1061
+
1062
+ /**
1063
+ * Form "sharedResources" from gltf materials array
1064
+ * @param gltfMaterials - GLTF materials array
1065
+ * @param nodeId - I3S node ID
1066
+ * @returns {materialDefinitionInfos: Object[], textureDefinitionInfos: Object[]} -
1067
+ * 2 arrays in format of i3s sharedResources data https://github.com/Esri/i3s-spec/blob/master/docs/1.7/sharedResource.cmn.md
1068
+ */
1069
+ function getSharedResources(
1070
+ gltfMaterials: GLTFMaterialPostprocessed[],
1071
+ nodeId: number
1072
+ ): SharedResourcesArrays {
1073
+ const i3sResources: SharedResourcesArrays = {};
1074
+
1075
+ if (!gltfMaterials || !gltfMaterials.length) {
1076
+ return i3sResources;
1077
+ }
1078
+
1079
+ i3sResources.materialDefinitionInfos = [];
1080
+ for (const gltfMaterial of gltfMaterials) {
1081
+ const {materialDefinitionInfo, textureDefinitionInfo} = convertGLTFMaterialToI3sSharedResources(
1082
+ gltfMaterial,
1083
+ nodeId
1084
+ );
1085
+ i3sResources.materialDefinitionInfos.push(materialDefinitionInfo);
1086
+ if (textureDefinitionInfo) {
1087
+ i3sResources.textureDefinitionInfos = i3sResources.textureDefinitionInfos || [];
1088
+ i3sResources.textureDefinitionInfos.push(textureDefinitionInfo);
1089
+ }
1090
+ }
1091
+ return i3sResources;
1092
+ }
1093
+
1094
+ /**
1095
+ * Convert gltf material into I3S sharedResources data
1096
+ * @param gltfMaterial - gltf material data
1097
+ * @param nodeId - I3S node ID
1098
+ * @returns - Couple {materialDefinitionInfo, textureDefinitionInfo} extracted from gltf material data
1099
+ */
1100
+ function convertGLTFMaterialToI3sSharedResources(
1101
+ gltfMaterial: GLTFMaterialPostprocessed,
1102
+ nodeId: number
1103
+ ): {
1104
+ materialDefinitionInfo: MaterialDefinitionInfo;
1105
+ textureDefinitionInfo: TextureDefinitionInfo | null;
1106
+ } {
1107
+ const texture =
1108
+ gltfMaterial?.pbrMetallicRoughness?.baseColorTexture || gltfMaterial.emissiveTexture;
1109
+ let textureDefinitionInfo: TextureDefinitionInfo | null = null;
1110
+ if (texture) {
1111
+ textureDefinitionInfo = extractSharedResourcesTextureInfo(texture.texture, nodeId);
1112
+ }
1113
+ const {baseColorFactor, metallicFactor} = gltfMaterial?.pbrMetallicRoughness || {};
1114
+ let colorFactor = baseColorFactor;
1115
+ // If alpha channel is 0 try to get emissive factor from gltf material.
1116
+ if ((!baseColorFactor || baseColorFactor[3] === 0) && gltfMaterial.emissiveFactor) {
1117
+ colorFactor = gltfMaterial.emissiveFactor;
1118
+ colorFactor[3] = colorFactor[3] || 1;
1119
+ }
1120
+
1121
+ return {
1122
+ materialDefinitionInfo: extractSharedResourcesMaterialInfo(
1123
+ colorFactor || [1, 1, 1, 1],
1124
+ metallicFactor
1125
+ ),
1126
+ textureDefinitionInfo
1127
+ };
1128
+ }
1129
+
1130
+ /**
1131
+ * Form "materialDefinition" which is part of "sharedResouces"
1132
+ * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materials
1133
+ * See formulas in appendix "Appendix B: BRDF Implementation":
1134
+ * const dielectricSpecular = rgb(0.04, 0.04, 0.04)
1135
+ * const black = rgb(0, 0, 0)
1136
+ * cdiff = lerp(baseColor.rgb * (1 - dielectricSpecular.r), black, metallic)
1137
+ * F0 = lerp(dieletricSpecular, baseColor.rgb, metallic)
1138
+ *
1139
+ * Assumption: F0 - specular in i3s ("specular reflection" <-> "reflectance value at normal incidence")
1140
+ * cdiff - diffuse in i3s ("Diffuse color" <-> "'c' diffuse" (c means color?))
1141
+ * @param baseColorFactor - RGBA color in 0..1 format
1142
+ * @param metallicFactor - "metallicFactor" attribute of gltf material object
1143
+ * @returns material definition info for I3S shared resource
1144
+ */
1145
+ function extractSharedResourcesMaterialInfo(
1146
+ baseColorFactor: number[],
1147
+ metallicFactor: number = 1
1148
+ ): MaterialDefinitionInfo {
1149
+ const matDielectricColorComponent = 0.04 / 255; // Color from rgb (255) to 0..1 resolution
1150
+ // All color resolutions are 0..1
1151
+ const black = new Vector4(0, 0, 0, 1);
1152
+ const unitVector = new Vector4(1, 1, 1, 1);
1153
+ const dielectricSpecular = new Vector4(
1154
+ matDielectricColorComponent,
1155
+ matDielectricColorComponent,
1156
+ matDielectricColorComponent,
1157
+ 0
1158
+ );
1159
+ const baseColorVector = new Vector4(baseColorFactor);
1160
+ // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
1161
+ // Formulas for Cdiff & F0
1162
+ const firstOperand = unitVector.subtract(dielectricSpecular).multiply(baseColorVector);
1163
+ const diffuse = firstOperand.lerp(firstOperand, black, metallicFactor);
1164
+ dielectricSpecular[3] = 1;
1165
+ const specular = dielectricSpecular.lerp(dielectricSpecular, baseColorVector, metallicFactor);
1166
+ return {
1167
+ params: {
1168
+ // @ts-expect-error NumericArray
1169
+ diffuse: diffuse.toArray(),
1170
+ // @ts-expect-error NumericArray
1171
+ specular: specular.toArray(),
1172
+ renderMode: 'solid'
1173
+ }
1174
+ };
1175
+ }
1176
+
1177
+ /**
1178
+ * Form "textureDefinition" which is part of "sharedResouces"
1179
+ * @param texture - texture image info
1180
+ * @param nodeId - I3S node ID
1181
+ * @returns texture definition infor for shared resource
1182
+ */
1183
+ function extractSharedResourcesTextureInfo(
1184
+ texture: GLTFTexturePostprocessed,
1185
+ nodeId: number
1186
+ ): TextureDefinitionInfo {
1187
+ return {
1188
+ encoding: texture?.source?.mimeType ? [texture.source.mimeType] : undefined,
1189
+ images: [
1190
+ {
1191
+ // 'i3s' has just size which is width of the image. Images are supposed to be square.
1192
+ // https://github.com/Esri/i3s-spec/blob/master/docs/1.7/image.cmn.md
1193
+ id: generateImageId(texture, nodeId),
1194
+ size: texture.source?.image.width,
1195
+ length: texture.source?.image.data.length ? [texture.source?.image.data.length] : undefined
1196
+ }
1197
+ ]
1198
+ };
1199
+ }
1200
+
1201
+ /**
1202
+ * Formula for calculating imageId:
1203
+ * https://github.com/Esri/i3s-spec/blob/0a6366a9249b831db8436c322f8d27521e86cf07/format/Indexed%203d%20Scene%20Layer%20Format%20Specification.md#generating-image-ids
1204
+ * @param texture - texture image info
1205
+ * @param nodeId - I3S node ID
1206
+ * @returns calculate image ID according to the spec
1207
+ */
1208
+ function generateImageId(texture: GLTFTexturePostprocessed, nodeId: number) {
1209
+ const {width, height} = texture.source?.image || {};
1210
+ if (!width || !height) {
1211
+ return '';
1212
+ }
1213
+ const levelCountOfTexture = 1;
1214
+ const indexOfLevel = 0;
1215
+ const indexOfTextureInStore = nodeId + 1;
1216
+
1217
+ const zerosCount = 32 - indexOfTextureInStore.toString(2).length;
1218
+ const rightHalf = '0'.repeat(zerosCount).concat(indexOfTextureInStore.toString(2));
1219
+
1220
+ const shiftedLevelCountOfTexture = levelCountOfTexture << 28;
1221
+ const shiftedIndexOfLevel = indexOfLevel << 24;
1222
+ const shiftedWidth = (width - 1) << 12;
1223
+ const shiftedHeight = (height - 1) << 0;
1224
+
1225
+ const leftHalf = shiftedLevelCountOfTexture + shiftedIndexOfLevel + shiftedWidth + shiftedHeight;
1226
+ const imageId = BigInt(`0b${leftHalf.toString(2)}${rightHalf}`);
1227
+ return imageId.toString();
1228
+ }
1229
+
1230
+ /**
1231
+ * Make all feature ids unique through all nodes in layout.
1232
+ * @param featureIds
1233
+ * @param featureIndices
1234
+ * @param featuresHashArray
1235
+ * @param batchTable
1236
+ * @returns {void}
1237
+ */
1238
+ function makeFeatureIdsUnique(
1239
+ featureIds: number[],
1240
+ featureIndices: number[],
1241
+ featuresHashArray: string[],
1242
+ batchTable: {[key: string]: any}
1243
+ ) {
1244
+ const replaceMap = getFeaturesReplaceMap(featureIds, batchTable, featuresHashArray);
1245
+ replaceIndicesByUnique(featureIndices, replaceMap);
1246
+ replaceIndicesByUnique(featureIds, replaceMap);
1247
+ }
1248
+
1249
+ /**
1250
+ * Generate replace map to make featureIds unique.
1251
+ * @param featureIds
1252
+ * @param batchTable
1253
+ * @param featuresHashArray
1254
+ * @returns
1255
+ */
1256
+ function getFeaturesReplaceMap(
1257
+ featureIds: any[],
1258
+ batchTable: object,
1259
+ featuresHashArray: any[]
1260
+ ): Record<string, any> {
1261
+ const featureMap: Record<string, any> = {};
1262
+
1263
+ for (let index = 0; index < featureIds.length; index++) {
1264
+ const oldFeatureId = featureIds[index];
1265
+ const uniqueFeatureId = getOrCreateUniqueFeatureId(index, batchTable, featuresHashArray);
1266
+ featureMap[oldFeatureId.toString()] = uniqueFeatureId;
1267
+ }
1268
+
1269
+ return featureMap;
1270
+ }
1271
+
1272
+ /**
1273
+ * Generates string for unique batch id creation.
1274
+ * @param batchTable
1275
+ * @param index
1276
+ * @returns
1277
+ */
1278
+ function generateStringFromBatchTableByIndex(batchTable: object, index: number): string {
1279
+ let str = '';
1280
+ for (const key in batchTable) {
1281
+ str += batchTable[key][index];
1282
+ }
1283
+ return str;
1284
+ }
1285
+
1286
+ /**
1287
+ * Return already exited featureId or creates and returns new to support unique feature ids throw nodes.
1288
+ * @param index
1289
+ * @param batchTable
1290
+ * @param featuresHashArray
1291
+ * @returns
1292
+ */
1293
+ function getOrCreateUniqueFeatureId(
1294
+ index: number,
1295
+ batchTable: object,
1296
+ featuresHashArray: any[]
1297
+ ): number {
1298
+ const batchTableStr = generateStringFromBatchTableByIndex(batchTable, index);
1299
+ const hash = md5(batchTableStr);
1300
+
1301
+ if (featuresHashArray.includes(hash)) {
1302
+ return featuresHashArray.indexOf(hash);
1303
+ }
1304
+ return featuresHashArray.push(hash) - 1;
1305
+ }
1306
+
1307
+ /**
1308
+ * Do replacement of indices for making them unique through all nodes.
1309
+ * @param indicesArray
1310
+ * @param featureMap
1311
+ * @returns
1312
+ */
1313
+ function replaceIndicesByUnique(indicesArray: any[], featureMap: Record<string, []>) {
1314
+ for (let index = 0; index < indicesArray.length; index++) {
1315
+ indicesArray[index] = featureMap[indicesArray[index]];
1316
+ }
1317
+ }
1318
+
1319
+ /**
1320
+ * Convert property table data to attribute buffers.
1321
+ * @param featureIds
1322
+ * @param propertyTable - table with metadata for particular feature.
1323
+ * @param attributeStorageInfo
1324
+ * @returns - Array of file buffers.
1325
+ */
1326
+ function convertPropertyTableToAttributeBuffers(
1327
+ featureIds: number[],
1328
+ propertyTable: FeatureTableJson,
1329
+ attributeStorageInfo: AttributeStorageInfo[]
1330
+ ): any[] {
1331
+ const attributeBuffers: ArrayBuffer[] = [];
1332
+
1333
+ const needFlattenPropertyTable = checkPropertiesLength(featureIds, propertyTable);
1334
+ const properties = needFlattenPropertyTable
1335
+ ? flattenPropertyTableByFeatureIds(featureIds, propertyTable)
1336
+ : propertyTable;
1337
+
1338
+ const propertyTableWithObjectIds = {
1339
+ OBJECTID: featureIds,
1340
+ ...properties
1341
+ };
1342
+
1343
+ for (const propertyName in propertyTableWithObjectIds) {
1344
+ const type = getAttributeType(propertyName, attributeStorageInfo);
1345
+ const value = propertyTableWithObjectIds[propertyName];
1346
+ const attributeBuffer = generateAttributeBuffer(type, value);
1347
+
1348
+ attributeBuffers.push(attributeBuffer);
1349
+ }
1350
+
1351
+ return attributeBuffers;
1352
+ }
1353
+
1354
+ /**
1355
+ * Generates attribute buffer based on attribute type
1356
+ * @param type
1357
+ * @param value
1358
+ */
1359
+ function generateAttributeBuffer(type: string, value: any): ArrayBuffer {
1360
+ let attributeBuffer: ArrayBuffer;
1361
+
1362
+ switch (type) {
1363
+ case OBJECT_ID_TYPE:
1364
+ case SHORT_INT_TYPE:
1365
+ attributeBuffer = generateShortIntegerAttributeBuffer(value);
1366
+ break;
1367
+ case DOUBLE_TYPE:
1368
+ attributeBuffer = generateDoubleAttributeBuffer(value);
1369
+ break;
1370
+ case STRING_TYPE:
1371
+ attributeBuffer = generateStringAttributeBuffer(value);
1372
+ break;
1373
+ default:
1374
+ attributeBuffer = generateStringAttributeBuffer(value);
1375
+ }
1376
+
1377
+ return attributeBuffer;
1378
+ }
1379
+
1380
+ /**
1381
+ * Return attribute type.
1382
+ * @param key
1383
+ * @param attributeStorageInfo
1384
+ * @returns attribute type.
1385
+ */
1386
+ function getAttributeType(key: string, attributeStorageInfo: any[]): string {
1387
+ const attribute = attributeStorageInfo.find((attr) => attr.name === key);
1388
+ return attribute.attributeValues.valueType;
1389
+ }
1390
+
1391
+ /**
1392
+ * Convert short integer to attribute arrayBuffer.
1393
+ * @param featureIds
1394
+ * @returns - Buffer with objectId data.
1395
+ */
1396
+ function generateShortIntegerAttributeBuffer(featureIds: any[]): ArrayBuffer {
1397
+ const count = new Uint32Array([featureIds.length]);
1398
+ const valuesArray = new Uint32Array(featureIds);
1399
+ return concatenateArrayBuffers(count.buffer, valuesArray.buffer);
1400
+ }
1401
+
1402
+ /**
1403
+ * Convert double to attribute arrayBuffer.
1404
+ * @param featureIds
1405
+ * @returns - Buffer with objectId data.
1406
+ */
1407
+ function generateDoubleAttributeBuffer(featureIds: any[]): ArrayBuffer {
1408
+ const count = new Uint32Array([featureIds.length]);
1409
+ const padding = new Uint8Array(4);
1410
+ const valuesArray = new Float64Array(featureIds);
1411
+
1412
+ return concatenateArrayBuffers(count.buffer, padding.buffer, valuesArray.buffer);
1413
+ }
1414
+
1415
+ /**
1416
+ * Convert batch table attributes to array buffer with batch table data.
1417
+ * @param batchAttributes
1418
+ * @returns - Buffer with batch table data.
1419
+ */
1420
+ function generateStringAttributeBuffer(batchAttributes: any[]): ArrayBuffer {
1421
+ const stringCountArray = new Uint32Array([batchAttributes.length]);
1422
+ let totalNumberOfBytes = 0;
1423
+ const stringSizesArray = new Uint32Array(batchAttributes.length);
1424
+ const stringBufferArray: ArrayBuffer[] = [];
1425
+
1426
+ for (let index = 0; index < batchAttributes.length; index++) {
1427
+ const currentString = `${String(batchAttributes[index])}\0`;
1428
+ const currentStringBuffer = Buffer.from(currentString);
1429
+ const currentStringSize = currentStringBuffer.length;
1430
+ totalNumberOfBytes += currentStringSize;
1431
+ stringSizesArray[index] = currentStringSize;
1432
+ stringBufferArray.push(currentStringBuffer);
1433
+ }
1434
+
1435
+ const totalBytes = new Uint32Array([totalNumberOfBytes]);
1436
+
1437
+ return concatenateArrayBuffers(
1438
+ stringCountArray.buffer,
1439
+ totalBytes.buffer,
1440
+ stringSizesArray.buffer,
1441
+ ...stringBufferArray
1442
+ );
1443
+ }
1444
+
1445
+ /**
1446
+ * Convert featureIds to BigUint64Array.
1447
+ * @param featureIds
1448
+ * @returns - Array of feature ids in BigUint64 format.
1449
+ */
1450
+ function generateBigUint64Array(featureIds: any[]): BigUint64Array {
1451
+ const typedFeatureIds = new BigUint64Array(featureIds.length);
1452
+ for (let index = 0; index < featureIds.length; index++) {
1453
+ typedFeatureIds[index] = BigInt(featureIds[index]);
1454
+ }
1455
+ return typedFeatureIds;
1456
+ }
1457
+
1458
+ /**
1459
+ * Generates draco compressed geometry
1460
+ * @param {Number} vertexCount
1461
+ * @param {Object} convertedAttributes - get rid of this argument here
1462
+ * @param {Object} attributes - geometry attributes to compress
1463
+ * @param {string} dracoWorkerSoure - draco worker source code
1464
+ * @returns {Promise<object>} - COmpressed geometry.
1465
+ */
1466
+ async function generateCompressedGeometry(
1467
+ vertexCount: number,
1468
+ convertedAttributes: Record<string, any>,
1469
+ attributes: Record<string, any>,
1470
+ dracoWorkerSoure: string
1471
+ ): Promise<ArrayBuffer> {
1472
+ const {positions, normals, texCoords, colors, uvRegions, featureIds, faceRange} = attributes;
1473
+ const indices = new Uint32Array(vertexCount);
1474
+
1475
+ for (let index = 0; index < indices.length; index++) {
1476
+ indices.set([index], index);
1477
+ }
1478
+
1479
+ const featureIndices = new Uint32Array(
1480
+ convertedAttributes.featureIndices.length ? convertedAttributes.featureIndices : vertexCount
1481
+ );
1482
+
1483
+ const featureIndex = generateFeatureIndexAttribute(featureIndices, faceRange);
1484
+
1485
+ const compressedAttributes: {
1486
+ positions: TypedArray;
1487
+ normals: TypedArray;
1488
+ colors: TypedArray;
1489
+ 'feature-index': TypedArray;
1490
+ texCoords?: TypedArray;
1491
+ 'uv-region'?: TypedArray;
1492
+ } = {
1493
+ positions,
1494
+ normals,
1495
+ colors,
1496
+ 'feature-index': featureIndex
1497
+ };
1498
+
1499
+ if (texCoords.length) {
1500
+ compressedAttributes.texCoords = texCoords;
1501
+ }
1502
+
1503
+ const attributesMetadata = {
1504
+ 'feature-index': {
1505
+ 'i3s-attribute-type': 'feature-index',
1506
+ 'i3s-feature-ids': new Int32Array(featureIds)
1507
+ }
1508
+ };
1509
+
1510
+ if (uvRegions.length) {
1511
+ compressedAttributes['uv-region'] = uvRegions;
1512
+ attributesMetadata['uv-region'] = {
1513
+ 'i3s-attribute-type': 'uv-region'
1514
+ };
1515
+ }
1516
+
1517
+ return encode({attributes: compressedAttributes, indices}, DracoWriterWorker, {
1518
+ ...DracoWriterWorker.options,
1519
+ source: dracoWorkerSoure,
1520
+ reuseWorkers: true,
1521
+ _nodeWorkers: true,
1522
+ draco: {
1523
+ method: 'MESH_SEQUENTIAL_ENCODING',
1524
+ attributesMetadata
1525
+ }
1526
+ });
1527
+ }
1528
+
1529
+ /**
1530
+ * Generates ordered feature indices based on face range
1531
+ * @param featureIndex
1532
+ * @param faceRange
1533
+ * @returns
1534
+ */
1535
+ function generateFeatureIndexAttribute(
1536
+ featureIndex: Uint32Array,
1537
+ faceRange: Uint32Array
1538
+ ): Uint32Array {
1539
+ const orderedFeatureIndices = new Uint32Array(featureIndex.length);
1540
+ let fillIndex = 0;
1541
+ let startIndex = 0;
1542
+
1543
+ for (let index = 1; index < faceRange.length; index += 2) {
1544
+ const endIndex = (faceRange[index] + 1) * VALUES_PER_VERTEX;
1545
+
1546
+ orderedFeatureIndices.fill(fillIndex, startIndex, endIndex);
1547
+
1548
+ fillIndex++;
1549
+ startIndex = endIndex + 1;
1550
+ }
1551
+
1552
+ return orderedFeatureIndices;
1553
+ }
1554
+
1555
+ /**
1556
+ * Find property table in tile
1557
+ * For example it can be batchTable for b3dm files or property table in gLTF extension.
1558
+ * @param tileContent - 3DTiles tile content
1559
+ * @return batch table from b3dm / feature properties from EXT_FEATURE_METADATA
1560
+ */
1561
+ export function getPropertyTable(tileContent: Tiles3DTileContent | null): FeatureTableJson | null {
1562
+ if (!tileContent) {
1563
+ return null;
1564
+ }
1565
+
1566
+ const batchTableJson = tileContent?.batchTableJson;
1567
+
1568
+ if (batchTableJson) {
1569
+ return batchTableJson;
1570
+ }
1571
+
1572
+ const {extensionName, extension} = getPropertyTableExtension(tileContent);
1573
+
1574
+ switch (extensionName) {
1575
+ case EXT_MESH_FEATURES: {
1576
+ console.warn('The I3S converter does not yet support the EXT_mesh_features extension');
1577
+ return null;
1578
+ }
1579
+ case EXT_FEATURE_METADATA: {
1580
+ return getPropertyTableFromExtFeatureMetadata(extension as GLTF_EXT_feature_metadata);
1581
+ }
1582
+ default:
1583
+ return null;
1584
+ }
1585
+ }
1586
+
1587
+ /**
1588
+ * Check extensions which can be with property table inside.
1589
+ * @param tileContent - 3DTiles tile content
1590
+ */
1591
+ function getPropertyTableExtension(
1592
+ tileContent: Tiles3DTileContent
1593
+ ): GLTF_EXT_feature_metadata | GLTF_EXT_mesh_features {
1594
+ const extensionsWithPropertyTables = [EXT_FEATURE_METADATA, EXT_MESH_FEATURES];
1595
+ const extensionsUsed = tileContent?.gltf?.extensionsUsed;
1596
+
1597
+ if (!extensionsUsed) {
1598
+ return {extensionName: null, extension: null};
1599
+ }
1600
+
1601
+ let extensionName: string = '';
1602
+
1603
+ for (const extensionItem of tileContent?.gltf?.extensionsUsed || []) {
1604
+ if (extensionsWithPropertyTables.includes(extensionItem)) {
1605
+ extensionName = extensionItem;
1606
+ break;
1607
+ }
1608
+ }
1609
+
1610
+ const extension = tileContent?.gltf?.extensions?.[extensionName];
1611
+
1612
+ return {extensionName, extension};
1613
+ }
1614
+
1615
+ /**
1616
+ * Handle EXT_feature_metadata to get property table
1617
+ * @param extension
1618
+ */
1619
+ function getPropertyTableFromExtFeatureMetadata(
1620
+ extension: GLTF_EXT_feature_metadata
1621
+ ): FeatureTableJson | null {
1622
+ if (extension?.featureTables) {
1623
+ /**
1624
+ * Take only first feature table to generate attributes storage info object.
1625
+ * TODO: Think about getting data from all feature tables?
1626
+ * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
1627
+ * In I3S we should decide which featureIds attribute will be passed to geometry data.
1628
+ */
1629
+ const firstFeatureTableName = Object.keys(extension.featureTables)?.[0];
1630
+
1631
+ if (firstFeatureTableName) {
1632
+ const featureTable = extension?.featureTables[firstFeatureTableName];
1633
+ const propertyTable = {};
1634
+
1635
+ for (const propertyName in featureTable.properties) {
1636
+ propertyTable[propertyName] = featureTable.properties[propertyName].data;
1637
+ }
1638
+
1639
+ return propertyTable;
1640
+ }
1641
+ }
1642
+
1643
+ if (extension?.featureTextures) {
1644
+ /**
1645
+ * Take only first feature texture to generate attributes storage info object.
1646
+ * TODO: Think about getting data from all feature textures?
1647
+ * It can be tricky just because 3dTiles is able to have multiple featureTextures.
1648
+ * In I3S we should decide which featureTextures will be passed to geometry data.
1649
+ */
1650
+ const firstTextureName = Object.keys(extension.featureTextures)?.[0];
1651
+ if (firstTextureName) {
1652
+ const featureTable = extension?.featureTextures[firstTextureName];
1653
+ const propertyTable = {};
1654
+
1655
+ for (const propertyName in featureTable.properties) {
1656
+ propertyTable[propertyName] = featureTable.properties[propertyName].data;
1657
+ }
1658
+
1659
+ return propertyTable;
1660
+ }
1661
+ }
1662
+
1663
+ console.warn(
1664
+ "The I3S converter couldn't handle EXT_feature_metadata extension: There is neither featureTables, no featureTextures in the extension."
1665
+ );
1666
+ return null;
1667
+ }