@loaders.gl/tile-converter 3.2.7 → 3.3.0-alpha.3

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 (77) 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 +1105 -621
  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 +146 -0
  13. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  14. package/dist/es5/i3s-converter/helpers/feature-attributes.js +60 -0
  15. package/dist/es5/i3s-converter/helpers/feature-attributes.js.map +1 -0
  16. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +39 -7
  17. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  18. package/dist/es5/i3s-converter/helpers/geometry-converter.js +177 -59
  19. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  20. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +15 -1
  21. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  22. package/dist/es5/i3s-converter/i3s-converter.js +50 -51
  23. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  24. package/dist/es5/lib/utils/write-queue.js +3 -5
  25. package/dist/es5/lib/utils/write-queue.js.map +1 -1
  26. package/dist/es5/pgm-loader.js +1 -1
  27. package/dist/es5/pgm-loader.js.map +1 -1
  28. package/dist/esm/3d-tiles-attributes-worker.js +1 -1
  29. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
  30. package/dist/esm/converter-cli.js +37 -7
  31. package/dist/esm/converter-cli.js.map +1 -1
  32. package/dist/esm/i3s-attributes-worker.js +1 -1
  33. package/dist/esm/i3s-attributes-worker.js.map +1 -1
  34. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +128 -0
  35. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
  36. package/dist/esm/i3s-converter/helpers/feature-attributes.js +34 -0
  37. package/dist/esm/i3s-converter/helpers/feature-attributes.js.map +1 -0
  38. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +23 -7
  39. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  40. package/dist/esm/i3s-converter/helpers/geometry-converter.js +145 -38
  41. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  42. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +15 -1
  43. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
  44. package/dist/esm/i3s-converter/i3s-converter.js +21 -27
  45. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  46. package/dist/esm/lib/utils/write-queue.js +3 -5
  47. package/dist/esm/lib/utils/write-queue.js.map +1 -1
  48. package/dist/esm/pgm-loader.js +1 -1
  49. package/dist/esm/pgm-loader.js.map +1 -1
  50. package/dist/i3s-attributes-worker.js +3 -3
  51. package/dist/i3s-attributes-worker.js.map +3 -3
  52. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +12 -0
  53. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -0
  54. package/dist/i3s-converter/helpers/batch-ids-extensions.js +138 -0
  55. package/dist/i3s-converter/helpers/feature-attributes.d.ts +24 -0
  56. package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -0
  57. package/dist/i3s-converter/helpers/feature-attributes.js +55 -0
  58. package/dist/i3s-converter/helpers/geometry-attributes.js +26 -7
  59. package/dist/i3s-converter/helpers/geometry-converter.d.ts +9 -2
  60. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  61. package/dist/i3s-converter/helpers/geometry-converter.js +140 -44
  62. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
  63. package/dist/i3s-converter/helpers/gltf-attributes.js +13 -0
  64. package/dist/i3s-converter/i3s-converter.d.ts +7 -14
  65. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  66. package/dist/i3s-converter/i3s-converter.js +44 -35
  67. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  68. package/dist/lib/utils/write-queue.js +3 -4
  69. package/package.json +15 -15
  70. package/src/converter-cli.ts +33 -7
  71. package/src/i3s-converter/helpers/batch-ids-extensions.ts +199 -0
  72. package/src/i3s-converter/helpers/feature-attributes.ts +65 -0
  73. package/src/i3s-converter/helpers/geometry-attributes.ts +30 -7
  74. package/src/i3s-converter/helpers/geometry-converter.ts +187 -48
  75. package/src/i3s-converter/helpers/gltf-attributes.ts +15 -0
  76. package/src/i3s-converter/i3s-converter.ts +38 -41
  77. package/src/lib/utils/write-queue.ts +7 -5
@@ -0,0 +1,12 @@
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
+ /**
4
+ * Getting batchIds from 3DTilesNext extensions.
5
+ * @param attributes
6
+ * @param primitive
7
+ * @param textures
8
+ */
9
+ export declare function handleBatchIdsExtensions(attributes: {
10
+ [key: string]: GLTFAccessorPostprocessed;
11
+ }, primitive: MeshPrimitive, images: Image[]): number[];
12
+ //# sourceMappingURL=batch-ids-extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-ids-extensions.d.ts","sourceRoot":"","sources":["../../../src/i3s-converter/helpers/batch-ids-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,uCAAuC,CAAC;AACrF,OAAO,KAAK,EAAC,KAAK,EAAE,aAAa,EAAC,MAAM,sDAAsD,CAAC;AAS/F;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE;IACV,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAAC;CAC1C,EACD,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,KAAK,EAAE,GACd,MAAM,EAAE,CAwBV"}
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleBatchIdsExtensions = void 0;
4
+ const EXT_MESH_FEATURES = 'EXT_mesh_features';
5
+ const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
6
+ /**
7
+ * Getting batchIds from 3DTilesNext extensions.
8
+ * @param attributes
9
+ * @param primitive
10
+ * @param textures
11
+ */
12
+ function handleBatchIdsExtensions(attributes, primitive, images) {
13
+ const extensions = primitive?.extensions;
14
+ if (!extensions) {
15
+ return [];
16
+ }
17
+ for (const [extensionName, extensionData] of Object.entries(extensions || {})) {
18
+ switch (extensionName) {
19
+ case EXT_FEATURE_METADATA:
20
+ return handleExtFeatureMetadataExtension(attributes, extensionData, images);
21
+ case EXT_MESH_FEATURES:
22
+ console.warn('EXT_mesh_features extension is not supported yet');
23
+ return [];
24
+ default:
25
+ return [];
26
+ }
27
+ }
28
+ return [];
29
+ }
30
+ exports.handleBatchIdsExtensions = handleBatchIdsExtensions;
31
+ /**
32
+ * Get batchIds from EXT_feature_metadata extension.
33
+ * Docs - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata
34
+ * @param attributes
35
+ * @param extFeatureMetadata
36
+ * @param textures
37
+ */
38
+ function handleExtFeatureMetadataExtension(attributes, extFeatureMetadata, images) {
39
+ // Take only first extension object to get batchIds attribute name.
40
+ const featureIdAttribute = extFeatureMetadata?.featureIdAttributes?.[0];
41
+ if (featureIdAttribute?.featureIds?.attribute) {
42
+ const batchIdsAttribute = attributes[featureIdAttribute.featureIds.attribute];
43
+ return batchIdsAttribute.value;
44
+ }
45
+ if (featureIdAttribute?.featureIds?.hasOwnProperty('constant') &&
46
+ featureIdAttribute?.featureIds?.hasOwnProperty('divisor')) {
47
+ const featuresCount = attributes?.POSITIONS?.value.length / 3 || 0;
48
+ return generateImplicitFeatureIds(featuresCount, featureIdAttribute.featureIds.constant, featureIdAttribute.featureIds.divisor);
49
+ }
50
+ // Take only first extension object to get batchIds attribute name.
51
+ const featureIdTexture = extFeatureMetadata?.featureIdTextures && extFeatureMetadata?.featureIdTextures[0];
52
+ if (featureIdTexture) {
53
+ const textureAttributeIndex = featureIdTexture?.featureIds?.texture?.texCoord || 0;
54
+ const textCoordAttribute = `TEXCOORD_${textureAttributeIndex}`;
55
+ const textureCoordinates = attributes[textCoordAttribute].value;
56
+ return generateBatchIdsFromTexture(featureIdTexture, textureCoordinates, images);
57
+ }
58
+ // Take only first extension texture to get batchIds from the root EXT_feature_metadata object.
59
+ const featureTexture = extFeatureMetadata?.featureTextures && extFeatureMetadata?.featureTextures[0];
60
+ /**
61
+ * TODO need to get batchIds from root extension
62
+ */
63
+ if (featureTexture) {
64
+ console.warn("EXT_feature_metadata doesn't yet support featureTextures in primitive");
65
+ return [];
66
+ }
67
+ return [];
68
+ }
69
+ /**
70
+ * Generates implicit feature ids
71
+ * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#implicit-feature-ids
72
+ * @param featuresCount
73
+ * @param constant
74
+ * @param devisor
75
+ */
76
+ function generateImplicitFeatureIds(featuresCount, constant = 0, divisor = 0) {
77
+ let featureIds = [];
78
+ if (divisor > 0) {
79
+ let currentValue = constant;
80
+ let devisorCounter = divisor;
81
+ for (let index = 0; index < featuresCount; index++) {
82
+ featureIds.push(currentValue);
83
+ devisorCounter -= 1;
84
+ if (devisorCounter === 0) {
85
+ currentValue++;
86
+ devisorCounter = divisor;
87
+ }
88
+ }
89
+ }
90
+ else {
91
+ featureIds = Array(featuresCount).fill(constant, 0, featuresCount);
92
+ }
93
+ return featureIds;
94
+ }
95
+ /**
96
+ * Get batchIds from texture.
97
+ * @param primitive
98
+ * @param featureIdTextures
99
+ */
100
+ function generateBatchIdsFromTexture(featureIdTexture, textureCoordinates, images) {
101
+ const CHANNELS_MAP = {
102
+ r: 0,
103
+ g: 1,
104
+ b: 2,
105
+ a: 3
106
+ };
107
+ const textureIndex = featureIdTexture?.featureIds?.texture?.index;
108
+ const featureChannel = featureIdTexture?.featureIds?.channels;
109
+ if (!featureChannel || textureIndex === undefined) {
110
+ return [];
111
+ }
112
+ const image = images[textureIndex];
113
+ const batchIds = [];
114
+ const channels = CHANNELS_MAP[featureChannel];
115
+ if (!image.compressed) {
116
+ for (let index = 0; index < textureCoordinates.length; index += 2) {
117
+ const u = textureCoordinates[index];
118
+ const v = textureCoordinates[index + 1];
119
+ const tx = Math.min((emod(u) * image.width) | 0, image.width - 1);
120
+ const ty = Math.min((emod(v) * image.height) | 0, image.height - 1);
121
+ const offset = (ty * image.width + tx) * image.components + channels;
122
+ const batchId = new Uint8Array(image.data)[offset];
123
+ batchIds.push(batchId);
124
+ }
125
+ }
126
+ else {
127
+ console.warn(`Can't get batch Ids from ${image.mimeType} compressed texture`);
128
+ }
129
+ return batchIds;
130
+ }
131
+ /**
132
+ * Handle UVs if they are out of range [0,1].
133
+ * @param n
134
+ * @param m
135
+ */
136
+ function emod(n) {
137
+ return ((n % 1) + 1) % 1;
138
+ }
@@ -0,0 +1,24 @@
1
+ import type { FeatureTableJson } from '@loaders.gl/3d-tiles';
2
+ /**
3
+ * Takes attributes from property table based on featureIds.
4
+ * If there is no property value for particular featureId (index) the property will be null.
5
+ * Example:
6
+ * Initial data:
7
+ * OBJECTID: [0, 1, 5]
8
+ * component: ['Windows', 'Frames', 'Wall', 'Roof', 'Skylight']
9
+ * Result:
10
+ * OBJECTID: [0, 1, 5]
11
+ * component: ['Windows', 'Frames', 'null']
12
+ * @param featureIds
13
+ * @param propertyTable
14
+ */
15
+ export declare function flattenPropertyTableByFeatureIds(featureIds: number[], propertyTable: FeatureTableJson): FeatureTableJson;
16
+ /**
17
+ * Check that all attributes in propertyTable have the same length as FeatureIds.
18
+ * If there are differencies between lengths we should flatten property table based on exiesting featureIds.
19
+ * @param featureIds
20
+ * @param propertyTable
21
+ * @returns
22
+ */
23
+ export declare function checkPropertiesLength(featureIds: number[], propertyTable: FeatureTableJson): boolean;
24
+ //# sourceMappingURL=feature-attributes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-attributes.d.ts","sourceRoot":"","sources":["../../../src/i3s-converter/helpers/feature-attributes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,gCAAgC,CAC9C,UAAU,EAAE,MAAM,EAAE,EACpB,aAAa,EAAE,gBAAgB,GAC9B,gBAAgB,CAQlB;AAkBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAAE,EACpB,aAAa,EAAE,gBAAgB,GAC9B,OAAO,CAUT"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkPropertiesLength = exports.flattenPropertyTableByFeatureIds = void 0;
4
+ /**
5
+ * Takes attributes from property table based on featureIds.
6
+ * If there is no property value for particular featureId (index) the property will be null.
7
+ * Example:
8
+ * Initial data:
9
+ * OBJECTID: [0, 1, 5]
10
+ * component: ['Windows', 'Frames', 'Wall', 'Roof', 'Skylight']
11
+ * Result:
12
+ * OBJECTID: [0, 1, 5]
13
+ * component: ['Windows', 'Frames', 'null']
14
+ * @param featureIds
15
+ * @param propertyTable
16
+ */
17
+ function flattenPropertyTableByFeatureIds(featureIds, propertyTable) {
18
+ const resultPropertyTable = {};
19
+ for (const propertyName in propertyTable) {
20
+ const properties = propertyTable[propertyName];
21
+ resultPropertyTable[propertyName] = getPropertiesByFeatureIds(properties, featureIds);
22
+ }
23
+ return resultPropertyTable;
24
+ }
25
+ exports.flattenPropertyTableByFeatureIds = flattenPropertyTableByFeatureIds;
26
+ /**
27
+ * Getting properties by featureId index
28
+ * @param properties
29
+ * @param featureIds
30
+ */
31
+ function getPropertiesByFeatureIds(properties, featureIds) {
32
+ const resultProperties = [];
33
+ for (const featureId of featureIds) {
34
+ const property = properties[featureId] || null;
35
+ resultProperties.push(property);
36
+ }
37
+ return resultProperties;
38
+ }
39
+ /**
40
+ * Check that all attributes in propertyTable have the same length as FeatureIds.
41
+ * If there are differencies between lengths we should flatten property table based on exiesting featureIds.
42
+ * @param featureIds
43
+ * @param propertyTable
44
+ * @returns
45
+ */
46
+ function checkPropertiesLength(featureIds, propertyTable) {
47
+ let needFlatten = false;
48
+ for (const attribute of Object.values(propertyTable)) {
49
+ if (featureIds.length !== attribute.length) {
50
+ needFlatten = true;
51
+ }
52
+ }
53
+ return needFlatten;
54
+ }
55
+ exports.checkPropertiesLength = checkPropertiesLength;
@@ -38,30 +38,49 @@ exports.generateAttributes = generateAttributes;
38
38
  function calculateFaceRangesAndFeaturesCount(featureIndices) {
39
39
  let rangeIndex = 1;
40
40
  let featureIndex = 1;
41
- let currentFeatureId = featureIndices[0];
41
+ let currentFeatureId = getFrequentValue(featureIndices.slice(0, VALUES_PER_VERTEX));
42
42
  const faceRangeList = [];
43
43
  const featureIds = [];
44
44
  const uniqueFeatureIds = [currentFeatureId];
45
45
  faceRangeList[0] = 0;
46
46
  featureIds[0] = currentFeatureId;
47
- for (let index = 1; index < featureIndices.length; index++) {
48
- if (currentFeatureId !== featureIndices[index]) {
47
+ for (let index = VALUES_PER_VERTEX; index < featureIndices.length; index += VALUES_PER_VERTEX) {
48
+ const newFeatureId = getFrequentValue(featureIndices.slice(index, index + VALUES_PER_VERTEX));
49
+ if (currentFeatureId !== newFeatureId) {
49
50
  faceRangeList[rangeIndex] = index / VALUES_PER_VERTEX - 1;
50
51
  faceRangeList[rangeIndex + 1] = index / VALUES_PER_VERTEX;
51
- featureIds[featureIndex] = featureIndices[index];
52
- if (!uniqueFeatureIds.includes(featureIndices[index])) {
53
- uniqueFeatureIds.push(featureIndices[index]);
52
+ featureIds[featureIndex] = newFeatureId;
53
+ if (!uniqueFeatureIds.includes(newFeatureId)) {
54
+ uniqueFeatureIds.push(newFeatureId);
54
55
  }
55
56
  rangeIndex += 2;
56
57
  featureIndex += 1;
57
58
  }
58
- currentFeatureId = featureIndices[index];
59
+ currentFeatureId = newFeatureId;
59
60
  }
60
61
  faceRangeList[rangeIndex] = featureIndices.length / VALUES_PER_VERTEX - 1;
61
62
  const faceRange = new Uint32Array(faceRangeList);
62
63
  const featureCount = uniqueFeatureIds.length;
63
64
  return { faceRange, featureCount, featureIds };
64
65
  }
66
+ /**
67
+ * Find most frequent value to avoid situation where one vertex can be part of multiple features (objects).
68
+ * @param values
69
+ */
70
+ function getFrequentValue(values) {
71
+ const map = {};
72
+ let mostFrequentValue = values[0];
73
+ let maxCount = 1;
74
+ for (const value of values) {
75
+ // Save item and it's frequency count to the map.
76
+ map[value] = (map[value] || 0) + 1;
77
+ // Find max count of frequency.
78
+ maxCount = maxCount > map[value] ? maxCount : map[value];
79
+ // Find the most frequent value.
80
+ mostFrequentValue = maxCount > map[value] ? mostFrequentValue : value;
81
+ }
82
+ return mostFrequentValue;
83
+ }
65
84
  /**
66
85
  * Generate list of attribute object grouped by feature ids.
67
86
  * @param attributes
@@ -1,5 +1,6 @@
1
+ import type { B3DMContent, FeatureTableJson } from '@loaders.gl/3d-tiles';
2
+ import { Tile3D } from '@loaders.gl/tiles';
1
3
  import { ConvertedAttributes, I3SConvertedResources } from '../types';
2
- import { B3DMContent } from '@loaders.gl/3d-tiles';
3
4
  import { AttributeStorageInfo } from '@loaders.gl/i3s';
4
5
  import { Geoid } from '@math.gl/geoid';
5
6
  import { B3DMAttributesData } from '../../i3s-attributes-worker';
@@ -15,7 +16,7 @@ import { B3DMAttributesData } from '../../i3s-attributes-worker';
15
16
  * @param geoidHeightModel - model to convert elevation from elipsoidal to geoid
16
17
  * @returns Array of node resources to create one or more i3s nodes
17
18
  */
18
- export default function convertB3dmToI3sGeometry(tileContent: B3DMContent, nodeId: number, featuresHashArray: string[], attributeStorageInfo: AttributeStorageInfo[] | undefined, draco: boolean, generateBoundingVolumes: boolean, geoidHeightModel: Geoid, workerSource: {
19
+ export default function convertB3dmToI3sGeometry(tileContent: B3DMContent, nodeId: number, propertyTable: FeatureTableJson | null, featuresHashArray: string[], attributeStorageInfo: AttributeStorageInfo[] | undefined, draco: boolean, generateBoundingVolumes: boolean, geoidHeightModel: Geoid, workerSource: {
19
20
  [key: string]: string;
20
21
  }): Promise<I3SConvertedResources[] | null>;
21
22
  /**
@@ -26,4 +27,10 @@ export default function convertB3dmToI3sGeometry(tileContent: B3DMContent, nodeI
26
27
  * @returns map of converted geometry attributes
27
28
  */
28
29
  export declare function convertAttributes(attributesData: B3DMAttributesData, useCartesianPositions: boolean): Promise<Map<string, ConvertedAttributes>>;
30
+ /**
31
+ * Find property table in tile
32
+ * For example it can be batchTable for b3dm files or property table in gLTF extension.
33
+ * @param sourceTile
34
+ */
35
+ export declare function getPropertyTable(sourceTile: Tile3D): FeatureTableJson | null;
29
36
  //# sourceMappingURL=geometry-converter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"geometry-converter.d.ts","sourceRoot":"","sources":["../../../src/i3s-converter/helpers/geometry-converter.ts"],"names":[],"mappings":"AASA,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EAGtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EACL,oBAAoB,EAIrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAC,KAAK,EAAC,MAAM,gBAAgB,CAAC;AAMrC,OAAO,EAAC,kBAAkB,EAAqC,MAAM,6BAA6B,CAAC;AAwBnG;;;;;;;;;;;GAWG;AACH,wBAA8B,wBAAwB,CACpD,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,MAAM,EAAE,EAC3B,oBAAoB,EAAE,oBAAoB,EAAE,GAAG,SAAS,EACxD,KAAK,EAAE,OAAO,EACd,uBAAuB,EAAE,OAAO,EAChC,gBAAgB,EAAE,KAAK,EACvB,YAAY,EAAE;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,2CAoEtC;AAiID;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,kBAAkB,EAClC,qBAAqB,EAAE,OAAO,GAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CA0C3C"}
1
+ {"version":3,"file":"geometry-converter.d.ts","sourceRoot":"","sources":["../../../src/i3s-converter/helpers/geometry-converter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,WAAW,EAAE,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AAQxE,OAAO,EAAC,MAAM,EAAC,MAAM,mBAAmB,CAAC;AAKzC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EAGtB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,oBAAoB,EAIrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAC,KAAK,EAAC,MAAM,gBAAgB,CAAC;AAMrC,OAAO,EAAC,kBAAkB,EAAqC,MAAM,6BAA6B,CAAC;AA6BnG;;;;;;;;;;;GAWG;AACH,wBAA8B,wBAAwB,CACpD,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,gBAAgB,GAAG,IAAI,EACtC,iBAAiB,EAAE,MAAM,EAAE,EAC3B,oBAAoB,EAAE,oBAAoB,EAAE,GAAG,SAAS,EACxD,KAAK,EAAE,OAAO,EACd,uBAAuB,EAAE,OAAO,EAChC,gBAAgB,EAAE,KAAK,EACvB,YAAY,EAAE;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,2CAqEtC;AAuID;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,kBAAkB,EAClC,qBAAqB,EAAE,OAAO,GAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CA0C3C;AAu7BD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAoB5E"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.convertAttributes = void 0;
6
+ exports.getPropertyTable = exports.convertAttributes = void 0;
7
7
  const core_1 = require("@math.gl/core");
8
8
  const geospatial_1 = require("@math.gl/geospatial");
9
9
  const draco_1 = require("@loaders.gl/draco");
@@ -13,6 +13,8 @@ const md5_1 = __importDefault(require("md5"));
13
13
  const geometry_attributes_1 = require("./geometry-attributes");
14
14
  const coordinate_converter_1 = require("./coordinate-converter");
15
15
  const gltf_attributes_1 = require("./gltf-attributes");
16
+ const batch_ids_extensions_1 = require("./batch-ids-extensions");
17
+ const feature_attributes_1 = require("./feature-attributes");
16
18
  // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
17
19
  const DEFAULT_ROUGHNESS_FACTOR = 1;
18
20
  const DEFAULT_METALLIC_FACTOR = 1;
@@ -29,6 +31,8 @@ const OBJECT_ID_TYPE = 'Oid32';
29
31
  * BATCHID - Legacy attribute name which includes batch info.
30
32
  */
31
33
  const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID'];
34
+ const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
35
+ const EXT_MESH_FEATURES = 'EXT_mesh_features';
32
36
  let scratchVector = new core_1.Vector3();
33
37
  /**
34
38
  * Convert binary data from b3dm file to i3s resources
@@ -42,7 +46,7 @@ let scratchVector = new core_1.Vector3();
42
46
  * @param geoidHeightModel - model to convert elevation from elipsoidal to geoid
43
47
  * @returns Array of node resources to create one or more i3s nodes
44
48
  */
45
- async function convertB3dmToI3sGeometry(tileContent, nodeId, featuresHashArray, attributeStorageInfo, draco, generateBoundingVolumes, geoidHeightModel, workerSource) {
49
+ async function convertB3dmToI3sGeometry(tileContent, nodeId, propertyTable, featuresHashArray, attributeStorageInfo, draco, generateBoundingVolumes, geoidHeightModel, workerSource) {
46
50
  const useCartesianPositions = generateBoundingVolumes;
47
51
  const materialAndTextureList = convertMaterials(tileContent.gltf?.materials);
48
52
  const dataForAttributesConversion = (0, gltf_attributes_1.prepareDataForAttributesConversion)(tileContent);
@@ -86,6 +90,7 @@ async function convertB3dmToI3sGeometry(tileContent, nodeId, featuresHashArray,
86
90
  tileContent,
87
91
  nodeId: nodesCounter,
88
92
  featuresHashArray,
93
+ propertyTable,
89
94
  attributeStorageInfo,
90
95
  draco,
91
96
  workerSource
@@ -131,7 +136,7 @@ function _generateBoundingVolumesFromGeometry(convertedAttributesMap, geoidHeigh
131
136
  * @param params.draco - is converter should create draco compressed geometry
132
137
  * @returns Array of I3S node resources
133
138
  */
134
- async function _makeNodeResources({ convertedAttributes, material, texture, tileContent, nodeId, featuresHashArray, attributeStorageInfo, draco, workerSource }) {
139
+ async function _makeNodeResources({ convertedAttributes, material, texture, tileContent, nodeId, featuresHashArray, propertyTable, attributeStorageInfo, draco, workerSource }) {
135
140
  const boundingVolumes = convertedAttributes.boundingVolumes;
136
141
  const vertexCount = convertedAttributes.positions.length / VALUES_PER_VERTEX;
137
142
  const { faceRange, featureIds, positions, normals, colors, texCoords, featureCount } = (0, geometry_attributes_1.generateAttributes)(convertedAttributes);
@@ -152,7 +157,10 @@ async function _makeNodeResources({ convertedAttributes, material, texture, tile
152
157
  faceRange
153
158
  }, workerSource.draco)
154
159
  : null;
155
- const attributes = convertBatchTableToAttributeBuffers(tileContent.batchTableJson, featureIds, attributeStorageInfo);
160
+ let attributes = [];
161
+ if (attributeStorageInfo && propertyTable) {
162
+ attributes = convertPropertyTableToAttributeBuffers(featureIds, propertyTable, attributeStorageInfo);
163
+ }
156
164
  return {
157
165
  geometry: fileBuffer,
158
166
  compressedGeometry,
@@ -258,8 +266,9 @@ function getCompositeTransformationMatrix(node, matrix) {
258
266
  function convertNode(node, cartographicOrigin, cartesianModelMatrix, attributesMap, useCartesianPositions, matrix = new core_1.Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])) {
259
267
  const transformationMatrix = getCompositeTransformationMatrix(node, matrix);
260
268
  const mesh = node.mesh;
269
+ const images = node.images;
261
270
  if (mesh) {
262
- convertMesh(mesh, cartographicOrigin, cartesianModelMatrix, attributesMap, useCartesianPositions, transformationMatrix);
271
+ convertMesh(mesh, images, cartographicOrigin, cartesianModelMatrix, attributesMap, useCartesianPositions, transformationMatrix);
263
272
  }
264
273
  convertNodes(node.children || [], cartographicOrigin, cartesianModelMatrix, attributesMap, useCartesianPositions, transformationMatrix);
265
274
  }
@@ -274,7 +283,7 @@ function convertNode(node, cartographicOrigin, cartesianModelMatrix, attributesM
274
283
 
275
284
  * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
276
285
  */
277
- function convertMesh(mesh, cartographicOrigin, cartesianModelMatrix, attributesMap, useCartesianPositions = false, matrix = new core_1.Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])) {
286
+ function convertMesh(mesh, images, cartographicOrigin, cartesianModelMatrix, attributesMap, useCartesianPositions = false, matrix = new core_1.Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])) {
278
287
  for (const primitive of mesh.primitives) {
279
288
  let outputAttributes = null;
280
289
  if (primitive.material) {
@@ -309,7 +318,7 @@ function convertMesh(mesh, cartographicOrigin, cartesianModelMatrix, attributesM
309
318
  outputAttributes.texCoords = (0, loader_utils_1.concatenateTypedArrays)(outputAttributes.texCoords, flattenTexCoords(attributes.TEXCOORD_0 && attributes.TEXCOORD_0.value, primitive.indices?.value));
310
319
  outputAttributes.colors = (0, loader_utils_1.concatenateTypedArrays)(outputAttributes.colors, flattenColors(attributes.COLOR_0, primitive.indices?.value));
311
320
  outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || [];
312
- outputAttributes.featureIndicesGroups.push(flattenBatchIds(getBatchIdsByAttributeName(attributes), primitive.indices?.value));
321
+ outputAttributes.featureIndicesGroups.push(flattenBatchIds(getBatchIds(attributes, primitive, images), primitive.indices?.value));
313
322
  }
314
323
  }
315
324
  /**
@@ -446,21 +455,24 @@ function flattenBatchIds(batchedIds, indices) {
446
455
  return newBatchIds;
447
456
  }
448
457
  /**
449
- * Return batchIds based on possible attribute names for different kind of maps.
450
- * @param attributes - the gltf primitive attributes
451
- * @returns batch ids attribute
458
+ * Get batchIds for featureIds creation
459
+ * @param attributes
460
+ * @param primitive
461
+ * @param textures
452
462
  */
453
- function getBatchIdsByAttributeName(attributes) {
454
- let batchIds = [];
463
+ function getBatchIds(attributes, primitive, images) {
464
+ const batchIds = (0, batch_ids_extensions_1.handleBatchIdsExtensions)(attributes, primitive, images);
465
+ if (batchIds.length) {
466
+ return batchIds;
467
+ }
455
468
  for (let index = 0; index < BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES.length; index++) {
456
469
  const possibleBatchIdAttributeName = BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES[index];
457
470
  if (attributes[possibleBatchIdAttributeName] &&
458
471
  attributes[possibleBatchIdAttributeName].value) {
459
- batchIds = attributes[possibleBatchIdAttributeName].value;
460
- break;
472
+ return attributes[possibleBatchIdAttributeName].value;
461
473
  }
462
474
  }
463
- return batchIds;
475
+ return [];
464
476
  }
465
477
  /**
466
478
  * Convert GLTF material to I3S material definitions and textures
@@ -738,43 +750,53 @@ function replaceIndicesByUnique(indicesArray, featureMap) {
738
750
  }
739
751
  }
740
752
  /**
741
- * Convert batch table data to attribute buffers.
742
- * @param {Object} batchTable - table with metadata for particular feature.
753
+ * Convert property table data to attribute buffers.
754
+ * @param {Object} propertyTable - table with metadata for particular feature.
743
755
  * @param {Array} featureIds
744
756
  * @param {Array} attributeStorageInfo
745
757
  * @returns {Array} - Array of file buffers.
746
758
  */
747
- function convertBatchTableToAttributeBuffers(batchTable, featureIds, attributeStorageInfo) {
759
+ function convertPropertyTableToAttributeBuffers(featureIds, propertyTable, attributeStorageInfo) {
748
760
  const attributeBuffers = [];
749
- if (batchTable) {
750
- const batchTableWithFeatureIds = {
751
- OBJECTID: featureIds,
752
- ...batchTable
753
- };
754
- for (const key in batchTableWithFeatureIds) {
755
- const type = getAttributeType(key, attributeStorageInfo);
756
- let attributeBuffer = null;
757
- switch (type) {
758
- case OBJECT_ID_TYPE:
759
- case SHORT_INT_TYPE:
760
- attributeBuffer = generateShortIntegerAttributeBuffer(batchTableWithFeatureIds[key]);
761
- break;
762
- case DOUBLE_TYPE:
763
- attributeBuffer = generateDoubleAttributeBuffer(batchTableWithFeatureIds[key]);
764
- break;
765
- case STRING_TYPE:
766
- attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
767
- break;
768
- default:
769
- attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
770
- }
771
- if (attributeBuffer) {
772
- attributeBuffers.push(attributeBuffer);
773
- }
774
- }
761
+ const needFlattenPropertyTable = (0, feature_attributes_1.checkPropertiesLength)(featureIds, propertyTable);
762
+ const properties = needFlattenPropertyTable
763
+ ? (0, feature_attributes_1.flattenPropertyTableByFeatureIds)(featureIds, propertyTable)
764
+ : propertyTable;
765
+ const propertyTableWithObjectIds = {
766
+ OBJECTID: featureIds,
767
+ ...properties
768
+ };
769
+ for (const propertyName in propertyTableWithObjectIds) {
770
+ const type = getAttributeType(propertyName, attributeStorageInfo);
771
+ const value = propertyTableWithObjectIds[propertyName];
772
+ const attributeBuffer = generateAttributeBuffer(type, value);
773
+ attributeBuffers.push(attributeBuffer);
775
774
  }
776
775
  return attributeBuffers;
777
776
  }
777
+ /**
778
+ * Generates attribute buffer based on attribute type
779
+ * @param type
780
+ * @param value
781
+ */
782
+ function generateAttributeBuffer(type, value) {
783
+ let attributeBuffer;
784
+ switch (type) {
785
+ case OBJECT_ID_TYPE:
786
+ case SHORT_INT_TYPE:
787
+ attributeBuffer = generateShortIntegerAttributeBuffer(value);
788
+ break;
789
+ case DOUBLE_TYPE:
790
+ attributeBuffer = generateDoubleAttributeBuffer(value);
791
+ break;
792
+ case STRING_TYPE:
793
+ attributeBuffer = generateStringAttributeBuffer(value);
794
+ break;
795
+ default:
796
+ attributeBuffer = generateStringAttributeBuffer(value);
797
+ }
798
+ return attributeBuffer;
799
+ }
778
800
  /**
779
801
  * Return attribute type.
780
802
  * @param {String} key
@@ -899,3 +921,77 @@ function generateFeatureIndexAttribute(featureIndex, faceRange) {
899
921
  }
900
922
  return orderedFeatureIndices;
901
923
  }
924
+ /**
925
+ * Find property table in tile
926
+ * For example it can be batchTable for b3dm files or property table in gLTF extension.
927
+ * @param sourceTile
928
+ */
929
+ function getPropertyTable(sourceTile) {
930
+ const batchTableJson = sourceTile?.content?.batchTableJson;
931
+ if (batchTableJson) {
932
+ return batchTableJson;
933
+ }
934
+ const { extensionName, extension } = getPropertyTableExtension(sourceTile);
935
+ switch (extensionName) {
936
+ case EXT_MESH_FEATURES: {
937
+ console.warn('The I3S converter does not yet support the EXT_mesh_features extension');
938
+ return null;
939
+ }
940
+ case EXT_FEATURE_METADATA: {
941
+ return getPropertyTableFromExtFeatureMetadata(extension);
942
+ }
943
+ default:
944
+ return null;
945
+ }
946
+ }
947
+ exports.getPropertyTable = getPropertyTable;
948
+ /**
949
+ * Check extensions which can be with property table inside.
950
+ * @param sourceTile
951
+ */
952
+ function getPropertyTableExtension(sourceTile) {
953
+ const extensionsWithPropertyTables = [EXT_FEATURE_METADATA, EXT_MESH_FEATURES];
954
+ const extensionsUsed = sourceTile?.content?.gltf?.extensionsUsed;
955
+ if (!extensionsUsed) {
956
+ return { extensionName: null, extension: null };
957
+ }
958
+ let extensionName = '';
959
+ for (const extensionItem of sourceTile?.content?.gltf?.extensionsUsed) {
960
+ if (extensionsWithPropertyTables.includes(extensionItem)) {
961
+ extensionName = extensionItem;
962
+ break;
963
+ }
964
+ }
965
+ const extension = sourceTile?.content?.gltf?.extensions?.[extensionName];
966
+ return { extensionName, extension };
967
+ }
968
+ /**
969
+ * Handle EXT_feature_metadata to get property table
970
+ * @param extension
971
+ * TODO add EXT_feature_metadata feature textures support.
972
+ */
973
+ function getPropertyTableFromExtFeatureMetadata(extension) {
974
+ if (extension?.featureTextures) {
975
+ console.warn('The I3S converter does not yet support the EXT_feature_metadata feature textures');
976
+ return null;
977
+ }
978
+ if (extension?.featureTables) {
979
+ /**
980
+ * Take only first feature table to generate attributes storage info object.
981
+ * TODO: Think about getting data from all feature tables?
982
+ * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
983
+ * In I3S we should decide which featureIds attribute will be passed to geometry data.
984
+ */
985
+ const firstFeatureTableName = Object.keys(extension.featureTables)?.[0];
986
+ if (firstFeatureTableName) {
987
+ const featureTable = extension?.featureTables[firstFeatureTableName];
988
+ const propertyTable = {};
989
+ for (const propertyName in featureTable.properties) {
990
+ propertyTable[propertyName] = featureTable.properties[propertyName].data;
991
+ }
992
+ return propertyTable;
993
+ }
994
+ }
995
+ console.warn("The I3S converter couldn't handle EXT_feature_metadata extension");
996
+ return null;
997
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"gltf-attributes.d.ts","sourceRoot":"","sources":["../../../src/i3s-converter/helpers/gltf-attributes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAsBpE;;;;GAIG;AACH,wBAAgB,kCAAkC,CAAC,WAAW,EAAE,WAAW,GAAG,kBAAkB,CAsC/F"}
1
+ {"version":3,"file":"gltf-attributes.d.ts","sourceRoot":"","sources":["../../../src/i3s-converter/helpers/gltf-attributes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAsBpE;;;;GAIG;AACH,wBAAgB,kCAAkC,CAAC,WAAW,EAAE,WAAW,GAAG,kBAAkB,CAqD/F"}
@@ -25,12 +25,25 @@ function prepareDataForAttributesConversion(tileContent) {
25
25
  tileContent.gltf?.scenes?.[0]?.nodes ||
26
26
  tileContent.gltf?.nodes ||
27
27
  [];
28
+ const images = tileContent.gltf?.images?.map((imageObject) => {
29
+ // Need data only for uncompressed images because we can't get batchIds from compressed textures.
30
+ const data = imageObject?.image?.compressed ? null : imageObject?.image?.data.subarray();
31
+ return {
32
+ data,
33
+ compressed: Boolean(imageObject?.image?.compressed),
34
+ height: imageObject.image.height,
35
+ width: imageObject.image.width,
36
+ components: imageObject.image.components,
37
+ mimeType: imageObject.mimeType
38
+ };
39
+ }) || [];
28
40
  const prepearedNodes = nodes.map((node) => {
29
41
  if (!node.mesh) {
30
42
  return node;
31
43
  }
32
44
  return {
33
45
  ...node,
46
+ images,
34
47
  mesh: {
35
48
  ...node.mesh,
36
49
  primitives: node.mesh?.primitives.map((primitive) => ({