@loaders.gl/i3s 4.2.0-alpha.4 → 4.2.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/arcgis-webscene-loader.d.ts +1 -1
  2. package/dist/arcgis-webscene-loader.d.ts.map +1 -1
  3. package/dist/arcgis-webscene-loader.js +20 -11
  4. package/dist/dist.dev.js +1244 -722
  5. package/dist/dist.min.js +9 -0
  6. package/dist/i3s-attribute-loader.d.ts +2 -2
  7. package/dist/i3s-attribute-loader.d.ts.map +1 -1
  8. package/dist/i3s-attribute-loader.js +146 -95
  9. package/dist/i3s-building-scene-layer-loader.d.ts +2 -2
  10. package/dist/i3s-building-scene-layer-loader.d.ts.map +1 -1
  11. package/dist/i3s-building-scene-layer-loader.js +18 -14
  12. package/dist/i3s-content-loader.d.ts +2 -2
  13. package/dist/i3s-content-loader.d.ts.map +1 -1
  14. package/dist/i3s-content-loader.js +24 -25
  15. package/dist/i3s-content-worker-node.js +46 -46
  16. package/dist/i3s-content-worker-node.js.map +4 -4
  17. package/dist/i3s-content-worker.js +15 -27
  18. package/dist/i3s-loader.d.ts +2 -2
  19. package/dist/i3s-loader.d.ts.map +1 -1
  20. package/dist/i3s-loader.js +78 -66
  21. package/dist/i3s-node-page-loader.d.ts +2 -2
  22. package/dist/i3s-node-page-loader.d.ts.map +1 -1
  23. package/dist/i3s-node-page-loader.js +17 -13
  24. package/dist/i3s-slpk-loader.js +19 -15
  25. package/dist/index.cjs +92 -163
  26. package/dist/index.cjs.map +7 -0
  27. package/dist/index.d.ts +14 -14
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -1
  30. package/dist/lib/helpers/i3s-nodepages-tiles.d.ts +1 -1
  31. package/dist/lib/helpers/i3s-nodepages-tiles.d.ts.map +1 -1
  32. package/dist/lib/helpers/i3s-nodepages-tiles.js +230 -184
  33. package/dist/lib/parsers/constants.js +72 -45
  34. package/dist/lib/parsers/parse-arcgis-webscene.d.ts +1 -1
  35. package/dist/lib/parsers/parse-arcgis-webscene.d.ts.map +1 -1
  36. package/dist/lib/parsers/parse-arcgis-webscene.js +80 -59
  37. package/dist/lib/parsers/parse-i3s-attribute.js +83 -46
  38. package/dist/lib/parsers/parse-i3s-building-scene-layer.d.ts +1 -1
  39. package/dist/lib/parsers/parse-i3s-building-scene-layer.d.ts.map +1 -1
  40. package/dist/lib/parsers/parse-i3s-building-scene-layer.js +36 -33
  41. package/dist/lib/parsers/parse-i3s-tile-content.d.ts +1 -1
  42. package/dist/lib/parsers/parse-i3s-tile-content.d.ts.map +1 -1
  43. package/dist/lib/parsers/parse-i3s-tile-content.js +433 -388
  44. package/dist/lib/parsers/parse-i3s.d.ts +1 -1
  45. package/dist/lib/parsers/parse-i3s.d.ts.map +1 -1
  46. package/dist/lib/parsers/parse-i3s.js +84 -81
  47. package/dist/lib/parsers/parse-slpk/parse-slpk.d.ts +1 -1
  48. package/dist/lib/parsers/parse-slpk/parse-slpk.d.ts.map +1 -1
  49. package/dist/lib/parsers/parse-slpk/parse-slpk.js +23 -16
  50. package/dist/lib/parsers/parse-slpk/slpk-archieve.js +134 -97
  51. package/dist/lib/utils/convert-i3s-obb-to-mbs.js +12 -5
  52. package/dist/lib/utils/customize-colors.d.ts +1 -1
  53. package/dist/lib/utils/customize-colors.d.ts.map +1 -1
  54. package/dist/lib/utils/customize-colors.js +95 -81
  55. package/dist/lib/utils/url-utils.d.ts +1 -1
  56. package/dist/lib/utils/url-utils.d.ts.map +1 -1
  57. package/dist/lib/utils/url-utils.js +48 -28
  58. package/dist/types.d.ts +1 -1
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/types.js +5 -6
  61. package/dist/workers/i3s-content-worker-node.js +1 -1
  62. package/dist/workers/i3s-content-worker.js +0 -1
  63. package/package.json +15 -12
  64. package/src/i3s-loader.ts +2 -1
  65. package/src/lib/helpers/i3s-nodepages-tiles.ts +1 -3
  66. package/src/lib/parsers/parse-i3s-tile-content.ts +4 -26
  67. package/dist/arcgis-webscene-loader.js.map +0 -1
  68. package/dist/i3s-attribute-loader.js.map +0 -1
  69. package/dist/i3s-building-scene-layer-loader.js.map +0 -1
  70. package/dist/i3s-content-loader.js.map +0 -1
  71. package/dist/i3s-loader.js.map +0 -1
  72. package/dist/i3s-node-page-loader.js.map +0 -1
  73. package/dist/i3s-slpk-loader.js.map +0 -1
  74. package/dist/index.js.map +0 -1
  75. package/dist/lib/helpers/i3s-nodepages-tiles.js.map +0 -1
  76. package/dist/lib/parsers/constants.js.map +0 -1
  77. package/dist/lib/parsers/parse-arcgis-webscene.js.map +0 -1
  78. package/dist/lib/parsers/parse-i3s-attribute.js.map +0 -1
  79. package/dist/lib/parsers/parse-i3s-building-scene-layer.js.map +0 -1
  80. package/dist/lib/parsers/parse-i3s-tile-content.js.map +0 -1
  81. package/dist/lib/parsers/parse-i3s.js.map +0 -1
  82. package/dist/lib/parsers/parse-slpk/parse-slpk.js.map +0 -1
  83. package/dist/lib/parsers/parse-slpk/slpk-archieve.js.map +0 -1
  84. package/dist/lib/utils/convert-i3s-obb-to-mbs.js.map +0 -1
  85. package/dist/lib/utils/customize-colors.js.map +0 -1
  86. package/dist/lib/utils/url-utils.js.map +0 -1
  87. package/dist/types.js.map +0 -1
  88. package/dist/workers/i3s-content-worker-node.js.map +0 -1
  89. package/dist/workers/i3s-content-worker.js.map +0 -1
@@ -10,440 +10,485 @@ import { getUrlWithToken } from "../utils/url-utils.js";
10
10
  import { GL_TYPE_MAP, getConstructorForDataFormat, sizeOf, COORDINATE_SYSTEM } from "./constants.js";
11
11
  const scratchVector = new Vector3([0, 0, 0]);
12
12
  function getLoaderForTextureFormat(textureFormat) {
13
- switch (textureFormat) {
14
- case 'ktx-etc2':
15
- case 'dds':
16
- return CompressedTextureLoader;
17
- case 'ktx2':
18
- return BasisLoader;
19
- case 'jpg':
20
- case 'png':
21
- default:
22
- return ImageLoader;
23
- }
13
+ switch (textureFormat) {
14
+ case 'ktx-etc2':
15
+ case 'dds':
16
+ return CompressedTextureLoader;
17
+ case 'ktx2':
18
+ return BasisLoader;
19
+ case 'jpg':
20
+ case 'png':
21
+ default:
22
+ return ImageLoader;
23
+ }
24
24
  }
25
25
  const I3S_ATTRIBUTE_TYPE = 'i3s-attribute-type';
26
26
  export async function parseI3STileContent(arrayBuffer, tileOptions, tilesetOptions, options, context) {
27
- const content = {
28
- attributes: {},
29
- indices: null,
30
- featureIds: [],
31
- vertexCount: 0,
32
- modelMatrix: new Matrix4(),
33
- coordinateSystem: 0,
34
- byteLength: 0,
35
- texture: null
36
- };
37
- if (tileOptions.textureUrl) {
38
- var _options$i3s;
39
- const url = getUrlWithToken(getInternalPathFromUrl(tileOptions.textureUrl), options === null || options === void 0 ? void 0 : (_options$i3s = options.i3s) === null || _options$i3s === void 0 ? void 0 : _options$i3s.token);
40
- const loader = getLoaderForTextureFormat(tileOptions.textureFormat);
41
- const fetchFunc = (context === null || context === void 0 ? void 0 : context.fetch) || fetch;
42
- const response = await fetchFunc(url);
43
- const arrayBuffer = await response.arrayBuffer();
44
- if (options !== null && options !== void 0 && options.i3s.decodeTextures) {
45
- if (loader === ImageLoader) {
46
- const options = {
47
- ...tileOptions.textureLoaderOptions,
48
- image: {
49
- type: 'data'
50
- }
51
- };
52
- try {
53
- const texture = await parseFromContext(arrayBuffer, [], options, context);
54
- content.texture = texture;
55
- } catch (e) {
56
- const texture = await parse(arrayBuffer, loader, options, context);
57
- content.texture = texture;
27
+ const content = {
28
+ attributes: {},
29
+ indices: null,
30
+ featureIds: [],
31
+ vertexCount: 0,
32
+ modelMatrix: new Matrix4(),
33
+ coordinateSystem: 0,
34
+ byteLength: 0,
35
+ texture: null
36
+ };
37
+ if (tileOptions.textureUrl) {
38
+ // @ts-expect-error options is not properly typed
39
+ const url = getUrlWithToken(tileOptions.textureUrl, options?.i3s?.token);
40
+ const loader = getLoaderForTextureFormat(tileOptions.textureFormat);
41
+ const fetchFunc = context?.fetch || fetch;
42
+ const response = await fetchFunc(url); // options?.fetch
43
+ const arrayBuffer = await response.arrayBuffer();
44
+ // @ts-expect-error options is not properly typed
45
+ if (options?.i3s.decodeTextures) {
46
+ // TODO - replace with switch
47
+ if (loader === ImageLoader) {
48
+ const options = { ...tileOptions.textureLoaderOptions, image: { type: 'data' } };
49
+ try {
50
+ // Image constructor is not supported in worker thread.
51
+ // Do parsing image data on the main thread by using context to avoid worker issues.
52
+ const texture = await parseFromContext(arrayBuffer, [], options, context);
53
+ // @ts-expect-error
54
+ content.texture = texture;
55
+ }
56
+ catch (e) {
57
+ // context object is different between worker and node.js conversion script.
58
+ // To prevent error we parse data in ordinary way if it is not parsed by using context.
59
+ const texture = await parse(arrayBuffer, loader, options, context);
60
+ content.texture = texture;
61
+ }
62
+ }
63
+ else if (loader === CompressedTextureLoader || loader === BasisLoader) {
64
+ let texture = await load(arrayBuffer, loader, tileOptions.textureLoaderOptions);
65
+ if (loader === BasisLoader) {
66
+ texture = texture[0];
67
+ }
68
+ content.texture = {
69
+ compressed: true,
70
+ mipmaps: false,
71
+ width: texture[0].width,
72
+ height: texture[0].height,
73
+ data: texture
74
+ };
75
+ }
58
76
  }
59
- } else if (loader === CompressedTextureLoader || loader === BasisLoader) {
60
- let texture = await load(arrayBuffer, loader, tileOptions.textureLoaderOptions);
61
- if (loader === BasisLoader) {
62
- texture = texture[0];
77
+ else {
78
+ content.texture = arrayBuffer;
63
79
  }
64
- content.texture = {
65
- compressed: true,
66
- mipmaps: false,
67
- width: texture[0].width,
68
- height: texture[0].height,
69
- data: texture
70
- };
71
- }
72
- } else {
73
- content.texture = arrayBuffer;
74
80
  }
75
- }
76
- content.material = makePbrMaterial(tileOptions.materialDefinition, content.texture);
77
- if (content.material) {
78
- content.texture = null;
79
- }
80
- return await parseI3SNodeGeometry(arrayBuffer, content, tileOptions, tilesetOptions, options);
81
- }
82
- function getInternalPathFromUrl(url) {
83
- const slpkUrlParts = url.split('.slpk');
84
- let filename;
85
- if (slpkUrlParts.length === 1) {
86
- filename = url;
87
- } else if (slpkUrlParts.length === 2) {
88
- filename = slpkUrlParts[1].slice(1);
89
- } else {
90
- filename = url;
91
- }
92
- return filename;
81
+ content.material = makePbrMaterial(tileOptions.materialDefinition, content.texture);
82
+ if (content.material) {
83
+ content.texture = null;
84
+ }
85
+ return await parseI3SNodeGeometry(arrayBuffer, content, tileOptions, tilesetOptions, options);
93
86
  }
87
+ /* eslint-disable max-statements */
94
88
  async function parseI3SNodeGeometry(arrayBuffer, content, tileOptions, tilesetOptions, options) {
95
- var _options$i3s2;
96
- const contentByteLength = arrayBuffer.byteLength;
97
- let attributes;
98
- let vertexCount;
99
- let byteOffset = 0;
100
- let featureCount = 0;
101
- let indices;
102
- if (tileOptions.isDracoGeometry) {
103
- var _decompressedGeometry;
104
- const decompressedGeometry = await parse(arrayBuffer, DracoLoader, {
105
- draco: {
106
- attributeNameEntry: I3S_ATTRIBUTE_TYPE
107
- }
108
- });
109
- vertexCount = decompressedGeometry.header.vertexCount;
110
- indices = (_decompressedGeometry = decompressedGeometry.indices) === null || _decompressedGeometry === void 0 ? void 0 : _decompressedGeometry.value;
111
- const {
112
- POSITION,
113
- NORMAL,
114
- COLOR_0,
115
- TEXCOORD_0,
116
- ['feature-index']: featureIndex,
117
- ['uv-region']: uvRegion
118
- } = decompressedGeometry.attributes;
119
- attributes = {
120
- position: POSITION,
121
- normal: NORMAL,
122
- color: COLOR_0,
123
- uv0: TEXCOORD_0,
124
- uvRegion,
125
- id: featureIndex
89
+ const contentByteLength = arrayBuffer.byteLength;
90
+ let attributes;
91
+ let vertexCount;
92
+ let byteOffset = 0;
93
+ let featureCount = 0;
94
+ let indices;
95
+ if (tileOptions.isDracoGeometry) {
96
+ const decompressedGeometry = await parse(arrayBuffer, DracoLoader, {
97
+ draco: {
98
+ attributeNameEntry: I3S_ATTRIBUTE_TYPE
99
+ }
100
+ });
101
+ // @ts-expect-error
102
+ vertexCount = decompressedGeometry.header.vertexCount;
103
+ indices = decompressedGeometry.indices?.value;
104
+ const { POSITION, NORMAL, COLOR_0, TEXCOORD_0, ['feature-index']: featureIndex, ['uv-region']: uvRegion } = decompressedGeometry.attributes;
105
+ attributes = {
106
+ position: POSITION,
107
+ normal: NORMAL,
108
+ color: COLOR_0,
109
+ uv0: TEXCOORD_0,
110
+ uvRegion,
111
+ id: featureIndex
112
+ };
113
+ updateAttributesMetadata(attributes, decompressedGeometry);
114
+ const featureIds = getFeatureIdsFromFeatureIndexMetadata(featureIndex);
115
+ if (featureIds) {
116
+ flattenFeatureIdsByFeatureIndices(attributes, featureIds);
117
+ }
118
+ }
119
+ else {
120
+ const { vertexAttributes, ordering: attributesOrder, featureAttributes, featureAttributeOrder } = tilesetOptions.store.defaultGeometrySchema;
121
+ // First 8 bytes reserved for header (vertexCount and featureCount)
122
+ const headers = parseHeaders(arrayBuffer, tilesetOptions);
123
+ byteOffset = headers.byteOffset;
124
+ vertexCount = headers.vertexCount;
125
+ featureCount = headers.featureCount;
126
+ // Getting vertex attributes such as positions, normals, colors, etc...
127
+ const { attributes: normalizedVertexAttributes, byteOffset: offset } = normalizeAttributes(arrayBuffer, byteOffset, vertexAttributes, vertexCount, attributesOrder);
128
+ // Getting feature attributes such as featureIds and faceRange
129
+ const { attributes: normalizedFeatureAttributes } = normalizeAttributes(arrayBuffer, offset, featureAttributes, featureCount, featureAttributeOrder);
130
+ flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes);
131
+ attributes = concatAttributes(normalizedVertexAttributes, normalizedFeatureAttributes);
132
+ }
133
+ if (!options?.i3s?.coordinateSystem ||
134
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
135
+ options.i3s.coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS) {
136
+ const enuMatrix = parsePositions(attributes.position, tileOptions);
137
+ content.modelMatrix = enuMatrix.invert();
138
+ content.coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS;
139
+ }
140
+ else {
141
+ content.modelMatrix = getModelMatrix(attributes.position);
142
+ content.coordinateSystem = COORDINATE_SYSTEM.LNGLAT_OFFSETS;
143
+ }
144
+ content.attributes = {
145
+ positions: attributes.position,
146
+ normals: attributes.normal,
147
+ colors: normalizeAttribute(attributes.color), // Normalize from UInt8
148
+ texCoords: attributes.uv0,
149
+ uvRegions: normalizeAttribute(attributes.uvRegion || attributes.region) // Normalize from UInt16
126
150
  };
127
- updateAttributesMetadata(attributes, decompressedGeometry);
128
- const featureIds = getFeatureIdsFromFeatureIndexMetadata(featureIndex);
129
- if (featureIds) {
130
- flattenFeatureIdsByFeatureIndices(attributes, featureIds);
151
+ content.indices = indices || null;
152
+ if (attributes.id && attributes.id.value) {
153
+ content.featureIds = attributes.id.value;
131
154
  }
132
- } else {
133
- const {
134
- vertexAttributes,
135
- ordering: attributesOrder,
136
- featureAttributes,
137
- featureAttributeOrder
138
- } = tilesetOptions.store.defaultGeometrySchema;
139
- const headers = parseHeaders(arrayBuffer, tilesetOptions);
140
- byteOffset = headers.byteOffset;
141
- vertexCount = headers.vertexCount;
142
- featureCount = headers.featureCount;
143
- const {
144
- attributes: normalizedVertexAttributes,
145
- byteOffset: offset
146
- } = normalizeAttributes(arrayBuffer, byteOffset, vertexAttributes, vertexCount, attributesOrder);
147
- const {
148
- attributes: normalizedFeatureAttributes
149
- } = normalizeAttributes(arrayBuffer, offset, featureAttributes, featureCount, featureAttributeOrder);
150
- flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes);
151
- attributes = concatAttributes(normalizedVertexAttributes, normalizedFeatureAttributes);
152
- }
153
- if (!(options !== null && options !== void 0 && (_options$i3s2 = options.i3s) !== null && _options$i3s2 !== void 0 && _options$i3s2.coordinateSystem) || options.i3s.coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS) {
154
- const enuMatrix = parsePositions(attributes.position, tileOptions);
155
- content.modelMatrix = enuMatrix.invert();
156
- content.coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS;
157
- } else {
158
- content.modelMatrix = getModelMatrix(attributes.position);
159
- content.coordinateSystem = COORDINATE_SYSTEM.LNGLAT_OFFSETS;
160
- }
161
- content.attributes = {
162
- positions: attributes.position,
163
- normals: attributes.normal,
164
- colors: normalizeAttribute(attributes.color),
165
- texCoords: attributes.uv0,
166
- uvRegions: normalizeAttribute(attributes.uvRegion || attributes.region)
167
- };
168
- content.indices = indices || null;
169
- if (attributes.id && attributes.id.value) {
170
- content.featureIds = attributes.id.value;
171
- }
172
- for (const attributeIndex in content.attributes) {
173
- if (!content.attributes[attributeIndex]) {
174
- delete content.attributes[attributeIndex];
155
+ // Remove undefined attributes
156
+ for (const attributeIndex in content.attributes) {
157
+ if (!content.attributes[attributeIndex]) {
158
+ delete content.attributes[attributeIndex];
159
+ }
175
160
  }
176
- }
177
- content.vertexCount = vertexCount;
178
- content.byteLength = contentByteLength;
179
- return content;
161
+ content.vertexCount = vertexCount;
162
+ content.byteLength = contentByteLength;
163
+ return content;
180
164
  }
165
+ /**
166
+ * Update attributes with metadata from decompressed geometry.
167
+ * @param decompressedGeometry
168
+ * @param attributes
169
+ */
181
170
  function updateAttributesMetadata(attributes, decompressedGeometry) {
182
- for (const key in decompressedGeometry.loaderData.attributes) {
183
- const dracoAttribute = decompressedGeometry.loaderData.attributes[key];
184
- switch (dracoAttribute.name) {
185
- case 'POSITION':
186
- attributes.position.metadata = dracoAttribute.metadata;
187
- break;
188
- case 'feature-index':
189
- attributes.id.metadata = dracoAttribute.metadata;
190
- break;
191
- default:
192
- break;
171
+ for (const key in decompressedGeometry.loaderData.attributes) {
172
+ const dracoAttribute = decompressedGeometry.loaderData.attributes[key];
173
+ switch (dracoAttribute.name) {
174
+ case 'POSITION':
175
+ attributes.position.metadata = dracoAttribute.metadata;
176
+ break;
177
+ case 'feature-index':
178
+ attributes.id.metadata = dracoAttribute.metadata;
179
+ break;
180
+ default:
181
+ break;
182
+ }
193
183
  }
194
- }
195
184
  }
185
+ /**
186
+ * Do concatenation of attribute objects.
187
+ * Done as separate fucntion to avoid ts errors.
188
+ * @param normalizedVertexAttributes
189
+ * @param normalizedFeatureAttributes
190
+ * @returns - result of attributes concatenation.
191
+ */
196
192
  function concatAttributes(normalizedVertexAttributes, normalizedFeatureAttributes) {
197
- return {
198
- ...normalizedVertexAttributes,
199
- ...normalizedFeatureAttributes
200
- };
193
+ return { ...normalizedVertexAttributes, ...normalizedFeatureAttributes };
201
194
  }
195
+ /**
196
+ * Normalize attribute to range [0..1] . Eg. convert colors buffer from [255,255,255,255] to [1,1,1,1]
197
+ * @param attribute - geometry attribute
198
+ * @returns - geometry attribute in right format
199
+ */
202
200
  function normalizeAttribute(attribute) {
203
- if (!attribute) {
201
+ if (!attribute) {
202
+ return attribute;
203
+ }
204
+ attribute.normalized = true;
204
205
  return attribute;
205
- }
206
- attribute.normalized = true;
207
- return attribute;
208
206
  }
209
207
  function parseHeaders(arrayBuffer, options) {
210
- let byteOffset = 0;
211
- let vertexCount = 0;
212
- let featureCount = 0;
213
- for (const {
214
- property,
215
- type
216
- } of options.store.defaultGeometrySchema.header) {
217
- const TypedArrayTypeHeader = getConstructorForDataFormat(type);
218
- switch (property) {
219
- case HeaderAttributeProperty.vertexCount:
220
- vertexCount = new TypedArrayTypeHeader(arrayBuffer, 0, 4)[0];
221
- byteOffset += sizeOf(type);
222
- break;
223
- case HeaderAttributeProperty.featureCount:
224
- featureCount = new TypedArrayTypeHeader(arrayBuffer, 4, 4)[0];
225
- byteOffset += sizeOf(type);
226
- break;
227
- default:
228
- break;
208
+ let byteOffset = 0;
209
+ // First 8 bytes reserved for header (vertexCount and featurecount)
210
+ let vertexCount = 0;
211
+ let featureCount = 0;
212
+ for (const { property, type } of options.store.defaultGeometrySchema.header) {
213
+ const TypedArrayTypeHeader = getConstructorForDataFormat(type);
214
+ switch (property) {
215
+ case HeaderAttributeProperty.vertexCount.toString():
216
+ vertexCount = new TypedArrayTypeHeader(arrayBuffer, 0, 4)[0];
217
+ byteOffset += sizeOf(type);
218
+ break;
219
+ case HeaderAttributeProperty.featureCount.toString():
220
+ featureCount = new TypedArrayTypeHeader(arrayBuffer, 4, 4)[0];
221
+ byteOffset += sizeOf(type);
222
+ break;
223
+ default:
224
+ break;
225
+ }
229
226
  }
230
- }
231
- return {
232
- vertexCount,
233
- featureCount,
234
- byteOffset
235
- };
227
+ return {
228
+ vertexCount,
229
+ featureCount,
230
+ byteOffset
231
+ };
236
232
  }
233
+ /* eslint-enable max-statements */
237
234
  function normalizeAttributes(arrayBuffer, byteOffset, vertexAttributes, attributeCount, attributesOrder) {
238
- const attributes = {};
239
- for (const attribute of attributesOrder) {
240
- if (vertexAttributes[attribute]) {
241
- const {
242
- valueType,
243
- valuesPerElement
244
- } = vertexAttributes[attribute];
245
- if (byteOffset + attributeCount * valuesPerElement * sizeOf(valueType) <= arrayBuffer.byteLength) {
246
- const buffer = arrayBuffer.slice(byteOffset);
247
- let value;
248
- if (valueType === 'UInt64') {
249
- value = parseUint64Values(buffer, attributeCount * valuesPerElement, sizeOf(valueType));
250
- } else {
251
- const TypedArrayType = getConstructorForDataFormat(valueType);
252
- value = new TypedArrayType(buffer, 0, attributeCount * valuesPerElement);
253
- }
254
- attributes[attribute] = {
255
- value,
256
- type: GL_TYPE_MAP[valueType],
257
- size: valuesPerElement
258
- };
259
- switch (attribute) {
260
- case 'color':
261
- attributes.color.normalized = true;
262
- break;
263
- case 'position':
264
- case 'region':
265
- case 'normal':
266
- default:
235
+ const attributes = {};
236
+ // the order of attributes depend on the order being added to the vertexAttributes object
237
+ for (const attribute of attributesOrder) {
238
+ if (vertexAttributes[attribute]) {
239
+ const { valueType, valuesPerElement } = vertexAttributes[attribute];
240
+ // protect from arrayBuffer read overunns by NOT assuming node has regions always even though its declared in defaultGeometrySchema.
241
+ // In i3s 1.6: client is required to decide that based on ./shared resource of the node (materialDefinitions.[Mat_id].params.vertexRegions == true)
242
+ // In i3s 1.7 the property has been rolled into the 3d scene layer json/node pages.
243
+ // Code below does not account when the bytelength is actually bigger than
244
+ // the calculated value (b\c the tile potentially could have mesh segmentation information).
245
+ // In those cases tiles without regions could fail or have garbage values.
246
+ if (byteOffset + attributeCount * valuesPerElement * sizeOf(valueType) <=
247
+ arrayBuffer.byteLength) {
248
+ const buffer = arrayBuffer.slice(byteOffset);
249
+ let value;
250
+ if (valueType === 'UInt64') {
251
+ value = parseUint64Values(buffer, attributeCount * valuesPerElement, sizeOf(valueType));
252
+ }
253
+ else {
254
+ const TypedArrayType = getConstructorForDataFormat(valueType);
255
+ value = new TypedArrayType(buffer, 0, attributeCount * valuesPerElement);
256
+ }
257
+ attributes[attribute] = {
258
+ value,
259
+ type: GL_TYPE_MAP[valueType],
260
+ size: valuesPerElement
261
+ };
262
+ switch (attribute) {
263
+ case 'color':
264
+ attributes.color.normalized = true;
265
+ break;
266
+ case 'position':
267
+ case 'region':
268
+ case 'normal':
269
+ default:
270
+ }
271
+ byteOffset = byteOffset + attributeCount * valuesPerElement * sizeOf(valueType);
272
+ }
273
+ else if (attribute !== 'uv0') {
274
+ break;
275
+ }
267
276
  }
268
- byteOffset = byteOffset + attributeCount * valuesPerElement * sizeOf(valueType);
269
- } else if (attribute !== 'uv0') {
270
- break;
271
- }
272
277
  }
273
- }
274
- return {
275
- attributes,
276
- byteOffset
277
- };
278
+ return { attributes, byteOffset };
278
279
  }
280
+ /**
281
+ * Parse buffer to return array of uint64 values
282
+ *
283
+ * @param buffer
284
+ * @param elementsCount
285
+ * @returns 64-bit array of values until precision is lost after Number.MAX_SAFE_INTEGER
286
+ */
279
287
  function parseUint64Values(buffer, elementsCount, attributeSize) {
280
- const values = [];
281
- const dataView = new DataView(buffer);
282
- let offset = 0;
283
- for (let index = 0; index < elementsCount; index++) {
284
- const left = dataView.getUint32(offset, true);
285
- const right = dataView.getUint32(offset + 4, true);
286
- const value = left + 2 ** 32 * right;
287
- values.push(value);
288
- offset += attributeSize;
289
- }
290
- return new Uint32Array(values);
288
+ const values = [];
289
+ const dataView = new DataView(buffer);
290
+ let offset = 0;
291
+ for (let index = 0; index < elementsCount; index++) {
292
+ // split 64-bit number into two 32-bit parts
293
+ const left = dataView.getUint32(offset, true);
294
+ const right = dataView.getUint32(offset + 4, true);
295
+ // combine the two 32-bit values
296
+ const value = left + 2 ** 32 * right;
297
+ values.push(value);
298
+ offset += attributeSize;
299
+ }
300
+ return new Uint32Array(values);
291
301
  }
292
302
  function parsePositions(attribute, options) {
293
- const mbs = options.mbs;
294
- const value = attribute.value;
295
- const metadata = attribute.metadata;
296
- const enuMatrix = new Matrix4();
297
- const cartographicOrigin = new Vector3(mbs[0], mbs[1], mbs[2]);
298
- const cartesianOrigin = new Vector3();
299
- Ellipsoid.WGS84.cartographicToCartesian(cartographicOrigin, cartesianOrigin);
300
- Ellipsoid.WGS84.eastNorthUpToFixedFrame(cartesianOrigin, enuMatrix);
301
- attribute.value = offsetsToCartesians(value, metadata, cartographicOrigin);
302
- return enuMatrix;
303
+ const mbs = options.mbs;
304
+ const value = attribute.value;
305
+ const metadata = attribute.metadata;
306
+ const enuMatrix = new Matrix4();
307
+ const cartographicOrigin = new Vector3(mbs[0], mbs[1], mbs[2]);
308
+ const cartesianOrigin = new Vector3();
309
+ Ellipsoid.WGS84.cartographicToCartesian(cartographicOrigin, cartesianOrigin);
310
+ Ellipsoid.WGS84.eastNorthUpToFixedFrame(cartesianOrigin, enuMatrix);
311
+ attribute.value = offsetsToCartesians(value, metadata, cartographicOrigin);
312
+ return enuMatrix;
303
313
  }
304
- function offsetsToCartesians(vertices) {
305
- let metadata = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
306
- let cartographicOrigin = arguments.length > 2 ? arguments[2] : undefined;
307
- const positions = new Float64Array(vertices.length);
308
- const scaleX = metadata['i3s-scale_x'] && metadata['i3s-scale_x'].double || 1;
309
- const scaleY = metadata['i3s-scale_y'] && metadata['i3s-scale_y'].double || 1;
310
- for (let i = 0; i < positions.length; i += 3) {
311
- positions[i] = vertices[i] * scaleX + cartographicOrigin.x;
312
- positions[i + 1] = vertices[i + 1] * scaleY + cartographicOrigin.y;
313
- positions[i + 2] = vertices[i + 2] + cartographicOrigin.z;
314
- }
315
- for (let i = 0; i < positions.length; i += 3) {
316
- Ellipsoid.WGS84.cartographicToCartesian(positions.subarray(i, i + 3), scratchVector);
317
- positions[i] = scratchVector.x;
318
- positions[i + 1] = scratchVector.y;
319
- positions[i + 2] = scratchVector.z;
320
- }
321
- return positions;
314
+ /**
315
+ * Converts position coordinates to absolute cartesian coordinates
316
+ * @param vertices - "position" attribute data
317
+ * @param metadata - When the geometry is DRACO compressed, contain position attribute's metadata
318
+ * https://github.com/Esri/i3s-spec/blob/master/docs/1.7/compressedAttributes.cmn.md
319
+ * @param cartographicOrigin - Cartographic origin coordinates
320
+ * @returns - converted "position" data
321
+ */
322
+ function offsetsToCartesians(vertices, metadata = {}, cartographicOrigin) {
323
+ const positions = new Float64Array(vertices.length);
324
+ const scaleX = (metadata['i3s-scale_x'] && metadata['i3s-scale_x'].double) || 1;
325
+ const scaleY = (metadata['i3s-scale_y'] && metadata['i3s-scale_y'].double) || 1;
326
+ for (let i = 0; i < positions.length; i += 3) {
327
+ positions[i] = vertices[i] * scaleX + cartographicOrigin.x;
328
+ positions[i + 1] = vertices[i + 1] * scaleY + cartographicOrigin.y;
329
+ positions[i + 2] = vertices[i + 2] + cartographicOrigin.z;
330
+ }
331
+ for (let i = 0; i < positions.length; i += 3) {
332
+ // @ts-ignore
333
+ Ellipsoid.WGS84.cartographicToCartesian(positions.subarray(i, i + 3), scratchVector);
334
+ positions[i] = scratchVector.x;
335
+ positions[i + 1] = scratchVector.y;
336
+ positions[i + 2] = scratchVector.z;
337
+ }
338
+ return positions;
322
339
  }
340
+ /**
341
+ * Get model matrix for loaded vertices
342
+ * @param positions positions attribute
343
+ * @returns Matrix4 - model matrix for geometry transformation
344
+ */
323
345
  function getModelMatrix(positions) {
324
- var _metadata$i3sScale_x, _metadata$i3sScale_y;
325
- const metadata = positions.metadata;
326
- const scaleX = (metadata === null || metadata === void 0 ? void 0 : (_metadata$i3sScale_x = metadata['i3s-scale_x']) === null || _metadata$i3sScale_x === void 0 ? void 0 : _metadata$i3sScale_x.double) || 1;
327
- const scaleY = (metadata === null || metadata === void 0 ? void 0 : (_metadata$i3sScale_y = metadata['i3s-scale_y']) === null || _metadata$i3sScale_y === void 0 ? void 0 : _metadata$i3sScale_y.double) || 1;
328
- const modelMatrix = new Matrix4();
329
- modelMatrix[0] = scaleX;
330
- modelMatrix[5] = scaleY;
331
- return modelMatrix;
346
+ const metadata = positions.metadata;
347
+ const scaleX = metadata?.['i3s-scale_x']?.double || 1;
348
+ const scaleY = metadata?.['i3s-scale_y']?.double || 1;
349
+ const modelMatrix = new Matrix4();
350
+ modelMatrix[0] = scaleX;
351
+ modelMatrix[5] = scaleY;
352
+ return modelMatrix;
332
353
  }
354
+ /**
355
+ * Makes a glTF-compatible PBR material from an I3S material definition
356
+ * @param materialDefinition - i3s material definition
357
+ * https://github.com/Esri/i3s-spec/blob/master/docs/1.7/materialDefinitions.cmn.md
358
+ * @param texture - texture image
359
+ * @returns {object}
360
+ */
333
361
  function makePbrMaterial(materialDefinition, texture) {
334
- let pbrMaterial;
335
- if (materialDefinition) {
336
- pbrMaterial = {
337
- ...materialDefinition,
338
- pbrMetallicRoughness: materialDefinition.pbrMetallicRoughness ? {
339
- ...materialDefinition.pbrMetallicRoughness
340
- } : {
341
- baseColorFactor: [255, 255, 255, 255]
342
- }
343
- };
344
- } else {
345
- pbrMaterial = {
346
- pbrMetallicRoughness: {}
347
- };
362
+ let pbrMaterial;
363
+ if (materialDefinition) {
364
+ pbrMaterial = {
365
+ ...materialDefinition,
366
+ pbrMetallicRoughness: materialDefinition.pbrMetallicRoughness
367
+ ? { ...materialDefinition.pbrMetallicRoughness }
368
+ : { baseColorFactor: [255, 255, 255, 255] }
369
+ };
370
+ }
371
+ else {
372
+ pbrMaterial = {
373
+ pbrMetallicRoughness: {}
374
+ };
375
+ if (texture) {
376
+ pbrMaterial.pbrMetallicRoughness.baseColorTexture = { texCoord: 0 };
377
+ }
378
+ else {
379
+ pbrMaterial.pbrMetallicRoughness.baseColorFactor = [255, 255, 255, 255];
380
+ }
381
+ }
382
+ // Set default 0.25 per spec https://github.com/Esri/i3s-spec/blob/master/docs/1.7/materialDefinitions.cmn.md
383
+ pbrMaterial.alphaCutoff = pbrMaterial.alphaCutoff || 0.25;
384
+ if (pbrMaterial.alphaMode) {
385
+ // I3S contain alphaMode in lowerCase
386
+ pbrMaterial.alphaMode = pbrMaterial.alphaMode.toUpperCase();
387
+ }
388
+ // Convert colors from [255,255,255,255] to [1,1,1,1]
389
+ if (pbrMaterial.emissiveFactor) {
390
+ pbrMaterial.emissiveFactor = convertColorFormat(pbrMaterial.emissiveFactor);
391
+ }
392
+ if (pbrMaterial.pbrMetallicRoughness && pbrMaterial.pbrMetallicRoughness.baseColorFactor) {
393
+ pbrMaterial.pbrMetallicRoughness.baseColorFactor = convertColorFormat(pbrMaterial.pbrMetallicRoughness.baseColorFactor);
394
+ }
348
395
  if (texture) {
349
- pbrMaterial.pbrMetallicRoughness.baseColorTexture = {
350
- texCoord: 0
351
- };
352
- } else {
353
- pbrMaterial.pbrMetallicRoughness.baseColorFactor = [255, 255, 255, 255];
396
+ setMaterialTexture(pbrMaterial, texture);
354
397
  }
355
- }
356
- pbrMaterial.alphaCutoff = pbrMaterial.alphaCutoff || 0.25;
357
- if (pbrMaterial.alphaMode) {
358
- pbrMaterial.alphaMode = pbrMaterial.alphaMode.toUpperCase();
359
- }
360
- if (pbrMaterial.emissiveFactor) {
361
- pbrMaterial.emissiveFactor = convertColorFormat(pbrMaterial.emissiveFactor);
362
- }
363
- if (pbrMaterial.pbrMetallicRoughness && pbrMaterial.pbrMetallicRoughness.baseColorFactor) {
364
- pbrMaterial.pbrMetallicRoughness.baseColorFactor = convertColorFormat(pbrMaterial.pbrMetallicRoughness.baseColorFactor);
365
- }
366
- if (texture) {
367
- setMaterialTexture(pbrMaterial, texture);
368
- }
369
- return pbrMaterial;
398
+ return pbrMaterial;
370
399
  }
400
+ /**
401
+ * Convert color from [255,255,255,255] to [1,1,1,1]
402
+ * @param colorFactor - color array
403
+ * @returns - new color array
404
+ */
371
405
  function convertColorFormat(colorFactor) {
372
- const normalizedColor = [...colorFactor];
373
- for (let index = 0; index < colorFactor.length; index++) {
374
- normalizedColor[index] = colorFactor[index] / 255;
375
- }
376
- return normalizedColor;
406
+ const normalizedColor = [...colorFactor];
407
+ for (let index = 0; index < colorFactor.length; index++) {
408
+ normalizedColor[index] = colorFactor[index] / 255;
409
+ }
410
+ return normalizedColor;
377
411
  }
412
+ /**
413
+ * Set texture in PBR material
414
+ * @param {object} material - i3s material definition
415
+ * @param image - texture image
416
+ * @returns
417
+ */
378
418
  function setMaterialTexture(material, image) {
379
- const texture = {
380
- source: {
381
- image
419
+ const texture = { source: { image } };
420
+ // I3SLoader now support loading only one texture. This elseif sequence will assign this texture to one of
421
+ // properties defined in materialDefinition
422
+ if (material.pbrMetallicRoughness && material.pbrMetallicRoughness.baseColorTexture) {
423
+ material.pbrMetallicRoughness.baseColorTexture = {
424
+ ...material.pbrMetallicRoughness.baseColorTexture,
425
+ texture
426
+ };
427
+ }
428
+ else if (material.emissiveTexture) {
429
+ material.emissiveTexture = { ...material.emissiveTexture, texture };
430
+ }
431
+ else if (material.pbrMetallicRoughness &&
432
+ material.pbrMetallicRoughness.metallicRoughnessTexture) {
433
+ material.pbrMetallicRoughness.metallicRoughnessTexture = {
434
+ ...material.pbrMetallicRoughness.metallicRoughnessTexture,
435
+ texture
436
+ };
437
+ }
438
+ else if (material.normalTexture) {
439
+ material.normalTexture = { ...material.normalTexture, texture };
440
+ }
441
+ else if (material.occlusionTexture) {
442
+ material.occlusionTexture = { ...material.occlusionTexture, texture };
382
443
  }
383
- };
384
- if (material.pbrMetallicRoughness && material.pbrMetallicRoughness.baseColorTexture) {
385
- material.pbrMetallicRoughness.baseColorTexture = {
386
- ...material.pbrMetallicRoughness.baseColorTexture,
387
- texture
388
- };
389
- } else if (material.emissiveTexture) {
390
- material.emissiveTexture = {
391
- ...material.emissiveTexture,
392
- texture
393
- };
394
- } else if (material.pbrMetallicRoughness && material.pbrMetallicRoughness.metallicRoughnessTexture) {
395
- material.pbrMetallicRoughness.metallicRoughnessTexture = {
396
- ...material.pbrMetallicRoughness.metallicRoughnessTexture,
397
- texture
398
- };
399
- } else if (material.normalTexture) {
400
- material.normalTexture = {
401
- ...material.normalTexture,
402
- texture
403
- };
404
- } else if (material.occlusionTexture) {
405
- material.occlusionTexture = {
406
- ...material.occlusionTexture,
407
- texture
408
- };
409
- }
410
444
  }
445
+ /**
446
+ * Flatten feature ids using face ranges
447
+ * @param normalizedFeatureAttributes
448
+ * @returns
449
+ */
411
450
  function flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes) {
412
- const {
413
- id,
414
- faceRange
415
- } = normalizedFeatureAttributes;
416
- if (!id || !faceRange) {
417
- return;
418
- }
419
- const featureIds = id.value;
420
- const range = faceRange.value;
421
- const featureIdsLength = range[range.length - 1] + 1;
422
- const orderedFeatureIndices = new Uint32Array(featureIdsLength * 3);
423
- let featureIndex = 0;
424
- let startIndex = 0;
425
- for (let index = 1; index < range.length; index += 2) {
426
- const fillId = Number(featureIds[featureIndex]);
427
- const endValue = range[index];
428
- const prevValue = range[index - 1];
429
- const trianglesCount = endValue - prevValue + 1;
430
- const endIndex = startIndex + trianglesCount * 3;
431
- orderedFeatureIndices.fill(fillId, startIndex, endIndex);
432
- featureIndex++;
433
- startIndex = endIndex;
434
- }
435
- normalizedFeatureAttributes.id.value = orderedFeatureIndices;
451
+ const { id, faceRange } = normalizedFeatureAttributes;
452
+ if (!id || !faceRange) {
453
+ return;
454
+ }
455
+ const featureIds = id.value;
456
+ const range = faceRange.value;
457
+ const featureIdsLength = range[range.length - 1] + 1;
458
+ const orderedFeatureIndices = new Uint32Array(featureIdsLength * 3);
459
+ let featureIndex = 0;
460
+ let startIndex = 0;
461
+ for (let index = 1; index < range.length; index += 2) {
462
+ const fillId = Number(featureIds[featureIndex]);
463
+ const endValue = range[index];
464
+ const prevValue = range[index - 1];
465
+ const trianglesCount = endValue - prevValue + 1;
466
+ const endIndex = startIndex + trianglesCount * 3;
467
+ orderedFeatureIndices.fill(fillId, startIndex, endIndex);
468
+ featureIndex++;
469
+ startIndex = endIndex;
470
+ }
471
+ normalizedFeatureAttributes.id.value = orderedFeatureIndices;
436
472
  }
473
+ /**
474
+ * Flatten feature ids using featureIndices
475
+ * @param attributes
476
+ * @param featureIds
477
+ * @returns
478
+ */
437
479
  function flattenFeatureIdsByFeatureIndices(attributes, featureIds) {
438
- const featureIndices = attributes.id.value;
439
- const result = new Float32Array(featureIndices.length);
440
- for (let index = 0; index < featureIndices.length; index++) {
441
- result[index] = featureIds[featureIndices[index]];
442
- }
443
- attributes.id.value = result;
480
+ const featureIndices = attributes.id.value;
481
+ const result = new Float32Array(featureIndices.length);
482
+ for (let index = 0; index < featureIndices.length; index++) {
483
+ result[index] = featureIds[featureIndices[index]];
484
+ }
485
+ attributes.id.value = result;
444
486
  }
487
+ /**
488
+ * Flatten feature ids using featureIndices
489
+ * @param featureIndex
490
+ * @returns
491
+ */
445
492
  function getFeatureIdsFromFeatureIndexMetadata(featureIndex) {
446
- var _featureIndex$metadat, _featureIndex$metadat2;
447
- return featureIndex === null || featureIndex === void 0 ? void 0 : (_featureIndex$metadat = featureIndex.metadata) === null || _featureIndex$metadat === void 0 ? void 0 : (_featureIndex$metadat2 = _featureIndex$metadat['i3s-feature-ids']) === null || _featureIndex$metadat2 === void 0 ? void 0 : _featureIndex$metadat2.intArray;
493
+ return featureIndex?.metadata?.['i3s-feature-ids']?.intArray;
448
494
  }
449
- //# sourceMappingURL=parse-i3s-tile-content.js.map