@loaders.gl/tile-converter 3.2.12 → 3.3.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/dist/3d-tiles-attributes-worker.d.ts +3 -3
  2. package/dist/3d-tiles-attributes-worker.d.ts.map +1 -1
  3. package/dist/3d-tiles-attributes-worker.js +2 -3
  4. package/dist/3d-tiles-attributes-worker.js.map +3 -3
  5. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts +8 -0
  6. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -1
  7. package/dist/3d-tiles-converter/3d-tiles-converter.js +57 -43
  8. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts +5 -5
  9. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts.map +1 -1
  10. package/dist/3d-tiles-converter/helpers/b3dm-converter.js +21 -17
  11. package/dist/converter-cli.js +43 -8
  12. package/dist/converter.min.js +24 -21
  13. package/dist/deps-installer/deps-installer.d.ts +5 -1
  14. package/dist/deps-installer/deps-installer.d.ts.map +1 -1
  15. package/dist/deps-installer/deps-installer.js +29 -1
  16. package/dist/dist.min.js +58405 -61237
  17. package/dist/es5/3d-tiles-attributes-worker.js +4 -7
  18. package/dist/es5/3d-tiles-attributes-worker.js.map +1 -1
  19. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js +125 -210
  20. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  21. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js +53 -85
  22. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  23. package/dist/es5/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +0 -8
  24. package/dist/es5/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js.map +1 -1
  25. package/dist/es5/3d-tiles-converter/helpers/texture-atlas.js +0 -5
  26. package/dist/es5/3d-tiles-converter/helpers/texture-atlas.js.map +1 -1
  27. package/dist/es5/3d-tiles-converter/json-templates/tileset.js +0 -6
  28. package/dist/es5/3d-tiles-converter/json-templates/tileset.js.map +1 -1
  29. package/dist/es5/bundle.js +0 -1
  30. package/dist/es5/bundle.js.map +1 -1
  31. package/dist/es5/constants.js.map +1 -1
  32. package/dist/es5/converter-cli.js +50 -60
  33. package/dist/es5/converter-cli.js.map +1 -1
  34. package/dist/es5/deps-installer/deps-installer.js +73 -28
  35. package/dist/es5/deps-installer/deps-installer.js.map +1 -1
  36. package/dist/es5/i3s-attributes-worker.js +3 -6
  37. package/dist/es5/i3s-attributes-worker.js.map +1 -1
  38. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +124 -0
  39. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  40. package/dist/es5/i3s-converter/helpers/coordinate-converter.js +2 -19
  41. package/dist/es5/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  42. package/dist/es5/i3s-converter/helpers/create-scene-server-path.js +0 -11
  43. package/dist/es5/i3s-converter/helpers/create-scene-server-path.js.map +1 -1
  44. package/dist/es5/i3s-converter/helpers/feature-attributes.js +184 -0
  45. package/dist/es5/i3s-converter/helpers/feature-attributes.js.map +1 -0
  46. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +60 -51
  47. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  48. package/dist/es5/i3s-converter/helpers/geometry-converter.js +516 -356
  49. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  50. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +57 -43
  51. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  52. package/dist/es5/i3s-converter/helpers/node-debug.js +4 -23
  53. package/dist/es5/i3s-converter/helpers/node-debug.js.map +1 -1
  54. package/dist/es5/i3s-converter/helpers/node-index-document.js +507 -0
  55. package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -0
  56. package/dist/es5/i3s-converter/helpers/node-pages.js +462 -208
  57. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
  58. package/dist/es5/i3s-converter/i3s-converter.js +722 -1153
  59. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  60. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js +107 -0
  61. package/dist/es5/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  62. package/dist/es5/i3s-converter/json-templates/layers.js +2 -107
  63. package/dist/es5/i3s-converter/json-templates/layers.js.map +1 -1
  64. package/dist/es5/i3s-converter/json-templates/metadata.js +0 -2
  65. package/dist/es5/i3s-converter/json-templates/metadata.js.map +1 -1
  66. package/dist/es5/i3s-converter/json-templates/node.js +2 -12
  67. package/dist/es5/i3s-converter/json-templates/node.js.map +1 -1
  68. package/dist/es5/i3s-converter/json-templates/scene-server.js +0 -2
  69. package/dist/es5/i3s-converter/json-templates/scene-server.js.map +1 -1
  70. package/dist/es5/i3s-converter/json-templates/shared-resources.js +9 -32
  71. package/dist/es5/i3s-converter/json-templates/shared-resources.js.map +1 -1
  72. package/dist/es5/i3s-converter/json-templates/store.js.map +1 -1
  73. package/dist/es5/i3s-converter/types.js.map +1 -1
  74. package/dist/es5/i3s-server/app.js +0 -5
  75. package/dist/es5/i3s-server/app.js.map +1 -1
  76. package/dist/es5/i3s-server/controllers/index-controller.js +0 -16
  77. package/dist/es5/i3s-server/controllers/index-controller.js.map +1 -1
  78. package/dist/es5/i3s-server/routes/index.js +1 -10
  79. package/dist/es5/i3s-server/routes/index.js.map +1 -1
  80. package/dist/es5/index.js +0 -3
  81. package/dist/es5/index.js.map +1 -1
  82. package/dist/es5/lib/utils/compress-util.js +19 -74
  83. package/dist/es5/lib/utils/compress-util.js.map +1 -1
  84. package/dist/es5/lib/utils/file-utils.js +103 -47
  85. package/dist/es5/lib/utils/file-utils.js.map +1 -1
  86. package/dist/es5/lib/utils/lod-conversion-utils.js +0 -7
  87. package/dist/es5/lib/utils/lod-conversion-utils.js.map +1 -1
  88. package/dist/es5/lib/utils/queue.js +0 -14
  89. package/dist/es5/lib/utils/queue.js.map +1 -1
  90. package/dist/es5/lib/utils/statistic-utills.js +1 -46
  91. package/dist/es5/lib/utils/statistic-utills.js.map +1 -1
  92. package/dist/es5/lib/utils/write-queue.js +41 -82
  93. package/dist/es5/lib/utils/write-queue.js.map +1 -1
  94. package/dist/es5/pgm-loader.js +1 -8
  95. package/dist/es5/pgm-loader.js.map +1 -1
  96. package/dist/es5/workers/3d-tiles-attributes-worker.js +2 -9
  97. package/dist/es5/workers/3d-tiles-attributes-worker.js.map +1 -1
  98. package/dist/es5/workers/i3s-attributes-worker.js +2 -10
  99. package/dist/es5/workers/i3s-attributes-worker.js.map +1 -1
  100. package/dist/esm/3d-tiles-attributes-worker.js +4 -2
  101. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
  102. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js +60 -77
  103. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  104. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js +29 -50
  105. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  106. package/dist/esm/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +1 -0
  107. package/dist/esm/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js.map +1 -1
  108. package/dist/esm/3d-tiles-converter/helpers/texture-atlas.js +1 -4
  109. package/dist/esm/3d-tiles-converter/helpers/texture-atlas.js.map +1 -1
  110. package/dist/esm/3d-tiles-converter/json-templates/tileset.js +0 -3
  111. package/dist/esm/3d-tiles-converter/json-templates/tileset.js.map +1 -1
  112. package/dist/esm/bundle.js +1 -1
  113. package/dist/esm/bundle.js.map +1 -1
  114. package/dist/esm/constants.js.map +1 -1
  115. package/dist/esm/converter-cli.js +46 -40
  116. package/dist/esm/converter-cli.js.map +1 -1
  117. package/dist/esm/deps-installer/deps-installer.js +30 -4
  118. package/dist/esm/deps-installer/deps-installer.js.map +1 -1
  119. package/dist/esm/i3s-attributes-worker.js +3 -1
  120. package/dist/esm/i3s-attributes-worker.js.map +1 -1
  121. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +113 -0
  122. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  123. package/dist/esm/i3s-converter/helpers/coordinate-converter.js +5 -6
  124. package/dist/esm/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  125. package/dist/esm/i3s-converter/helpers/create-scene-server-path.js +1 -0
  126. package/dist/esm/i3s-converter/helpers/create-scene-server-path.js.map +1 -1
  127. package/dist/esm/i3s-converter/helpers/feature-attributes.js +158 -0
  128. package/dist/esm/i3s-converter/helpers/feature-attributes.js.map +1 -0
  129. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +39 -33
  130. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  131. package/dist/esm/i3s-converter/helpers/geometry-converter.js +295 -196
  132. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  133. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +59 -34
  134. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  135. package/dist/esm/i3s-converter/helpers/node-debug.js +3 -13
  136. package/dist/esm/i3s-converter/helpers/node-debug.js.map +1 -1
  137. package/dist/esm/i3s-converter/helpers/node-index-document.js +197 -0
  138. package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -0
  139. package/dist/esm/i3s-converter/helpers/node-pages.js +161 -87
  140. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
  141. package/dist/esm/i3s-converter/i3s-converter.js +216 -491
  142. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  143. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js +89 -0
  144. package/dist/esm/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
  145. package/dist/esm/i3s-converter/json-templates/layers.js +2 -95
  146. package/dist/esm/i3s-converter/json-templates/layers.js.map +1 -1
  147. package/dist/esm/i3s-converter/json-templates/metadata.js.map +1 -1
  148. package/dist/esm/i3s-converter/json-templates/node.js +0 -4
  149. package/dist/esm/i3s-converter/json-templates/node.js.map +1 -1
  150. package/dist/esm/i3s-converter/json-templates/scene-server.js.map +1 -1
  151. package/dist/esm/i3s-converter/json-templates/shared-resources.js +3 -15
  152. package/dist/esm/i3s-converter/json-templates/shared-resources.js.map +1 -1
  153. package/dist/esm/i3s-converter/json-templates/store.js.map +1 -1
  154. package/dist/esm/i3s-converter/types.js.map +1 -1
  155. package/dist/esm/i3s-server/app.js +0 -5
  156. package/dist/esm/i3s-server/app.js.map +1 -1
  157. package/dist/esm/i3s-server/controllers/index-controller.js +0 -5
  158. package/dist/esm/i3s-server/controllers/index-controller.js.map +1 -1
  159. package/dist/esm/i3s-server/routes/index.js +0 -3
  160. package/dist/esm/i3s-server/routes/index.js.map +1 -1
  161. package/dist/esm/index.js.map +1 -1
  162. package/dist/esm/lib/utils/compress-util.js +19 -12
  163. package/dist/esm/lib/utils/compress-util.js.map +1 -1
  164. package/dist/esm/lib/utils/file-utils.js +54 -11
  165. package/dist/esm/lib/utils/file-utils.js.map +1 -1
  166. package/dist/esm/lib/utils/lod-conversion-utils.js +2 -6
  167. package/dist/esm/lib/utils/lod-conversion-utils.js.map +1 -1
  168. package/dist/esm/lib/utils/queue.js +0 -4
  169. package/dist/esm/lib/utils/queue.js.map +1 -1
  170. package/dist/esm/lib/utils/statistic-utills.js +0 -11
  171. package/dist/esm/lib/utils/statistic-utills.js.map +1 -1
  172. package/dist/esm/lib/utils/write-queue.js +27 -38
  173. package/dist/esm/lib/utils/write-queue.js.map +1 -1
  174. package/dist/esm/pgm-loader.js +3 -1
  175. package/dist/esm/pgm-loader.js.map +1 -1
  176. package/dist/esm/workers/3d-tiles-attributes-worker.js +4 -1
  177. package/dist/esm/workers/3d-tiles-attributes-worker.js.map +1 -1
  178. package/dist/esm/workers/i3s-attributes-worker.js +4 -1
  179. package/dist/esm/workers/i3s-attributes-worker.js.map +1 -1
  180. package/dist/i3s-attributes-worker.d.ts +7 -3
  181. package/dist/i3s-attributes-worker.d.ts.map +1 -1
  182. package/dist/i3s-attributes-worker.js +2 -3
  183. package/dist/i3s-attributes-worker.js.map +3 -3
  184. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +11 -0
  185. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -0
  186. package/dist/i3s-converter/helpers/batch-ids-extensions.js +141 -0
  187. package/dist/i3s-converter/helpers/coordinate-converter.d.ts +2 -2
  188. package/dist/i3s-converter/helpers/coordinate-converter.d.ts.map +1 -1
  189. package/dist/i3s-converter/helpers/feature-attributes.d.ts +56 -0
  190. package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -0
  191. package/dist/i3s-converter/helpers/feature-attributes.js +216 -0
  192. package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -1
  193. package/dist/i3s-converter/helpers/geometry-attributes.js +42 -17
  194. package/dist/i3s-converter/helpers/geometry-converter.d.ts +18 -6
  195. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  196. package/dist/i3s-converter/helpers/geometry-converter.js +349 -99
  197. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
  198. package/dist/i3s-converter/helpers/gltf-attributes.js +53 -21
  199. package/dist/i3s-converter/helpers/node-index-document.d.ts +91 -0
  200. package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -0
  201. package/dist/i3s-converter/helpers/node-index-document.js +242 -0
  202. package/dist/i3s-converter/helpers/node-pages.d.ts +81 -42
  203. package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -1
  204. package/dist/i3s-converter/helpers/node-pages.js +200 -92
  205. package/dist/i3s-converter/i3s-converter.d.ts +52 -108
  206. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  207. package/dist/i3s-converter/i3s-converter.js +218 -403
  208. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts +7 -0
  209. package/dist/i3s-converter/json-templates/geometry-definitions.d.ts.map +1 -0
  210. package/dist/i3s-converter/json-templates/geometry-definitions.js +87 -0
  211. package/dist/i3s-converter/json-templates/layers.d.ts +1 -30
  212. package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -1
  213. package/dist/i3s-converter/json-templates/layers.js +2 -86
  214. package/dist/i3s-converter/json-templates/shared-resources.js +3 -3
  215. package/dist/i3s-converter/types.d.ts +38 -8
  216. package/dist/i3s-converter/types.d.ts.map +1 -1
  217. package/dist/lib/utils/file-utils.d.ts +17 -1
  218. package/dist/lib/utils/file-utils.d.ts.map +1 -1
  219. package/dist/lib/utils/file-utils.js +64 -7
  220. package/dist/lib/utils/write-queue.d.ts +19 -3
  221. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  222. package/dist/lib/utils/write-queue.js +21 -16
  223. package/dist/pgm-loader.d.ts.map +1 -1
  224. package/dist/pgm-loader.js +2 -1
  225. package/dist/workers/3d-tiles-attributes-worker.js +1 -1
  226. package/dist/workers/i3s-attributes-worker.js +1 -1
  227. package/package.json +18 -16
  228. package/src/3d-tiles-attributes-worker.ts +1 -1
  229. package/src/3d-tiles-converter/3d-tiles-converter.ts +71 -55
  230. package/src/3d-tiles-converter/helpers/b3dm-converter.ts +25 -18
  231. package/src/converter-cli.ts +54 -8
  232. package/src/deps-installer/deps-installer.ts +38 -2
  233. package/src/i3s-attributes-worker.ts +5 -1
  234. package/src/i3s-converter/helpers/batch-ids-extensions.ts +206 -0
  235. package/src/i3s-converter/helpers/coordinate-converter.ts +2 -2
  236. package/src/i3s-converter/helpers/feature-attributes.ts +247 -0
  237. package/src/i3s-converter/helpers/geometry-attributes.ts +46 -18
  238. package/src/i3s-converter/helpers/geometry-converter.ts +423 -111
  239. package/src/i3s-converter/helpers/gltf-attributes.ts +59 -24
  240. package/src/i3s-converter/helpers/node-index-document.ts +306 -0
  241. package/src/i3s-converter/helpers/node-pages.ts +222 -109
  242. package/src/i3s-converter/i3s-converter.ts +264 -487
  243. package/src/i3s-converter/json-templates/geometry-definitions.ts +83 -0
  244. package/src/i3s-converter/json-templates/layers.ts +2 -91
  245. package/src/i3s-converter/json-templates/shared-resources.ts +3 -3
  246. package/src/i3s-converter/types.ts +33 -2
  247. package/src/lib/utils/file-utils.ts +62 -7
  248. package/src/lib/utils/write-queue.ts +42 -19
  249. package/src/pgm-loader.ts +2 -2
  250. package/src/workers/3d-tiles-attributes-worker.ts +1 -1
  251. package/src/workers/i3s-attributes-worker.ts +2 -1
@@ -1,3 +1,15 @@
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
+
1
13
  import {Vector3, Matrix4, Vector4} from '@math.gl/core';
2
14
  import {Ellipsoid} from '@math.gl/geospatial';
3
15
 
@@ -5,16 +17,16 @@ import {DracoWriterWorker} from '@loaders.gl/draco';
5
17
  import {assert, encode} from '@loaders.gl/core';
6
18
  import {concatenateArrayBuffers, concatenateTypedArrays} from '@loaders.gl/loader-utils';
7
19
  import md5 from 'md5';
20
+ import {v4 as uuidv4} from 'uuid';
8
21
  import {generateAttributes} from './geometry-attributes';
9
22
  import {createBoundingVolumesFromGeometry} from './coordinate-converter';
10
23
  import {
11
24
  ConvertedAttributes,
12
25
  I3SConvertedResources,
13
26
  I3SMaterialWithTexture,
27
+ MergedMaterial,
14
28
  SharedResourcesArrays
15
29
  } from '../types';
16
- import {B3DMContent} from '@loaders.gl/3d-tiles';
17
- import {GLTFMaterialPostprocessed, GLTFNodePostprocessed} from '@loaders.gl/gltf';
18
30
  import {
19
31
  AttributeStorageInfo,
20
32
  I3SMaterialDefinition,
@@ -23,13 +35,11 @@ import {
23
35
  } from '@loaders.gl/i3s';
24
36
  import {TypedArray} from '@loaders.gl/schema';
25
37
  import {Geoid} from '@math.gl/geoid';
26
- import {
27
- GLTFAccessorPostprocessed,
28
- GLTFMeshPostprocessed,
29
- GLTFTexturePostprocessed
30
- } from 'modules/gltf/src/lib/types/gltf-types';
31
- import {B3DMAttributesData /*transformI3SAttributesOnWorker */} from '../../i3s-attributes-worker';
38
+ /** Usage of worker here brings more overhead than advantage */
39
+ import {B3DMAttributesData /*, transformI3SAttributesOnWorker*/} from '../../i3s-attributes-worker';
32
40
  import {prepareDataForAttributesConversion} from './gltf-attributes';
41
+ import {handleBatchIdsExtensions} from './batch-ids-extensions';
42
+ import {checkPropertiesLength, flattenPropertyTableByFeatureIds} from './feature-attributes';
33
43
 
34
44
  // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
35
45
  const DEFAULT_ROUGHNESS_FACTOR = 1;
@@ -50,90 +60,89 @@ const OBJECT_ID_TYPE = 'Oid32';
50
60
  */
51
61
  const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID'];
52
62
 
63
+ const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
64
+ const EXT_MESH_FEATURES = 'EXT_mesh_features';
65
+
53
66
  let scratchVector = new Vector3();
54
67
 
55
68
  /**
56
69
  * Convert binary data from b3dm file to i3s resources
57
70
  *
58
71
  * @param tileContent - 3d tile content
59
- * @param nodeId - target nodeId. If a few nodes will be created - ids will be nodeId+n where n - index in the resulting array
72
+ * @param addNodeToNodePage - function to add new node to node pages
73
+ * @param propertyTable - batch table (corresponding to feature attributes data)
60
74
  * @param featuresHashArray - hash array of features that is needed to not to mix up same features in parent and child nodes
61
75
  * @param attributeStorageInfo - attributes metadata from 3DSceneLayer json
62
76
  * @param draco - is converter should create draco compressed geometry
63
77
  * @param generateBoundingVolumes - is converter should create accurate bounding voulmes from geometry attributes
78
+ * @param shouldMergeMaterials - Try to merge similar materials to be able to merge meshes into one node
64
79
  * @param geoidHeightModel - model to convert elevation from elipsoidal to geoid
80
+ * @param workerSource - source code of used workers
65
81
  * @returns Array of node resources to create one or more i3s nodes
66
82
  */
67
83
  export default async function convertB3dmToI3sGeometry(
68
84
  tileContent: B3DMContent,
69
- nodeId: number,
85
+ addNodeToNodePage: () => Promise<number>,
86
+ propertyTable: FeatureTableJson | null,
70
87
  featuresHashArray: string[],
71
88
  attributeStorageInfo: AttributeStorageInfo[] | undefined,
72
89
  draco: boolean,
73
90
  generateBoundingVolumes: boolean,
91
+ shouldMergeMaterials: boolean,
74
92
  geoidHeightModel: Geoid,
75
93
  workerSource: {[key: string]: string}
76
- ) {
94
+ ): Promise<I3SConvertedResources[] | null> {
77
95
  const useCartesianPositions = generateBoundingVolumes;
78
- const materialAndTextureList: I3SMaterialWithTexture[] = convertMaterials(
79
- tileContent.gltf?.materials
96
+ const materialAndTextureList: I3SMaterialWithTexture[] = await convertMaterials(
97
+ tileContent.gltf?.materials,
98
+ shouldMergeMaterials
80
99
  );
81
100
 
82
101
  const dataForAttributesConversion = prepareDataForAttributesConversion(tileContent);
83
-
84
102
  const convertedAttributesMap: Map<string, ConvertedAttributes> = await convertAttributes(
85
103
  dataForAttributesConversion,
104
+ materialAndTextureList,
86
105
  useCartesianPositions
87
106
  );
88
- // TODO uncomment it when worker will be published on CDN.
89
- /*
90
- const convertedAttributesMap: Map<string, ConvertedAttributes> =
91
- await transformI3SAttributesOnWorker(dataForAttributesConversion, {
92
- useCartesianPositions,
93
- source: workerSource.I3SAttributes
94
- });
95
- */
107
+ /** Usage of worker here brings more overhead than advantage */
108
+ // const convertedAttributesMap: Map<string, ConvertedAttributes> =
109
+ // await transformI3SAttributesOnWorker(dataForAttributesConversion, {
110
+ // reuseWorkers: true,
111
+ // _nodeWorkers: true,
112
+ // useCartesianPositions,
113
+ // source: workerSource.I3SAttributes
114
+ // });
96
115
 
97
116
  if (generateBoundingVolumes) {
98
117
  _generateBoundingVolumesFromGeometry(convertedAttributesMap, geoidHeightModel);
99
118
  }
100
119
 
101
- if (convertedAttributesMap.has('default')) {
102
- materialAndTextureList.push({
103
- material: getDefaultMaterial()
104
- });
105
- }
106
-
107
120
  const result: I3SConvertedResources[] = [];
108
- let nodesCounter = nodeId;
109
- let {materials = []} = tileContent.gltf || {materials: []};
110
- if (!materials?.length) {
111
- materials.push({id: 'default'});
112
- }
113
- for (let i = 0; i < materials.length; i++) {
114
- const sourceMaterial = materials[i];
115
- if (!convertedAttributesMap.has(sourceMaterial.id)) {
121
+ for (const materialAndTexture of materialAndTextureList) {
122
+ const originarMaterialId = materialAndTexture.mergedMaterials[0].originalMaterialId;
123
+ if (!convertedAttributesMap.has(originarMaterialId)) {
116
124
  continue; // eslint-disable-line no-continue
117
125
  }
118
- const convertedAttributes = convertedAttributesMap.get(sourceMaterial.id);
126
+ const convertedAttributes = convertedAttributesMap.get(originarMaterialId);
119
127
  if (!convertedAttributes) {
120
128
  continue;
121
129
  }
122
- const {material, texture} = materialAndTextureList[i];
130
+ const {material, texture} = materialAndTexture;
131
+ const nodeId = await addNodeToNodePage();
123
132
  result.push(
124
133
  await _makeNodeResources({
125
134
  convertedAttributes,
126
135
  material,
127
136
  texture,
128
137
  tileContent,
129
- nodeId: nodesCounter,
138
+ nodeId,
130
139
  featuresHashArray,
140
+ propertyTable,
131
141
  attributeStorageInfo,
132
142
  draco,
133
143
  workerSource
134
144
  })
135
145
  );
136
- nodesCounter++;
137
146
  }
138
147
 
139
148
  if (!result.length) {
@@ -180,8 +189,10 @@ function _generateBoundingVolumesFromGeometry(
180
189
  * @param params.tileContent - B3DM decoded content
181
190
  * @param params.nodeId - new node ID
182
191
  * @param params.featuresHashArray - hash array of features that is needed to not to mix up same features in parent and child nodes
183
- * @param params.attributesStorageInfo - attributes metadata from 3DSceneLayer json
192
+ * @param params.propertyTable - batch table (corresponding to feature attributes data)
193
+ * @param params.attributeStorageInfo - attributes metadata from 3DSceneLayer json
184
194
  * @param params.draco - is converter should create draco compressed geometry
195
+ * @param params.workerSource - source code of used workers
185
196
  * @returns Array of I3S node resources
186
197
  */
187
198
  async function _makeNodeResources({
@@ -191,6 +202,7 @@ async function _makeNodeResources({
191
202
  tileContent,
192
203
  nodeId,
193
204
  featuresHashArray,
205
+ propertyTable,
194
206
  attributeStorageInfo,
195
207
  draco,
196
208
  workerSource
@@ -201,13 +213,14 @@ async function _makeNodeResources({
201
213
  tileContent: B3DMContent;
202
214
  nodeId: number;
203
215
  featuresHashArray: string[];
216
+ propertyTable: FeatureTableJson | null;
204
217
  attributeStorageInfo?: AttributeStorageInfo[];
205
218
  draco: boolean;
206
219
  workerSource: {[key: string]: string};
207
220
  }): Promise<I3SConvertedResources> {
208
221
  const boundingVolumes = convertedAttributes.boundingVolumes;
209
222
  const vertexCount = convertedAttributes.positions.length / VALUES_PER_VERTEX;
210
- const {faceRange, featureIds, positions, normals, colors, texCoords, featureCount} =
223
+ const {faceRange, featureIds, positions, normals, colors, uvRegions, texCoords, featureCount} =
211
224
  generateAttributes(convertedAttributes);
212
225
 
213
226
  if (tileContent.batchTableJson) {
@@ -230,6 +243,7 @@ async function _makeNodeResources({
230
243
  normals.buffer,
231
244
  texture ? texCoords.buffer : new ArrayBuffer(0),
232
245
  colors.buffer,
246
+ uvRegions,
233
247
  typedFeatureIds.buffer,
234
248
  faceRange.buffer
235
249
  )
@@ -243,6 +257,7 @@ async function _makeNodeResources({
243
257
  normals,
244
258
  texCoords: texture ? texCoords : new Float32Array(0),
245
259
  colors,
260
+ uvRegions,
246
261
  featureIds,
247
262
  faceRange
248
263
  },
@@ -250,16 +265,22 @@ async function _makeNodeResources({
250
265
  )
251
266
  : null;
252
267
 
253
- const attributes = convertBatchTableToAttributeBuffers(
254
- tileContent.batchTableJson,
255
- featureIds,
256
- attributeStorageInfo
257
- );
268
+ let attributes: ArrayBuffer[] = [];
269
+
270
+ if (attributeStorageInfo && propertyTable) {
271
+ attributes = convertPropertyTableToAttributeBuffers(
272
+ featureIds,
273
+ propertyTable,
274
+ attributeStorageInfo
275
+ );
276
+ }
258
277
 
259
278
  return {
279
+ nodeId,
260
280
  geometry: fileBuffer,
261
281
  compressedGeometry,
262
282
  texture,
283
+ hasUvRegions: Boolean(uvRegions.length),
263
284
  sharedResources: getSharedResources(tileContent.gltf?.materials || [], nodeId),
264
285
  meshMaterial: material,
265
286
  vertexCount,
@@ -271,32 +292,40 @@ async function _makeNodeResources({
271
292
 
272
293
  /**
273
294
  * Convert attributes from the gltf nodes tree to i3s plain geometry
274
- * @param tileContent - 3d tile content
295
+ * @param attributesData - geometry attributes from gltf
296
+ * @param materialAndTextureList - array of data about materials and textures of the content
275
297
  * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
276
298
  * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
277
299
  * @returns map of converted geometry attributes
278
300
  */
279
301
  export async function convertAttributes(
280
302
  attributesData: B3DMAttributesData,
303
+ materialAndTextureList: I3SMaterialWithTexture[],
281
304
  useCartesianPositions: boolean
282
305
  ): Promise<Map<string, ConvertedAttributes>> {
283
- const {gltfMaterials, nodes, cartographicOrigin, cartesianModelMatrix} = attributesData;
306
+ const {nodes, images, cartographicOrigin, cartesianModelMatrix} = attributesData;
284
307
  const attributesMap = new Map<string, ConvertedAttributes>();
285
308
 
286
- for (const material of gltfMaterials || [{id: 'default'}]) {
287
- attributesMap.set(material.id, {
309
+ for (const materialAndTexture of materialAndTextureList) {
310
+ const attributes = {
288
311
  positions: new Float32Array(0),
289
312
  normals: new Float32Array(0),
290
313
  texCoords: new Float32Array(0),
291
314
  colors: new Uint8Array(0),
315
+ uvRegions: new Uint16Array(0),
292
316
  featureIndicesGroups: [],
293
317
  featureIndices: [],
294
- boundingVolumes: null
295
- });
318
+ boundingVolumes: null,
319
+ mergedMaterials: materialAndTexture.mergedMaterials
320
+ };
321
+ for (const mergedMaterial of materialAndTexture.mergedMaterials) {
322
+ attributesMap.set(mergedMaterial.originalMaterialId, attributes);
323
+ }
296
324
  }
297
325
 
298
326
  convertNodes(
299
327
  nodes,
328
+ images,
300
329
  cartographicOrigin,
301
330
  cartesianModelMatrix,
302
331
  attributesMap,
@@ -327,7 +356,9 @@ export async function convertAttributes(
327
356
  * Gltf has hierarchical structure of nodes. This function converts nodes starting from those which are in gltf scene object.
328
357
  * The goal is applying tranformation matrix for all children. Functions "convertNodes" and "convertNode" work together recursively.
329
358
  * @param nodes - gltf nodes array
330
- * @param tileContent - 3d tile content
359
+ * @param images - gltf images array
360
+ * @param cartographicOrigin - cartographic origin of bounding volume
361
+ * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
331
362
  * @param attributesMap - for recursive concatenation of attributes
332
363
  * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
333
364
  * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
@@ -336,6 +367,7 @@ export async function convertAttributes(
336
367
  */
337
368
  function convertNodes(
338
369
  nodes: GLTFNodePostprocessed[],
370
+ images: GLTFImagePostprocessed[],
339
371
  cartographicOrigin: Vector3,
340
372
  cartesianModelMatrix: Matrix4,
341
373
  attributesMap: Map<string, ConvertedAttributes>,
@@ -346,6 +378,7 @@ function convertNodes(
346
378
  for (const node of nodes) {
347
379
  convertNode(
348
380
  node,
381
+ images,
349
382
  cartographicOrigin,
350
383
  cartesianModelMatrix,
351
384
  attributesMap,
@@ -362,7 +395,7 @@ function convertNodes(
362
395
  * @param node
363
396
  * @param matrix
364
397
  */
365
- function getCompositeTransformationMatrix(node, matrix) {
398
+ function getCompositeTransformationMatrix(node: GLTFNodePostprocessed, matrix: Matrix4) {
366
399
  let transformationMatrix = matrix;
367
400
 
368
401
  const {matrix: nodeMatrix, rotation, scale, translation} = node;
@@ -389,7 +422,9 @@ function getCompositeTransformationMatrix(node, matrix) {
389
422
  /**
390
423
  * Convert all primitives of node and all children nodes
391
424
  * @param node - gltf node
392
- * @param {Object} tileContent - 3d tile content
425
+ * @param images - gltf images array
426
+ * @param cartographicOrigin - cartographic origin of bounding volume
427
+ * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
393
428
  * @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
394
429
  * attributes
395
430
  * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
@@ -398,6 +433,7 @@ function getCompositeTransformationMatrix(node, matrix) {
398
433
  */
399
434
  function convertNode(
400
435
  node: GLTFNodePostprocessed,
436
+ images: GLTFImagePostprocessed[],
401
437
  cartographicOrigin: Vector3,
402
438
  cartesianModelMatrix: Matrix4,
403
439
  attributesMap: Map<string, ConvertedAttributes>,
@@ -407,9 +443,11 @@ function convertNode(
407
443
  const transformationMatrix = getCompositeTransformationMatrix(node, matrix);
408
444
 
409
445
  const mesh = node.mesh;
446
+
410
447
  if (mesh) {
411
448
  convertMesh(
412
449
  mesh,
450
+ images,
413
451
  cartographicOrigin,
414
452
  cartesianModelMatrix,
415
453
  attributesMap,
@@ -420,6 +458,7 @@ function convertNode(
420
458
 
421
459
  convertNodes(
422
460
  node.children || [],
461
+ images,
423
462
  cartographicOrigin,
424
463
  cartesianModelMatrix,
425
464
  attributesMap,
@@ -429,9 +468,13 @@ function convertNode(
429
468
  }
430
469
 
431
470
  /**
432
- * Convert all primitives of node and all children nodes
433
- * @param mesh - gltf node
434
- * @param content - 3d tile content
471
+ * Convert all primitives of the mesh
472
+ * @param mesh - gltf mesh data
473
+ * @param images - gltf images array
474
+ * @param cartographicOrigin - cartographic origin of bounding volume
475
+ * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
476
+ * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
477
+ * attributes
435
478
  * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
436
479
  * Cartesian coordinates will be required for creating bounding voulmest from geometry positions
437
480
  * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
@@ -441,6 +484,7 @@ function convertNode(
441
484
  */
442
485
  function convertMesh(
443
486
  mesh: GLTFMeshPostprocessed,
487
+ images: GLTFImagePostprocessed[],
444
488
  cartographicOrigin: Vector3,
445
489
  cartesianModelMatrix: Matrix4,
446
490
  attributesMap: Map<string, ConvertedAttributes>,
@@ -449,8 +493,12 @@ function convertMesh(
449
493
  ) {
450
494
  for (const primitive of mesh.primitives) {
451
495
  let outputAttributes: ConvertedAttributes | null | undefined = null;
496
+ let materialUvRegion: Uint16Array | undefined;
452
497
  if (primitive.material) {
453
- outputAttributes = attributesMap.get(primitive.material.id);
498
+ outputAttributes = attributesMap.get(primitive.material.uniqueId);
499
+ materialUvRegion = outputAttributes?.mergedMaterials.find(
500
+ ({originalMaterialId}) => originalMaterialId === primitive.material?.uniqueId
501
+ )?.uvRegion;
454
502
  } else if (attributesMap.has('default')) {
455
503
  outputAttributes = attributesMap.get('default');
456
504
  }
@@ -496,9 +544,16 @@ function convertMesh(
496
544
  flattenColors(attributes.COLOR_0, primitive.indices?.value)
497
545
  );
498
546
 
547
+ if (materialUvRegion) {
548
+ outputAttributes.uvRegions = concatenateTypedArrays(
549
+ outputAttributes.uvRegions,
550
+ createUvRegion(materialUvRegion, primitive.indices?.value)
551
+ );
552
+ }
553
+
499
554
  outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || [];
500
555
  outputAttributes.featureIndicesGroups.push(
501
- flattenBatchIds(getBatchIdsByAttributeName(attributes), primitive.indices?.value)
556
+ flattenBatchIds(getBatchIds(attributes, primitive, images), primitive.indices?.value)
502
557
  );
503
558
  }
504
559
  }
@@ -646,6 +701,20 @@ function flattenColors(
646
701
  return newColors;
647
702
  }
648
703
 
704
+ /**
705
+ * Create per-vertex uv-region array
706
+ * @param materialUvRegion - uv-region fragment for a single vertex
707
+ * @param indices - geometry indices data
708
+ * @returns - uv-region array
709
+ */
710
+ function createUvRegion(materialUvRegion: Uint16Array, indices: Uint8Array): Uint16Array {
711
+ const result = new Uint16Array(indices.length * 4);
712
+ for (let i = 0; i < result.length; i += 4) {
713
+ result.set(materialUvRegion, i);
714
+ }
715
+ return result;
716
+ }
717
+
649
718
  /**
650
719
  * Flatten batchedIds list based on indices to right ordered array, compatible with i3s
651
720
  * @param batchedIds - gltf primitive
@@ -665,14 +734,23 @@ function flattenBatchIds(batchedIds: number[], indices: Uint8Array): number[] {
665
734
  }
666
735
 
667
736
  /**
668
- * Return batchIds based on possible attribute names for different kind of maps.
669
- * @param attributes - the gltf primitive attributes
670
- * @returns batch ids attribute
737
+ * Get batchIds for featureIds creation
738
+ * @param attributes - gltf accessors
739
+ * @param primitive - gltf primitive data
740
+ * @param images - gltf texture images
671
741
  */
672
- function getBatchIdsByAttributeName(attributes: {
673
- [key: string]: GLTFAccessorPostprocessed;
674
- }): number[] {
675
- let batchIds: number[] = [];
742
+ function getBatchIds(
743
+ attributes: {
744
+ [key: string]: GLTFAccessorPostprocessed;
745
+ },
746
+ primitive: GLTFMeshPrimitivePostprocessed,
747
+ images: GLTFImagePostprocessed[]
748
+ ): number[] {
749
+ const batchIds: number[] = handleBatchIdsExtensions(attributes, primitive, images);
750
+
751
+ if (batchIds.length) {
752
+ return batchIds;
753
+ }
676
754
 
677
755
  for (let index = 0; index < BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES.length; index++) {
678
756
  const possibleBatchIdAttributeName = BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES[index];
@@ -680,29 +758,137 @@ function getBatchIdsByAttributeName(attributes: {
680
758
  attributes[possibleBatchIdAttributeName] &&
681
759
  attributes[possibleBatchIdAttributeName].value
682
760
  ) {
683
- batchIds = attributes[possibleBatchIdAttributeName].value;
684
- break;
761
+ return attributes[possibleBatchIdAttributeName].value;
685
762
  }
686
763
  }
687
764
 
688
- return batchIds;
765
+ return [];
689
766
  }
690
767
 
691
768
  /**
692
769
  * Convert GLTF material to I3S material definitions and textures
693
770
  * @param sourceMaterials Source GLTF materials
771
+ * @param shouldMergeMaterials - if true - the converter will try to merge similar materials
772
+ * to be able to merge primitives having those materials
694
773
  * @returns Array of Couples I3SMaterialDefinition + texture content
695
774
  */
696
- function convertMaterials(
697
- sourceMaterials: GLTFMaterialPostprocessed[] = []
698
- ): I3SMaterialWithTexture[] {
699
- const result: I3SMaterialWithTexture[] = [];
775
+ async function convertMaterials(
776
+ sourceMaterials: GLTFMaterialPostprocessed[] = [],
777
+ shouldMergeMaterials: boolean
778
+ ): Promise<I3SMaterialWithTexture[]> {
779
+ let materials: I3SMaterialWithTexture[] = [];
700
780
  for (const sourceMaterial of sourceMaterials) {
701
- result.push(convertMaterial(sourceMaterial));
781
+ materials.push(convertMaterial(sourceMaterial));
782
+ }
783
+
784
+ if (shouldMergeMaterials) {
785
+ materials = await mergeAllMaterials(materials);
786
+ }
787
+
788
+ return materials;
789
+ }
790
+
791
+ /**
792
+ * Merge materials when possible
793
+ * @param materials materials array
794
+ * @returns merged materials array
795
+ */
796
+ async function mergeAllMaterials(
797
+ materials: I3SMaterialWithTexture[]
798
+ ): Promise<I3SMaterialWithTexture[]> {
799
+ const result: I3SMaterialWithTexture[] = [];
800
+ while (materials.length > 0) {
801
+ let newMaterial = materials.splice(0, 1)[0];
802
+ const mergedIndices: number[] = [];
803
+ for (let i = 0; i < materials.length; i++) {
804
+ const material = materials[i];
805
+ if (
806
+ (newMaterial.texture && material.texture) ||
807
+ (!newMaterial.texture && !material.texture)
808
+ ) {
809
+ newMaterial = await mergeMaterials(newMaterial, material);
810
+ mergedIndices.push(i);
811
+ }
812
+ }
813
+ if (newMaterial.texture && mergedIndices.length) {
814
+ const newWidth = newMaterial.mergedMaterials?.reduce(
815
+ (accum, {textureSize}) => accum + (textureSize?.width || 0),
816
+ 0
817
+ );
818
+ const newHeight = newMaterial.mergedMaterials?.reduce(
819
+ (accum, {textureSize}) => Math.max(accum, textureSize?.height || 0),
820
+ 0
821
+ );
822
+ let currentX = -1;
823
+ for (const aTextureMetadata of newMaterial.mergedMaterials) {
824
+ if (aTextureMetadata.textureSize) {
825
+ const newX =
826
+ currentX +
827
+ 1 +
828
+ (aTextureMetadata.textureSize.width / newWidth) *
829
+ 2 ** (Uint16Array.BYTES_PER_ELEMENT * 8) -
830
+ 1;
831
+ aTextureMetadata.uvRegion = new Uint16Array([
832
+ currentX + 1,
833
+ 0,
834
+ newX,
835
+ (aTextureMetadata.textureSize.height / newHeight) *
836
+ 2 ** (Uint16Array.BYTES_PER_ELEMENT * 8) -
837
+ 1
838
+ ]);
839
+ currentX = newX;
840
+ }
841
+ }
842
+
843
+ newMaterial.texture.image.width = newWidth;
844
+ newMaterial.texture.image.height = newHeight;
845
+ }
846
+ for (const index of mergedIndices.reverse()) {
847
+ materials.splice(index, 1);
848
+ }
849
+ result.push(newMaterial);
850
+ }
851
+
852
+ if (!result.length) {
853
+ result.push({
854
+ material: getDefaultMaterial(),
855
+ mergedMaterials: [{originalMaterialId: 'default'}]
856
+ });
702
857
  }
703
858
  return result;
704
859
  }
705
860
 
861
+ /**
862
+ * Merge 2 materials including texture
863
+ * @param material1
864
+ * @param material2
865
+ * @returns
866
+ */
867
+ async function mergeMaterials(
868
+ material1: I3SMaterialWithTexture,
869
+ material2: I3SMaterialWithTexture
870
+ ): Promise<I3SMaterialWithTexture> {
871
+ if (
872
+ material1.texture?.bufferView &&
873
+ material2.texture?.bufferView &&
874
+ material1.mergedMaterials &&
875
+ material2.mergedMaterials
876
+ ) {
877
+ const buffer1 = Buffer.from(material1.texture.bufferView.data);
878
+ const buffer2 = Buffer.from(material2.texture.bufferView.data);
879
+ // @ts-ignore
880
+ const {joinImages} = await import('join-images');
881
+ const sharpData = await joinImages([buffer1, buffer2], {direction: 'horizontal'});
882
+ material1.texture.bufferView.data = await sharpData
883
+ .toFormat(material1.texture.mimeType === 'image/png' ? 'png' : 'jpeg')
884
+ .toBuffer();
885
+ // @ts-ignore
886
+ material1.material.pbrMetallicRoughness.baseColorTexture.textureSetDefinitionId = 1;
887
+ }
888
+ material1.mergedMaterials = material1.mergedMaterials.concat(material2.mergedMaterials);
889
+ return material1;
890
+ }
891
+
706
892
  /**
707
893
  * Convert texture and material from gltf 2.0 material object
708
894
  * @param sourceMaterial - material object
@@ -741,6 +927,9 @@ function convertMaterial(sourceMaterial: GLTFMaterialPostprocessed): I3SMaterial
741
927
  };
742
928
  }
743
929
 
930
+ const uniqueId = uuidv4();
931
+ sourceMaterial.uniqueId = uniqueId;
932
+ let mergedMaterials: MergedMaterial[] = [{originalMaterialId: uniqueId}];
744
933
  if (!texture) {
745
934
  // Should use default baseColorFactor if it is not present in source material
746
935
  // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-pbrmetallicroughness
@@ -752,9 +941,11 @@ function convertMaterial(sourceMaterial: GLTFMaterialPostprocessed): I3SMaterial
752
941
  number,
753
942
  number
754
943
  ]) || undefined;
944
+ } else {
945
+ mergedMaterials[0].textureSize = {width: texture.image.width, height: texture.image.height};
755
946
  }
756
947
 
757
- return {material, texture};
948
+ return {material, texture, mergedMaterials};
758
949
  }
759
950
 
760
951
  /**
@@ -897,7 +1088,9 @@ function extractSharedResourcesMaterialInfo(
897
1088
  const specular = dielectricSpecular.lerp(dielectricSpecular, baseColorVector, metallicFactor);
898
1089
  return {
899
1090
  params: {
1091
+ // @ts-expect-error NumericArray
900
1092
  diffuse: diffuse.toArray(),
1093
+ // @ts-expect-error NumericArray
901
1094
  specular: specular.toArray(),
902
1095
  renderMode: 'solid'
903
1096
  }
@@ -929,13 +1122,13 @@ function extractSharedResourcesTextureInfo(
929
1122
  }
930
1123
 
931
1124
  /**
932
- * Formula for counting imageId:
1125
+ * Formula for calculating imageId:
933
1126
  * https://github.com/Esri/i3s-spec/blob/0a6366a9249b831db8436c322f8d27521e86cf07/format/Indexed%203d%20Scene%20Layer%20Format%20Specification.md#generating-image-ids
934
1127
  * @param texture - texture image info
935
1128
  * @param nodeId - I3S node ID
936
1129
  * @returns calculate image ID according to the spec
937
1130
  */
938
- function generateImageId(texture: GLTFTexturePostprocessed, nodeId) {
1131
+ function generateImageId(texture: GLTFTexturePostprocessed, nodeId: number) {
939
1132
  const {width, height} = texture.source?.image;
940
1133
  const levelCountOfTexture = 1;
941
1134
  const indexOfLevel = 0;
@@ -1036,49 +1229,66 @@ function replaceIndicesByUnique(indicesArray, featureMap) {
1036
1229
  }
1037
1230
 
1038
1231
  /**
1039
- * Convert batch table data to attribute buffers.
1040
- * @param {Object} batchTable - table with metadata for particular feature.
1232
+ * Convert property table data to attribute buffers.
1041
1233
  * @param {Array} featureIds
1234
+ * @param {Object} propertyTable - table with metadata for particular feature.
1042
1235
  * @param {Array} attributeStorageInfo
1043
1236
  * @returns {Array} - Array of file buffers.
1044
1237
  */
1045
- function convertBatchTableToAttributeBuffers(batchTable, featureIds, attributeStorageInfo) {
1238
+ function convertPropertyTableToAttributeBuffers(
1239
+ featureIds: number[],
1240
+ propertyTable: FeatureTableJson,
1241
+ attributeStorageInfo: AttributeStorageInfo[]
1242
+ ) {
1046
1243
  const attributeBuffers: ArrayBuffer[] = [];
1047
1244
 
1048
- if (batchTable) {
1049
- const batchTableWithFeatureIds = {
1050
- OBJECTID: featureIds,
1051
- ...batchTable
1052
- };
1245
+ const needFlattenPropertyTable = checkPropertiesLength(featureIds, propertyTable);
1246
+ const properties = needFlattenPropertyTable
1247
+ ? flattenPropertyTableByFeatureIds(featureIds, propertyTable)
1248
+ : propertyTable;
1053
1249
 
1054
- for (const key in batchTableWithFeatureIds) {
1055
- const type = getAttributeType(key, attributeStorageInfo);
1056
-
1057
- let attributeBuffer: ArrayBuffer | null = null;
1058
-
1059
- switch (type) {
1060
- case OBJECT_ID_TYPE:
1061
- case SHORT_INT_TYPE:
1062
- attributeBuffer = generateShortIntegerAttributeBuffer(batchTableWithFeatureIds[key]);
1063
- break;
1064
- case DOUBLE_TYPE:
1065
- attributeBuffer = generateDoubleAttributeBuffer(batchTableWithFeatureIds[key]);
1066
- break;
1067
- case STRING_TYPE:
1068
- attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
1069
- break;
1070
- default:
1071
- attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
1072
- }
1250
+ const propertyTableWithObjectIds = {
1251
+ OBJECTID: featureIds,
1252
+ ...properties
1253
+ };
1073
1254
 
1074
- if (attributeBuffer) {
1075
- attributeBuffers.push(attributeBuffer);
1076
- }
1077
- }
1255
+ for (const propertyName in propertyTableWithObjectIds) {
1256
+ const type = getAttributeType(propertyName, attributeStorageInfo);
1257
+ const value = propertyTableWithObjectIds[propertyName];
1258
+ const attributeBuffer = generateAttributeBuffer(type, value);
1259
+
1260
+ attributeBuffers.push(attributeBuffer);
1078
1261
  }
1079
1262
 
1080
1263
  return attributeBuffers;
1081
1264
  }
1265
+
1266
+ /**
1267
+ * Generates attribute buffer based on attribute type
1268
+ * @param type
1269
+ * @param value
1270
+ */
1271
+ function generateAttributeBuffer(type: string, value: any): ArrayBuffer {
1272
+ let attributeBuffer: ArrayBuffer;
1273
+
1274
+ switch (type) {
1275
+ case OBJECT_ID_TYPE:
1276
+ case SHORT_INT_TYPE:
1277
+ attributeBuffer = generateShortIntegerAttributeBuffer(value);
1278
+ break;
1279
+ case DOUBLE_TYPE:
1280
+ attributeBuffer = generateDoubleAttributeBuffer(value);
1281
+ break;
1282
+ case STRING_TYPE:
1283
+ attributeBuffer = generateStringAttributeBuffer(value);
1284
+ break;
1285
+ default:
1286
+ attributeBuffer = generateStringAttributeBuffer(value);
1287
+ }
1288
+
1289
+ return attributeBuffer;
1290
+ }
1291
+
1082
1292
  /**
1083
1293
  * Return attribute type.
1084
1294
  * @param {String} key
@@ -1171,7 +1381,7 @@ async function generateCompressedGeometry(
1171
1381
  attributes,
1172
1382
  dracoWorkerSoure
1173
1383
  ) {
1174
- const {positions, normals, texCoords, colors, featureIds, faceRange} = attributes;
1384
+ const {positions, normals, texCoords, colors, uvRegions, featureIds, faceRange} = attributes;
1175
1385
  const indices = new Uint32Array(vertexCount);
1176
1386
 
1177
1387
  for (let index = 0; index < indices.length; index++) {
@@ -1190,6 +1400,7 @@ async function generateCompressedGeometry(
1190
1400
  colors: TypedArray;
1191
1401
  'feature-index': TypedArray;
1192
1402
  texCoords?: TypedArray;
1403
+ 'uv-region'?: TypedArray;
1193
1404
  } = {
1194
1405
  positions,
1195
1406
  normals,
@@ -1208,6 +1419,13 @@ async function generateCompressedGeometry(
1208
1419
  }
1209
1420
  };
1210
1421
 
1422
+ if (uvRegions.length) {
1423
+ compressedAttributes['uv-region'] = uvRegions;
1424
+ attributesMetadata['uv-region'] = {
1425
+ 'i3s-attribute-type': 'uv-region'
1426
+ };
1427
+ }
1428
+
1211
1429
  return encode({attributes: compressedAttributes, indices}, DracoWriterWorker, {
1212
1430
  ...DracoWriterWorker.options,
1213
1431
  source: dracoWorkerSoure,
@@ -1242,3 +1460,97 @@ function generateFeatureIndexAttribute(featureIndex, faceRange) {
1242
1460
 
1243
1461
  return orderedFeatureIndices;
1244
1462
  }
1463
+
1464
+ /**
1465
+ * Find property table in tile
1466
+ * For example it can be batchTable for b3dm files or property table in gLTF extension.
1467
+ * @param sourceTile
1468
+ * @return batch table from b3dm / feature properties from EXT_FEATURE_METADATA
1469
+ */
1470
+ export function getPropertyTable(tileContent: B3DMContent): FeatureTableJson | null {
1471
+ const batchTableJson = tileContent?.batchTableJson;
1472
+
1473
+ if (batchTableJson) {
1474
+ return batchTableJson;
1475
+ }
1476
+
1477
+ const {extensionName, extension} = getPropertyTableExtension(tileContent);
1478
+
1479
+ switch (extensionName) {
1480
+ case EXT_MESH_FEATURES: {
1481
+ console.warn('The I3S converter does not yet support the EXT_mesh_features extension');
1482
+ return null;
1483
+ }
1484
+ case EXT_FEATURE_METADATA: {
1485
+ return getPropertyTableFromExtFeatureMetadata(extension);
1486
+ }
1487
+ default:
1488
+ return null;
1489
+ }
1490
+ }
1491
+
1492
+ /**
1493
+ * Check extensions which can be with property table inside.
1494
+ * @param sourceTile
1495
+ */
1496
+ function getPropertyTableExtension(tileContent: B3DMContent) {
1497
+ const extensionsWithPropertyTables = [EXT_FEATURE_METADATA, EXT_MESH_FEATURES];
1498
+ const extensionsUsed = tileContent?.gltf?.extensionsUsed;
1499
+
1500
+ if (!extensionsUsed) {
1501
+ return {extensionName: null, extension: null};
1502
+ }
1503
+
1504
+ let extensionName: string = '';
1505
+
1506
+ for (const extensionItem of tileContent?.gltf?.extensionsUsed || []) {
1507
+ if (extensionsWithPropertyTables.includes(extensionItem)) {
1508
+ extensionName = extensionItem;
1509
+ break;
1510
+ }
1511
+ }
1512
+
1513
+ const extension = tileContent?.gltf?.extensions?.[extensionName];
1514
+
1515
+ return {extensionName, extension};
1516
+ }
1517
+
1518
+ /**
1519
+ * Handle EXT_feature_metadata to get property table
1520
+ * @param extension
1521
+ * TODO add EXT_feature_metadata feature textures support.
1522
+ */
1523
+ function getPropertyTableFromExtFeatureMetadata(
1524
+ extension: GLTF_EXT_feature_metadata
1525
+ ): FeatureTableJson | null {
1526
+ if (extension?.featureTextures) {
1527
+ console.warn(
1528
+ 'The I3S converter does not yet support the EXT_feature_metadata feature textures'
1529
+ );
1530
+ return null;
1531
+ }
1532
+
1533
+ if (extension?.featureTables) {
1534
+ /**
1535
+ * Take only first feature table to generate attributes storage info object.
1536
+ * TODO: Think about getting data from all feature tables?
1537
+ * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
1538
+ * In I3S we should decide which featureIds attribute will be passed to geometry data.
1539
+ */
1540
+ const firstFeatureTableName = Object.keys(extension.featureTables)?.[0];
1541
+
1542
+ if (firstFeatureTableName) {
1543
+ const featureTable = extension?.featureTables[firstFeatureTableName];
1544
+ const propertyTable = {};
1545
+
1546
+ for (const propertyName in featureTable.properties) {
1547
+ propertyTable[propertyName] = featureTable.properties[propertyName].data;
1548
+ }
1549
+
1550
+ return propertyTable;
1551
+ }
1552
+ }
1553
+
1554
+ console.warn("The I3S converter couldn't handle EXT_feature_metadata extension");
1555
+ return null;
1556
+ }