UNPKG

@loaders.gl/3d-tiles

Version:

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

1,279 lines (1,258 loc) 98.9 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // dist/index.js var dist_exports = {}; __export(dist_exports, { CesiumIonLoader: () => CesiumIonLoader, TILE3D_TYPE: () => TILE3D_TYPE, Tile3DBatchTable: () => Tile3DBatchTableParser, Tile3DFeatureTable: () => Tile3DFeatureTable, Tile3DSubtreeLoader: () => Tile3DSubtreeLoader, Tile3DWriter: () => Tile3DWriter, Tiles3DArchive: () => Tiles3DArchive, Tiles3DArchiveFileLoader: () => Tiles3DArchiveFileLoader, Tiles3DLoader: () => Tiles3DLoader, _getIonTilesetMetadata: () => getIonTilesetMetadata }); module.exports = __toCommonJS(dist_exports); // dist/tiles-3d-loader.js var import_loader_utils8 = require("@loaders.gl/loader-utils"); var import_tiles2 = require("@loaders.gl/tiles"); // dist/lib/utils/version.js var VERSION = true ? "4.3.2" : "latest"; // dist/lib/constants.js var TILE3D_TYPE = { COMPOSITE: "cmpt", POINT_CLOUD: "pnts", BATCHED_3D_MODEL: "b3dm", INSTANCED_3D_MODEL: "i3dm", GEOMETRY: "geom", VECTOR: "vect", GLTF: "glTF" }; var TILE3D_TYPES = Object.keys(TILE3D_TYPE); var MAGIC_ARRAY = { BATCHED_MODEL: [98, 51, 100, 109], INSTANCED_MODEL: [105, 51, 100, 109], POINT_CLOUD: [112, 110, 116, 115], COMPOSITE: [99, 109, 112, 116] }; // dist/lib/parsers/helpers/parse-utils.js var import_loader_utils = require("@loaders.gl/loader-utils"); function getStringFromArrayBuffer(arrayBuffer, byteOffset, byteLength) { (0, import_loader_utils.assert)(arrayBuffer instanceof ArrayBuffer); const textDecoder = new TextDecoder("utf8"); const typedArray = new Uint8Array(arrayBuffer, byteOffset, byteLength); const string = textDecoder.decode(typedArray); return string; } function getMagicString(arrayBuffer, byteOffset = 0) { const dataView = new DataView(arrayBuffer); return `${String.fromCharCode(dataView.getUint8(byteOffset + 0))}${String.fromCharCode(dataView.getUint8(byteOffset + 1))}${String.fromCharCode(dataView.getUint8(byteOffset + 2))}${String.fromCharCode(dataView.getUint8(byteOffset + 3))}`; } // dist/lib/parsers/parse-3d-tile-point-cloud.js var import_draco = require("@loaders.gl/draco"); var import_loader_utils4 = require("@loaders.gl/loader-utils"); var import_math6 = require("@loaders.gl/math"); var import_core3 = require("@math.gl/core"); // dist/lib/classes/tile-3d-feature-table.js var import_math = require("@loaders.gl/math"); var Tile3DFeatureTable = class { json; buffer; featuresLength = 0; _cachedTypedArrays = {}; constructor(featureTableJson, featureTableBinary) { this.json = featureTableJson; this.buffer = featureTableBinary; } getExtension(extensionName) { return this.json.extensions && this.json.extensions[extensionName]; } hasProperty(propertyName) { return Boolean(this.json[propertyName]); } getGlobalProperty(propertyName, componentType = import_math.GL.UNSIGNED_INT, componentLength = 1) { const jsonValue = this.json[propertyName]; if (jsonValue && Number.isFinite(jsonValue.byteOffset)) { return this._getTypedArrayFromBinary(propertyName, componentType, componentLength, 1, jsonValue.byteOffset); } return jsonValue; } getPropertyArray(propertyName, componentType, componentLength) { const jsonValue = this.json[propertyName]; if (jsonValue && Number.isFinite(jsonValue.byteOffset)) { if ("componentType" in jsonValue) { componentType = import_math.GLType.fromName(jsonValue.componentType); } return this._getTypedArrayFromBinary(propertyName, componentType, componentLength, this.featuresLength, jsonValue.byteOffset); } return this._getTypedArrayFromArray(propertyName, componentType, jsonValue); } getProperty(propertyName, componentType, componentLength, featureId, result) { const jsonValue = this.json[propertyName]; if (!jsonValue) { return jsonValue; } const typedArray = this.getPropertyArray(propertyName, componentType, componentLength); if (componentLength === 1) { return typedArray[featureId]; } for (let i = 0; i < componentLength; ++i) { result[i] = typedArray[componentLength * featureId + i]; } return result; } // HELPERS _getTypedArrayFromBinary(propertyName, componentType, componentLength, count, byteOffset) { const cachedTypedArrays = this._cachedTypedArrays; let typedArray = cachedTypedArrays[propertyName]; if (!typedArray) { typedArray = import_math.GLType.createTypedArray(componentType, this.buffer.buffer, this.buffer.byteOffset + byteOffset, count * componentLength); cachedTypedArrays[propertyName] = typedArray; } return typedArray; } _getTypedArrayFromArray(propertyName, componentType, array) { const cachedTypedArrays = this._cachedTypedArrays; let typedArray = cachedTypedArrays[propertyName]; if (!typedArray) { typedArray = import_math.GLType.createTypedArray(componentType, array); cachedTypedArrays[propertyName] = typedArray; } return typedArray; } }; // dist/lib/classes/tile-3d-batch-table.js var import_loader_utils3 = require("@loaders.gl/loader-utils"); // dist/lib/classes/helpers/tile-3d-accessor-utils.js var import_math2 = require("@loaders.gl/math"); var import_loader_utils2 = require("@loaders.gl/loader-utils"); var COMPONENTS_PER_ATTRIBUTE = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 }; var UNPACKER = { SCALAR: (values, i) => values[i], VEC2: (values, i) => [values[2 * i + 0], values[2 * i + 1]], VEC3: (values, i) => [values[3 * i + 0], values[3 * i + 1], values[3 * i + 2]], VEC4: (values, i) => [values[4 * i + 0], values[4 * i + 1], values[4 * i + 2], values[4 * i + 3]], // TODO: check column major MAT2: (values, i) => [ values[4 * i + 0], values[4 * i + 1], values[4 * i + 2], values[4 * i + 3] ], MAT3: (values, i) => [ values[9 * i + 0], values[9 * i + 1], values[9 * i + 2], values[9 * i + 3], values[9 * i + 4], values[9 * i + 5], values[9 * i + 6], values[9 * i + 7], values[9 * i + 8] ], MAT4: (values, i) => [ values[16 * i + 0], values[16 * i + 1], values[16 * i + 2], values[16 * i + 3], values[16 * i + 4], values[16 * i + 5], values[16 * i + 6], values[16 * i + 7], values[16 * i + 8], values[16 * i + 9], values[16 * i + 10], values[16 * i + 11], values[16 * i + 12], values[16 * i + 13], values[16 * i + 14], values[16 * i + 15] ] }; var PACKER = { SCALAR: (x, values, i) => { values[i] = x; }, VEC2: (x, values, i) => { values[2 * i + 0] = x[0]; values[2 * i + 1] = x[1]; }, VEC3: (x, values, i) => { values[3 * i + 0] = x[0]; values[3 * i + 1] = x[1]; values[3 * i + 2] = x[2]; }, VEC4: (x, values, i) => { values[4 * i + 0] = x[0]; values[4 * i + 1] = x[1]; values[4 * i + 2] = x[2]; values[4 * i + 3] = x[3]; }, // TODO: check column major correctness MAT2: (x, values, i) => { values[4 * i + 0] = x[0]; values[4 * i + 1] = x[1]; values[4 * i + 2] = x[2]; values[4 * i + 3] = x[3]; }, MAT3: (x, values, i) => { values[9 * i + 0] = x[0]; values[9 * i + 1] = x[1]; values[9 * i + 2] = x[2]; values[9 * i + 3] = x[3]; values[9 * i + 4] = x[4]; values[9 * i + 5] = x[5]; values[9 * i + 6] = x[6]; values[9 * i + 7] = x[7]; values[9 * i + 8] = x[8]; values[9 * i + 9] = x[9]; }, MAT4: (x, values, i) => { values[16 * i + 0] = x[0]; values[16 * i + 1] = x[1]; values[16 * i + 2] = x[2]; values[16 * i + 3] = x[3]; values[16 * i + 4] = x[4]; values[16 * i + 5] = x[5]; values[16 * i + 6] = x[6]; values[16 * i + 7] = x[7]; values[16 * i + 8] = x[8]; values[16 * i + 9] = x[9]; values[16 * i + 10] = x[10]; values[16 * i + 11] = x[11]; values[16 * i + 12] = x[12]; values[16 * i + 13] = x[13]; values[16 * i + 14] = x[14]; values[16 * i + 15] = x[15]; } }; function createTypedArrayFromAccessor(tile3DAccessor, buffer, byteOffset, length) { const { componentType } = tile3DAccessor; (0, import_loader_utils2.assert)(tile3DAccessor.componentType); const type = typeof componentType === "string" ? import_math2.GLType.fromName(componentType) : componentType; const size = COMPONENTS_PER_ATTRIBUTE[tile3DAccessor.type]; const unpacker = UNPACKER[tile3DAccessor.type]; const packer = PACKER[tile3DAccessor.type]; byteOffset += tile3DAccessor.byteOffset; const values = import_math2.GLType.createTypedArray(type, buffer, byteOffset, size * length); return { values, type, size, unpacker, packer }; } // dist/lib/classes/tile-3d-batch-table-hierarchy.js var defined = (x) => x !== void 0; function initializeHierarchy(batchTable, jsonHeader, binaryBody) { if (!jsonHeader) { return null; } let hierarchy = batchTable.getExtension("3DTILES_batch_table_hierarchy"); const legacyHierarchy = jsonHeader.HIERARCHY; if (legacyHierarchy) { console.warn("3D Tile Parser: HIERARCHY is deprecated. Use 3DTILES_batch_table_hierarchy."); jsonHeader.extensions = jsonHeader.extensions || {}; jsonHeader.extensions["3DTILES_batch_table_hierarchy"] = legacyHierarchy; hierarchy = legacyHierarchy; } if (!hierarchy) { return null; } return initializeHierarchyValues(hierarchy, binaryBody); } function initializeHierarchyValues(hierarchyJson, binaryBody) { let i; let classId; let binaryAccessor; const instancesLength = hierarchyJson.instancesLength; const classes = hierarchyJson.classes; let classIds = hierarchyJson.classIds; let parentCounts = hierarchyJson.parentCounts; let parentIds = hierarchyJson.parentIds; let parentIdsLength = instancesLength; if (defined(classIds.byteOffset)) { classIds.componentType = defaultValue(classIds.componentType, GL.UNSIGNED_SHORT); classIds.type = AttributeType.SCALAR; binaryAccessor = getBinaryAccessor(classIds); classIds = binaryAccessor.createArrayBufferView(binaryBody.buffer, binaryBody.byteOffset + classIds.byteOffset, instancesLength); } let parentIndexes; if (defined(parentCounts)) { if (defined(parentCounts.byteOffset)) { parentCounts.componentType = defaultValue(parentCounts.componentType, GL.UNSIGNED_SHORT); parentCounts.type = AttributeType.SCALAR; binaryAccessor = getBinaryAccessor(parentCounts); parentCounts = binaryAccessor.createArrayBufferView(binaryBody.buffer, binaryBody.byteOffset + parentCounts.byteOffset, instancesLength); } parentIndexes = new Uint16Array(instancesLength); parentIdsLength = 0; for (i = 0; i < instancesLength; ++i) { parentIndexes[i] = parentIdsLength; parentIdsLength += parentCounts[i]; } } if (defined(parentIds) && defined(parentIds.byteOffset)) { parentIds.componentType = defaultValue(parentIds.componentType, GL.UNSIGNED_SHORT); parentIds.type = AttributeType.SCALAR; binaryAccessor = getBinaryAccessor(parentIds); parentIds = binaryAccessor.createArrayBufferView(binaryBody.buffer, binaryBody.byteOffset + parentIds.byteOffset, parentIdsLength); } const classesLength = classes.length; for (i = 0; i < classesLength; ++i) { const classInstancesLength = classes[i].length; const properties = classes[i].instances; const binaryProperties = getBinaryProperties(classInstancesLength, properties, binaryBody); classes[i].instances = combine(binaryProperties, properties); } const classCounts = new Array(classesLength).fill(0); const classIndexes = new Uint16Array(instancesLength); for (i = 0; i < instancesLength; ++i) { classId = classIds[i]; classIndexes[i] = classCounts[classId]; ++classCounts[classId]; } const hierarchy = { classes, classIds, classIndexes, parentCounts, parentIndexes, parentIds }; validateHierarchy(hierarchy); return hierarchy; } function traverseHierarchy(hierarchy, instanceIndex, endConditionCallback) { if (!hierarchy) { return; } const parentCounts = hierarchy.parentCounts; const parentIds = hierarchy.parentIds; if (parentIds) { return endConditionCallback(hierarchy, instanceIndex); } if (parentCounts > 0) { return traverseHierarchyMultipleParents(hierarchy, instanceIndex, endConditionCallback); } return traverseHierarchySingleParent(hierarchy, instanceIndex, endConditionCallback); } function traverseHierarchyMultipleParents(hierarchy, instanceIndex, endConditionCallback) { const classIds = hierarchy.classIds; const parentCounts = hierarchy.parentCounts; const parentIds = hierarchy.parentIds; const parentIndexes = hierarchy.parentIndexes; const instancesLength = classIds.length; const visited = scratchVisited; visited.length = Math.max(visited.length, instancesLength); const visitedMarker = ++marker; const stack2 = scratchStack; stack2.length = 0; stack2.push(instanceIndex); while (stack2.length > 0) { instanceIndex = stack2.pop(); if (visited[instanceIndex] === visitedMarker) { continue; } visited[instanceIndex] = visitedMarker; const result = endConditionCallback(hierarchy, instanceIndex); if (defined(result)) { return result; } const parentCount = parentCounts[instanceIndex]; const parentIndex = parentIndexes[instanceIndex]; for (let i = 0; i < parentCount; ++i) { const parentId = parentIds[parentIndex + i]; if (parentId !== instanceIndex) { stack2.push(parentId); } } } return null; } function traverseHierarchySingleParent(hierarchy, instanceIndex, endConditionCallback) { let hasParent = true; while (hasParent) { const result = endConditionCallback(hierarchy, instanceIndex); if (defined(result)) { return result; } const parentId = hierarchy.parentIds[instanceIndex]; hasParent = parentId !== instanceIndex; instanceIndex = parentId; } throw new Error("traverseHierarchySingleParent"); } function validateHierarchy(hierarchy) { const scratchValidateStack = []; const classIds = hierarchy.classIds; const instancesLength = classIds.length; for (let i = 0; i < instancesLength; ++i) { validateInstance(hierarchy, i, stack); } } function validateInstance(hierarchy, instanceIndex, stack2) { const parentCounts = hierarchy.parentCounts; const parentIds = hierarchy.parentIds; const parentIndexes = hierarchy.parentIndexes; const classIds = hierarchy.classIds; const instancesLength = classIds.length; if (!defined(parentIds)) { return; } assert(instanceIndex < instancesLength, `Parent index ${instanceIndex} exceeds the total number of instances: ${instancesLength}`); assert(stack2.indexOf(instanceIndex) === -1, "Circular dependency detected in the batch table hierarchy."); stack2.push(instanceIndex); const parentCount = defined(parentCounts) ? parentCounts[instanceIndex] : 1; const parentIndex = defined(parentCounts) ? parentIndexes[instanceIndex] : instanceIndex; for (let i = 0; i < parentCount; ++i) { const parentId = parentIds[parentIndex + i]; if (parentId !== instanceIndex) { validateInstance(hierarchy, parentId, stack2); } } stack2.pop(instanceIndex); } // dist/lib/classes/tile-3d-batch-table.js function defined2(x) { return x !== void 0 && x !== null; } var clone = (x, y) => x; var IGNORED_PROPERTY_FIELDS = { HIERARCHY: true, // Deprecated HIERARCHY property extensions: true, extras: true }; var Tile3DBatchTableParser = class { json; binary; featureCount; _extensions; // Copy all top-level property fields from the json object, ignoring special fields _properties; _binaryProperties; // TODO: hierarchy support is only partially implemented and not tested _hierarchy; constructor(json, binary, featureCount, options = {}) { var _a; (0, import_loader_utils3.assert)(featureCount >= 0); this.json = json || {}; this.binary = binary; this.featureCount = featureCount; this._extensions = ((_a = this.json) == null ? void 0 : _a.extensions) || {}; this._properties = {}; for (const propertyName in this.json) { if (!IGNORED_PROPERTY_FIELDS[propertyName]) { this._properties[propertyName] = this.json[propertyName]; } } this._binaryProperties = this._initializeBinaryProperties(); if (options["3DTILES_batch_table_hierarchy"]) { this._hierarchy = initializeHierarchy(this, this.json, this.binary); } } getExtension(extensionName) { return this.json && this.json.extensions && this.json.extensions[extensionName]; } memorySizeInBytes() { return 0; } isClass(batchId, className) { this._checkBatchId(batchId); (0, import_loader_utils3.assert)(typeof className === "string", className); if (this._hierarchy) { const result = traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => { const classId = hierarchy.classIds[instanceIndex]; const instanceClass = hierarchy.classes[classId]; return instanceClass.name === className; }); return defined2(result); } return false; } isExactClass(batchId, className) { (0, import_loader_utils3.assert)(typeof className === "string", className); return this.getExactClassName(batchId) === className; } getExactClassName(batchId) { this._checkBatchId(batchId); if (this._hierarchy) { const classId = this._hierarchy.classIds[batchId]; const instanceClass = this._hierarchy.classes[classId]; return instanceClass.name; } return void 0; } hasProperty(batchId, name) { this._checkBatchId(batchId); (0, import_loader_utils3.assert)(typeof name === "string", name); return defined2(this._properties[name]) || this._hasPropertyInHierarchy(batchId, name); } getPropertyNames(batchId, results) { this._checkBatchId(batchId); results = defined2(results) ? results : []; results.length = 0; const propertyNames = Object.keys(this._properties); results.push(...propertyNames); if (this._hierarchy) { this._getPropertyNamesInHierarchy(batchId, results); } return results; } getProperty(batchId, name) { this._checkBatchId(batchId); (0, import_loader_utils3.assert)(typeof name === "string", name); if (this._binaryProperties) { const binaryProperty = this._binaryProperties[name]; if (defined2(binaryProperty)) { return this._getBinaryProperty(binaryProperty, batchId); } } const propertyValues = this._properties[name]; if (defined2(propertyValues)) { return clone(propertyValues[batchId], true); } if (this._hierarchy) { const hierarchyProperty = this._getHierarchyProperty(batchId, name); if (defined2(hierarchyProperty)) { return hierarchyProperty; } } return void 0; } setProperty(batchId, name, value) { const featureCount = this.featureCount; this._checkBatchId(batchId); (0, import_loader_utils3.assert)(typeof name === "string", name); if (this._binaryProperties) { const binaryProperty = this._binaryProperties[name]; if (binaryProperty) { this._setBinaryProperty(binaryProperty, batchId, value); return; } } if (this._hierarchy) { if (this._setHierarchyProperty(this, batchId, name, value)) { return; } } let propertyValues = this._properties[name]; if (!defined2(propertyValues)) { this._properties[name] = new Array(featureCount); propertyValues = this._properties[name]; } propertyValues[batchId] = clone(value, true); } // PRIVATE METHODS _checkBatchId(batchId) { const valid = batchId >= 0 && batchId < this.featureCount; if (!valid) { throw new Error("batchId not in range [0, featureCount - 1]."); } } _getBinaryProperty(binaryProperty, index) { return binaryProperty.unpack(binaryProperty.typedArray, index); } _setBinaryProperty(binaryProperty, index, value) { binaryProperty.pack(value, binaryProperty.typedArray, index); } _initializeBinaryProperties() { let binaryProperties = null; for (const name in this._properties) { const property = this._properties[name]; const binaryProperty = this._initializeBinaryProperty(name, property); if (binaryProperty) { binaryProperties = binaryProperties || {}; binaryProperties[name] = binaryProperty; } } return binaryProperties; } _initializeBinaryProperty(name, property) { if ("byteOffset" in property) { const tile3DAccessor = property; (0, import_loader_utils3.assert)(this.binary, `Property ${name} requires a batch table binary.`); (0, import_loader_utils3.assert)(tile3DAccessor.type, `Property ${name} requires a type.`); const accessor = createTypedArrayFromAccessor(tile3DAccessor, this.binary.buffer, this.binary.byteOffset | 0, this.featureCount); return { typedArray: accessor.values, componentCount: accessor.size, unpack: accessor.unpacker, pack: accessor.packer }; } return null; } // EXTENSION SUPPORT: 3DTILES_batch_table_hierarchy _hasPropertyInHierarchy(batchId, name) { if (!this._hierarchy) { return false; } const result = traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => { const classId = hierarchy.classIds[instanceIndex]; const instances = hierarchy.classes[classId].instances; return defined2(instances[name]); }); return defined2(result); } _getPropertyNamesInHierarchy(batchId, results) { traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => { const classId = hierarchy.classIds[instanceIndex]; const instances = hierarchy.classes[classId].instances; for (const name in instances) { if (instances.hasOwnProperty(name)) { if (results.indexOf(name) === -1) { results.push(name); } } } }); } _getHierarchyProperty(batchId, name) { return traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => { const classId = hierarchy.classIds[instanceIndex]; const instanceClass = hierarchy.classes[classId]; const indexInClass = hierarchy.classIndexes[instanceIndex]; const propertyValues = instanceClass.instances[name]; if (defined2(propertyValues)) { if (defined2(propertyValues.typedArray)) { return this._getBinaryProperty(propertyValues, indexInClass); } return clone(propertyValues[indexInClass], true); } return null; }); } _setHierarchyProperty(batchTable, batchId, name, value) { const result = traverseHierarchy(this._hierarchy, batchId, (hierarchy, instanceIndex) => { const classId = hierarchy.classIds[instanceIndex]; const instanceClass = hierarchy.classes[classId]; const indexInClass = hierarchy.classIndexes[instanceIndex]; const propertyValues = instanceClass.instances[name]; if (defined2(propertyValues)) { (0, import_loader_utils3.assert)(instanceIndex === batchId, `Inherited property "${name}" is read-only.`); if (defined2(propertyValues.typedArray)) { this._setBinaryProperty(propertyValues, indexInClass, value); } else { propertyValues[indexInClass] = clone(value, true); } return true; } return false; }); return defined2(result); } }; // dist/lib/parsers/helpers/parse-3d-tile-header.js var SIZEOF_UINT32 = 4; function parse3DTileHeaderSync(tile, arrayBuffer, byteOffset = 0) { const view = new DataView(arrayBuffer); tile.magic = view.getUint32(byteOffset, true); byteOffset += SIZEOF_UINT32; tile.version = view.getUint32(byteOffset, true); byteOffset += SIZEOF_UINT32; tile.byteLength = view.getUint32(byteOffset, true); byteOffset += SIZEOF_UINT32; if (tile.version !== 1) { throw new Error(`3D Tile Version ${tile.version} not supported`); } return byteOffset; } // dist/lib/parsers/helpers/parse-3d-tile-tables.js var SIZEOF_UINT322 = 4; var DEPRECATION_WARNING = "b3dm tile in legacy format."; function parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset) { const view = new DataView(arrayBuffer); let batchLength; tile.header = tile.header || {}; let featureTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += SIZEOF_UINT322; let featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += SIZEOF_UINT322; let batchTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += SIZEOF_UINT322; let batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += SIZEOF_UINT322; if (batchTableJsonByteLength >= 570425344) { byteOffset -= SIZEOF_UINT322 * 2; batchLength = featureTableJsonByteLength; batchTableJsonByteLength = featureTableBinaryByteLength; batchTableBinaryByteLength = 0; featureTableJsonByteLength = 0; featureTableBinaryByteLength = 0; console.warn(DEPRECATION_WARNING); } else if (batchTableBinaryByteLength >= 570425344) { byteOffset -= SIZEOF_UINT322; batchLength = batchTableJsonByteLength; batchTableJsonByteLength = featureTableJsonByteLength; batchTableBinaryByteLength = featureTableBinaryByteLength; featureTableJsonByteLength = 0; featureTableBinaryByteLength = 0; console.warn(DEPRECATION_WARNING); } tile.header.featureTableJsonByteLength = featureTableJsonByteLength; tile.header.featureTableBinaryByteLength = featureTableBinaryByteLength; tile.header.batchTableJsonByteLength = batchTableJsonByteLength; tile.header.batchTableBinaryByteLength = batchTableBinaryByteLength; tile.header.batchLength = batchLength; return byteOffset; } function parse3DTileTablesSync(tile, arrayBuffer, byteOffset, options) { byteOffset = parse3DTileFeatureTable(tile, arrayBuffer, byteOffset, options); byteOffset = parse3DTileBatchTable(tile, arrayBuffer, byteOffset, options); return byteOffset; } function parse3DTileFeatureTable(tile, arrayBuffer, byteOffset, options) { const { featureTableJsonByteLength, featureTableBinaryByteLength, batchLength } = tile.header || {}; tile.featureTableJson = { BATCH_LENGTH: batchLength || 0 }; if (featureTableJsonByteLength && featureTableJsonByteLength > 0) { const featureTableString = getStringFromArrayBuffer(arrayBuffer, byteOffset, featureTableJsonByteLength); tile.featureTableJson = JSON.parse(featureTableString); } byteOffset += featureTableJsonByteLength || 0; tile.featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength); byteOffset += featureTableBinaryByteLength || 0; return byteOffset; } function parse3DTileBatchTable(tile, arrayBuffer, byteOffset, options) { const { batchTableJsonByteLength, batchTableBinaryByteLength } = tile.header || {}; if (batchTableJsonByteLength && batchTableJsonByteLength > 0) { const batchTableString = getStringFromArrayBuffer(arrayBuffer, byteOffset, batchTableJsonByteLength); tile.batchTableJson = JSON.parse(batchTableString); byteOffset += batchTableJsonByteLength; if (batchTableBinaryByteLength && batchTableBinaryByteLength > 0) { tile.batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength); tile.batchTableBinary = new Uint8Array(tile.batchTableBinary); byteOffset += batchTableBinaryByteLength; } } return byteOffset; } // dist/lib/parsers/helpers/normalize-3d-tile-colors.js var import_math3 = require("@loaders.gl/math"); function normalize3DTileColorAttribute(tile, colors, batchTable) { if (!colors && (!tile || !tile.batchIds || !batchTable)) { return null; } const { batchIds, isRGB565, pointCount = 0 } = tile; if (batchIds && batchTable) { const colorArray = new Uint8ClampedArray(pointCount * 3); for (let i = 0; i < pointCount; i++) { const batchId = batchIds[i]; const dimensions = batchTable.getProperty(batchId, "dimensions"); const color = dimensions.map((d) => d * 255); colorArray[i * 3] = color[0]; colorArray[i * 3 + 1] = color[1]; colorArray[i * 3 + 2] = color[2]; } return { type: import_math3.GL.UNSIGNED_BYTE, value: colorArray, size: 3, normalized: true }; } if (colors && isRGB565) { const colorArray = new Uint8ClampedArray(pointCount * 3); for (let i = 0; i < pointCount; i++) { const color = (0, import_math3.decodeRGB565)(colors[i]); colorArray[i * 3] = color[0]; colorArray[i * 3 + 1] = color[1]; colorArray[i * 3 + 2] = color[2]; } return { type: import_math3.GL.UNSIGNED_BYTE, value: colorArray, size: 3, normalized: true }; } if (colors && colors.length === pointCount * 3) { return { type: import_math3.GL.UNSIGNED_BYTE, value: colors, size: 3, normalized: true }; } return { type: import_math3.GL.UNSIGNED_BYTE, value: colors || new Uint8ClampedArray(), size: 4, normalized: true }; } // dist/lib/parsers/helpers/normalize-3d-tile-normals.js var import_core = require("@math.gl/core"); var import_math4 = require("@loaders.gl/math"); var scratchNormal = new import_core.Vector3(); function normalize3DTileNormalAttribute(tile, normals) { if (!normals) { return null; } if (tile.isOctEncoded16P) { const decodedArray = new Float32Array((tile.pointsLength || 0) * 3); for (let i = 0; i < (tile.pointsLength || 0); i++) { (0, import_math4.octDecode)(normals[i * 2], normals[i * 2 + 1], scratchNormal); scratchNormal.toArray(decodedArray, i * 3); } return { type: import_math4.GL.FLOAT, size: 2, value: decodedArray }; } return { type: import_math4.GL.FLOAT, size: 2, value: normals }; } // dist/lib/parsers/helpers/normalize-3d-tile-positions.js var import_core2 = require("@math.gl/core"); var import_math5 = require("@loaders.gl/math"); function normalize3DTilePositionAttribute(tile, positions, options) { if (!tile.isQuantized) { return positions; } if (options["3d-tiles"] && options["3d-tiles"].decodeQuantizedPositions) { tile.isQuantized = false; return decodeQuantizedPositions(tile, positions); } return { type: import_math5.GL.UNSIGNED_SHORT, value: positions, size: 3, normalized: true }; } function decodeQuantizedPositions(tile, positions) { const scratchPosition = new import_core2.Vector3(); const decodedArray = new Float32Array(tile.pointCount * 3); for (let i = 0; i < tile.pointCount; i++) { scratchPosition.set(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]).scale(1 / tile.quantizedRange).multiply(tile.quantizedVolumeScale).add(tile.quantizedVolumeOffset).toArray(decodedArray, i * 3); } return decodedArray; } // dist/lib/parsers/parse-3d-tile-point-cloud.js async function parsePointCloud3DTile(tile, arrayBuffer, byteOffset, options, context) { byteOffset = parse3DTileHeaderSync(tile, arrayBuffer, byteOffset); byteOffset = parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset); byteOffset = parse3DTileTablesSync(tile, arrayBuffer, byteOffset, options); initializeTile(tile); const { featureTable, batchTable } = parsePointCloudTables(tile); await parseDraco(tile, featureTable, batchTable, options, context); parsePositions(tile, featureTable, options); parseColors(tile, featureTable, batchTable); parseNormals(tile, featureTable); return byteOffset; } function initializeTile(tile) { tile.attributes = { positions: null, colors: null, normals: null, batchIds: null }; tile.isQuantized = false; tile.isTranslucent = false; tile.isRGB565 = false; tile.isOctEncoded16P = false; } function parsePointCloudTables(tile) { const featureTable = new Tile3DFeatureTable(tile.featureTableJson, tile.featureTableBinary); const pointsLength = featureTable.getGlobalProperty("POINTS_LENGTH"); if (!Number.isFinite(pointsLength)) { throw new Error("POINTS_LENGTH must be defined"); } featureTable.featuresLength = pointsLength; tile.featuresLength = pointsLength; tile.pointsLength = pointsLength; tile.pointCount = pointsLength; tile.rtcCenter = featureTable.getGlobalProperty("RTC_CENTER", import_math6.GL.FLOAT, 3); const batchTable = parseBatchIds(tile, featureTable); return { featureTable, batchTable }; } function parsePositions(tile, featureTable, options) { tile.attributes = tile.attributes || { positions: null, colors: null, normals: null, batchIds: null }; if (!tile.attributes.positions) { if (featureTable.hasProperty("POSITION")) { tile.attributes.positions = featureTable.getPropertyArray("POSITION", import_math6.GL.FLOAT, 3); } else if (featureTable.hasProperty("POSITION_QUANTIZED")) { const positions = featureTable.getPropertyArray("POSITION_QUANTIZED", import_math6.GL.UNSIGNED_SHORT, 3); tile.isQuantized = true; tile.quantizedRange = (1 << 16) - 1; tile.quantizedVolumeScale = featureTable.getGlobalProperty("QUANTIZED_VOLUME_SCALE", import_math6.GL.FLOAT, 3); if (!tile.quantizedVolumeScale) { throw new Error("QUANTIZED_VOLUME_SCALE must be defined for quantized positions."); } tile.quantizedVolumeOffset = featureTable.getGlobalProperty("QUANTIZED_VOLUME_OFFSET", import_math6.GL.FLOAT, 3); if (!tile.quantizedVolumeOffset) { throw new Error("QUANTIZED_VOLUME_OFFSET must be defined for quantized positions."); } tile.attributes.positions = normalize3DTilePositionAttribute(tile, positions, options); } } if (!tile.attributes.positions) { throw new Error("Either POSITION or POSITION_QUANTIZED must be defined."); } } function parseColors(tile, featureTable, batchTable) { tile.attributes = tile.attributes || { positions: null, colors: null, normals: null, batchIds: null }; if (!tile.attributes.colors) { let colors = null; if (featureTable.hasProperty("RGBA")) { colors = featureTable.getPropertyArray("RGBA", import_math6.GL.UNSIGNED_BYTE, 4); tile.isTranslucent = true; } else if (featureTable.hasProperty("RGB")) { colors = featureTable.getPropertyArray("RGB", import_math6.GL.UNSIGNED_BYTE, 3); } else if (featureTable.hasProperty("RGB565")) { colors = featureTable.getPropertyArray("RGB565", import_math6.GL.UNSIGNED_SHORT, 1); tile.isRGB565 = true; } tile.attributes.colors = normalize3DTileColorAttribute(tile, colors, batchTable); } if (featureTable.hasProperty("CONSTANT_RGBA")) { tile.constantRGBA = featureTable.getGlobalProperty("CONSTANT_RGBA", import_math6.GL.UNSIGNED_BYTE, 4); } } function parseNormals(tile, featureTable) { tile.attributes = tile.attributes || { positions: null, colors: null, normals: null, batchIds: null }; if (!tile.attributes.normals) { let normals = null; if (featureTable.hasProperty("NORMAL")) { normals = featureTable.getPropertyArray("NORMAL", import_math6.GL.FLOAT, 3); } else if (featureTable.hasProperty("NORMAL_OCT16P")) { normals = featureTable.getPropertyArray("NORMAL_OCT16P", import_math6.GL.UNSIGNED_BYTE, 2); tile.isOctEncoded16P = true; } tile.attributes.normals = normalize3DTileNormalAttribute(tile, normals); } } function parseBatchIds(tile, featureTable) { let batchTable = null; if (!tile.batchIds && featureTable.hasProperty("BATCH_ID")) { tile.batchIds = featureTable.getPropertyArray("BATCH_ID", import_math6.GL.UNSIGNED_SHORT, 1); if (tile.batchIds) { const batchFeatureLength = featureTable.getGlobalProperty("BATCH_LENGTH"); if (!batchFeatureLength) { throw new Error("Global property: BATCH_LENGTH must be defined when BATCH_ID is defined."); } const { batchTableJson, batchTableBinary } = tile; batchTable = new Tile3DBatchTableParser(batchTableJson, batchTableBinary, batchFeatureLength); } } return batchTable; } async function parseDraco(tile, featureTable, batchTable, options, context) { let dracoBuffer; let dracoFeatureTableProperties; let dracoBatchTableProperties; const batchTableDraco = tile.batchTableJson && tile.batchTableJson.extensions && tile.batchTableJson.extensions["3DTILES_draco_point_compression"]; if (batchTableDraco) { dracoBatchTableProperties = batchTableDraco.properties; } const featureTableDraco = featureTable.getExtension("3DTILES_draco_point_compression"); if (featureTableDraco) { dracoFeatureTableProperties = featureTableDraco.properties; const dracoByteOffset = featureTableDraco.byteOffset; const dracoByteLength = featureTableDraco.byteLength; if (!dracoFeatureTableProperties || !Number.isFinite(dracoByteOffset) || !dracoByteLength) { throw new Error("Draco properties, byteOffset, and byteLength must be defined"); } dracoBuffer = (tile.featureTableBinary || []).slice(dracoByteOffset, dracoByteOffset + dracoByteLength); tile.hasPositions = Number.isFinite(dracoFeatureTableProperties.POSITION); tile.hasColors = Number.isFinite(dracoFeatureTableProperties.RGB) || Number.isFinite(dracoFeatureTableProperties.RGBA); tile.hasNormals = Number.isFinite(dracoFeatureTableProperties.NORMAL); tile.hasBatchIds = Number.isFinite(dracoFeatureTableProperties.BATCH_ID); tile.isTranslucent = Number.isFinite(dracoFeatureTableProperties.RGBA); } if (!dracoBuffer) { return true; } const dracoData = { buffer: dracoBuffer, properties: { ...dracoFeatureTableProperties, ...dracoBatchTableProperties }, featureTableProperties: dracoFeatureTableProperties, batchTableProperties: dracoBatchTableProperties, dequantizeInShader: false }; return await loadDraco(tile, dracoData, options, context); } async function loadDraco(tile, dracoData, options, context) { if (!context) { return; } const dracoOptions = { ...options, draco: { ...options == null ? void 0 : options.draco, extraAttributes: dracoData.batchTableProperties || {} } }; delete dracoOptions["3d-tiles"]; const data = await (0, import_loader_utils4.parseFromContext)(dracoData.buffer, import_draco.DracoLoader, dracoOptions, context); const decodedPositions = data.attributes.POSITION && data.attributes.POSITION.value; const decodedColors = data.attributes.COLOR_0 && data.attributes.COLOR_0.value; const decodedNormals = data.attributes.NORMAL && data.attributes.NORMAL.value; const decodedBatchIds = data.attributes.BATCH_ID && data.attributes.BATCH_ID.value; const isQuantizedDraco = decodedPositions && data.attributes.POSITION.value.quantization; const isOctEncodedDraco = decodedNormals && data.attributes.NORMAL.value.quantization; if (isQuantizedDraco) { const quantization = data.POSITION.data.quantization; const range = quantization.range; tile.quantizedVolumeScale = new import_core3.Vector3(range, range, range); tile.quantizedVolumeOffset = new import_core3.Vector3(quantization.minValues); tile.quantizedRange = (1 << quantization.quantizationBits) - 1; tile.isQuantizedDraco = true; } if (isOctEncodedDraco) { tile.octEncodedRange = (1 << data.NORMAL.data.quantization.quantizationBits) - 1; tile.isOctEncodedDraco = true; } const batchTableAttributes = {}; if (dracoData.batchTableProperties) { for (const attributeName of Object.keys(dracoData.batchTableProperties)) { if (data.attributes[attributeName] && data.attributes[attributeName].value) { batchTableAttributes[attributeName.toLowerCase()] = data.attributes[attributeName].value; } } } tile.attributes = { // @ts-expect-error positions: decodedPositions, // @ts-expect-error colors: normalize3DTileColorAttribute(tile, decodedColors, void 0), // @ts-expect-error normals: decodedNormals, // @ts-expect-error batchIds: decodedBatchIds, ...batchTableAttributes }; } // dist/lib/parsers/parse-3d-tile-batched-model.js var import_math7 = require("@loaders.gl/math"); // dist/lib/parsers/helpers/parse-3d-tile-gltf-view.js var import_gltf = require("@loaders.gl/gltf"); var import_loader_utils5 = require("@loaders.gl/loader-utils"); var GLTF_FORMAT = { URI: 0, EMBEDDED: 1 }; function parse3DTileGLTFViewSync(tile, arrayBuffer, byteOffset, options) { tile.rotateYtoZ = true; const gltfByteLength = (tile.byteOffset || 0) + (tile.byteLength || 0) - byteOffset; if (gltfByteLength === 0) { throw new Error("glTF byte length must be greater than 0."); } tile.gltfUpAxis = (options == null ? void 0 : options["3d-tiles"]) && options["3d-tiles"].assetGltfUpAxis ? options["3d-tiles"].assetGltfUpAxis : "Y"; tile.gltfArrayBuffer = (0, import_loader_utils5.sliceArrayBuffer)(arrayBuffer, byteOffset, gltfByteLength); tile.gltfByteOffset = 0; tile.gltfByteLength = gltfByteLength; if (byteOffset % 4 === 0) { } else { console.warn(`${tile.type}: embedded glb is not aligned to a 4-byte boundary.`); } return (tile.byteOffset || 0) + (tile.byteLength || 0); } async function extractGLTF(tile, gltfFormat, options, context) { const tile3DOptions = (options == null ? void 0 : options["3d-tiles"]) || {}; extractGLTFBufferOrURL(tile, gltfFormat, options); if (tile3DOptions.loadGLTF) { if (!context) { return; } if (tile.gltfUrl) { const { fetch } = context; const response = await fetch(tile.gltfUrl, options); tile.gltfArrayBuffer = await response.arrayBuffer(); tile.gltfByteOffset = 0; } if (tile.gltfArrayBuffer) { const gltfWithBuffers = await (0, import_loader_utils5.parseFromContext)(tile.gltfArrayBuffer, import_gltf.GLTFLoader, options, context); tile.gltf = (0, import_gltf.postProcessGLTF)(gltfWithBuffers); tile.gpuMemoryUsageInBytes = (0, import_gltf._getMemoryUsageGLTF)(tile.gltf); delete tile.gltfArrayBuffer; delete tile.gltfByteOffset; delete tile.gltfByteLength; } } } function extractGLTFBufferOrURL(tile, gltfFormat, options) { switch (gltfFormat) { case GLTF_FORMAT.URI: if (tile.gltfArrayBuffer) { const gltfUrlBytes = new Uint8Array(tile.gltfArrayBuffer, tile.gltfByteOffset); const textDecoder = new TextDecoder(); const gltfUrl = textDecoder.decode(gltfUrlBytes); tile.gltfUrl = gltfUrl.replace(/[\s\0]+$/, ""); } delete tile.gltfArrayBuffer; delete tile.gltfByteOffset; delete tile.gltfByteLength; break; case GLTF_FORMAT.EMBEDDED: break; default: throw new Error("b3dm: Illegal glTF format field"); } } // dist/lib/parsers/parse-3d-tile-batched-model.js async function parseBatchedModel3DTile(tile, arrayBuffer, byteOffset, options, context) { var _a; byteOffset = parseBatchedModel(tile, arrayBuffer, byteOffset, options, context); await extractGLTF(tile, GLTF_FORMAT.EMBEDDED, options, context); const extensions = (_a = tile == null ? void 0 : tile.gltf) == null ? void 0 : _a.extensions; if (extensions && extensions.CESIUM_RTC) { tile.rtcCenter = extensions.CESIUM_RTC.center; } return byteOffset; } function parseBatchedModel(tile, arrayBuffer, byteOffset, options, context) { byteOffset = parse3DTileHeaderSync(tile, arrayBuffer, byteOffset); byteOffset = parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset); byteOffset = parse3DTileTablesSync(tile, arrayBuffer, byteOffset, options); byteOffset = parse3DTileGLTFViewSync(tile, arrayBuffer, byteOffset, options); const featureTable = new Tile3DFeatureTable(tile.featureTableJson, tile.featureTableBinary); tile.rtcCenter = featureTable.getGlobalProperty("RTC_CENTER", import_math7.GL.FLOAT, 3); return byteOffset; } // dist/lib/parsers/parse-3d-tile-instanced-model.js var import_core4 = require("@math.gl/core"); var import_geospatial = require("@math.gl/geospatial"); var import_math8 = require("@loaders.gl/math"); async function parseInstancedModel3DTile(tile, arrayBuffer, byteOffset, options, context) { byteOffset = parseInstancedModel(tile, arrayBuffer, byteOffset, options, context); await extractGLTF(tile, tile.gltfFormat || 0, options, context); return byteOffset; } function parseInstancedModel(tile, arrayBuffer, byteOffset, options, context) { var _a; byteOffset = parse3DTileHeaderSync(tile, arrayBuffer, byteOffset); if (tile.version !== 1) { throw new Error(`Instanced 3D Model version ${tile.version} is not supported`); } byteOffset = parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset); const view = new DataView(arrayBuffer); tile.gltfFormat = view.getUint32(byteOffset, true); byteOffset += 4; byteOffset = parse3DTileTablesSync(tile, arrayBuffer, byteOffset, options); byteOffset = parse3DTileGLTFViewSync(tile, arrayBuffer, byteOffset, options); if (!((_a = tile == null ? void 0 : tile.header) == null ? void 0 : _a.featureTableJsonByteLength) || tile.header.featureTableJsonByteLength === 0) { throw new Error("i3dm parser: featureTableJsonByteLength is zero."); } const featureTable = new Tile3DFeatureTable(tile.featureTableJson, tile.featureTableBinary); const instancesLength = featureTable.getGlobalProperty("INSTANCES_LENGTH"); featureTable.featuresLength = instancesLength; if (!Number.isFinite(instancesLength)) { throw new Error("i3dm parser: INSTANCES_LENGTH must be defined"); } tile.eastNorthUp = featureTable.getGlobalProperty("EAST_NORTH_UP"); tile.rtcCenter = featureTable.getGlobalProperty("RTC_CENTER", import_math8.GL.FLOAT, 3); const batchTable = new Tile3DBatchTableParser(tile.batchTableJson, tile.batchTableBinary, instancesLength); extractInstancedAttributes(tile, featureTable, batchTable, instancesLength); return byteOffset; } function extractInstancedAttributes(tile, featureTable, batchTable, instancesLength) { const instances = new Array(instancesLength); const instancePosition = new import_core4.Vector3(); const instanceNormalRight = new import_core4.Vector3(); const instanceNormalUp = new import_core4.Vector3(); const instanceNormalForward = new import_core4.Vector3(); const instanceRotation = new import_core4.Matrix3(); const instanceQuaternion = new import_core4.Quaternion(); const instanceScale = new import_core4.Vector3(); const instanceTranslationRotationScale = {}; const instanceTransform = new import_core4.Matrix4(); const scratch1 = []; const scratch2 = []; const scratch3 = []; const scratch4 = []; for (let i = 0; i < instancesLength; i++) { let position; if (featureTable.hasProperty("POSITION")) { position = featureTable.getProperty("POSITION", import_math8.GL.FLOAT, 3, i, instancePosition); } else if (featureTable.hasProperty("POSITION_QUANTIZED")) { position = featureTable.getProperty("POSITION_QUANTIZED", import_math8.GL.UNSIGNED_SHORT, 3, i, instancePosition); const quantizedVolumeOffset = featureTable.getGlobalProperty("QUANTIZED_VOLUME_OFFSET", import_math8.GL.FLOAT, 3); if (!quantizedVolumeOffset) { throw new Error("i3dm parser: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions."); } const quantizedVolumeScale = featureTable.getGlobalProperty("QUANTIZED_VOLUME_SCALE", import_math8.GL.FLOAT, 3); if (!quantizedVolumeScale) { throw new Error("i3dm parser: QUANTIZED_VOLUME_SCALE must be defined for quantized positions."); } const MAX_UNSIGNED_SHORT = 65535; for (let j = 0; j < 3; j++) { position[j] = position[j] / MAX_UNSIGNED_SHORT * quantizedVolumeScale[j] + quantizedVolumeOffset[j]; } } if (!position) { throw new Error("i3dm: POSITION or POSITION_QUANTIZED must be defined for each instance."); } instancePosition.copy(position); instanceTranslationRotationScale.translation = instancePosition; tile.normalUp = featureTable.getProperty("NORMAL_UP", import_math8.GL.FLOAT, 3, i, scratch1); tile.normalRight = featureTable.getProperty("NORMAL_RIGHT", import_math8.GL.FLOAT, 3, i, scratch2); const hasCustomOrientation = false; if (tile.normalUp) { if (!tile.normalRight) { throw new Error("i3dm: Custom orientation requires both NORMAL_UP and NORMAL_RIGHT."); } tile.hasCustomOrientation = true; } else