@loaders.gl/tile-converter 4.0.0-alpha.5 → 4.0.0-alpha.7

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