@loaders.gl/tile-converter 3.1.6 → 3.2.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 (117) hide show
  1. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts +78 -0
  2. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -0
  3. package/dist/3d-tiles-converter/3d-tiles-converter.js +242 -0
  4. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts +83 -0
  5. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts.map +1 -0
  6. package/dist/3d-tiles-converter/helpers/b3dm-converter.js +278 -0
  7. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.d.ts +13 -0
  8. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.d.ts.map +1 -0
  9. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +23 -0
  10. package/dist/3d-tiles-converter/helpers/texture-atlas.d.ts +9 -0
  11. package/dist/3d-tiles-converter/helpers/texture-atlas.d.ts.map +1 -0
  12. package/dist/3d-tiles-converter/helpers/texture-atlas.js +52 -0
  13. package/dist/3d-tiles-converter/json-templates/tileset.d.ts +15 -0
  14. package/dist/3d-tiles-converter/json-templates/tileset.d.ts.map +1 -0
  15. package/dist/3d-tiles-converter/json-templates/tileset.js +43 -0
  16. package/dist/bundle.d.ts +2 -0
  17. package/dist/bundle.d.ts.map +1 -0
  18. package/dist/bundle.js +5 -0
  19. package/dist/converter.min.js +21 -21
  20. package/dist/deps-installer/deps-installer.d.ts +4 -0
  21. package/dist/deps-installer/deps-installer.d.ts.map +1 -0
  22. package/dist/deps-installer/deps-installer.js +21 -0
  23. package/dist/dist.min.js +1913 -908
  24. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js +2 -1
  25. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  26. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js +14 -21
  27. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  28. package/dist/es5/i3s-converter/helpers/node-pages.js +78 -35
  29. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
  30. package/dist/es5/i3s-converter/i3s-converter.js +35 -25
  31. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  32. package/dist/es5/pgm-loader.js +1 -1
  33. package/dist/es5/pgm-loader.js.map +1 -1
  34. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js +2 -1
  35. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  36. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js +7 -5
  37. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  38. package/dist/esm/i3s-converter/helpers/node-pages.js +2 -7
  39. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
  40. package/dist/esm/i3s-converter/i3s-converter.js +10 -0
  41. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  42. package/dist/esm/pgm-loader.js +1 -1
  43. package/dist/esm/pgm-loader.js.map +1 -1
  44. package/dist/i3s-converter/helpers/coordinate-converter.d.ts +41 -0
  45. package/dist/i3s-converter/helpers/coordinate-converter.d.ts.map +1 -0
  46. package/dist/i3s-converter/helpers/coordinate-converter.js +118 -0
  47. package/dist/i3s-converter/helpers/create-scene-server-path.d.ts +9 -0
  48. package/dist/i3s-converter/helpers/create-scene-server-path.d.ts.map +1 -0
  49. package/dist/i3s-converter/helpers/create-scene-server-path.js +28 -0
  50. package/dist/i3s-converter/helpers/geometry-attributes.d.ts +8 -0
  51. package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -0
  52. package/dist/i3s-converter/helpers/geometry-attributes.js +176 -0
  53. package/dist/i3s-converter/helpers/geometry-converter.d.ts +12 -0
  54. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -0
  55. package/dist/i3s-converter/helpers/geometry-converter.js +791 -0
  56. package/dist/i3s-converter/helpers/node-debug.d.ts +8 -0
  57. package/dist/i3s-converter/helpers/node-debug.d.ts.map +1 -0
  58. package/dist/i3s-converter/helpers/node-debug.js +114 -0
  59. package/dist/i3s-converter/helpers/node-pages.d.ts +116 -0
  60. package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -0
  61. package/dist/i3s-converter/helpers/node-pages.js +203 -0
  62. package/dist/i3s-converter/i3s-converter.d.ts +321 -0
  63. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -0
  64. package/dist/i3s-converter/i3s-converter.js +994 -0
  65. package/dist/i3s-converter/json-templates/layers.d.ts +95 -0
  66. package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -0
  67. package/dist/i3s-converter/json-templates/layers.js +199 -0
  68. package/dist/i3s-converter/json-templates/metadata.d.ts +22 -0
  69. package/dist/i3s-converter/json-templates/metadata.d.ts.map +1 -0
  70. package/dist/i3s-converter/json-templates/metadata.js +25 -0
  71. package/dist/i3s-converter/json-templates/node.d.ts +61 -0
  72. package/dist/i3s-converter/json-templates/node.d.ts.map +1 -0
  73. package/dist/i3s-converter/json-templates/node.js +89 -0
  74. package/dist/i3s-converter/json-templates/scene-server.d.ts +28 -0
  75. package/dist/i3s-converter/json-templates/scene-server.d.ts.map +1 -0
  76. package/dist/i3s-converter/json-templates/scene-server.js +31 -0
  77. package/dist/i3s-converter/json-templates/shared-resources.d.ts +14 -0
  78. package/dist/i3s-converter/json-templates/shared-resources.d.ts.map +1 -0
  79. package/dist/i3s-converter/json-templates/shared-resources.js +129 -0
  80. package/dist/i3s-converter/json-templates/store.d.ts +95 -0
  81. package/dist/i3s-converter/json-templates/store.d.ts.map +1 -0
  82. package/dist/i3s-converter/json-templates/store.js +103 -0
  83. package/dist/i3s-converter/types.d.ts +39 -0
  84. package/dist/i3s-converter/types.d.ts.map +1 -0
  85. package/dist/i3s-converter/types.js +2 -0
  86. package/dist/i3s-server/app.d.ts +3 -0
  87. package/dist/i3s-server/app.d.ts.map +1 -0
  88. package/dist/i3s-server/app.js +14 -0
  89. package/dist/i3s-server/controllers/index-controller.d.ts +2 -0
  90. package/dist/i3s-server/controllers/index-controller.d.ts.map +1 -0
  91. package/dist/i3s-server/controllers/index-controller.js +23 -0
  92. package/dist/i3s-server/routes/index.d.ts +3 -0
  93. package/dist/i3s-server/routes/index.d.ts.map +1 -0
  94. package/dist/i3s-server/routes/index.js +16 -0
  95. package/dist/index.d.ts +5 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +14 -0
  98. package/dist/lib/utils/compress-util.d.ts +6 -0
  99. package/dist/lib/utils/compress-util.d.ts.map +1 -0
  100. package/dist/lib/utils/compress-util.js +190 -0
  101. package/dist/lib/utils/file-utils.d.ts +6 -0
  102. package/dist/lib/utils/file-utils.d.ts.map +1 -0
  103. package/dist/lib/utils/file-utils.js +42 -0
  104. package/dist/lib/utils/lod-conversion-utils.d.ts +20 -0
  105. package/dist/lib/utils/lod-conversion-utils.d.ts.map +1 -0
  106. package/dist/lib/utils/lod-conversion-utils.js +57 -0
  107. package/dist/lib/utils/statistic-utills.d.ts +3 -0
  108. package/dist/lib/utils/statistic-utills.d.ts.map +1 -0
  109. package/dist/lib/utils/statistic-utills.js +64 -0
  110. package/dist/pgm-loader.d.ts +6 -0
  111. package/dist/pgm-loader.d.ts.map +1 -0
  112. package/dist/pgm-loader.js +23 -0
  113. package/package.json +16 -17
  114. package/src/3d-tiles-converter/3d-tiles-converter.ts +4 -1
  115. package/src/3d-tiles-converter/helpers/b3dm-converter.ts +6 -5
  116. package/src/i3s-converter/helpers/node-pages.ts +6 -6
  117. package/src/i3s-converter/i3s-converter.ts +12 -0
@@ -0,0 +1,791 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@math.gl/core");
7
+ const geospatial_1 = require("@math.gl/geospatial");
8
+ const draco_1 = require("@loaders.gl/draco");
9
+ const core_2 = require("@loaders.gl/core");
10
+ const loader_utils_1 = require("@loaders.gl/loader-utils");
11
+ const md5_1 = __importDefault(require("md5"));
12
+ const geometry_attributes_1 = require("./geometry-attributes");
13
+ const coordinate_converter_1 = require("./coordinate-converter");
14
+ // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
15
+ const DEFAULT_ROUGHNESS_FACTOR = 1;
16
+ const DEFAULT_METALLIC_FACTOR = 1;
17
+ const VALUES_PER_VERTEX = 3;
18
+ const VALUES_PER_TEX_COORD = 2;
19
+ const VALUES_PER_COLOR_ELEMENT = 4;
20
+ const STRING_TYPE = 'string';
21
+ const SHORT_INT_TYPE = 'Int32';
22
+ const DOUBLE_TYPE = 'Float64';
23
+ const OBJECT_ID_TYPE = 'Oid32';
24
+ /*
25
+ * 'CUSTOM_ATTRIBUTE_2' - Attribute name which includes batch info and used by New York map.
26
+ * _BATCHID - Default attribute name which includes batch info.
27
+ * BATCHID - Legacy attribute name which includes batch info.
28
+ */
29
+ const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID'];
30
+ let scratchVector = new core_1.Vector3();
31
+ async function convertB3dmToI3sGeometry(tileContent, nodeId, featuresHashArray, attributeStorageInfo, draco, generateBoundingVolumes, geoidHeightModel) {
32
+ const useCartesianPositions = generateBoundingVolumes;
33
+ const materialAndTextureList = convertMaterials(tileContent);
34
+ const convertedAttributesMap = convertAttributes(tileContent, useCartesianPositions);
35
+ if (generateBoundingVolumes) {
36
+ _generateBoundingVolumesFromGeometry(convertedAttributesMap, geoidHeightModel);
37
+ }
38
+ if (convertedAttributesMap.has('default')) {
39
+ materialAndTextureList.push({
40
+ material: getDefaultMaterial()
41
+ });
42
+ }
43
+ const result = [];
44
+ let nodesCounter = nodeId;
45
+ let { materials = [] } = tileContent.gltf;
46
+ if (!materials?.length) {
47
+ materials.push({ id: 'default' });
48
+ }
49
+ for (let i = 0; i < materials.length; i++) {
50
+ const sourceMaterial = materials[i];
51
+ if (!convertedAttributesMap.has(sourceMaterial.id)) {
52
+ continue; // eslint-disable-line no-continue
53
+ }
54
+ const convertedAttributes = convertedAttributesMap.get(sourceMaterial.id);
55
+ const { material, texture } = materialAndTextureList[i];
56
+ result.push(await _makeNodeResources({
57
+ convertedAttributes,
58
+ material,
59
+ texture,
60
+ tileContent,
61
+ nodeId: nodesCounter,
62
+ featuresHashArray,
63
+ attributeStorageInfo,
64
+ draco
65
+ }));
66
+ nodesCounter++;
67
+ }
68
+ if (!result.length) {
69
+ return null;
70
+ }
71
+ return result;
72
+ }
73
+ exports.default = convertB3dmToI3sGeometry;
74
+ /**
75
+ * Create bounding volumes based on positions
76
+ * @param convertedAttributesMap
77
+ * @param geoidHeightModel
78
+ */
79
+ function _generateBoundingVolumesFromGeometry(convertedAttributesMap, geoidHeightModel) {
80
+ for (const attributes of convertedAttributesMap.values()) {
81
+ const boundingVolumes = (0, coordinate_converter_1.createBoundingVolumesFromGeometry)(attributes.positions, geoidHeightModel);
82
+ attributes.boundingVolumes = boundingVolumes;
83
+ const cartographicOrigin = boundingVolumes.obb.center;
84
+ for (let index = 0; index < attributes.positions.length; index += VALUES_PER_VERTEX) {
85
+ const vertex = attributes.positions.subarray(index, index + VALUES_PER_VERTEX);
86
+ geospatial_1.Ellipsoid.WGS84.cartesianToCartographic(Array.from(vertex), scratchVector);
87
+ scratchVector[2] =
88
+ scratchVector[2] - geoidHeightModel.getHeight(scratchVector[1], scratchVector[0]);
89
+ scratchVector = scratchVector.subtract(cartographicOrigin);
90
+ attributes.positions.set(scratchVector, index);
91
+ }
92
+ }
93
+ }
94
+ async function _makeNodeResources({ convertedAttributes, material, texture, tileContent, nodeId, featuresHashArray, attributeStorageInfo, draco }) {
95
+ const boundingVolumes = convertedAttributes.boundingVolumes;
96
+ const vertexCount = convertedAttributes.positions.length / VALUES_PER_VERTEX;
97
+ const triangleCount = vertexCount / 3;
98
+ const { faceRange, featureIds, positions, normals, colors, texCoords, featureCount } = (0, geometry_attributes_1.generateAttributes)({ triangleCount, ...convertedAttributes });
99
+ if (tileContent.batchTableJson) {
100
+ makeFeatureIdsUnique(featureIds, convertedAttributes.featureIndices, featuresHashArray, tileContent.batchTableJson);
101
+ }
102
+ const header = new Uint32Array(2);
103
+ const typedFeatureIds = generateBigUint64Array(featureIds);
104
+ header.set([vertexCount, featureCount], 0);
105
+ const fileBuffer = new Uint8Array((0, loader_utils_1.concatenateArrayBuffers)(header.buffer, positions.buffer, normals.buffer, texture ? texCoords.buffer : new ArrayBuffer(0), colors.buffer, typedFeatureIds.buffer, faceRange.buffer));
106
+ const compressedGeometry = draco
107
+ ? await generateCompressedGeometry(vertexCount, convertedAttributes, {
108
+ positions,
109
+ normals,
110
+ texCoords: texture ? texCoords : new Float32Array(0),
111
+ colors,
112
+ featureIds,
113
+ faceRange
114
+ })
115
+ : null;
116
+ const attributes = convertBatchTableToAttributeBuffers(tileContent.batchTableJson, featureIds, attributeStorageInfo);
117
+ return {
118
+ geometry: fileBuffer,
119
+ compressedGeometry,
120
+ texture,
121
+ sharedResources: getSharedResources(tileContent, nodeId),
122
+ meshMaterial: material,
123
+ vertexCount,
124
+ attributes,
125
+ featureCount,
126
+ boundingVolumes
127
+ };
128
+ }
129
+ /**
130
+ * Convert attributes from the gltf nodes tree to i3s plain geometry
131
+ * @param {Object} tileContent - 3d tile content
132
+ * @returns {Map}
133
+ * Map<{
134
+ * positions: Float32Array,
135
+ * normals: Float32Array,
136
+ * colors: Uint8Array,
137
+ * texCoords: Float32Array
138
+ * }>
139
+ * @todo implement colors support (if applicable for gltf format)
140
+ */
141
+ function convertAttributes(tileContent, useCartesianPositions) {
142
+ const attributesMap = new Map();
143
+ for (const material of tileContent.gltf.materials || [{ id: 'default' }]) {
144
+ attributesMap.set(material.id, {
145
+ positions: new Float32Array(0),
146
+ normals: new Float32Array(0),
147
+ texCoords: new Float32Array(0),
148
+ colors: new Uint8Array(0),
149
+ featureIndices: [],
150
+ boundingVolumes: null
151
+ });
152
+ }
153
+ const nodes = (tileContent.gltf.scene || tileContent.gltf.scenes?.[0] || tileContent.gltf).nodes;
154
+ convertNodes(nodes, tileContent, attributesMap, useCartesianPositions);
155
+ for (const attrKey of attributesMap.keys()) {
156
+ const attributes = attributesMap.get(attrKey);
157
+ if (attributes.positions.length === 0) {
158
+ attributesMap.delete(attrKey);
159
+ continue; // eslint-disable-line no-continue
160
+ }
161
+ attributes.featureIndices = attributes.featureIndices.reduce((acc, value) => acc.concat(value));
162
+ }
163
+ return attributesMap;
164
+ }
165
+ /**
166
+ * Gltf has hierarchical structure of nodes. This function converts nodes starting from those which are in gltf scene object.
167
+ * The goal is applying tranformation matrix for all children. Functions "convertNodes" and "convertNode" work together recursively.
168
+ * @param {Object[]} nodes - gltf nodes array
169
+ * @param {Object} tileContent - 3d tile content
170
+ * @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: UInt8Array, featureIndices: Array}> - for recursive concatenation of
171
+ * attributes
172
+ * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
173
+ * @returns {void}
174
+ */
175
+ function convertNodes(nodes, tileContent, attributesMap, useCartesianPositions, matrix = new core_1.Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])) {
176
+ if (nodes) {
177
+ for (const node of nodes) {
178
+ convertNode(node, tileContent, attributesMap, useCartesianPositions, matrix);
179
+ }
180
+ }
181
+ }
182
+ /**
183
+ * Generate transformation matrix for node
184
+ * Aapply all gltf transformations to initial transformation matrix.
185
+ * @param node
186
+ * @param matrix
187
+ */
188
+ function getCompositeTransformationMatrix(node, matrix) {
189
+ let transformationMatrix = matrix;
190
+ const { matrix: nodeMatrix, rotation, scale, translation } = node;
191
+ if (nodeMatrix) {
192
+ transformationMatrix = matrix.multiplyRight(nodeMatrix);
193
+ }
194
+ if (rotation) {
195
+ transformationMatrix = transformationMatrix.rotateXYZ(rotation);
196
+ }
197
+ if (scale) {
198
+ transformationMatrix = transformationMatrix.scale(scale);
199
+ }
200
+ if (translation) {
201
+ transformationMatrix = transformationMatrix.translate(translation);
202
+ }
203
+ return transformationMatrix;
204
+ }
205
+ /**
206
+ * Convert all primitives of node and all children nodes
207
+ * @param {Object} node - gltf node
208
+ * @param {Object} tileContent - 3d tile content
209
+ * @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
210
+ * attributes
211
+ * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
212
+ * @todo: optimize arrays concatenation
213
+ */
214
+ function convertNode(node, tileContent, attributesMap, useCartesianPositions, matrix = new core_1.Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])) {
215
+ const transformationMatrix = getCompositeTransformationMatrix(node, matrix);
216
+ const mesh = node.mesh;
217
+ if (mesh) {
218
+ convertMesh(mesh, tileContent, attributesMap, useCartesianPositions, transformationMatrix);
219
+ }
220
+ convertNodes(node.children, tileContent, attributesMap, useCartesianPositions, transformationMatrix);
221
+ }
222
+ /**
223
+ * Convert all primitives of node and all children nodes
224
+ * @param {Object} mesh - gltf node
225
+ * @param {Object} content - 3d tile content
226
+ * @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
227
+ * attributes
228
+ * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices
229
+ * @todo: optimize arrays concatenation
230
+ */
231
+ function convertMesh(mesh, content, attributesMap, useCartesianPositions = false, matrix = new core_1.Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])) {
232
+ for (const primitive of mesh.primitives) {
233
+ let outputAttributes = null;
234
+ if (primitive.material) {
235
+ outputAttributes = attributesMap.get(primitive.material.id);
236
+ }
237
+ else if (attributesMap.has('default')) {
238
+ outputAttributes = attributesMap.get('default');
239
+ }
240
+ (0, core_2.assert)(outputAttributes !== null, 'Primitive - material mapping failed');
241
+ const attributes = primitive.attributes;
242
+ outputAttributes.positions = (0, loader_utils_1.concatenateTypedArrays)(outputAttributes.positions, transformVertexArray({
243
+ vertices: attributes.POSITION.value,
244
+ cartographicOrigin: content.cartographicOrigin,
245
+ cartesianModelMatrix: content.cartesianModelMatrix,
246
+ nodeMatrix: matrix,
247
+ indices: primitive.indices.value,
248
+ attributeSpecificTransformation: transformVertexPositions,
249
+ useCartesianPositions
250
+ }));
251
+ outputAttributes.normals = (0, loader_utils_1.concatenateTypedArrays)(outputAttributes.normals, transformVertexArray({
252
+ vertices: attributes.NORMAL && attributes.NORMAL.value,
253
+ cartographicOrigin: content.cartographicOrigin,
254
+ cartesianModelMatrix: content.cartesianModelMatrix,
255
+ nodeMatrix: matrix,
256
+ indices: primitive.indices.value,
257
+ attributeSpecificTransformation: transformVertexNormals,
258
+ useCartesianPositions: false
259
+ }));
260
+ outputAttributes.texCoords = (0, loader_utils_1.concatenateTypedArrays)(outputAttributes.texCoords, flattenTexCoords(attributes.TEXCOORD_0 && attributes.TEXCOORD_0.value, primitive.indices.value));
261
+ outputAttributes.colors = (0, loader_utils_1.concatenateTypedArrays)(outputAttributes.colors, flattenColors(attributes.COLOR_0, primitive.indices.value));
262
+ outputAttributes.featureIndices.push(flattenBatchIds(getBatchIdsByAttributeName(attributes), primitive.indices.value));
263
+ }
264
+ }
265
+ /**
266
+ * Convert vertices attributes (POSITIONS or NORMALS) to i3s compatible format
267
+ * @param {object} args - source tile (3DTile)
268
+ * @param {Float32Array} args.vertices - gltf primitive POSITION or NORMAL attribute
269
+ * @param {Object} args.cartographicOrigin - cartographic origin coordinates
270
+ * @param {Object} args.cartesianModelMatrix - a cartesian model matrix to transform coordnates from cartesian to cartographic format
271
+ * @param {Matrix4} args.nodeMatrix - a gltf node transformation matrix - cumulative transformation matrix formed from all parent node matrices
272
+ * @param {Uint8Array} args.indices - gltf primitive indices
273
+ * @param {Function} args.attributeSpecificTransformation - function to do attribute - specific transformations
274
+ * @param {Boolean} args.useCartesianPositions - use coordinates as it is.
275
+ * @returns {Float32Array}
276
+ */
277
+ function transformVertexArray(args) {
278
+ const { vertices, indices, attributeSpecificTransformation } = args;
279
+ const newVertices = new Float32Array(indices.length * VALUES_PER_VERTEX);
280
+ if (!vertices) {
281
+ return newVertices;
282
+ }
283
+ for (let i = 0; i < indices.length; i++) {
284
+ const coordIndex = indices[i] * VALUES_PER_VERTEX;
285
+ const vertex = vertices.subarray(coordIndex, coordIndex + VALUES_PER_VERTEX);
286
+ let vertexVector = new core_1.Vector3(Array.from(vertex));
287
+ vertexVector = attributeSpecificTransformation(vertexVector, args);
288
+ newVertices[i * VALUES_PER_VERTEX] = vertexVector.x;
289
+ newVertices[i * VALUES_PER_VERTEX + 1] = vertexVector.y;
290
+ newVertices[i * VALUES_PER_VERTEX + 2] = vertexVector.z;
291
+ }
292
+ return newVertices;
293
+ }
294
+ function transformVertexPositions(vertexVector, calleeArgs) {
295
+ const { cartesianModelMatrix, cartographicOrigin, nodeMatrix, useCartesianPositions } = calleeArgs;
296
+ if (nodeMatrix) {
297
+ vertexVector = vertexVector.transform(nodeMatrix);
298
+ }
299
+ vertexVector = vertexVector.transform(cartesianModelMatrix);
300
+ if (useCartesianPositions) {
301
+ return vertexVector;
302
+ }
303
+ geospatial_1.Ellipsoid.WGS84.cartesianToCartographic([vertexVector[0], vertexVector[1], vertexVector[2]], vertexVector);
304
+ vertexVector = vertexVector.subtract(cartographicOrigin);
305
+ return vertexVector;
306
+ }
307
+ function transformVertexNormals(vertexVector, calleeArgs) {
308
+ const { cartesianModelMatrix, nodeMatrix } = calleeArgs;
309
+ if (nodeMatrix) {
310
+ vertexVector = vertexVector.transformAsVector(nodeMatrix);
311
+ }
312
+ vertexVector = vertexVector.transformAsVector(cartesianModelMatrix);
313
+ return vertexVector;
314
+ }
315
+ /**
316
+ * Convert uv0 (texture coordinates) from coords based on indices to plain arrays, compatible with i3s
317
+ * @param {Float32Array} texCoords - gltf primitive TEXCOORD_0 attribute
318
+ * @param {Uint8Array} indices - gltf primitive indices
319
+ * @returns {Float32Array}
320
+ */
321
+ function flattenTexCoords(texCoords, indices) {
322
+ const newTexCoords = new Float32Array(indices.length * VALUES_PER_TEX_COORD);
323
+ if (!texCoords) {
324
+ // We need dummy UV0s because it is required in 1.6
325
+ // https://github.com/Esri/i3s-spec/blob/master/docs/1.6/vertexAttribute.cmn.md
326
+ newTexCoords.fill(1);
327
+ return newTexCoords;
328
+ }
329
+ for (let i = 0; i < indices.length; i++) {
330
+ const coordIndex = indices[i] * VALUES_PER_TEX_COORD;
331
+ const texCoord = texCoords.subarray(coordIndex, coordIndex + VALUES_PER_TEX_COORD);
332
+ newTexCoords[i * VALUES_PER_TEX_COORD] = texCoord[0];
333
+ newTexCoords[i * VALUES_PER_TEX_COORD + 1] = texCoord[1];
334
+ }
335
+ return newTexCoords;
336
+ }
337
+ /**
338
+ * Convert color from COLOR_0 based on indices to plain arrays, compatible with i3s
339
+ * @param {object} colorsAttribute - gltf primitive COLOR_0 attribute
340
+ * @param {Uint8Array} indices - gltf primitive indices
341
+ * @returns {Uint8Array}
342
+ */
343
+ function flattenColors(colorsAttribute, indices) {
344
+ const components = colorsAttribute?.components || VALUES_PER_COLOR_ELEMENT;
345
+ const newColors = new Uint8Array(indices.length * components);
346
+ if (!colorsAttribute) {
347
+ // Vertex color multiplies by material color so it must be normalized 1 by default
348
+ newColors.fill(255);
349
+ return newColors;
350
+ }
351
+ const colors = colorsAttribute.value;
352
+ for (let i = 0; i < indices.length; i++) {
353
+ const colorIndex = indices[i] * components;
354
+ const color = colors.subarray(colorIndex, colorIndex + components);
355
+ const colorUint8 = new Uint8Array(components);
356
+ for (let j = 0; j < color.length; j++) {
357
+ colorUint8[j] = color[j] * 255;
358
+ }
359
+ newColors.set(colorUint8, i * components);
360
+ }
361
+ return newColors;
362
+ }
363
+ /**
364
+ * Flatten batchedIds list based on indices to right ordered array, compatible with i3s
365
+ * @param {Array} batchedIds - gltf primitive
366
+ * @param {Uint8Array} indices - gltf primitive indices
367
+ * @returns {Array}
368
+ */
369
+ function flattenBatchIds(batchedIds, indices) {
370
+ if (!batchedIds.length || !indices.length) {
371
+ return [];
372
+ }
373
+ const newBatchIds = [];
374
+ for (let i = 0; i < indices.length; i++) {
375
+ const coordIndex = indices[i];
376
+ newBatchIds.push(batchedIds[coordIndex]);
377
+ }
378
+ return newBatchIds;
379
+ }
380
+ /**
381
+ * Return batchIds based on possible attribute names for different kind of maps.
382
+ * @param {Object} attributes {attributeName: Float32Array}
383
+ * @returns {Array}
384
+ */
385
+ function getBatchIdsByAttributeName(attributes) {
386
+ let batchIds = [];
387
+ for (let index = 0; index < BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES.length; index++) {
388
+ const possibleBatchIdAttributeName = BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES[index];
389
+ if (attributes[possibleBatchIdAttributeName] &&
390
+ attributes[possibleBatchIdAttributeName].value) {
391
+ batchIds = attributes[possibleBatchIdAttributeName].value;
392
+ break;
393
+ }
394
+ }
395
+ return batchIds;
396
+ }
397
+ function convertMaterials(tileContent) {
398
+ const result = [];
399
+ const sourceMaterials = tileContent.gltf.materials || [];
400
+ for (const sourceMaterial of sourceMaterials) {
401
+ result.push(convertMaterial(sourceMaterial));
402
+ }
403
+ return result;
404
+ }
405
+ /**
406
+ * Convert texture and material from gltf 2.0 material object
407
+ * @param {Object} sourceMaterial - material object
408
+ * @returns {Object}
409
+ */
410
+ function convertMaterial(sourceMaterial) {
411
+ const material = {
412
+ doubleSided: sourceMaterial.doubleSided,
413
+ emissiveFactor: sourceMaterial.emissiveFactor.map((c) => Math.round(c * 255)),
414
+ // It is in upper case in GLTF: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#alpha-coverage
415
+ // But it is in lower case in I3S: https://github.com/Esri/i3s-spec/blob/master/docs/1.7/materialDefinitions.cmn.md
416
+ alphaMode: (sourceMaterial.alphaMode || 'OPAQUE').toLowerCase(),
417
+ pbrMetallicRoughness: {
418
+ roughnessFactor: sourceMaterial?.pbrMetallicRoughness?.roughnessFactor || DEFAULT_ROUGHNESS_FACTOR,
419
+ metallicFactor: sourceMaterial?.pbrMetallicRoughness?.metallicFactor || DEFAULT_METALLIC_FACTOR
420
+ }
421
+ };
422
+ let texture;
423
+ if (sourceMaterial?.pbrMetallicRoughness?.baseColorTexture) {
424
+ texture = sourceMaterial.pbrMetallicRoughness.baseColorTexture.texture.source;
425
+ material.pbrMetallicRoughness.baseColorTexture = {
426
+ textureSetDefinitionId: 0
427
+ };
428
+ }
429
+ else if (sourceMaterial.emissiveTexture) {
430
+ texture = sourceMaterial.emissiveTexture.texture.source;
431
+ // ArcGIS webscene doesn't show emissiveTexture but shows baseColorTexture
432
+ material.pbrMetallicRoughness.baseColorTexture = {
433
+ textureSetDefinitionId: 0
434
+ };
435
+ }
436
+ if (!texture) {
437
+ // Should use default baseColorFactor if it is not present in source material
438
+ // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-pbrmetallicroughness
439
+ const baseColorFactor = sourceMaterial?.pbrMetallicRoughness?.baseColorFactor;
440
+ material.pbrMetallicRoughness.baseColorFactor =
441
+ (baseColorFactor && baseColorFactor.map((c) => Math.round(c * 255))) || undefined;
442
+ }
443
+ return { material, texture };
444
+ }
445
+ function getDefaultMaterial() {
446
+ return {
447
+ alphaMode: 'opaque',
448
+ pbrMetallicRoughness: {}
449
+ };
450
+ }
451
+ /**
452
+ * Form "sharedResources" from gltf materials array
453
+ * @param {Object} tileContent - 3d tile content
454
+ * @returns {Object} {materialDefinitionInfos: Object[], textureDefinitionInfos: Object[]} -
455
+ * 2 arrays in format of i3s sharedResources data https://github.com/Esri/i3s-spec/blob/master/docs/1.7/sharedResource.cmn.md
456
+ */
457
+ function getSharedResources(tileContent, nodeId) {
458
+ const gltfMaterials = tileContent.gltf.materials;
459
+ const i3sResources = {};
460
+ if (!gltfMaterials || !gltfMaterials.length) {
461
+ return i3sResources;
462
+ }
463
+ i3sResources.materialDefinitionInfos = [];
464
+ for (const gltfMaterial of gltfMaterials) {
465
+ const { materialDefinitionInfo, textureDefinitionInfo } = convertGLTFMaterialToI3sSharedResources(gltfMaterial, nodeId);
466
+ i3sResources.materialDefinitionInfos.push(materialDefinitionInfo);
467
+ if (textureDefinitionInfo) {
468
+ i3sResources.textureDefinitionInfos = i3sResources.textureDefinitionInfos || [];
469
+ i3sResources.textureDefinitionInfos.push(textureDefinitionInfo);
470
+ }
471
+ }
472
+ return i3sResources;
473
+ }
474
+ /**
475
+ * Convert gltf material into I3S sharedResources data
476
+ * @param {Object} gltfMaterial - gltf material data
477
+ * @returns {Object} - Couple {materialDefinitionInfo, textureDefinitionInfo} extracted from gltf material data
478
+ */
479
+ function convertGLTFMaterialToI3sSharedResources(gltfMaterial, nodeId) {
480
+ const texture = gltfMaterial?.pbrMetallicRoughness?.baseColorTexture || gltfMaterial.emissiveTexture;
481
+ let textureDefinitionInfo = null;
482
+ if (texture) {
483
+ textureDefinitionInfo = extractSharedResourcesTextureInfo(texture.texture, nodeId);
484
+ }
485
+ const { baseColorFactor, metallicFactor } = gltfMaterial?.pbrMetallicRoughness || {};
486
+ let colorFactor = baseColorFactor;
487
+ // If alpha channel is 0 try to get emissive factor from gltf material.
488
+ if ((!baseColorFactor || baseColorFactor[3] === 0) && gltfMaterial.emissiveFactor) {
489
+ colorFactor = gltfMaterial.emissiveFactor;
490
+ colorFactor[3] = colorFactor[3] || 1;
491
+ }
492
+ return {
493
+ materialDefinitionInfo: extractSharedResourcesMaterialInfo(colorFactor, metallicFactor),
494
+ textureDefinitionInfo
495
+ };
496
+ }
497
+ /**
498
+ * Form "materialDefinition" which is part of "sharedResouces"
499
+ * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materials
500
+ * See formulas in appendix "Appendix B: BRDF Implementation":
501
+ * const dielectricSpecular = rgb(0.04, 0.04, 0.04)
502
+ * const black = rgb(0, 0, 0)
503
+ * cdiff = lerp(baseColor.rgb * (1 - dielectricSpecular.r), black, metallic)
504
+ * F0 = lerp(dieletricSpecular, baseColor.rgb, metallic)
505
+ *
506
+ * Assumption: F0 - specular in i3s ("specular reflection" <-> "reflectance value at normal incidence")
507
+ * cdiff - diffuse in i3s ("Diffuse color" <-> "'c' diffuse" (c means color?))
508
+ * @param {number[]} baseColorFactor - RGBA color in 0..1 format
509
+ * @param {number} metallicFactor - "metallicFactor" attribute of gltf material object
510
+ * @returns {Object}
511
+ */
512
+ function extractSharedResourcesMaterialInfo(baseColorFactor, metallicFactor = 1) {
513
+ const matDielectricColorComponent = 0.04 / 255; // Color from rgb (255) to 0..1 resolution
514
+ // All color resolutions are 0..1
515
+ const black = new core_1.Vector4(0, 0, 0, 1);
516
+ const unitVector = new core_1.Vector4(1, 1, 1, 1);
517
+ const dielectricSpecular = new core_1.Vector4(matDielectricColorComponent, matDielectricColorComponent, matDielectricColorComponent, 0);
518
+ const baseColorVector = new core_1.Vector4(baseColorFactor);
519
+ // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
520
+ // Formulas for Cdiff & F0
521
+ const firstOperand = unitVector.subtract(dielectricSpecular).multiply(baseColorVector);
522
+ const diffuse = firstOperand.lerp(firstOperand, black, metallicFactor);
523
+ dielectricSpecular[3] = 1;
524
+ const specular = dielectricSpecular.lerp(dielectricSpecular, baseColorVector, metallicFactor);
525
+ return {
526
+ diffuse: diffuse.toArray(),
527
+ specular: specular.toArray()
528
+ };
529
+ }
530
+ /**
531
+ * Form "textureDefinition" which is part of "sharedResouces"
532
+ * @param {Object} texture - texture image info
533
+ * @returns {Object}
534
+ */
535
+ function extractSharedResourcesTextureInfo(texture, nodeId) {
536
+ return {
537
+ encoding: [texture.source.mimeType],
538
+ images: [
539
+ {
540
+ // 'i3s' has just size which is width of the image. Images are supposed to be square.
541
+ // https://github.com/Esri/i3s-spec/blob/master/docs/1.7/image.cmn.md
542
+ id: generateImageId(texture, nodeId),
543
+ size: texture.source.image.width,
544
+ length: [texture.source.image.data.length]
545
+ }
546
+ ]
547
+ };
548
+ }
549
+ /*
550
+ * Formula for counting imageId:
551
+ * https://github.com/Esri/i3s-spec/blob/0a6366a9249b831db8436c322f8d27521e86cf07/format/Indexed%203d%20Scene%20Layer%20Format%20Specification.md#generating-image-ids
552
+ * @param {Object} texture - texture image info
553
+ * @returns {string}
554
+ */
555
+ function generateImageId(texture, nodeId) {
556
+ const { width, height } = texture.source.image;
557
+ const levelCountOfTexture = 1;
558
+ const indexOfLevel = 0;
559
+ const indexOfTextureInStore = nodeId + 1;
560
+ const zerosCount = 32 - indexOfTextureInStore.toString(2).length;
561
+ const rightHalf = '0'.repeat(zerosCount).concat(indexOfTextureInStore.toString(2));
562
+ const shiftedLevelCountOfTexture = levelCountOfTexture << 28;
563
+ const shiftedIndexOfLevel = indexOfLevel << 24;
564
+ const shiftedWidth = (width - 1) << 12;
565
+ const shiftedHeight = (height - 1) << 0;
566
+ const leftHalf = shiftedLevelCountOfTexture + shiftedIndexOfLevel + shiftedWidth + shiftedHeight;
567
+ const imageId = BigInt(`0b${leftHalf.toString(2)}${rightHalf}`);
568
+ return imageId.toString();
569
+ }
570
+ /**
571
+ * Make all feature ids unique through all nodes in layout.
572
+ * @param {Array} featureIds
573
+ * @param {Array} featureIndices
574
+ * @param {Array} featuresHashArray
575
+ * @param {Object} batchTable
576
+ * @returns {void}
577
+ */
578
+ function makeFeatureIdsUnique(featureIds, featureIndices, featuresHashArray, batchTable) {
579
+ const replaceMap = getFeaturesReplaceMap(featureIds, batchTable, featuresHashArray);
580
+ replaceIndicesByUnique(featureIndices, replaceMap);
581
+ replaceIndicesByUnique(featureIds, replaceMap);
582
+ }
583
+ /**
584
+ * Generate replace map to make featureIds unique.
585
+ * @param {Array} featureIds
586
+ * @param {Object} batchTable
587
+ * @param {Array} featuresHashArray
588
+ * @returns {Object}
589
+ */
590
+ function getFeaturesReplaceMap(featureIds, batchTable, featuresHashArray) {
591
+ const featureMap = {};
592
+ for (let index = 0; index < featureIds.length; index++) {
593
+ const oldFeatureId = featureIds[index];
594
+ const uniqueFeatureId = getOrCreateUniqueFeatureId(index, batchTable, featuresHashArray);
595
+ featureMap[oldFeatureId.toString()] = uniqueFeatureId;
596
+ }
597
+ return featureMap;
598
+ }
599
+ /**
600
+ * Generates string for unique batch id creation.
601
+ * @param {Object} batchTable
602
+ * @param {Number} index
603
+ * @returns {String}
604
+ */
605
+ function generateStringFromBatchTableByIndex(batchTable, index) {
606
+ let str = '';
607
+ for (const key in batchTable) {
608
+ str += batchTable[key][index];
609
+ }
610
+ return str;
611
+ }
612
+ /**
613
+ * Return already exited featureId or creates and returns new to support unique feature ids throw nodes.
614
+ * @param {Number} index
615
+ * @param {Object} batchTable
616
+ * @param {Array} featuresHashArray
617
+ * @returns {Number}
618
+ */
619
+ function getOrCreateUniqueFeatureId(index, batchTable, featuresHashArray) {
620
+ const batchTableStr = generateStringFromBatchTableByIndex(batchTable, index);
621
+ const hash = (0, md5_1.default)(batchTableStr);
622
+ if (featuresHashArray.includes(hash)) {
623
+ return featuresHashArray.indexOf(hash);
624
+ }
625
+ return featuresHashArray.push(hash) - 1;
626
+ }
627
+ /**
628
+ * Do replacement of indices for making them unique through all nodes.
629
+ * @param {Array} indicesArray
630
+ * @param {Object} featureMap
631
+ * @returns {void}
632
+ */
633
+ function replaceIndicesByUnique(indicesArray, featureMap) {
634
+ for (let index = 0; index < indicesArray.length; index++) {
635
+ indicesArray[index] = featureMap[indicesArray[index]];
636
+ }
637
+ }
638
+ /**
639
+ * Convert batch table data to attribute buffers.
640
+ * @param {Object} batchTable - table with metadata for particular feature.
641
+ * @param {Array} featureIds
642
+ * @param {Array} attributeStorageInfo
643
+ * @returns {Array} - Array of file buffers.
644
+ */
645
+ function convertBatchTableToAttributeBuffers(batchTable, featureIds, attributeStorageInfo) {
646
+ const attributeBuffers = [];
647
+ if (batchTable) {
648
+ const batchTableWithFeatureIds = {
649
+ OBJECTID: featureIds,
650
+ ...batchTable
651
+ };
652
+ for (const key in batchTableWithFeatureIds) {
653
+ const type = getAttributeType(key, attributeStorageInfo);
654
+ let attributeBuffer = null;
655
+ switch (type) {
656
+ case OBJECT_ID_TYPE:
657
+ case SHORT_INT_TYPE:
658
+ attributeBuffer = generateShortIntegerAttributeBuffer(batchTableWithFeatureIds[key]);
659
+ break;
660
+ case DOUBLE_TYPE:
661
+ attributeBuffer = generateDoubleAttributeBuffer(batchTableWithFeatureIds[key]);
662
+ break;
663
+ case STRING_TYPE:
664
+ attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
665
+ break;
666
+ default:
667
+ attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
668
+ }
669
+ attributeBuffers.push(attributeBuffer);
670
+ }
671
+ }
672
+ return attributeBuffers;
673
+ }
674
+ /**
675
+ * Return attribute type.
676
+ * @param {String} key
677
+ * @param {Array} attributeStorageInfo
678
+ * @returns {String} attribute type.
679
+ */
680
+ function getAttributeType(key, attributeStorageInfo) {
681
+ const attribute = attributeStorageInfo.find((attr) => attr.name === key);
682
+ return attribute.attributeValues.valueType;
683
+ }
684
+ /**
685
+ * Convert short integer to attribute arrayBuffer.
686
+ * @param {Array} featureIds
687
+ * @returns {ArrayBuffer} - Buffer with objectId data.
688
+ */
689
+ function generateShortIntegerAttributeBuffer(featureIds) {
690
+ const count = new Uint32Array([featureIds.length]);
691
+ const valuesArray = new Uint32Array(featureIds);
692
+ return (0, loader_utils_1.concatenateArrayBuffers)(count.buffer, valuesArray.buffer);
693
+ }
694
+ /**
695
+ * Convert double to attribute arrayBuffer.
696
+ * @param {Array} featureIds
697
+ * @returns {ArrayBuffer} - Buffer with objectId data.
698
+ */
699
+ function generateDoubleAttributeBuffer(featureIds) {
700
+ const count = new Uint32Array([featureIds.length]);
701
+ const padding = new Uint8Array(4);
702
+ const valuesArray = new Float64Array(featureIds);
703
+ return (0, loader_utils_1.concatenateArrayBuffers)(count.buffer, padding.buffer, valuesArray.buffer);
704
+ }
705
+ /**
706
+ * Convert batch table attributes to array buffer with batch table data.
707
+ * @param {Array} batchAttributes
708
+ * @returns {ArrayBuffer} - Buffer with batch table data.
709
+ */
710
+ function generateStringAttributeBuffer(batchAttributes) {
711
+ const stringCountArray = new Uint32Array([batchAttributes.length]);
712
+ let totalNumberOfBytes = 0;
713
+ const stringSizesArray = new Uint32Array(batchAttributes.length);
714
+ const stringBufferArray = [];
715
+ for (let index = 0; index < batchAttributes.length; index++) {
716
+ const currentString = `${String(batchAttributes[index])}\0`;
717
+ const currentStringBuffer = Buffer.from(currentString);
718
+ const currentStringSize = currentStringBuffer.length;
719
+ totalNumberOfBytes += currentStringSize;
720
+ stringSizesArray[index] = currentStringSize;
721
+ stringBufferArray.push(currentStringBuffer);
722
+ }
723
+ const totalBytes = new Uint32Array([totalNumberOfBytes]);
724
+ return (0, loader_utils_1.concatenateArrayBuffers)(stringCountArray.buffer, totalBytes.buffer, stringSizesArray.buffer, ...stringBufferArray);
725
+ }
726
+ /**
727
+ * Convert featureIds to BigUint64Array.
728
+ * @param {Array} featureIds
729
+ * @returns {BigUint64Array} - Array of feature ids in BigUint64 format.
730
+ */
731
+ function generateBigUint64Array(featureIds) {
732
+ const typedFeatureIds = new BigUint64Array(featureIds.length);
733
+ for (let index = 0; index < featureIds.length; index++) {
734
+ typedFeatureIds[index] = BigInt(featureIds[index]);
735
+ }
736
+ return typedFeatureIds;
737
+ }
738
+ /**
739
+ * Generates draco compressed geometry
740
+ * @param {Number} vertexCount
741
+ * @param {Object} convertedAttributes
742
+ * @returns {Promise<object>} - COmpressed geometry.
743
+ */
744
+ async function generateCompressedGeometry(vertexCount, convertedAttributes, attributes) {
745
+ const { positions, normals, texCoords, colors, featureIds, faceRange } = attributes;
746
+ const indices = new Uint32Array(vertexCount);
747
+ for (let index = 0; index < indices.length; index++) {
748
+ indices.set([index], index);
749
+ }
750
+ const featureIndices = new Uint32Array(convertedAttributes.featureIndices.length ? convertedAttributes.featureIndices : vertexCount);
751
+ const featureIndex = generateFeatureIndexAttribute(featureIndices, faceRange);
752
+ const compressedAttributes = {
753
+ positions,
754
+ normals,
755
+ colors,
756
+ 'feature-index': featureIndex
757
+ };
758
+ if (texCoords.length) {
759
+ compressedAttributes.texCoords = texCoords;
760
+ }
761
+ const attributesMetadata = {
762
+ 'feature-index': {
763
+ 'i3s-attribute-type': 'feature-index',
764
+ 'i3s-feature-ids': new Int32Array(featureIds)
765
+ }
766
+ };
767
+ return new Uint8Array(await (0, core_2.encode)({ attributes: compressedAttributes, indices }, draco_1.DracoWriter, {
768
+ draco: {
769
+ method: 'MESH_SEQUENTIAL_ENCODING',
770
+ attributesMetadata
771
+ }
772
+ }));
773
+ }
774
+ /**
775
+ * Generates ordered feature indices based on face range
776
+ * @param {Uint32Array} featureIndex
777
+ * @param {Uint32Array} faceRange
778
+ * @returns {Uint32Array}
779
+ */
780
+ function generateFeatureIndexAttribute(featureIndex, faceRange) {
781
+ const orderedFeatureIndices = new Uint32Array(featureIndex.length);
782
+ let fillIndex = 0;
783
+ let startIndex = 0;
784
+ for (let index = 1; index < faceRange.length; index += 2) {
785
+ const endIndex = (faceRange[index] + 1) * VALUES_PER_VERTEX;
786
+ orderedFeatureIndices.fill(fillIndex, startIndex, endIndex);
787
+ fillIndex++;
788
+ startIndex = endIndex + 1;
789
+ }
790
+ return orderedFeatureIndices;
791
+ }