@loaders.gl/tile-converter 3.2.5 → 3.3.0-alpha.1

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 (64) hide show
  1. package/dist/3d-tiles-attributes-worker.js +3 -3
  2. package/dist/3d-tiles-attributes-worker.js.map +1 -1
  3. package/dist/converter-cli.js +30 -7
  4. package/dist/converter.min.js +1 -1
  5. package/dist/dist.min.js +819 -516
  6. package/dist/es5/3d-tiles-attributes-worker.js +1 -1
  7. package/dist/es5/3d-tiles-attributes-worker.js.map +1 -1
  8. package/dist/es5/converter-cli.js +42 -12
  9. package/dist/es5/converter-cli.js.map +1 -1
  10. package/dist/es5/i3s-attributes-worker.js +1 -1
  11. package/dist/es5/i3s-attributes-worker.js.map +1 -1
  12. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +135 -0
  13. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  14. package/dist/es5/i3s-converter/helpers/geometry-converter.js +16 -10
  15. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  16. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +15 -1
  17. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  18. package/dist/es5/i3s-converter/i3s-converter.js +19 -15
  19. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  20. package/dist/es5/lib/utils/write-queue.js +3 -5
  21. package/dist/es5/lib/utils/write-queue.js.map +1 -1
  22. package/dist/es5/pgm-loader.js +1 -1
  23. package/dist/es5/pgm-loader.js.map +1 -1
  24. package/dist/esm/3d-tiles-attributes-worker.js +1 -1
  25. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
  26. package/dist/esm/converter-cli.js +37 -7
  27. package/dist/esm/converter-cli.js.map +1 -1
  28. package/dist/esm/i3s-attributes-worker.js +1 -1
  29. package/dist/esm/i3s-attributes-worker.js.map +1 -1
  30. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +117 -0
  31. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  32. package/dist/esm/i3s-converter/helpers/geometry-converter.js +13 -8
  33. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  34. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +15 -1
  35. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  36. package/dist/esm/i3s-converter/i3s-converter.js +5 -1
  37. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  38. package/dist/esm/lib/utils/write-queue.js +3 -5
  39. package/dist/esm/lib/utils/write-queue.js.map +1 -1
  40. package/dist/esm/pgm-loader.js +1 -1
  41. package/dist/esm/pgm-loader.js.map +1 -1
  42. package/dist/i3s-attributes-worker.js +3 -3
  43. package/dist/i3s-attributes-worker.js.map +3 -3
  44. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +12 -0
  45. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -0
  46. package/dist/i3s-converter/helpers/batch-ids-extensions.js +127 -0
  47. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  48. package/dist/i3s-converter/helpers/geometry-converter.js +16 -11
  49. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
  50. package/dist/i3s-converter/helpers/gltf-attributes.js +13 -0
  51. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  52. package/dist/i3s-converter/i3s-converter.js +4 -1
  53. package/dist/i3s-converter/types.d.ts +48 -0
  54. package/dist/i3s-converter/types.d.ts.map +1 -1
  55. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  56. package/dist/lib/utils/write-queue.js +3 -4
  57. package/package.json +15 -15
  58. package/src/converter-cli.ts +33 -7
  59. package/src/i3s-converter/helpers/batch-ids-extensions.ts +182 -0
  60. package/src/i3s-converter/helpers/geometry-converter.ts +26 -11
  61. package/src/i3s-converter/helpers/gltf-attributes.ts +15 -0
  62. package/src/i3s-converter/i3s-converter.ts +13 -6
  63. package/src/i3s-converter/types.ts +51 -0
  64. package/src/lib/utils/write-queue.ts +7 -5
@@ -0,0 +1,182 @@
1
+ import type {GLTFAccessorPostprocessed} from 'modules/gltf/src/lib/types/gltf-types';
2
+ import type {Image, MeshPrimitive} from 'modules/gltf/src/lib/types/gltf-postprocessed-schema';
3
+ import type {ExtFeatureMetadata, ExtFeatureMetadataAttribute} from '../types';
4
+
5
+ const EXT_MESH_FEATURES = 'EXT_mesh_features';
6
+ const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
7
+
8
+ /**
9
+ * Getting batchIds from 3DTilesNext extensions.
10
+ * @param attributes
11
+ * @param primitive
12
+ * @param textures
13
+ */
14
+ export function handleBatchIdsExtensions(
15
+ attributes: {
16
+ [key: string]: GLTFAccessorPostprocessed;
17
+ },
18
+ primitive: MeshPrimitive,
19
+ images: Image[]
20
+ ): number[] {
21
+ const extensions = primitive?.extensions;
22
+
23
+ if (!extensions) {
24
+ return [];
25
+ }
26
+
27
+ for (const [extensionName, extensionData] of Object.entries(extensions || {})) {
28
+ switch (extensionName) {
29
+ case EXT_FEATURE_METADATA:
30
+ return handleExtFeatureMetadataExtension(
31
+ attributes,
32
+ extensionData as ExtFeatureMetadata,
33
+ images
34
+ );
35
+ case EXT_MESH_FEATURES:
36
+ console.warn('EXT_mesh_features extension is not supported yet');
37
+ return [];
38
+ default:
39
+ return [];
40
+ }
41
+ }
42
+
43
+ return [];
44
+ }
45
+
46
+ /**
47
+ * Get batchIds from EXT_feature_metadata extension.
48
+ * Docs - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata
49
+ * @param attributes
50
+ * @param extFeatureMetadata
51
+ * @param textures
52
+ */
53
+ function handleExtFeatureMetadataExtension(
54
+ attributes: {
55
+ [key: string]: GLTFAccessorPostprocessed;
56
+ },
57
+ extFeatureMetadata: ExtFeatureMetadata,
58
+ images: Image[]
59
+ ): number[] {
60
+ // Take only first extension object to get batchIds attribute name.
61
+ const featureIdAttribute = extFeatureMetadata?.featureIdAttributes?.[0];
62
+
63
+ if (featureIdAttribute?.featureIds?.attribute) {
64
+ const batchIdsAttribute = attributes[featureIdAttribute.featureIds.attribute];
65
+ return batchIdsAttribute.value;
66
+ }
67
+
68
+ if (
69
+ featureIdAttribute?.featureIds?.hasOwnProperty('constant') &&
70
+ featureIdAttribute?.featureIds?.hasOwnProperty('divisor')
71
+ ) {
72
+ const featuresCount = attributes?.POSITIONS?.value.length / 3 || 0;
73
+ return generateImplicitFeatureIds(
74
+ featuresCount,
75
+ featureIdAttribute.featureIds.constant,
76
+ featureIdAttribute.featureIds.divisor
77
+ );
78
+ }
79
+
80
+ // Take only first extension object to get batchIds attribute name.
81
+ const featureIdTexture =
82
+ extFeatureMetadata?.featureIdTextures && extFeatureMetadata?.featureIdTextures[0];
83
+
84
+ if (featureIdTexture) {
85
+ const textureCoordinates = attributes.TEXCOORD_0.value;
86
+ return generateBatchIdsFromTexture(featureIdTexture, textureCoordinates, images);
87
+ }
88
+
89
+ return [];
90
+ }
91
+
92
+ /**
93
+ * Generates implicit feature ids
94
+ * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#implicit-feature-ids
95
+ * @param featuresCount
96
+ * @param constant
97
+ * @param devisor
98
+ */
99
+ function generateImplicitFeatureIds(
100
+ featuresCount: number,
101
+ constant: number = 0,
102
+ divisor: number = 0
103
+ ): number[] {
104
+ let featureIds: number[] = [];
105
+
106
+ if (divisor > 0) {
107
+ let currentValue = constant;
108
+ let devisorCounter = divisor;
109
+
110
+ for (let index = 0; index < featuresCount; index++) {
111
+ featureIds.push(currentValue);
112
+
113
+ devisorCounter -= 1;
114
+
115
+ if (devisorCounter === 0) {
116
+ currentValue++;
117
+ devisorCounter = divisor;
118
+ }
119
+ }
120
+ } else {
121
+ featureIds = Array<number>(featuresCount).fill(constant, 0, featuresCount);
122
+ }
123
+
124
+ return featureIds;
125
+ }
126
+
127
+ /**
128
+ * Get batchIds from texture.
129
+ * @param primitive
130
+ * @param featureIdTextures
131
+ */
132
+ function generateBatchIdsFromTexture(
133
+ featureIdTexture: ExtFeatureMetadataAttribute,
134
+ textureCoordinates: Float32Array,
135
+ images: Image[]
136
+ ) {
137
+ const CHANNELS_MAP = {
138
+ r: 0,
139
+ g: 1,
140
+ b: 2,
141
+ a: 3
142
+ };
143
+
144
+ const textureIndex = featureIdTexture?.featureIds?.texture?.index;
145
+ const featureChannel = featureIdTexture?.featureIds?.channels;
146
+
147
+ if (!featureChannel || textureIndex === undefined) {
148
+ return [];
149
+ }
150
+
151
+ const image = images[textureIndex];
152
+ const batchIds: number[] = [];
153
+ const channels = CHANNELS_MAP[featureChannel];
154
+
155
+ if (!image.compressed) {
156
+ for (let index = 0; index < textureCoordinates.length; index += 2) {
157
+ const u = textureCoordinates[index];
158
+ const v = textureCoordinates[index + 1];
159
+
160
+ const tx = Math.min((emod(u) * image.width) | 0, image.width - 1);
161
+ const ty = Math.min((emod(v) * image.height) | 0, image.height - 1);
162
+
163
+ const offset = (ty * image.width + tx) * image.components + channels;
164
+ const batchId = new Uint8Array(image.data)[offset];
165
+
166
+ batchIds.push(batchId);
167
+ }
168
+ } else {
169
+ console.warn(`Can't get batch Ids from ${image.mimeType} compressed texture`);
170
+ }
171
+
172
+ return batchIds;
173
+ }
174
+
175
+ /**
176
+ * Handle UVs if they are out of range [0,1].
177
+ * @param n
178
+ * @param m
179
+ */
180
+ function emod(n: number): number {
181
+ return ((n % 1) + 1) % 1;
182
+ }
@@ -1,3 +1,5 @@
1
+ import type {Image, MeshPrimitive} from 'modules/gltf/src/lib/types/gltf-postprocessed-schema';
2
+
1
3
  import {Vector3, Matrix4, Vector4} from '@math.gl/core';
2
4
  import {Ellipsoid} from '@math.gl/geospatial';
3
5
 
@@ -30,6 +32,7 @@ import {
30
32
  } from 'modules/gltf/src/lib/types/gltf-types';
31
33
  import {B3DMAttributesData /*transformI3SAttributesOnWorker */} from '../../i3s-attributes-worker';
32
34
  import {prepareDataForAttributesConversion} from './gltf-attributes';
35
+ import {handleBatchIdsExtensions} from './batch-ids-extensions';
33
36
 
34
37
  // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
35
38
  const DEFAULT_ROUGHNESS_FACTOR = 1;
@@ -407,9 +410,12 @@ function convertNode(
407
410
  const transformationMatrix = getCompositeTransformationMatrix(node, matrix);
408
411
 
409
412
  const mesh = node.mesh;
413
+ const images = node.images;
414
+
410
415
  if (mesh) {
411
416
  convertMesh(
412
417
  mesh,
418
+ images,
413
419
  cartographicOrigin,
414
420
  cartesianModelMatrix,
415
421
  attributesMap,
@@ -441,6 +447,7 @@ function convertNode(
441
447
  */
442
448
  function convertMesh(
443
449
  mesh: GLTFMeshPostprocessed,
450
+ images: Image[],
444
451
  cartographicOrigin: Vector3,
445
452
  cartesianModelMatrix: Matrix4,
446
453
  attributesMap: Map<string, ConvertedAttributes>,
@@ -498,7 +505,7 @@ function convertMesh(
498
505
 
499
506
  outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || [];
500
507
  outputAttributes.featureIndicesGroups.push(
501
- flattenBatchIds(getBatchIdsByAttributeName(attributes), primitive.indices?.value)
508
+ flattenBatchIds(getBatchIds(attributes, primitive, images), primitive.indices?.value)
502
509
  );
503
510
  }
504
511
  }
@@ -665,14 +672,23 @@ function flattenBatchIds(batchedIds: number[], indices: Uint8Array): number[] {
665
672
  }
666
673
 
667
674
  /**
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
675
+ * Get batchIds for featureIds creation
676
+ * @param attributes
677
+ * @param primitive
678
+ * @param textures
671
679
  */
672
- function getBatchIdsByAttributeName(attributes: {
673
- [key: string]: GLTFAccessorPostprocessed;
674
- }): number[] {
675
- let batchIds: number[] = [];
680
+ function getBatchIds(
681
+ attributes: {
682
+ [key: string]: GLTFAccessorPostprocessed;
683
+ },
684
+ primitive: MeshPrimitive,
685
+ images: Image[]
686
+ ): number[] {
687
+ const batchIds: number[] = handleBatchIdsExtensions(attributes, primitive, images);
688
+
689
+ if (batchIds.length) {
690
+ return batchIds;
691
+ }
676
692
 
677
693
  for (let index = 0; index < BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES.length; index++) {
678
694
  const possibleBatchIdAttributeName = BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES[index];
@@ -680,12 +696,11 @@ function getBatchIdsByAttributeName(attributes: {
680
696
  attributes[possibleBatchIdAttributeName] &&
681
697
  attributes[possibleBatchIdAttributeName].value
682
698
  ) {
683
- batchIds = attributes[possibleBatchIdAttributeName].value;
684
- break;
699
+ return attributes[possibleBatchIdAttributeName].value;
685
700
  }
686
701
  }
687
702
 
688
- return batchIds;
703
+ return [];
689
704
  }
690
705
 
691
706
  /**
@@ -35,6 +35,20 @@ export function prepareDataForAttributesConversion(tileContent: B3DMContent): B3
35
35
  tileContent.gltf?.nodes ||
36
36
  [];
37
37
 
38
+ const images =
39
+ tileContent.gltf?.images?.map((imageObject) => {
40
+ // Need data only for uncompressed images because we can't get batchIds from compressed textures.
41
+ const data = imageObject?.image?.compressed ? null : imageObject?.image?.data.subarray();
42
+ return {
43
+ data,
44
+ compressed: Boolean(imageObject?.image?.compressed),
45
+ height: imageObject.image.height,
46
+ width: imageObject.image.width,
47
+ components: imageObject.image.components,
48
+ mimeType: imageObject.mimeType
49
+ };
50
+ }) || [];
51
+
38
52
  const prepearedNodes = nodes.map((node) => {
39
53
  if (!node.mesh) {
40
54
  return node;
@@ -42,6 +56,7 @@ export function prepareDataForAttributesConversion(tileContent: B3DMContent): B3
42
56
 
43
57
  return {
44
58
  ...node,
59
+ images,
45
60
  mesh: {
46
61
  ...node.mesh,
47
62
  primitives: node.mesh?.primitives.map((primitive) => ({
@@ -938,12 +938,19 @@ export default class I3SConverter {
938
938
 
939
939
  if (this.generateTextures) {
940
940
  formats.push({name: '1', format: 'ktx2'});
941
- const ktx2TextureData = encode(texture.image, KTX2BasisWriterWorker, {
942
- ...KTX2BasisWriterWorker.options,
943
- source: this.workerSource.ktx2,
944
- reuseWorkers: true,
945
- _nodeWorkers: true
946
- });
941
+ // For Node.js texture.image.data is type of Buffer
942
+ const copyArrayBuffer = texture.image.data.subarray();
943
+ const arrayToEncode = new Uint8Array(copyArrayBuffer);
944
+ const ktx2TextureData = encode(
945
+ {...texture.image, data: arrayToEncode},
946
+ KTX2BasisWriterWorker,
947
+ {
948
+ ...KTX2BasisWriterWorker.options,
949
+ source: this.workerSource.ktx2,
950
+ reuseWorkers: true,
951
+ _nodeWorkers: true
952
+ }
953
+ );
947
954
 
948
955
  await this.writeTextureFile(ktx2TextureData, '1', 'ktx2', childPath, slpkChildPath);
949
956
  }
@@ -122,3 +122,54 @@ export type I3SMaterialWithTexture = {
122
122
  /** Texture content (image) */
123
123
  texture?: ImageDataType;
124
124
  };
125
+
126
+ /**
127
+ * 3DTilesNext EXT_feature_metadata extension
128
+ * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata
129
+ */
130
+ export type ExtFeatureMetadata = {
131
+ /** Feature ids definition in attributes */
132
+ featureIdAttributes?: ExtFeatureMetadataAttribute[];
133
+ /** Feature ids definition in textures */
134
+ featureIdTextures?: ExtFeatureMetadataAttribute[];
135
+ };
136
+
137
+ /**
138
+ * Attribute which described featureIds definition.
139
+ */
140
+ export type ExtFeatureMetadataAttribute = {
141
+ /** Name of feature table */
142
+ featureTable: string;
143
+ /** Described how feature ids are defined */
144
+ featureIds: ExtFeatureMetadataFeatureIds;
145
+ };
146
+
147
+ /**
148
+ * Defining featureIds by attributes or implicitly.
149
+ */
150
+ type ExtFeatureMetadataFeatureIds = {
151
+ /** Name of attribute where featureIds are defined */
152
+ attribute?: string;
153
+ /** Sets a constant feature ID for each vertex. The default is 0. */
154
+ constant?: number;
155
+ /** Sets the rate at which feature IDs increment.
156
+ * If divisor is zero then constant is used.
157
+ * If divisor is greater than zero the feature ID increments once per divisor sets of vertices, starting at constant.
158
+ * The default is 0
159
+ */
160
+ divisor?: number;
161
+ /** gLTF textureInfo object - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/specification/2.0/schema/textureInfo.schema.json */
162
+ texture?: ExtFeatureMetadataTexture;
163
+ /** Must be a single channel ("r", "g", "b", or "a") */
164
+ channels?: 'r' | 'g' | 'b' | 'a';
165
+ };
166
+
167
+ /**
168
+ * Reference to a texture.
169
+ */
170
+ type ExtFeatureMetadataTexture = {
171
+ /** The set index of texture's TEXCOORD attribute used for texture coordinate mapping.*/
172
+ texCoord: number;
173
+ /** The index of the texture. */
174
+ index: number;
175
+ };
@@ -57,19 +57,21 @@ export default class WriteQueue<T extends WriteQueueItem> extends Queue<T> {
57
57
  archiveKeys.push(archiveKey);
58
58
  promises.push(writePromise);
59
59
  }
60
- const writeResults = await Promise.all(promises);
60
+ const writeResults = await Promise.allSettled(promises);
61
61
  this.updateFileMap(archiveKeys, writeResults);
62
62
  }
63
63
  this.writePromise = null;
64
64
  }
65
65
 
66
- private updateFileMap(archiveKeys: (string | undefined)[], writeResults: string[]) {
66
+ private updateFileMap(
67
+ archiveKeys: (string | undefined)[],
68
+ writeResults: PromiseSettledResult<string>[]
69
+ ) {
67
70
  for (let i = 0; i < archiveKeys.length; i++) {
68
71
  const archiveKey = archiveKeys[i];
69
- if (!archiveKey) {
70
- continue;
72
+ if (archiveKey && 'value' in writeResults[i]) {
73
+ this.fileMap[archiveKey] = (writeResults[i] as PromiseFulfilledResult<string>).value;
71
74
  }
72
- this.fileMap[archiveKey] = writeResults[i];
73
75
  }
74
76
  }
75
77
  }