@xeokit/xeokit-sdk 2.6.65 → 2.6.67

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 (46) hide show
  1. package/{LICENSE.txt → LICENSE} +30 -27
  2. package/README.md +13 -6
  3. package/dist/xeokit-sdk.cjs.js +50322 -25848
  4. package/dist/xeokit-sdk.es.js +50322 -25848
  5. package/dist/xeokit-sdk.es5.js +7982 -4807
  6. package/dist/xeokit-sdk.min.cjs.js +5 -5
  7. package/dist/xeokit-sdk.min.es.js +5 -5
  8. package/dist/xeokit-sdk.min.es5.js +4 -4
  9. package/package.json +34 -32
  10. package/src/extras/PointerCircle/PointerCircle.js +1 -1
  11. package/src/plugins/AnnotationsPlugin/Annotation.js +45 -5
  12. package/src/plugins/CityJSONLoaderPlugin/CityJSONLoaderPlugin.js +1 -1
  13. package/src/plugins/GLTFLoaderPlugin/GLTFSceneModelLoader.js +3 -2
  14. package/src/plugins/LASLoaderPlugin/LASLoaderPlugin.js +1 -1
  15. package/src/plugins/SectionPlanesPlugin/Control.js +11 -12
  16. package/src/plugins/XKTLoaderPlugin/XKTLoaderPlugin.js +57 -2
  17. package/src/plugins/XKTLoaderPlugin/parsers/ParserV10.js +6 -0
  18. package/src/plugins/XKTLoaderPlugin/parsers/ParserV11.js +6 -0
  19. package/src/plugins/XKTLoaderPlugin/parsers/ParserV12.js +822 -0
  20. package/src/plugins/XKTLoaderPlugin/parsers/ParserV6.js +6 -0
  21. package/src/plugins/XKTLoaderPlugin/parsers/ParserV7.js +6 -0
  22. package/src/plugins/XKTLoaderPlugin/parsers/ParserV8.js +6 -0
  23. package/src/plugins/XKTLoaderPlugin/parsers/ParserV9.js +6 -0
  24. package/src/{plugins/lib → viewer/scene/libs}/earcut.js +194 -189
  25. package/src/viewer/scene/materials/EmphasisMaterial.js +5 -1
  26. package/src/viewer/scene/math/math.js +6 -2
  27. package/src/viewer/scene/model/SceneModel.js +1 -0
  28. package/src/viewer/scene/model/SceneModelEntity.js +26 -0
  29. package/src/viewer/scene/model/SceneModelMesh.js +4 -0
  30. package/src/viewer/scene/model/dtx/triangles/DTXTrianglesLayer.js +1 -1
  31. package/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js +1 -1
  32. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesColorRenderer.js +4 -1
  33. package/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js +1 -1
  34. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesColorRenderer.js +4 -1
  35. package/src/viewer/scene/scene/Scene.js +50 -6
  36. package/src/viewer/scene/sectionCaps/SectionCaps.js +774 -0
  37. package/src/viewer/scene/sectionCaps/index.js +1 -0
  38. package/src/viewer/scene/sectionPlane/SectionPlane.js +79 -1
  39. package/src/viewer/scene/webgl/Renderer.js +19 -10
  40. package/types/extras/PointerCircle/PointerCircle.d.ts +50 -0
  41. package/types/extras/PointerCircle/index.d.ts +1 -0
  42. package/types/plugins/WebIFCLoaderPlugin/WebIFCLoaderPlugin.d.ts +2 -1
  43. package/types/plugins/XKTLoaderPlugin/XKTLoaderPlugin.d.ts +28 -0
  44. package/types/plugins/index.d.ts +1 -0
  45. package/types/plugins/lib/ui/index.d.ts +12 -0
  46. package/types/viewer/scene/models/SceneModelMesh.d.ts +7 -0
@@ -0,0 +1,822 @@
1
+ /*
2
+ Parser for .XKT Format V11
3
+ */
4
+
5
+ import {utils} from "../../../viewer/scene/utils.js";
6
+ import * as p from "./lib/pako.js";
7
+ import {math} from "../../../viewer/scene/math/math.js";
8
+ import {geometryCompressionUtils} from "../../../viewer/scene/math/geometryCompressionUtils.js";
9
+ import {JPEGMediaType, PNGMediaType} from "../../../viewer/scene/constants/constants.js";
10
+ import { buildVectorTextGeometry } from "../../../viewer/index.js";
11
+
12
+ let pako = window.pako || p;
13
+ if (!pako.inflate) { // See https://github.com/nodeca/pako/issues/97
14
+ pako = pako.default;
15
+ }
16
+
17
+ const tempVec4a = math.vec4();
18
+ const tempVec4b = math.vec4();
19
+
20
+ const NUM_TEXTURE_ATTRIBUTES = 9;
21
+
22
+ function decodeData(arrayBuffer) {
23
+ const requiresSwapFromLittleEndian = (function() {
24
+ const buffer = new ArrayBuffer(2);
25
+ new Uint16Array(buffer)[0] = 1;
26
+ return new Uint8Array(buffer)[0] !== 1;
27
+ })();
28
+
29
+ const nextArray = (function() {
30
+ let i = 0;
31
+ const dataView = new DataView(arrayBuffer);
32
+ return function(type) {
33
+ const idx = 1 + 2 * i++; // `1' for the version nr
34
+ const byteOffset = dataView.getUint32(idx * 4, true);
35
+ const byteLength = dataView.getUint32((idx + 1) * 4, true);
36
+
37
+ const BPE = type.BYTES_PER_ELEMENT;
38
+ if (requiresSwapFromLittleEndian && (BPE > 1)) {
39
+ const subarray = new Uint8Array(arrayBuffer, byteOffset, byteLength);
40
+ const swaps = BPE / 2;
41
+ const cnt = subarray.length / BPE;
42
+ for (let b = 0; b < cnt; b++) {
43
+ const offset = b * BPE;
44
+ for (let j = 0; j < swaps; j++) {
45
+ const i1 = offset + j;
46
+ const i2 = offset - j + BPE - 1;
47
+ const tmp = subarray[i1];
48
+ subarray[i1] = subarray[i2];
49
+ subarray[i2] = tmp;
50
+ }
51
+ }
52
+ }
53
+
54
+ return new type(arrayBuffer, byteOffset, byteLength / BPE);
55
+ };
56
+ })();
57
+
58
+ const nextObject = (function() {
59
+ const decoder = new TextDecoder();
60
+ return () => JSON.parse(decoder.decode(nextArray(Uint8Array)));
61
+ })();
62
+
63
+ return {
64
+ metadata: nextObject(),
65
+ textureData: nextArray(Uint8Array), // <<----------------------------- ??? ZIPPing to blame?
66
+ eachTextureDataPortion: nextArray(Uint32Array),
67
+ eachTextureAttributes: nextArray(Uint16Array),
68
+ positions: nextArray(Uint16Array),
69
+ normals: nextArray(Int8Array),
70
+ colors: nextArray(Uint8Array),
71
+ uvs: nextArray(Float32Array),
72
+ indices: nextArray(Uint32Array),
73
+ edgeIndices: nextArray(Uint32Array),
74
+ eachTextureSetTextures: nextArray(Int32Array),
75
+ matrices: nextArray(Float32Array),
76
+ reusedGeometriesDecodeMatrix: nextArray(Float32Array),
77
+ eachGeometryPrimitiveType: nextArray(Uint8Array),
78
+ eachGeometryAxisLabel: nextObject(),
79
+ eachGeometryPositionsPortion: nextArray(Uint32Array),
80
+ eachGeometryNormalsPortion: nextArray(Uint32Array),
81
+ eachGeometryColorsPortion: nextArray(Uint32Array),
82
+ eachGeometryUVsPortion: nextArray(Uint32Array),
83
+ eachGeometryIndicesPortion: nextArray(Uint32Array),
84
+ eachGeometryEdgeIndicesPortion: nextArray(Uint32Array),
85
+ eachMeshGeometriesPortion: nextArray(Uint32Array),
86
+ eachMeshMatricesPortion: nextArray(Uint32Array),
87
+ eachMeshTextureSet: nextArray(Int32Array), // Can be -1
88
+ eachMeshMaterialAttributes: nextArray(Uint8Array),
89
+ eachEntityId: nextObject(),
90
+ eachEntityMeshesPortion: nextArray(Uint32Array),
91
+ eachTileAABB: nextArray(Float64Array),
92
+ eachTileEntitiesPortion: nextArray(Uint32Array),
93
+ };
94
+ }
95
+
96
+ function extract(elements) {
97
+
98
+ let i = 0;
99
+
100
+ return {
101
+ metadata: elements[i++],
102
+ textureData: elements[i++],
103
+ eachTextureDataPortion: elements[i++],
104
+ eachTextureAttributes: elements[i++],
105
+ positions: elements[i++],
106
+ normals: elements[i++],
107
+ colors: elements[i++],
108
+ uvs: elements[i++],
109
+ indices: elements[i++],
110
+ edgeIndices: elements[i++],
111
+ eachTextureSetTextures: elements[i++],
112
+ matrices: elements[i++],
113
+ reusedGeometriesDecodeMatrix: elements[i++],
114
+ eachGeometryPrimitiveType: elements[i++],
115
+ eachGeometryAxisLabel: elements[i++],
116
+ eachGeometryPositionsPortion: elements[i++],
117
+ eachGeometryNormalsPortion: elements[i++],
118
+ eachGeometryColorsPortion: elements[i++],
119
+ eachGeometryUVsPortion: elements[i++],
120
+ eachGeometryIndicesPortion: elements[i++],
121
+ eachGeometryEdgeIndicesPortion: elements[i++],
122
+ eachMeshGeometriesPortion: elements[i++],
123
+ eachMeshMatricesPortion: elements[i++],
124
+ eachMeshTextureSet: elements[i++],
125
+ eachMeshMaterialAttributes: elements[i++],
126
+ eachEntityId: elements[i++],
127
+ eachEntityMeshesPortion: elements[i++],
128
+ eachTileAABB: elements[i++],
129
+ eachTileEntitiesPortion: elements[i++],
130
+ };
131
+ }
132
+
133
+ function inflate(deflatedData) {
134
+
135
+ function inflate(array, options) {
136
+ return (array.length === 0) ? [] : pako.inflate(array, options).buffer;
137
+ }
138
+
139
+ return {
140
+ metadata: JSON.parse(pako.inflate(deflatedData.metadata, {to: 'string'})),
141
+ textureData: new Uint8Array(inflate(deflatedData.textureData)), // <<----------------------------- ??? ZIPPing to blame?
142
+ eachTextureDataPortion: new Uint32Array(inflate(deflatedData.eachTextureDataPortion)),
143
+ eachTextureAttributes: new Uint16Array(inflate(deflatedData.eachTextureAttributes)),
144
+ positions: new Uint16Array(inflate(deflatedData.positions)),
145
+ normals: new Int8Array(inflate(deflatedData.normals)),
146
+ colors: new Uint8Array(inflate(deflatedData.colors)),
147
+ uvs: new Float32Array(inflate(deflatedData.uvs)),
148
+ indices: new Uint32Array(inflate(deflatedData.indices)),
149
+ edgeIndices: new Uint32Array(inflate(deflatedData.edgeIndices)),
150
+ eachTextureSetTextures: new Int32Array(inflate(deflatedData.eachTextureSetTextures)),
151
+ matrices: new Float32Array(inflate(deflatedData.matrices)),
152
+ reusedGeometriesDecodeMatrix: new Float32Array(inflate(deflatedData.reusedGeometriesDecodeMatrix)),
153
+ eachGeometryPrimitiveType: new Uint8Array(inflate(deflatedData.eachGeometryPrimitiveType)),
154
+ eachGeometryAxisLabel: JSON.parse(pako.inflate(deflatedData.eachGeometryAxisLabel, {to: 'string'})),
155
+ eachGeometryPositionsPortion: new Uint32Array(inflate(deflatedData.eachGeometryPositionsPortion)),
156
+ eachGeometryNormalsPortion: new Uint32Array(inflate(deflatedData.eachGeometryNormalsPortion)),
157
+ eachGeometryColorsPortion: new Uint32Array(inflate(deflatedData.eachGeometryColorsPortion)),
158
+ eachGeometryUVsPortion: new Uint32Array(inflate(deflatedData.eachGeometryUVsPortion)),
159
+ eachGeometryIndicesPortion: new Uint32Array(inflate(deflatedData.eachGeometryIndicesPortion)),
160
+ eachGeometryEdgeIndicesPortion: new Uint32Array(inflate(deflatedData.eachGeometryEdgeIndicesPortion)),
161
+ eachMeshGeometriesPortion: new Uint32Array(inflate(deflatedData.eachMeshGeometriesPortion)),
162
+ eachMeshMatricesPortion: new Uint32Array(inflate(deflatedData.eachMeshMatricesPortion)),
163
+ eachMeshTextureSet: new Int32Array(inflate(deflatedData.eachMeshTextureSet)), // Can be -1
164
+ eachMeshMaterialAttributes: new Uint8Array(inflate(deflatedData.eachMeshMaterialAttributes)),
165
+ eachEntityId: JSON.parse(pako.inflate(deflatedData.eachEntityId, {to: 'string'})),
166
+ eachEntityMeshesPortion: new Uint32Array(inflate(deflatedData.eachEntityMeshesPortion)),
167
+ eachTileAABB: new Float64Array(inflate(deflatedData.eachTileAABB)),
168
+ eachTileEntitiesPortion: new Uint32Array(inflate(deflatedData.eachTileEntitiesPortion)),
169
+ };
170
+ }
171
+
172
+ const decompressColor = (function () {
173
+ const floatColor = new Float32Array(3);
174
+ return function (intColor) {
175
+ floatColor[0] = intColor[0] / 255.0;
176
+ floatColor[1] = intColor[1] / 255.0;
177
+ floatColor[2] = intColor[2] / 255.0;
178
+ return floatColor;
179
+ };
180
+ })();
181
+
182
+ const imagDataToImage = (function () {
183
+ const canvas = document.createElement('canvas');
184
+ const context = canvas.getContext('2d');
185
+ return function (imagedata) {
186
+ canvas.width = imagedata.width;
187
+ canvas.height = imagedata.height;
188
+ context.putImageData(imagedata, 0, 0);
189
+ return canvas.toDataURL();
190
+ };
191
+ })();
192
+
193
+ function load(viewer, options, inflatedData, sceneModel, metaModel, manifestCtx) {
194
+
195
+ const modelPartId = manifestCtx.getNextId();
196
+
197
+ const metadata = inflatedData.metadata;
198
+ const textureData = inflatedData.textureData;
199
+ const eachTextureDataPortion = inflatedData.eachTextureDataPortion;
200
+ const eachTextureAttributes = inflatedData.eachTextureAttributes;
201
+ const positions = inflatedData.positions;
202
+ const normals = inflatedData.normals;
203
+ const colors = inflatedData.colors;
204
+ const uvs = inflatedData.uvs;
205
+ const indices = inflatedData.indices;
206
+ const edgeIndices = inflatedData.edgeIndices;
207
+ const eachTextureSetTextures = inflatedData.eachTextureSetTextures;
208
+ const matrices = inflatedData.matrices;
209
+ const reusedGeometriesDecodeMatrix = inflatedData.reusedGeometriesDecodeMatrix;
210
+ const eachGeometryPrimitiveType = inflatedData.eachGeometryPrimitiveType;
211
+ const eachGeometryAxisLabel = inflatedData.eachGeometryAxisLabel;
212
+ const eachGeometryPositionsPortion = inflatedData.eachGeometryPositionsPortion;
213
+ const eachGeometryNormalsPortion = inflatedData.eachGeometryNormalsPortion;
214
+ const eachGeometryColorsPortion = inflatedData.eachGeometryColorsPortion;
215
+ const eachGeometryUVsPortion = inflatedData.eachGeometryUVsPortion;
216
+ const eachGeometryIndicesPortion = inflatedData.eachGeometryIndicesPortion;
217
+ const eachGeometryEdgeIndicesPortion = inflatedData.eachGeometryEdgeIndicesPortion;
218
+ const eachMeshGeometriesPortion = inflatedData.eachMeshGeometriesPortion;
219
+ const eachMeshMatricesPortion = inflatedData.eachMeshMatricesPortion;
220
+ const eachMeshTextureSet = inflatedData.eachMeshTextureSet;
221
+ const eachMeshMaterialAttributes = inflatedData.eachMeshMaterialAttributes;
222
+ const eachEntityId = inflatedData.eachEntityId;
223
+ const eachEntityMeshesPortion = inflatedData.eachEntityMeshesPortion;
224
+ const eachTileAABB = inflatedData.eachTileAABB;
225
+ const eachTileEntitiesPortion = inflatedData.eachTileEntitiesPortion;
226
+
227
+ const numTextures = eachTextureDataPortion.length;
228
+ const numTextureSets = eachTextureSetTextures.length / 5;
229
+ const numGeometries = eachGeometryPositionsPortion.length;
230
+ const numMeshes = eachMeshGeometriesPortion.length;
231
+ const numEntities = eachEntityMeshesPortion.length;
232
+ const numTiles = eachTileEntitiesPortion.length;
233
+
234
+ if (metaModel) {
235
+ metaModel.loadData(metadata, {
236
+ includeTypes: options.includeTypes,
237
+ excludeTypes: options.excludeTypes,
238
+ globalizeObjectIds: options.globalizeObjectIds
239
+ }); // Can be empty
240
+ }
241
+
242
+ // Create textures
243
+
244
+ for (let textureIndex = 0; textureIndex < numTextures; textureIndex++) {
245
+ const atLastTexture = (textureIndex === (numTextures - 1));
246
+ const textureDataPortionStart = eachTextureDataPortion[textureIndex];
247
+ const textureDataPortionEnd = atLastTexture ? textureData.length : (eachTextureDataPortion[textureIndex + 1]);
248
+
249
+ const textureDataPortionSize = textureDataPortionEnd - textureDataPortionStart;
250
+ const textureDataPortionExists = (textureDataPortionSize > 0);
251
+
252
+ const textureAttrBaseIdx = (textureIndex * NUM_TEXTURE_ATTRIBUTES);
253
+
254
+ const compressed = (eachTextureAttributes[textureAttrBaseIdx + 0] === 1);
255
+ const mediaType = eachTextureAttributes[textureAttrBaseIdx + 1];
256
+ const width = eachTextureAttributes[textureAttrBaseIdx + 2];
257
+ const height = eachTextureAttributes[textureAttrBaseIdx + 3];
258
+ const minFilter = eachTextureAttributes[textureAttrBaseIdx + 4];
259
+ const magFilter = eachTextureAttributes[textureAttrBaseIdx + 5]; // LinearFilter | NearestFilter
260
+ const wrapS = eachTextureAttributes[textureAttrBaseIdx + 6]; // ClampToEdgeWrapping | MirroredRepeatWrapping | RepeatWrapping
261
+ const wrapT = eachTextureAttributes[textureAttrBaseIdx + 7]; // ClampToEdgeWrapping | MirroredRepeatWrapping | RepeatWrapping
262
+ const wrapR = eachTextureAttributes[textureAttrBaseIdx + 8]; // ClampToEdgeWrapping | MirroredRepeatWrapping | RepeatWrapping
263
+
264
+ if (textureDataPortionExists) {
265
+
266
+ const imageDataSubarray = new Uint8Array(textureData.subarray(textureDataPortionStart, textureDataPortionEnd));
267
+ const arrayBuffer = imageDataSubarray.buffer;
268
+ const textureId = `${modelPartId}-texture-${textureIndex}`;
269
+
270
+ if (compressed) {
271
+
272
+ sceneModel.createTexture({
273
+ id: textureId,
274
+ buffers: [arrayBuffer],
275
+ minFilter,
276
+ magFilter,
277
+ wrapS,
278
+ wrapT,
279
+ wrapR
280
+ });
281
+
282
+ } else {
283
+
284
+ const mimeType = mediaType === JPEGMediaType ? "image/jpeg" : (mediaType === PNGMediaType ? "image/png" : "image/gif");
285
+ const blob = new Blob([arrayBuffer], {type: mimeType});
286
+ const urlCreator = window.URL || window.webkitURL;
287
+ const imageUrl = urlCreator.createObjectURL(blob);
288
+ const img = document.createElement('img');
289
+ img.src = imageUrl;
290
+
291
+ sceneModel.createTexture({
292
+ id: textureId,
293
+ image: img,
294
+ //mediaType,
295
+ minFilter,
296
+ magFilter,
297
+ wrapS,
298
+ wrapT,
299
+ wrapR
300
+ });
301
+ }
302
+ }
303
+ }
304
+
305
+ // Create texture sets
306
+
307
+ for (let textureSetIndex = 0; textureSetIndex < numTextureSets; textureSetIndex++) {
308
+ const eachTextureSetTexturesIndex = textureSetIndex * 5;
309
+ const textureSetId = `${modelPartId}-textureSet-${textureSetIndex}`;
310
+ const colorTextureIndex = eachTextureSetTextures[eachTextureSetTexturesIndex + 0];
311
+ const metallicRoughnessTextureIndex = eachTextureSetTextures[eachTextureSetTexturesIndex + 1];
312
+ const normalsTextureIndex = eachTextureSetTextures[eachTextureSetTexturesIndex + 2];
313
+ const emissiveTextureIndex = eachTextureSetTextures[eachTextureSetTexturesIndex + 3];
314
+ const occlusionTextureIndex = eachTextureSetTextures[eachTextureSetTexturesIndex + 4];
315
+ sceneModel.createTextureSet({
316
+ id: textureSetId,
317
+ colorTextureId: colorTextureIndex >= 0 ? `${modelPartId}-texture-${colorTextureIndex}` : null,
318
+ normalsTextureId: normalsTextureIndex >= 0 ? `${modelPartId}-texture-${normalsTextureIndex}` : null,
319
+ metallicRoughnessTextureId: metallicRoughnessTextureIndex >= 0 ? `${modelPartId}-texture-${metallicRoughnessTextureIndex}` : null,
320
+ emissiveTextureId: emissiveTextureIndex >= 0 ? `${modelPartId}-texture-${emissiveTextureIndex}` : null,
321
+ occlusionTextureId: occlusionTextureIndex >= 0 ? `${modelPartId}-texture-${occlusionTextureIndex}` : null
322
+ });
323
+ }
324
+
325
+ // Count instances of each geometry
326
+
327
+ const geometryReuseCounts = new Uint32Array(numGeometries);
328
+
329
+ for (let meshIndex = 0; meshIndex < numMeshes; meshIndex++) {
330
+ const geometryIndex = eachMeshGeometriesPortion[meshIndex];
331
+ if (geometryReuseCounts[geometryIndex] !== undefined) {
332
+ geometryReuseCounts[geometryIndex]++;
333
+ } else {
334
+ geometryReuseCounts[geometryIndex] = 1;
335
+ }
336
+ }
337
+
338
+ // Iterate over tiles
339
+
340
+ const tileCenter = math.vec3();
341
+ const rtcAABB = math.AABB3();
342
+
343
+ const geometryArraysCache = {};
344
+
345
+ for (let tileIndex = 0; tileIndex < numTiles; tileIndex++) {
346
+
347
+ const lastTileIndex = (numTiles - 1);
348
+
349
+ const atLastTile = (tileIndex === lastTileIndex);
350
+
351
+ const firstTileEntityIndex = eachTileEntitiesPortion [tileIndex];
352
+ const lastTileEntityIndex = atLastTile ? (numEntities - 1) : (eachTileEntitiesPortion[tileIndex + 1] - 1);
353
+
354
+ const tileAABBIndex = tileIndex * 6;
355
+ const tileAABB = eachTileAABB.subarray(tileAABBIndex, tileAABBIndex + 6);
356
+
357
+ math.getAABB3Center(tileAABB, tileCenter);
358
+
359
+ rtcAABB[0] = tileAABB[0] - tileCenter[0];
360
+ rtcAABB[1] = tileAABB[1] - tileCenter[1];
361
+ rtcAABB[2] = tileAABB[2] - tileCenter[2];
362
+ rtcAABB[3] = tileAABB[3] - tileCenter[0];
363
+ rtcAABB[4] = tileAABB[4] - tileCenter[1];
364
+ rtcAABB[5] = tileAABB[5] - tileCenter[2];
365
+
366
+ const tileDecodeMatrix = geometryCompressionUtils.createPositionsDecodeMatrix(rtcAABB);
367
+
368
+ const geometryCreatedInTile = {};
369
+
370
+ // Iterate over each tile's entities
371
+
372
+ for (let tileEntityIndex = firstTileEntityIndex; tileEntityIndex <= lastTileEntityIndex; tileEntityIndex++) {
373
+
374
+ const xktEntityId = eachEntityId[tileEntityIndex];
375
+
376
+ const entityId = options.globalizeObjectIds ? math.globalizeObjectId(sceneModel.id, xktEntityId) : xktEntityId;
377
+
378
+ const finalTileEntityIndex = (numEntities - 1);
379
+ const atLastTileEntity = (tileEntityIndex === finalTileEntityIndex);
380
+ const firstMeshIndex = eachEntityMeshesPortion [tileEntityIndex];
381
+ const lastMeshIndex = atLastTileEntity ? (eachMeshGeometriesPortion.length - 1) : (eachEntityMeshesPortion[tileEntityIndex + 1] - 1);
382
+
383
+ const meshIds = [];
384
+
385
+ const metaObject = viewer.metaScene.metaObjects[entityId];
386
+ const entityDefaults = {};
387
+ const meshDefaults = {};
388
+
389
+ if (metaObject) {
390
+
391
+ // Mask loading of object types
392
+
393
+ if (options.excludeTypesMap && metaObject.type && options.excludeTypesMap[metaObject.type]) {
394
+ continue;
395
+ }
396
+
397
+ if (options.includeTypesMap && metaObject.type && (!options.includeTypesMap[metaObject.type])) {
398
+ continue;
399
+ }
400
+
401
+ // Mask loading of object ids
402
+
403
+ if (options.includeIdsMap && metaObject.id && (!options.includeIdsMap[metaObject.id])) {
404
+ continue;
405
+ }
406
+
407
+ // Get initial property values for object types
408
+
409
+ const props = options.objectDefaults ? options.objectDefaults[metaObject.type] || options.objectDefaults["DEFAULT"] : null;
410
+
411
+ if (props) {
412
+ if (props.visible === false) {
413
+ entityDefaults.visible = false;
414
+ }
415
+ if (props.pickable === false) {
416
+ entityDefaults.pickable = false;
417
+ }
418
+ if (props.colorize) {
419
+ meshDefaults.color = props.colorize;
420
+ }
421
+ if (props.opacity !== undefined && props.opacity !== null) {
422
+ meshDefaults.opacity = props.opacity;
423
+ }
424
+ if (props.metallic !== undefined && props.metallic !== null) {
425
+ meshDefaults.metallic = props.metallic;
426
+ }
427
+ if (props.roughness !== undefined && props.roughness !== null) {
428
+ meshDefaults.roughness = props.roughness;
429
+ }
430
+ }
431
+
432
+ } else {
433
+ if (options.excludeUnclassifiedObjects) {
434
+ continue;
435
+ }
436
+ }
437
+
438
+ // Iterate each entity's meshes
439
+
440
+ for (let meshIndex = firstMeshIndex; meshIndex <= lastMeshIndex; meshIndex++) {
441
+
442
+ const geometryIndex = eachMeshGeometriesPortion[meshIndex];
443
+ const geometryReuseCount = geometryReuseCounts[geometryIndex];
444
+ const isReusedGeometry = (geometryReuseCount > 1);
445
+
446
+ const atLastGeometry = (geometryIndex === (numGeometries - 1));
447
+
448
+ const textureSetIndex = eachMeshTextureSet[meshIndex];
449
+
450
+ const textureSetId = (textureSetIndex >= 0) ? `${modelPartId}-textureSet-${textureSetIndex}` : null;
451
+
452
+ const meshColor = decompressColor(eachMeshMaterialAttributes.subarray((meshIndex * 6), (meshIndex * 6) + 3));
453
+ const meshOpacity = eachMeshMaterialAttributes[(meshIndex * 6) + 3] / 255.0;
454
+ const meshMetallic = eachMeshMaterialAttributes[(meshIndex * 6) + 4] / 255.0;
455
+ const meshRoughness = eachMeshMaterialAttributes[(meshIndex * 6) + 5] / 255.0;
456
+
457
+ const meshId = manifestCtx.getNextId();
458
+
459
+ if (isReusedGeometry) {
460
+
461
+ // Create mesh for multi-use geometry - create (or reuse) geometry, create mesh using that geometry
462
+
463
+ const meshMatrixIndex = eachMeshMatricesPortion[meshIndex];
464
+ const meshMatrix = matrices.slice(meshMatrixIndex, meshMatrixIndex + 16);
465
+
466
+ const geometryId = `${modelPartId}-geometry.${tileIndex}.${geometryIndex}`; // These IDs are local to the SceneModel
467
+
468
+ let geometryArrays = geometryArraysCache[geometryId];
469
+
470
+ if (!geometryArrays) {
471
+ geometryArrays = {
472
+ batchThisMesh: (!options.reuseGeometries)
473
+ };
474
+ const primitiveType = eachGeometryPrimitiveType[geometryIndex];
475
+ const axisLabel = eachGeometryAxisLabel[geometryIndex];
476
+ let geometryValid = false;
477
+ let ifcLabel;
478
+ switch (primitiveType) {
479
+ case 0:
480
+ geometryArrays.primitiveName = "solid";
481
+ geometryArrays.geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
482
+ geometryArrays.geometryNormals = normals.subarray(eachGeometryNormalsPortion [geometryIndex], atLastGeometry ? normals.length : eachGeometryNormalsPortion [geometryIndex + 1]);
483
+ geometryArrays.geometryUVs = uvs.subarray(eachGeometryUVsPortion [geometryIndex], atLastGeometry ? uvs.length : eachGeometryUVsPortion [geometryIndex + 1]);
484
+ geometryArrays.geometryIndices = indices.subarray(eachGeometryIndicesPortion [geometryIndex], atLastGeometry ? indices.length : eachGeometryIndicesPortion [geometryIndex + 1]);
485
+ geometryArrays.geometryEdgeIndices = edgeIndices.subarray(eachGeometryEdgeIndicesPortion [geometryIndex], atLastGeometry ? edgeIndices.length : eachGeometryEdgeIndicesPortion [geometryIndex + 1]);
486
+ geometryValid = (geometryArrays.geometryPositions.length > 0 && geometryArrays.geometryIndices.length > 0);
487
+ break;
488
+ case 1:
489
+ geometryArrays.primitiveName = "surface";
490
+ geometryArrays.geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
491
+ geometryArrays.geometryNormals = normals.subarray(eachGeometryNormalsPortion [geometryIndex], atLastGeometry ? normals.length : eachGeometryNormalsPortion [geometryIndex + 1]);
492
+ geometryArrays.geometryUVs = uvs.subarray(eachGeometryUVsPortion [geometryIndex], atLastGeometry ? uvs.length : eachGeometryUVsPortion [geometryIndex + 1]);
493
+ geometryArrays.geometryIndices = indices.subarray(eachGeometryIndicesPortion [geometryIndex], atLastGeometry ? indices.length : eachGeometryIndicesPortion [geometryIndex + 1]);
494
+ geometryArrays.geometryEdgeIndices = edgeIndices.subarray(eachGeometryEdgeIndicesPortion [geometryIndex], atLastGeometry ? edgeIndices.length : eachGeometryEdgeIndicesPortion [geometryIndex + 1]);
495
+ geometryValid = (geometryArrays.geometryPositions.length > 0 && geometryArrays.geometryIndices.length > 0);
496
+ break;
497
+ case 2:
498
+ geometryArrays.primitiveName = "points";
499
+ geometryArrays.geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
500
+ geometryArrays.geometryColors = colors.subarray(eachGeometryColorsPortion [geometryIndex], atLastGeometry ? colors.length : eachGeometryColorsPortion [geometryIndex + 1]);
501
+ geometryValid = (geometryArrays.geometryPositions.length > 0);
502
+ break;
503
+ case 3:
504
+ geometryArrays.primitiveName = "lines";
505
+ geometryArrays.geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
506
+ geometryArrays.geometryIndices = indices.subarray(eachGeometryIndicesPortion [geometryIndex], atLastGeometry ? indices.length : eachGeometryIndicesPortion [geometryIndex + 1]);
507
+ geometryValid = (geometryArrays.geometryPositions.length > 0 && geometryArrays.geometryIndices.length > 0);
508
+ break;
509
+ case 4:
510
+ geometryArrays.primitiveName = "lines";
511
+ geometryArrays.geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
512
+ geometryArrays.geometryIndices = lineStripToLines(
513
+ geometryArrays.geometryPositions,
514
+ indices.subarray(eachGeometryIndicesPortion [geometryIndex],
515
+ atLastGeometry
516
+ ? indices.length
517
+ : eachGeometryIndicesPortion [geometryIndex + 1]));
518
+ geometryValid = (geometryArrays.geometryPositions.length > 0 && geometryArrays.geometryIndices.length > 0);
519
+ break;
520
+ case 7:
521
+ geometryArrays.primitiveName = "lines";
522
+ geometryArrays.geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
523
+ geometryArrays.decompressedPositions = geometryCompressionUtils.decompressPositions(geometryArrays.geometryPositions, tileDecodeMatrix);
524
+ ifcLabel = buildVectorTextGeometry({
525
+ origin: geometryArrays.decompressedPositions,
526
+ text: axisLabel,
527
+ size: 2
528
+ });
529
+
530
+ geometryArrays.decompressedPositions = ifcLabel.positions;
531
+ geometryArrays.geometryIndices = ifcLabel.indices;
532
+ geometryArrays.geometryPositions = null;
533
+
534
+ geometryValid = (geometryArrays.decompressedPositions.length > 0 && geometryArrays.geometryIndices.length > 0);
535
+ break;
536
+ default:
537
+ continue;
538
+ }
539
+
540
+ if (!geometryValid) {
541
+ geometryArrays = null;
542
+ }
543
+
544
+ if (geometryArrays) {
545
+ if (geometryReuseCount > 1000) { // TODO: Heuristic to force batching of instanced geometry beyond a certain reuse count (or budget)?
546
+ // geometryArrays.batchThisMesh = true;
547
+ }
548
+ if (geometryArrays.geometryPositions.length > 1000) { // TODO: Heuristic to force batching on instanced geometry above certain vertex size?
549
+ // geometryArrays.batchThisMesh = true;
550
+ }
551
+ if (geometryArrays.batchThisMesh) {
552
+ if(primitiveType === 7) {
553
+ geometryArrays.transformedAndRecompressedPositions = new Uint16Array(geometryArrays.decompressedPositions.length);
554
+ }
555
+ else {
556
+ geometryArrays.decompressedPositions = new Float32Array(geometryArrays.geometryPositions.length);
557
+ geometryArrays.transformedAndRecompressedPositions = new Uint16Array(geometryArrays.geometryPositions.length)
558
+ const geometryPositions = geometryArrays.geometryPositions;
559
+ const decompressedPositions = geometryArrays.decompressedPositions;
560
+ for (let i = 0, len = geometryPositions.length; i < len; i += 3) {
561
+ decompressedPositions[i + 0] = geometryPositions[i + 0] * reusedGeometriesDecodeMatrix[0] + reusedGeometriesDecodeMatrix[12];
562
+ decompressedPositions[i + 1] = geometryPositions[i + 1] * reusedGeometriesDecodeMatrix[5] + reusedGeometriesDecodeMatrix[13];
563
+ decompressedPositions[i + 2] = geometryPositions[i + 2] * reusedGeometriesDecodeMatrix[10] + reusedGeometriesDecodeMatrix[14];
564
+ }
565
+ geometryArrays.geometryPositions = null;
566
+ }
567
+
568
+ geometryArraysCache[geometryId] = geometryArrays;
569
+ }
570
+ }
571
+ }
572
+
573
+ if (geometryArrays) {
574
+
575
+ if (geometryArrays.batchThisMesh) {
576
+
577
+ const decompressedPositions = geometryArrays.decompressedPositions;
578
+ const transformedAndRecompressedPositions = geometryArrays.transformedAndRecompressedPositions;
579
+
580
+ for (let i = 0, len = decompressedPositions.length; i < len; i += 3) {
581
+ tempVec4a[0] = decompressedPositions[i + 0];
582
+ tempVec4a[1] = decompressedPositions[i + 1];
583
+ tempVec4a[2] = decompressedPositions[i + 2];
584
+ tempVec4a[3] = 1;
585
+ math.transformVec4(meshMatrix, tempVec4a, tempVec4b);
586
+ geometryCompressionUtils.compressPosition(tempVec4b, rtcAABB, tempVec4a)
587
+ transformedAndRecompressedPositions[i + 0] = tempVec4a[0];
588
+ transformedAndRecompressedPositions[i + 1] = tempVec4a[1];
589
+ transformedAndRecompressedPositions[i + 2] = tempVec4a[2];
590
+ }
591
+
592
+ sceneModel.createMesh(utils.apply(meshDefaults, {
593
+ id: meshId,
594
+ textureSetId,
595
+ origin: tileCenter,
596
+ primitive: geometryArrays.primitiveName,
597
+ positionsCompressed: transformedAndRecompressedPositions,
598
+ normalsCompressed: geometryArrays.geometryNormals,
599
+ uv: geometryArrays.geometryUVs,
600
+ colorsCompressed: geometryArrays.geometryColors,
601
+ indices: geometryArrays.geometryIndices,
602
+ edgeIndices: geometryArrays.geometryEdgeIndices,
603
+ positionsDecodeMatrix: tileDecodeMatrix,
604
+ color: meshColor,
605
+ metallic: meshMetallic,
606
+ roughness: meshRoughness,
607
+ opacity: meshOpacity
608
+ }));
609
+
610
+ meshIds.push(meshId);
611
+
612
+ } else {
613
+
614
+ if (!geometryCreatedInTile[geometryId]) {
615
+ let geometryCfg = {
616
+ id: geometryId,
617
+ primitive: geometryArrays.primitiveName,
618
+ normalsCompressed: geometryArrays.geometryNormals,
619
+ uv: geometryArrays.geometryUVs,
620
+ colorsCompressed: geometryArrays.geometryColors,
621
+ indices: geometryArrays.geometryIndices,
622
+ edgeIndices: geometryArrays.geometryEdgeIndices
623
+ }
624
+
625
+ if(geometryArrays.geometryPositions){
626
+ geometryCfg = {
627
+ ...geometryCfg,
628
+ positionsCompressed: geometryArrays.geometryPositions,
629
+ positionsDecodeMatrix: reusedGeometriesDecodeMatrix
630
+ }
631
+ }
632
+ else {
633
+ geometryCfg = {
634
+ ...geometryCfg,
635
+ positions: geometryArrays.decompressedPositions,
636
+ }
637
+ }
638
+
639
+ sceneModel.createGeometry(geometryCfg);
640
+
641
+ geometryCreatedInTile[geometryId] = true;
642
+ }
643
+
644
+ sceneModel.createMesh(utils.apply(meshDefaults, {
645
+ id: meshId,
646
+ geometryId,
647
+ textureSetId,
648
+ matrix: meshMatrix,
649
+ color: meshColor,
650
+ metallic: meshMetallic,
651
+ roughness: meshRoughness,
652
+ opacity: meshOpacity,
653
+ origin: tileCenter
654
+ }));
655
+
656
+ meshIds.push(meshId);
657
+ }
658
+ }
659
+
660
+ } else { // Do not reuse geometry
661
+
662
+ const primitiveType = eachGeometryPrimitiveType[geometryIndex];
663
+ const axisLabel = eachGeometryAxisLabel[geometryIndex];
664
+
665
+ let primitiveName;
666
+ let geometryPositions;
667
+ let geometryNormals;
668
+ let geometryUVs;
669
+ let geometryColors;
670
+ let geometryIndices;
671
+ let geometryEdgeIndices;
672
+ let geometryValid = false;
673
+ let ifcLabel;
674
+
675
+ switch (primitiveType) {
676
+ case 0:
677
+ primitiveName = "solid";
678
+ geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
679
+ geometryNormals = normals.subarray(eachGeometryNormalsPortion [geometryIndex], atLastGeometry ? normals.length : eachGeometryNormalsPortion [geometryIndex + 1]);
680
+ geometryUVs = uvs.subarray(eachGeometryUVsPortion [geometryIndex], atLastGeometry ? uvs.length : eachGeometryUVsPortion [geometryIndex + 1]);
681
+ geometryIndices = indices.subarray(eachGeometryIndicesPortion [geometryIndex], atLastGeometry ? indices.length : eachGeometryIndicesPortion [geometryIndex + 1]);
682
+ geometryEdgeIndices = edgeIndices.subarray(eachGeometryEdgeIndicesPortion [geometryIndex], atLastGeometry ? edgeIndices.length : eachGeometryEdgeIndicesPortion [geometryIndex + 1]);
683
+ geometryValid = (geometryPositions.length > 0 && geometryIndices.length > 0);
684
+ break;
685
+ case 1:
686
+ primitiveName = "surface";
687
+ geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
688
+ geometryNormals = normals.subarray(eachGeometryNormalsPortion [geometryIndex], atLastGeometry ? normals.length : eachGeometryNormalsPortion [geometryIndex + 1]);
689
+ geometryUVs = uvs.subarray(eachGeometryUVsPortion [geometryIndex], atLastGeometry ? uvs.length : eachGeometryUVsPortion [geometryIndex + 1]);
690
+ geometryIndices = indices.subarray(eachGeometryIndicesPortion [geometryIndex], atLastGeometry ? indices.length : eachGeometryIndicesPortion [geometryIndex + 1]);
691
+ geometryEdgeIndices = edgeIndices.subarray(eachGeometryEdgeIndicesPortion [geometryIndex], atLastGeometry ? edgeIndices.length : eachGeometryEdgeIndicesPortion [geometryIndex + 1]);
692
+ geometryValid = (geometryPositions.length > 0 && geometryIndices.length > 0);
693
+ break;
694
+ case 2:
695
+ primitiveName = "points";
696
+ geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
697
+ geometryColors = colors.subarray(eachGeometryColorsPortion [geometryIndex], atLastGeometry ? colors.length : eachGeometryColorsPortion [geometryIndex + 1]);
698
+ geometryValid = (geometryPositions.length > 0);
699
+ break;
700
+ case 3:
701
+ primitiveName = "lines";
702
+ geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
703
+ geometryIndices = indices.subarray(eachGeometryIndicesPortion [geometryIndex], atLastGeometry ? indices.length : eachGeometryIndicesPortion [geometryIndex + 1]);
704
+ geometryValid = (geometryPositions.length > 0 && geometryIndices.length > 0);
705
+ break;
706
+ case 4:
707
+ primitiveName = "lines";
708
+ geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
709
+ geometryIndices = lineStripToLines(
710
+ geometryPositions,
711
+ indices.subarray(eachGeometryIndicesPortion [geometryIndex], atLastGeometry
712
+ ? indices.length
713
+ : eachGeometryIndicesPortion [geometryIndex + 1]));
714
+ geometryValid = (geometryPositions.length > 0 && geometryIndices.length > 0);
715
+ break;
716
+ //Ifc Labels
717
+ case 7:
718
+ primitiveName = "lines";
719
+ geometryPositions = positions.subarray(eachGeometryPositionsPortion [geometryIndex], atLastGeometry ? positions.length : eachGeometryPositionsPortion [geometryIndex + 1]);
720
+ geometryPositions = geometryCompressionUtils.decompressPositions(geometryPositions, tileDecodeMatrix);
721
+ ifcLabel = buildVectorTextGeometry({
722
+ origin: geometryPositions,
723
+ text: axisLabel,
724
+ size: 2
725
+ });
726
+
727
+ geometryPositions = ifcLabel.positions;
728
+ geometryIndices = ifcLabel.indices;
729
+
730
+ geometryValid = (geometryPositions.length > 0 && geometryIndices.length > 0);
731
+ break;
732
+ default:
733
+ continue;
734
+ }
735
+
736
+ if (geometryValid) {
737
+
738
+ let meshCfg = utils.apply(meshDefaults, {
739
+ id: meshId,
740
+ textureSetId,
741
+ origin: tileCenter,
742
+ primitive: primitiveName,
743
+ normalsCompressed: geometryNormals,
744
+ uv: geometryUVs && geometryUVs.length > 0 ? geometryUVs : null,
745
+ colorsCompressed: geometryColors,
746
+ indices: geometryIndices,
747
+ edgeIndices: geometryEdgeIndices,
748
+ color: meshColor,
749
+ metallic: meshMetallic,
750
+ roughness: meshRoughness,
751
+ opacity: meshOpacity
752
+ })
753
+
754
+ if(primitiveType !== 7) {
755
+ meshCfg = {
756
+ ...meshCfg,
757
+ positionsCompressed: geometryPositions,
758
+ positionsDecodeMatrix: tileDecodeMatrix
759
+ }
760
+ }
761
+ else {
762
+ meshCfg = {
763
+ ...meshCfg,
764
+ positions: geometryPositions
765
+ }
766
+ }
767
+
768
+ sceneModel.createMesh(meshCfg);
769
+
770
+ meshIds.push(meshId);
771
+ }
772
+ }
773
+ }
774
+
775
+ if (meshIds.length > 0) {
776
+
777
+ sceneModel.createEntity(utils.apply(entityDefaults, {
778
+ id: entityId,
779
+ isObject: true,
780
+ meshIds: meshIds
781
+ }));
782
+ }
783
+ }
784
+ }
785
+ }
786
+
787
+ function lineStripToLines(positions, indices) {
788
+ const linesIndices = [];
789
+ if (indices.length > 1) {
790
+ for (let i = 0, len = indices.length - 1; i < len; i++) {
791
+ linesIndices.push(indices[i]);
792
+ linesIndices.push(indices[i + 1]);
793
+ }
794
+ } else if (positions.length > 1) {
795
+ for (let i = 0, len = (positions.length / 3) - 1; i < len; i++) {
796
+ linesIndices.push(i);
797
+ linesIndices.push(i + 1);
798
+ }
799
+ }
800
+ return linesIndices;
801
+ }
802
+
803
+ /** @private */
804
+ // V11 uses a single uncompressed Uint8Array buffer to store arrays of different types.
805
+ // To efficiently create typed arrays from this buffer,
806
+ // each typed array's source data needs to be aligned with its element byte size.
807
+ // This sometimes requires padding subarrays inside the single Uint8Array, so the byteOffset needs to be stored alongside element count.
808
+ // It is a different encoding than used in earlier versions, and requires different approach to parsing elements.
809
+ const ParserV12 = {
810
+ version: 12,
811
+ parseArrayBuffer: function (viewer, options, arrayBuffer, sceneModel, metaModel, manifestCtx) {
812
+ const inflatedData = decodeData(arrayBuffer);
813
+ load(viewer, options, inflatedData, sceneModel, metaModel, manifestCtx);
814
+ },
815
+ parse: function (viewer, options, elements, sceneModel, metaModel, manifestCtx) {
816
+ const deflatedData = extract(elements);
817
+ const inflatedData = inflate(deflatedData);
818
+ load(viewer, options, inflatedData, sceneModel, metaModel, manifestCtx);
819
+ }
820
+ };
821
+
822
+ export {ParserV12};