@loaders.gl/tile-converter 4.0.0-alpha.4 → 4.0.0-alpha.6

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