UNPKG

@loaders.gl/3d-tiles

Version:

3D Tiles, an open standard for streaming massive heterogeneous 3D geospatial datasets.

4 lines 215 kB
{ "version": 3, "sources": ["index.js", "tiles-3d-loader.js", "lib/utils/version.js", "lib/constants.js", "lib/parsers/helpers/parse-utils.js", "lib/parsers/parse-3d-tile-point-cloud.js", "lib/classes/tile-3d-feature-table.js", "lib/classes/tile-3d-batch-table.js", "lib/classes/helpers/tile-3d-accessor-utils.js", "lib/classes/tile-3d-batch-table-hierarchy.js", "lib/parsers/helpers/parse-3d-tile-header.js", "lib/parsers/helpers/parse-3d-tile-tables.js", "lib/parsers/helpers/normalize-3d-tile-colors.js", "lib/parsers/helpers/normalize-3d-tile-normals.js", "lib/parsers/helpers/normalize-3d-tile-positions.js", "lib/parsers/parse-3d-tile-batched-model.js", "lib/parsers/helpers/parse-3d-tile-gltf-view.js", "lib/parsers/parse-3d-tile-instanced-model.js", "lib/parsers/parse-3d-tile-composite.js", "lib/parsers/parse-3d-tile-gltf.js", "lib/parsers/parse-3d-tile.js", "lib/parsers/parse-3d-tile-header.js", "lib/parsers/helpers/parse-3d-tile-subtree.js", "tile-3d-subtree-loader.js", "lib/parsers/helpers/parse-3d-implicit-tiles.js", "lib/utils/s2/s2-token-functions.js", "lib/utils/s2/s2geometry/s2-geometry.js", "lib/utils/s2/converters/s2-to-boundary.js", "lib/utils/s2/s2geometry/s2-cell-utils.js", "lib/utils/s2/s2-geometry-functions.js", "lib/utils/s2/converters/s2-to-region.js", "lib/utils/s2/converters/s2-to-obb-points.js", "lib/utils/obb/s2-corners-to-obb.js", "lib/ion/ion.js", "cesium-ion-loader.js", "3d-tiles-archive-loader.js", "3d-tiles-archive/3d-tiles-archive-parser.js", "3d-tiles-archive/3d-tiles-archive-archive.js", "lib/encoders/encode-3d-tile.js", "lib/encoders/helpers/encode-3d-tile-header.js", "lib/encoders/encode-3d-tile-composite.js", "lib/encoders/encode-3d-tile-batched-model.js", "lib/encoders/encode-3d-tile-instanced-model.js", "lib/encoders/encode-3d-tile-point-cloud.js", "tile-3d-writer.js"], "sourcesContent": ["// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\n// LOADERS\nexport { Tiles3DLoader } from \"./tiles-3d-loader.js\";\nexport { CesiumIonLoader } from \"./cesium-ion-loader.js\";\nexport { Tile3DSubtreeLoader } from \"./tile-3d-subtree-loader.js\";\nexport { Tiles3DArchiveFileLoader } from \"./3d-tiles-archive-loader.js\";\n// WRITERS\nexport { Tile3DWriter } from \"./tile-3d-writer.js\";\n// CLASSES\nexport { default as Tile3DFeatureTable } from \"./lib/classes/tile-3d-feature-table.js\";\nexport { default as Tile3DBatchTable } from \"./lib/classes/tile-3d-batch-table.js\";\n// EXPERIMENTAL\nexport { TILE3D_TYPE } from \"./lib/constants.js\";\nexport { getIonTilesetMetadata as _getIonTilesetMetadata } from \"./lib/ion/ion.js\";\nexport { Tiles3DArchive } from \"./3d-tiles-archive/3d-tiles-archive-archive.js\";\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\nimport { path } from '@loaders.gl/loader-utils';\nimport { TILESET_TYPE, LOD_METRIC_TYPE } from '@loaders.gl/tiles';\nimport { VERSION } from \"./lib/utils/version.js\";\nimport { parse3DTile } from \"./lib/parsers/parse-3d-tile.js\";\nimport { normalizeTileHeaders } from \"./lib/parsers/parse-3d-tile-header.js\";\n/**\n * Loader for 3D Tiles\n */\nexport const Tiles3DLoader = {\n dataType: null,\n batchType: null,\n id: '3d-tiles',\n name: '3D Tiles',\n module: '3d-tiles',\n version: VERSION,\n extensions: ['cmpt', 'pnts', 'b3dm', 'i3dm'],\n mimeTypes: ['application/octet-stream'],\n tests: ['cmpt', 'pnts', 'b3dm', 'i3dm'],\n parse,\n options: {\n '3d-tiles': {\n loadGLTF: true,\n decodeQuantizedPositions: false,\n isTileset: 'auto',\n assetGltfUpAxis: null\n }\n }\n};\n/** Parses a tileset or tile */\nasync function parse(data, options = {}, context) {\n // auto detect file type\n const loaderOptions = options['3d-tiles'] || {};\n let isTileset;\n if (loaderOptions.isTileset === 'auto') {\n isTileset = context?.url && context.url.indexOf('.json') !== -1;\n }\n else {\n isTileset = loaderOptions.isTileset;\n }\n return isTileset ? parseTileset(data, options, context) : parseTile(data, options, context);\n}\n/** Parse a tileset */\nasync function parseTileset(data, options, context) {\n const tilesetJson = JSON.parse(new TextDecoder().decode(data));\n const tilesetUrl = context?.url || '';\n const basePath = getBaseUri(tilesetUrl);\n const normalizedRoot = await normalizeTileHeaders(tilesetJson, basePath, options || {});\n const tilesetJsonPostprocessed = {\n ...tilesetJson,\n shape: 'tileset3d',\n loader: Tiles3DLoader,\n url: tilesetUrl,\n queryString: context?.queryString || '',\n basePath,\n root: normalizedRoot || tilesetJson.root,\n type: TILESET_TYPE.TILES3D,\n lodMetricType: LOD_METRIC_TYPE.GEOMETRIC_ERROR,\n lodMetricValue: tilesetJson.root?.geometricError || 0\n };\n return tilesetJsonPostprocessed;\n}\n/** Parse a tile */\nasync function parseTile(arrayBuffer, options, context) {\n const tile = {\n content: {\n shape: 'tile3d',\n featureIds: null\n }\n };\n const byteOffset = 0;\n // @ts-expect-error\n await parse3DTile(arrayBuffer, byteOffset, options, context, tile.content);\n // @ts-expect-error\n return tile.content;\n}\n/** Get base name */\nfunction getBaseUri(tilesetUrl) {\n return path.dirname(tilesetUrl);\n}\n", "// Version constant cannot be imported, it needs to correspond to the build version of **this** module.\n// __VERSION__ is injected by babel-plugin-version-inline\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nexport const VERSION = typeof \"4.3.2\" !== 'undefined' ? \"4.3.2\" : 'latest';\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\n// TILE TYPES\nexport const TILE3D_TYPE = {\n COMPOSITE: 'cmpt',\n POINT_CLOUD: 'pnts',\n BATCHED_3D_MODEL: 'b3dm',\n INSTANCED_3D_MODEL: 'i3dm',\n GEOMETRY: 'geom',\n VECTOR: 'vect',\n GLTF: 'glTF'\n};\nexport const TILE3D_TYPES = Object.keys(TILE3D_TYPE);\nexport const MAGIC_ARRAY = {\n BATCHED_MODEL: [98, 51, 100, 109],\n INSTANCED_MODEL: [105, 51, 100, 109],\n POINT_CLOUD: [112, 110, 116, 115],\n COMPOSITE: [99, 109, 112, 116]\n};\n// TILE CONSTANTS\nexport const TILE3D_OPTIMIZATION_HINT = {\n NOT_COMPUTED: -1,\n USE_OPTIMIZATION: 1,\n SKIP_OPTIMIZATION: 0\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT AND Apache-2.0\n// Copyright vis.gl contributors\n// This file is derived from the Cesium code base under Apache 2 license\n// See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md\nimport { assert } from '@loaders.gl/loader-utils';\n// Decode the JSON binary array into clear text\nexport function getStringFromArrayBuffer(arrayBuffer, byteOffset, byteLength) {\n assert(arrayBuffer instanceof ArrayBuffer);\n const textDecoder = new TextDecoder('utf8');\n const typedArray = new Uint8Array(arrayBuffer, byteOffset, byteLength);\n const string = textDecoder.decode(typedArray);\n return string;\n}\n// Decode the JSON binary array into clear text\nexport function getStringFromTypedArray(typedArray) {\n assert(ArrayBuffer.isView(typedArray));\n const textDecoder = new TextDecoder('utf8');\n const string = textDecoder.decode(typedArray);\n return string;\n}\nexport function getMagicString(arrayBuffer, byteOffset = 0) {\n const dataView = new DataView(arrayBuffer);\n return `\\\n${String.fromCharCode(dataView.getUint8(byteOffset + 0))}\\\n${String.fromCharCode(dataView.getUint8(byteOffset + 1))}\\\n${String.fromCharCode(dataView.getUint8(byteOffset + 2))}\\\n${String.fromCharCode(dataView.getUint8(byteOffset + 3))}`;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT AND Apache-2.0\n// Copyright vis.gl contributors\n// This file is derived from the Cesium code base under Apache 2 license\n// See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md\nimport { DracoLoader } from '@loaders.gl/draco';\nimport { parseFromContext } from '@loaders.gl/loader-utils';\nimport { GL } from '@loaders.gl/math';\nimport { Vector3 } from '@math.gl/core';\nimport Tile3DFeatureTable from \"../classes/tile-3d-feature-table.js\";\nimport Tile3DBatchTable from \"../classes/tile-3d-batch-table.js\";\nimport { parse3DTileHeaderSync } from \"./helpers/parse-3d-tile-header.js\";\nimport { parse3DTileTablesHeaderSync, parse3DTileTablesSync } from \"./helpers/parse-3d-tile-tables.js\";\nimport { normalize3DTileColorAttribute } from \"./helpers/normalize-3d-tile-colors.js\";\nimport { normalize3DTileNormalAttribute } from \"./helpers/normalize-3d-tile-normals.js\";\nimport { normalize3DTilePositionAttribute } from \"./helpers/normalize-3d-tile-positions.js\";\nexport async function parsePointCloud3DTile(tile, arrayBuffer, byteOffset, options, context) {\n byteOffset = parse3DTileHeaderSync(tile, arrayBuffer, byteOffset);\n byteOffset = parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset);\n byteOffset = parse3DTileTablesSync(tile, arrayBuffer, byteOffset, options);\n initializeTile(tile);\n const { featureTable, batchTable } = parsePointCloudTables(tile);\n await parseDraco(tile, featureTable, batchTable, options, context);\n parsePositions(tile, featureTable, options);\n // @ts-expect-error TODO - do we need to assert on the batch table?\n parseColors(tile, featureTable, batchTable);\n parseNormals(tile, featureTable);\n return byteOffset;\n}\nfunction initializeTile(tile) {\n // Initialize point cloud tile defaults\n tile.attributes = {\n positions: null,\n colors: null,\n normals: null,\n batchIds: null\n };\n tile.isQuantized = false;\n tile.isTranslucent = false;\n tile.isRGB565 = false;\n tile.isOctEncoded16P = false;\n}\nfunction parsePointCloudTables(tile) {\n const featureTable = new Tile3DFeatureTable(tile.featureTableJson, tile.featureTableBinary);\n const pointsLength = featureTable.getGlobalProperty('POINTS_LENGTH');\n if (!Number.isFinite(pointsLength)) {\n throw new Error('POINTS_LENGTH must be defined');\n }\n featureTable.featuresLength = pointsLength;\n tile.featuresLength = pointsLength;\n tile.pointsLength = pointsLength;\n tile.pointCount = pointsLength;\n tile.rtcCenter = featureTable.getGlobalProperty('RTC_CENTER', GL.FLOAT, 3);\n const batchTable = parseBatchIds(tile, featureTable);\n return { featureTable, batchTable };\n}\nfunction parsePositions(tile, featureTable, options) {\n tile.attributes = tile.attributes || {\n positions: null,\n colors: null,\n normals: null,\n batchIds: null\n };\n if (!tile.attributes.positions) {\n if (featureTable.hasProperty('POSITION')) {\n tile.attributes.positions = featureTable.getPropertyArray('POSITION', GL.FLOAT, 3);\n }\n else if (featureTable.hasProperty('POSITION_QUANTIZED')) {\n const positions = featureTable.getPropertyArray('POSITION_QUANTIZED', GL.UNSIGNED_SHORT, 3);\n tile.isQuantized = true;\n tile.quantizedRange = (1 << 16) - 1;\n tile.quantizedVolumeScale = featureTable.getGlobalProperty('QUANTIZED_VOLUME_SCALE', GL.FLOAT, 3);\n if (!tile.quantizedVolumeScale) {\n throw new Error('QUANTIZED_VOLUME_SCALE must be defined for quantized positions.');\n }\n tile.quantizedVolumeOffset = featureTable.getGlobalProperty('QUANTIZED_VOLUME_OFFSET', GL.FLOAT, 3);\n if (!tile.quantizedVolumeOffset) {\n throw new Error('QUANTIZED_VOLUME_OFFSET must be defined for quantized positions.');\n }\n tile.attributes.positions = normalize3DTilePositionAttribute(tile, positions, options);\n }\n }\n if (!tile.attributes.positions) {\n throw new Error('Either POSITION or POSITION_QUANTIZED must be defined.');\n }\n}\nfunction parseColors(tile, featureTable, batchTable) {\n tile.attributes = tile.attributes || {\n positions: null,\n colors: null,\n normals: null,\n batchIds: null\n };\n if (!tile.attributes.colors) {\n let colors = null;\n if (featureTable.hasProperty('RGBA')) {\n colors = featureTable.getPropertyArray('RGBA', GL.UNSIGNED_BYTE, 4);\n tile.isTranslucent = true;\n }\n else if (featureTable.hasProperty('RGB')) {\n colors = featureTable.getPropertyArray('RGB', GL.UNSIGNED_BYTE, 3);\n }\n else if (featureTable.hasProperty('RGB565')) {\n colors = featureTable.getPropertyArray('RGB565', GL.UNSIGNED_SHORT, 1);\n tile.isRGB565 = true;\n }\n tile.attributes.colors = normalize3DTileColorAttribute(tile, colors, batchTable);\n }\n if (featureTable.hasProperty('CONSTANT_RGBA')) {\n tile.constantRGBA = featureTable.getGlobalProperty('CONSTANT_RGBA', GL.UNSIGNED_BYTE, 4);\n }\n}\nfunction parseNormals(tile, featureTable) {\n tile.attributes = tile.attributes || {\n positions: null,\n colors: null,\n normals: null,\n batchIds: null\n };\n if (!tile.attributes.normals) {\n let normals = null;\n if (featureTable.hasProperty('NORMAL')) {\n normals = featureTable.getPropertyArray('NORMAL', GL.FLOAT, 3);\n }\n else if (featureTable.hasProperty('NORMAL_OCT16P')) {\n normals = featureTable.getPropertyArray('NORMAL_OCT16P', GL.UNSIGNED_BYTE, 2);\n tile.isOctEncoded16P = true;\n }\n tile.attributes.normals = normalize3DTileNormalAttribute(tile, normals);\n }\n}\nfunction parseBatchIds(tile, featureTable) {\n let batchTable = null;\n if (!tile.batchIds && featureTable.hasProperty('BATCH_ID')) {\n tile.batchIds = featureTable.getPropertyArray('BATCH_ID', GL.UNSIGNED_SHORT, 1);\n if (tile.batchIds) {\n const batchFeatureLength = featureTable.getGlobalProperty('BATCH_LENGTH');\n if (!batchFeatureLength) {\n throw new Error('Global property: BATCH_LENGTH must be defined when BATCH_ID is defined.');\n }\n const { batchTableJson, batchTableBinary } = tile;\n batchTable = new Tile3DBatchTable(batchTableJson, batchTableBinary, batchFeatureLength);\n }\n }\n return batchTable;\n}\n// eslint-disable-next-line complexity\nasync function parseDraco(tile, featureTable, batchTable, options, context) {\n let dracoBuffer;\n let dracoFeatureTableProperties;\n let dracoBatchTableProperties;\n const batchTableDraco = tile.batchTableJson &&\n tile.batchTableJson.extensions &&\n tile.batchTableJson.extensions['3DTILES_draco_point_compression'];\n if (batchTableDraco) {\n dracoBatchTableProperties = batchTableDraco.properties;\n }\n const featureTableDraco = featureTable.getExtension('3DTILES_draco_point_compression');\n if (featureTableDraco) {\n dracoFeatureTableProperties = featureTableDraco.properties;\n const dracoByteOffset = featureTableDraco.byteOffset;\n const dracoByteLength = featureTableDraco.byteLength;\n if (!dracoFeatureTableProperties || !Number.isFinite(dracoByteOffset) || !dracoByteLength) {\n throw new Error('Draco properties, byteOffset, and byteLength must be defined');\n }\n dracoBuffer = (tile.featureTableBinary || []).slice(dracoByteOffset, dracoByteOffset + dracoByteLength);\n tile.hasPositions = Number.isFinite(dracoFeatureTableProperties.POSITION);\n tile.hasColors =\n Number.isFinite(dracoFeatureTableProperties.RGB) ||\n Number.isFinite(dracoFeatureTableProperties.RGBA);\n tile.hasNormals = Number.isFinite(dracoFeatureTableProperties.NORMAL);\n tile.hasBatchIds = Number.isFinite(dracoFeatureTableProperties.BATCH_ID);\n tile.isTranslucent = Number.isFinite(dracoFeatureTableProperties.RGBA);\n }\n if (!dracoBuffer) {\n return true;\n }\n const dracoData = {\n buffer: dracoBuffer,\n properties: { ...dracoFeatureTableProperties, ...dracoBatchTableProperties },\n featureTableProperties: dracoFeatureTableProperties,\n batchTableProperties: dracoBatchTableProperties,\n dequantizeInShader: false\n };\n return await loadDraco(tile, dracoData, options, context);\n}\n// eslint-disable-next-line complexity, max-statements\nexport async function loadDraco(tile, dracoData, options, context) {\n if (!context) {\n return;\n }\n const dracoOptions = {\n ...options,\n draco: {\n ...options?.draco,\n extraAttributes: dracoData.batchTableProperties || {}\n }\n };\n // The entire tileset might be included, too expensive to serialize\n delete dracoOptions['3d-tiles'];\n const data = await parseFromContext(dracoData.buffer, DracoLoader, dracoOptions, context);\n const decodedPositions = data.attributes.POSITION && data.attributes.POSITION.value;\n const decodedColors = data.attributes.COLOR_0 && data.attributes.COLOR_0.value;\n const decodedNormals = data.attributes.NORMAL && data.attributes.NORMAL.value;\n const decodedBatchIds = data.attributes.BATCH_ID && data.attributes.BATCH_ID.value;\n // @ts-expect-error\n const isQuantizedDraco = decodedPositions && data.attributes.POSITION.value.quantization;\n // @ts-expect-error\n const isOctEncodedDraco = decodedNormals && data.attributes.NORMAL.value.quantization;\n if (isQuantizedDraco) {\n // Draco quantization range == quantized volume scale - size in meters of the quantized volume\n // Internal quantized range is the range of values of the quantized data, e.g. 255 for 8-bit, 1023 for 10-bit, etc\n // @ts-expect-error This doesn't look right\n const quantization = data.POSITION.data.quantization;\n const range = quantization.range;\n tile.quantizedVolumeScale = new Vector3(range, range, range);\n tile.quantizedVolumeOffset = new Vector3(quantization.minValues);\n tile.quantizedRange = (1 << quantization.quantizationBits) - 1.0;\n tile.isQuantizedDraco = true;\n }\n if (isOctEncodedDraco) {\n // @ts-expect-error This doesn't look right\n tile.octEncodedRange = (1 << data.NORMAL.data.quantization.quantizationBits) - 1.0;\n tile.isOctEncodedDraco = true;\n }\n // Extra batch table attributes\n const batchTableAttributes = {};\n if (dracoData.batchTableProperties) {\n for (const attributeName of Object.keys(dracoData.batchTableProperties)) {\n if (data.attributes[attributeName] && data.attributes[attributeName].value) {\n batchTableAttributes[attributeName.toLowerCase()] = data.attributes[attributeName].value;\n }\n }\n }\n tile.attributes = {\n // @ts-expect-error\n positions: decodedPositions,\n // @ts-expect-error\n colors: normalize3DTileColorAttribute(tile, decodedColors, undefined),\n // @ts-expect-error\n normals: decodedNormals,\n // @ts-expect-error\n batchIds: decodedBatchIds,\n ...batchTableAttributes\n };\n}\n// TODO - this is the remaining code from Cesium's parser\n/*\n const batchTable = new Tile3DBatchTable(tile);\n\n // parseDracoBuffer(tile, featureTable, batchTable);\n\n if (!tile.attributes.positions) {\n throw new Error('Either POSITION or POSITION_QUANTIZED must be defined.');\n }\n}\n/*\n\n if (!tile.attributes.positions) {\n if (featureTable.hasProperty('POSITION')) {\n tile.attributes.positions = featureTable.getPropertyArray('POSITION', GL.FLOAT, 3);\n } else if (featureTable.hasProperty('POSITION_QUANTIZED')) {\n tile.attributes.positions = featureTable.getPropertyArray('POSITION_QUANTIZED', GL.UNSIGNED_SHORT, 3);\n\n\n if (!tile.colors) {\n if (featureTable.hasProperty('RGBA')) {\n tile.colors = featureTable.getPropertyArray('RGBA', GL.UNSIGNED_BYTE, 4);\n tile.isTranslucent = true;\n } else if (featureTable.hasProperty('RGB')) {\n tile.colors = featureTable.getPropertyArray('RGB', GL.UNSIGNED_BYTE, 3);\n } else if (featureTable.hasPropertry('RGB565')) {\n tile.colors = featureTable.getPropertyArray('RGB565', GL.UNSIGNED_SHORT, 1);\n tile.isRGB565 = true;\n }\n }\n\n if (!tile.attributes.normals) {\n if (featureTable.getPropertry('NORMAL')) {\n tile.attributes.normals = featureTable.getPropertyArray('NORMAL', GL.FLOAT, 3);\n } else if (featureTable.getProperty('NORMAL_OCT16P')) {\n tile.attributes.normals = featureTable.getPropertyArray('NORMAL_OCT16P', GL.UNSIGNED_BYTE, 2);\n tile.isOctEncoded16P = true;\n }\n }\n\n if (!tile.batchIds) {\n if (featureTable.hasProperty('BATCH_ID')) {\n tile.batchIds = featureTable.getPropertyArray('BATCH_ID', GL.UNSIGNED_SHORT, 1);\n }\n }\n\n if (!tile.attributes.positions) {\n throw new Error('Either POSITION or POSITION_QUANTIZED must be defined.');\n }\n\n if (featureTable.getPropertry('CONSTANT_RGBA')) {\n tile.constantRGBA = featureTable.getGlobalProperty('CONSTANT_RGBA', GL.UNSIGNED_BYTE, 4);\n }\n\n if (tile.batchIds) {\n const batchLength = featureTable.getGlobalProperty('BATCH_LENGTH');\n if (!defined(batchLength)) {\n throw new Error('Global property: BATCH_LENGTH must be defined when BATCH_ID is defined.');\n }\n\n if (defined(batchTableBinary)) {\n // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed\n batchTableBinary = new Uint8Array(batchTableBinary);\n }\n\n if (defined(pointCloud._batchTableLoaded)) {\n pointCloud._batchTableLoaded(batchLength, batchTableJson, batchTableBinary);\n }\n }\n\n // If points are not batched and there are per-point properties, use these properties for styling purposes\n var styleableProperties;\n if (!hasBatchIds && defined(batchTableBinary)) {\n tile.styleableProperties = Cesium3DTileBatchTable.getBinaryProperties(\n pointsLength,\n batchTableJson,\n batchTableBinary\n );\n }\n\n tile.draco = draco;\n}\n\n// Separate parsing and decoding of Draco\nexport function parseDracoBuffer(tile, featureTable, batchTable) {\n let dracoBuffer;\n let dracoFeatureTableProperties;\n let dracoBatchTableProperties;\n\n const batchTableDraco = batchTable.getExtension('3DTILES_draco_point_compression');\n if (batchTableDraco) {\n dracoBatchTableProperties = batchTableDraco.properties;\n }\n\n const featureTableDraco = featureTable.getExtension('3DTILES_draco_point_compression');\n if (featureTableDraco) {\n dracoFeatureTableProperties = featureTableDraco.properties;\n const dracoByteOffset = featureTableDraco.byteOffset;\n const dracoByteLength = featureTableDraco.byteLength;\n if (!dracoFeatureTableProperties || !dracoByteOffset || !dracoByteLength) {\n throw new Error('Draco properties, byteOffset, and byteLength must be defined');\n }\n\n dracoBuffer = arraySlice(\n featureTableBinary,\n dracoByteOffset,\n dracoByteOffset + dracoByteLength\n );\n tile.hasPositions = dracoFeatureTableProperties.POSITION;\n tile.hasColors = dracoFeatureTableProperties.RGB || dracoFeatureTableProperties.RGBA;\n tile.hasNormals = dracoFeatureTableProperties.NORMAL;\n tile.hasBatchIds = dracoFeatureTableProperties.BATCH_ID;\n tile.isTranslucent = dracoFeatureTableProperties.RGBA;\n }\n\n if (dracoBuffer) {\n tile.draco = {\n buffer: dracoBuffer,\n properties: {...dracoFeatureTableProperties, ...dracoBatchTableProperties},\n featureTableProperties: dracoFeatureTableProperties,\n batchTableProperties: dracoBatchTableProperties,\n dequantizeInShader: false\n };\n\n tile.decodingState = DECODING_STATE.NEEDS_DECODE;\n }\n}\n\n/*\nfunction decodeDraco(tile, context) {\n if (tile.decodingState === DECODING_STATE.READY) {\n return false;\n }\n if (tile.decodingState === DECODING_STATE.NEEDS_DECODE) {\n var parsedContent = tile._parsedContent;\n var draco = parsedContent.draco;\n var decodePromise = DracoLoader.decodePointCloud(draco, context);\n if (defined(decodePromise)) {\n tile.decodingState = DECODING_STATE.DECODING;\n decodePromise.then(function(result) {\n tile.decodingState = DECODING_STATE.READY;\n var decodedPositions = defined(result.POSITION) ? result.POSITION.array : undefined;\n var decodedRgb = defined(result.RGB) ? result.RGB.array : undefined;\n var decodedRgba = defined(result.RGBA) ? result.RGBA.array : undefined;\n var decodedNormals = defined(result.NORMAL) ? result.NORMAL.array : undefined;\n var decodedBatchIds = defined(result.BATCH_ID) ? result.BATCH_ID.array : undefined;\n var isQuantizedDraco = defined(decodedPositions) && defined(result.POSITION.data.quantization);\n var isOctEncodedDraco = defined(decodedNormals) && defined(result.NORMAL.data.quantization);\n if (isQuantizedDraco) {\n // Draco quantization range == quantized volume scale - size in meters of the quantized volume\n // Internal quantized range is the range of values of the quantized data, e.g. 255 for 8-bit, 1023 for 10-bit, etc\n var quantization = result.POSITION.data.quantization;\n var range = quantization.range;\n tile._quantizedVolumeScale = Cartesian3.fromElements(range, range, range);\n tile._quantizedVolumeOffset = Cartesian3.unpack(quantization.minValues);\n tile._quantizedRange = (1 << quantization.quantizationBits) - 1.0;\n tile._isQuantizedDraco = true;\n }\n if (isOctEncodedDraco) {\n tile._octEncodedRange = (1 << result.NORMAL.data.quantization.quantizationBits) - 1.0;\n tile._isOctEncodedDraco = true;\n }\n var styleableProperties = parsedContent.styleableProperties;\n var batchTableProperties = draco.batchTableProperties;\n for (var name in batchTableProperties) {\n if (batchTableProperties.hasOwnProperty(name)) {\n var property = result[name];\n if (!defined(styleableProperties)) {\n styleableProperties = {};\n }\n styleableProperties[name] = {\n typedArray : property.array,\n componentCount : property.data.componentsPerAttribute\n };\n }\n }\n parsedContent.positions = defaultValue(decodedPositions, parsedContent.positions);\n parsedContent.colors = defaultValue(defaultValue(decodedRgba, decodedRgb), parsedContent.colors);\n parsedContent.normals = defaultValue(decodedNormals, parsedContent.normals);\n parsedContent.batchIds = defaultValue(decodedBatchIds, parsedContent.batchIds);\n parsedContent.styleableProperties = styleableProperties;\n }).otherwise(function(error) {\n tile.decodingState = DECODING_STATE.FAILED;\n tile._readyPromise.reject(error);\n });\n }\n }\n return true;\n}\n*/\n", "// loaders.gl\n// SPDX-License-Identifier: MIT AND Apache-2.0\n// Copyright vis.gl contributors\n// This file is derived from the Cesium code base under Apache 2 license\n// See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md\nimport { GL, GLType } from '@loaders.gl/math';\n// Reference:\n// https://github.com/AnalyticalGraphicsInc/cesium/blob/1de96d087f0b17575eb1a3f736407b348c765d59/Source/Scene/Cesium3DTileFeatureTable.js\nexport default class Tile3DFeatureTable {\n json;\n buffer;\n featuresLength = 0;\n _cachedTypedArrays = {};\n constructor(featureTableJson, featureTableBinary) {\n this.json = featureTableJson;\n this.buffer = featureTableBinary;\n }\n getExtension(extensionName) {\n return this.json.extensions && this.json.extensions[extensionName];\n }\n hasProperty(propertyName) {\n return Boolean(this.json[propertyName]);\n }\n getGlobalProperty(propertyName, componentType = GL.UNSIGNED_INT, componentLength = 1) {\n const jsonValue = this.json[propertyName];\n if (jsonValue && Number.isFinite(jsonValue.byteOffset)) {\n return this._getTypedArrayFromBinary(propertyName, componentType, componentLength, 1, jsonValue.byteOffset);\n }\n return jsonValue;\n }\n getPropertyArray(propertyName, componentType, componentLength) {\n const jsonValue = this.json[propertyName];\n if (jsonValue && Number.isFinite(jsonValue.byteOffset)) {\n if ('componentType' in jsonValue) {\n componentType = GLType.fromName(jsonValue.componentType);\n }\n return this._getTypedArrayFromBinary(propertyName, componentType, componentLength, this.featuresLength, jsonValue.byteOffset);\n }\n return this._getTypedArrayFromArray(propertyName, componentType, jsonValue);\n }\n getProperty(propertyName, componentType, componentLength, featureId, result) {\n const jsonValue = this.json[propertyName];\n if (!jsonValue) {\n return jsonValue;\n }\n const typedArray = this.getPropertyArray(propertyName, componentType, componentLength);\n if (componentLength === 1) {\n return typedArray[featureId];\n }\n for (let i = 0; i < componentLength; ++i) {\n result[i] = typedArray[componentLength * featureId + i];\n }\n return result;\n }\n // HELPERS\n _getTypedArrayFromBinary(propertyName, componentType, componentLength, count, byteOffset) {\n const cachedTypedArrays = this._cachedTypedArrays;\n let typedArray = cachedTypedArrays[propertyName];\n if (!typedArray) {\n typedArray = GLType.createTypedArray(componentType, this.buffer.buffer, this.buffer.byteOffset + byteOffset, count * componentLength);\n cachedTypedArrays[propertyName] = typedArray;\n }\n return typedArray;\n }\n _getTypedArrayFromArray(propertyName, componentType, array) {\n const cachedTypedArrays = this._cachedTypedArrays;\n let typedArray = cachedTypedArrays[propertyName];\n if (!typedArray) {\n typedArray = GLType.createTypedArray(componentType, array);\n cachedTypedArrays[propertyName] = typedArray;\n }\n return typedArray;\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT AND Apache-2.0\n// Copyright vis.gl contributors\n// This file is derived from the Cesium code base under Apache 2 license\n// See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md\nimport { assert } from '@loaders.gl/loader-utils';\nimport { createTypedArrayFromAccessor } from \"./helpers/tile-3d-accessor-utils.js\";\nimport { initializeHierarchy, traverseHierarchy } from \"./tile-3d-batch-table-hierarchy.js\";\nfunction defined(x) {\n return x !== undefined && x !== null;\n}\nconst clone = (x, y) => x;\n// These top level fields in the batch table json are not properties\nconst IGNORED_PROPERTY_FIELDS = {\n HIERARCHY: true, // Deprecated HIERARCHY property\n extensions: true,\n extras: true\n};\n// The size of this array equals the maximum instance count among all loaded tiles, which has the potential to be large.\nexport default class Tile3DBatchTableParser {\n json;\n binary;\n featureCount;\n _extensions;\n // Copy all top-level property fields from the json object, ignoring special fields\n _properties;\n _binaryProperties;\n // TODO: hierarchy support is only partially implemented and not tested\n _hierarchy;\n constructor(json, binary, featureCount, options = {}) {\n assert(featureCount >= 0);\n this.json = json || {};\n this.binary = binary;\n this.featureCount = featureCount;\n this._extensions = this.json?.extensions || {};\n // Copy all top-level property fields from the json object, ignoring special fields\n this._properties = {};\n for (const propertyName in this.json) {\n if (!IGNORED_PROPERTY_FIELDS[propertyName]) {\n this._properties[propertyName] = this.json[propertyName];\n }\n }\n this._binaryProperties = this._initializeBinaryProperties();\n // TODO: hierarchy support is only partially implemented and not tested\n if (options['3DTILES_batch_table_hierarchy']) {\n this._hierarchy = initializeHierarchy(this, this.json, this.binary);\n }\n }\n getExtension(extensionName) {\n return this.json && this.json.extensions && this.json.extensions[extensionName];\n }\n memorySizeInBytes() {\n return 0;\n }\n isClass(batchId, className) {\n this._checkBatchId(batchId);\n assert(typeof className === 'string', className);\n // extension: 3DTILES_batch_table_hierarchy\n if (this._hierarchy) {\n // PERFORMANCE_IDEA : cache results in the ancestor classes\n // to speed up this check if this area becomes a hotspot\n // PERFORMANCE_IDEA : treat class names as integers for faster comparisons\n const result = traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => {\n const classId = hierarchy.classIds[instanceIndex];\n const instanceClass = hierarchy.classes[classId];\n return instanceClass.name === className;\n });\n return defined(result);\n }\n return false;\n }\n isExactClass(batchId, className) {\n assert(typeof className === 'string', className);\n return this.getExactClassName(batchId) === className;\n }\n getExactClassName(batchId) {\n this._checkBatchId(batchId);\n // extension: 3DTILES_batch_table_hierarchy\n if (this._hierarchy) {\n const classId = this._hierarchy.classIds[batchId];\n const instanceClass = this._hierarchy.classes[classId];\n return instanceClass.name;\n }\n return undefined;\n }\n hasProperty(batchId, name) {\n this._checkBatchId(batchId);\n assert(typeof name === 'string', name);\n return defined(this._properties[name]) || this._hasPropertyInHierarchy(batchId, name);\n }\n getPropertyNames(batchId, results) {\n this._checkBatchId(batchId);\n results = defined(results) ? results : [];\n results.length = 0;\n const propertyNames = Object.keys(this._properties);\n results.push(...propertyNames);\n if (this._hierarchy) {\n this._getPropertyNamesInHierarchy(batchId, results);\n }\n return results;\n }\n getProperty(batchId, name) {\n this._checkBatchId(batchId);\n assert(typeof name === 'string', name);\n if (this._binaryProperties) {\n const binaryProperty = this._binaryProperties[name];\n if (defined(binaryProperty)) {\n return this._getBinaryProperty(binaryProperty, batchId);\n }\n }\n const propertyValues = this._properties[name];\n if (defined(propertyValues)) {\n return clone(propertyValues[batchId], true);\n }\n // EXTENSION: 3DTILES_batch_table_hierarchy\n if (this._hierarchy) {\n const hierarchyProperty = this._getHierarchyProperty(batchId, name);\n if (defined(hierarchyProperty)) {\n return hierarchyProperty;\n }\n }\n return undefined;\n }\n setProperty(batchId, name, value) {\n const featureCount = this.featureCount;\n this._checkBatchId(batchId);\n assert(typeof name === 'string', name);\n if (this._binaryProperties) {\n const binaryProperty = this._binaryProperties[name];\n if (binaryProperty) {\n this._setBinaryProperty(binaryProperty, batchId, value);\n return;\n }\n }\n // EXTENSION: 3DTILES_batch_table_hierarchy\n if (this._hierarchy) {\n if (this._setHierarchyProperty(this, batchId, name, value)) {\n return;\n }\n }\n let propertyValues = this._properties[name];\n if (!defined(propertyValues)) {\n // Property does not exist. Create it.\n this._properties[name] = new Array(featureCount);\n propertyValues = this._properties[name];\n }\n propertyValues[batchId] = clone(value, true);\n }\n // PRIVATE METHODS\n _checkBatchId(batchId) {\n const valid = batchId >= 0 && batchId < this.featureCount;\n if (!valid) {\n throw new Error('batchId not in range [0, featureCount - 1].');\n }\n }\n _getBinaryProperty(binaryProperty, index) {\n return binaryProperty.unpack(binaryProperty.typedArray, index);\n }\n _setBinaryProperty(binaryProperty, index, value) {\n binaryProperty.pack(value, binaryProperty.typedArray, index);\n }\n _initializeBinaryProperties() {\n let binaryProperties = null;\n for (const name in this._properties) {\n const property = this._properties[name];\n const binaryProperty = this._initializeBinaryProperty(name, property);\n // Store any information needed to access the binary data, including the typed array,\n // componentCount (e.g. a VEC4 would be 4), and the type used to pack and unpack (e.g. Cartesian4).\n if (binaryProperty) {\n binaryProperties = binaryProperties || {};\n binaryProperties[name] = binaryProperty;\n }\n }\n return binaryProperties;\n }\n _initializeBinaryProperty(name, property) {\n if ('byteOffset' in property) {\n // This is a binary property\n const tile3DAccessor = property;\n assert(this.binary, `Property ${name} requires a batch table binary.`);\n assert(tile3DAccessor.type, `Property ${name} requires a type.`);\n const accessor = createTypedArrayFromAccessor(tile3DAccessor, this.binary.buffer, this.binary.byteOffset | 0, this.featureCount);\n // Store any information needed to access the binary data, including the typed array,\n // componentCount (e.g. a VEC4 would be 4), and the type used to pack and unpack (e.g. Cartesian4).\n return {\n typedArray: accessor.values,\n componentCount: accessor.size,\n unpack: accessor.unpacker,\n pack: accessor.packer\n };\n }\n return null;\n }\n // EXTENSION SUPPORT: 3DTILES_batch_table_hierarchy\n _hasPropertyInHierarchy(batchId, name) {\n if (!this._hierarchy) {\n return false;\n }\n const result = traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => {\n const classId = hierarchy.classIds[instanceIndex];\n const instances = hierarchy.classes[classId].instances;\n return defined(instances[name]);\n });\n return defined(result);\n }\n _getPropertyNamesInHierarchy(batchId, results) {\n traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => {\n const classId = hierarchy.classIds[instanceIndex];\n const instances = hierarchy.classes[classId].instances;\n for (const name in instances) {\n if (instances.hasOwnProperty(name)) {\n if (results.indexOf(name) === -1) {\n results.push(name);\n }\n }\n }\n });\n }\n _getHierarchyProperty(batchId, name) {\n return traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => {\n const classId = hierarchy.classIds[instanceIndex];\n const instanceClass = hierarchy.classes[classId];\n const indexInClass = hierarchy.classIndexes[instanceIndex];\n const propertyValues = instanceClass.instances[name];\n if (defined(propertyValues)) {\n if (defined(propertyValues.typedArray)) {\n return this._getBinaryProperty(propertyValues, indexInClass);\n }\n return clone(propertyValues[indexInClass], true);\n }\n return null;\n });\n }\n _setHierarchyProperty(batchTable, batchId, name, value) {\n const result = traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => {\n const classId = hierarchy.classIds[instanceIndex];\n const instanceClass = hierarchy.classes[classId];\n const indexInClass = hierarchy.classIndexes[instanceIndex];\n const propertyValues = instanceClass.instances[name];\n if (defined(propertyValues)) {\n assert(instanceIndex === batchId, `Inherited property \"${name}\" is read-only.`);\n if (defined(propertyValues.typedArray)) {\n this._setBinaryProperty(propertyValues, indexInClass, value);\n }\n else {\n propertyValues[indexInClass] = clone(value, true);\n }\n return true;\n }\n return false;\n });\n return defined(result);\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\nimport { GLType } from '@loaders.gl/math'; // '@math.gl/geometry';\nimport { assert } from '@loaders.gl/loader-utils';\nconst COMPONENTS_PER_ATTRIBUTE = {\n SCALAR: 1,\n VEC2: 2,\n VEC3: 3,\n VEC4: 4,\n MAT2: 4,\n MAT3: 9,\n MAT4: 16\n};\n// TODO - could just return typed array views...\n// prettier-ignore\nconst UNPACKER = {\n SCALAR: (values, i) => values[i],\n VEC2: (values, i) => [values[2 * i + 0], values[2 * i + 1]],\n VEC3: (values, i) => [values[3 * i + 0], values[3 * i + 1], values[3 * i + 2]],\n VEC4: (values, i) => [values[4 * i + 0], values[4 * i + 1], values[4 * i + 2], values[4 * i + 3]],\n // TODO: check column major\n MAT2: (values, i) => [\n values[4 * i + 0], values[4 * i + 1],\n values[4 * i + 2], values[4 * i + 3]\n ],\n MAT3: (values, i) => [\n values[9 * i + 0], values[9 * i + 1], values[9 * i + 2],\n values[9 * i + 3], values[9 * i + 4], values[9 * i + 5],\n values[9 * i + 6], values[9 * i + 7], values[9 * i + 8]\n ],\n MAT4: (values, i) => [\n values[16 * i + 0], values[16 * i + 1], values[16 * i + 2], values[16 * i + 3],\n values[16 * i + 4], values[16 * i + 5], values[16 * i + 6], values[16 * i + 7],\n values[16 * i + 8], values[16 * i + 9], values[16 * i + 10], values[16 * i + 11],\n values[16 * i + 12], values[16 * i + 13], values[16 * i + 14], values[16 * i + 15]\n ]\n};\nconst PACKER = {\n SCALAR: (x, values, i) => {\n values[i] = x;\n },\n VEC2: (x, values, i) => {\n values[2 * i + 0] = x[0];\n values[2 * i + 1] = x[1];\n },\n VEC3: (x, values, i) => {\n values[3 * i + 0] = x[0];\n values[3 * i + 1] = x[1];\n values[3 * i + 2] = x[2];\n },\n VEC4: (x, values, i) => {\n values[4 * i + 0] = x[0];\n values[4 * i + 1] = x[1];\n values[4 * i + 2] = x[2];\n values[4 * i + 3] = x[3];\n },\n // TODO: check column major correctness\n MAT2: (x, values, i) => {\n values[4 * i + 0] = x[0];\n values[4 * i + 1] = x[1];\n values[4 * i + 2] = x[2];\n values[4 * i + 3] = x[3];\n },\n MAT3: (x, values, i) => {\n values[9 * i + 0] = x[0];\n values[9 * i + 1] = x[1];\n values[9 * i + 2] = x[2];\n values[9 * i + 3] = x[3];\n values[9 * i + 4] = x[4];\n values[9 * i + 5] = x[5];\n values[9 * i + 6] = x[6];\n values[9 * i + 7] = x[7];\n values[9 * i + 8] = x[8];\n values[9 * i + 9] = x[9];\n },\n MAT4: (x, values, i) => {\n values[16 * i + 0] = x[0];\n values[16 * i + 1] = x[1];\n values[16 * i + 2] = x[2];\n values[16 * i + 3] = x[3];\n values[16 * i + 4] = x[4];\n values[16 * i + 5] = x[5];\n values[16 * i + 6] = x[6];\n values[16 * i + 7] = x[7];\n values[16 * i + 8] = x[8];\n values[16 * i + 9] = x[9];\n values[16 * i + 10] = x[10];\n values[16 * i + 11] = x[11];\n values[16 * i + 12] = x[12];\n values[16 * i + 13] = x[13];\n values[16 * i + 14] = x[14];\n values[16 * i + 15] = x[15];\n }\n};\nexport function createTypedArrayFromAccessor(tile3DAccessor, buffer, byteOffset, length) {\n const { componentType } = tile3DAccessor;\n assert(tile3DAccessor.componentType);\n const type = typeof componentType === 'string' ? GLType.fromName(componentType) : componentType;\n const size = COMPONENTS_PER_ATTRIBUTE[tile3DAccessor.type];\n const unpacker = UNPACKER[tile3DAccessor.type];\n const packer = PACKER[tile3DAccessor.type];\n byteOffset += tile3DAccessor.byteOffset;\n const values = GLType.createTypedArray(type, buffer, byteOffset, size * length);\n return {\n values,\n type,\n size,\n unpacker,\n packer\n };\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT AND Apache-2.0\n// Copyright vis.gl contributors\n// This file is derived from the Cesium code base under Apache 2 license\n// See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md\n// TODO - Finish hierarchy suypport: this file is only half ported\n/* eslint-disable */\n// @ts-nocheck\nconst defined = (x) => x !== undefined;\nexport function initializeHierarchy(batchTable, jsonHeader, binaryBody) {\n if (!jsonHeader) {\n return null;\n }\n let hierarchy = batchTable.getExtension('3DTILES_batch_table_hierarchy');\n const legacyHierarchy = jsonHeader.HIERARCHY;\n if (legacyHierarchy) {\n // eslint-disable-next-line\n console.warn('3D Tile Parser: HIERARCHY is deprecated. Use 3DTILES_batch_table_hierarchy.');\n jsonHeader.extensions = jsonHeader.extensions || {};\n jsonHeader.extensions['3DTILES_batch_table_hierarchy'] = legacyHierarchy;\n hierarchy = legacyHierarchy;\n }\n if (!hierarchy) {\n return null;\n }\n return initializeHierarchyValues(hierarchy, binaryBody);\n}\n// eslint-disable-next-line max-statements\nfunction initializeHierarchyValues(hierarchyJson, binaryBody) {\n let i;\n let classId;\n let binaryAccessor;\n const instancesLength = hierarchyJson.instancesLength;\n const classes = hierarchyJson.classes;\n let classIds = hierarchyJson.classIds;\n let parentCounts = hierarchyJson.parentCounts;\n let parentIds = hierarchyJson.parentIds;\n let parentIdsLength = instancesLength;\n if (defined(classIds.byteOffset)) {\n classIds.componentType = defaultValue(classIds.componentType, GL.UNSIGNED_SHORT);\n classIds.type = AttributeType.SCALAR;\n binaryAccessor = getBinaryAccessor(classIds);\n classIds = binaryAccessor.createArrayBufferView(binaryBody.buffer, binaryBody.byteOffset + classIds.byteOffset, instancesLength);\n }\n let parentIndexes;\n if (defined(parentCounts)) {\n if (defined(parentCounts.byteOffset)) {\n parentCounts.componentType = defaultValue(parentCounts.componentType, GL.UNSIGNED_SHORT);\n parentCounts.type = AttributeType.SCALAR;\n binaryAccessor = getBinaryAccessor(parentCounts);\n parentCounts = binaryAccessor.createArrayBufferView(binaryBody.buffer, binaryBody.byteOffset + parentCounts.byteOffset, instancesLength);\n }\n parentIndexes = new Uint16Array(instancesLength);\n parentIdsLength = 0;\n for (i = 0; i < instancesLength; ++i) {\n parentIndexes[i] = parentIdsLength;\n parentIdsLength += parentCounts[i];\n }\n }\n if (defined(parentIds) && defined(parentIds.byteOffset)) {\n parentIds.componentType = defaultValue(parentIds.componentType, GL.UNSIGNED_SHORT);\n parentIds.type = AttributeType.SCALAR;\n binaryAccessor = getBinaryAccessor(parentIds);\n parentIds = binaryAccessor.createArrayBufferView(binaryBody.buffer, binaryBody.byteOffset + parentIds.byteOffset, parentIdsLength);\n }\n const classesLength = classes.length;\n for (i = 0; i < classesLength; ++i) {\n const classInstancesLength = classes[i].length;\n const properties = classes[i].instances;\n const binaryProperties = getBinaryProperties(classInstancesLength, properties, binaryBody);\n classes[i].instances = combine(binaryProperties, properties);\n }\n const classCounts = new Array(classesLength).fill(0);\n const classIndexes = new Uint16Array(instancesLength);\n for (i = 0; i < instancesLength; ++i) {\n classId = classIds[i];\n classIndexes[i] = classCounts[classId];\n ++classCounts[classId];\n }\n const hierarchy = {\n classes,\n classIds,\n classIndexes,\n parentCounts,\n parentIndexes,\n parentIds\n };\n validateHierarchy(hierarchy);\n return hierarchy;\n}\n// HELPER CODE\n// Traverse over the hierarchy and process each instance with the endConditionCallback.\n// When the endConditionCallback returns a value