UNPKG

@itwin/core-frontend

Version:
169 lines 9.74 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Tiles */ import { JsonUtils } from "@itwin/core-bentley"; import { Transform, Vector3d } from "@itwin/core-geometry"; import { B3dmHeader, ColorDef, Feature, FeatureTable, TileReadStatus } from "@itwin/core-common"; import { GltfDataType } from "../../common/gltf/GltfSchema"; import { GltfReader, GltfReaderProps, } from "../../tile/internal"; /** * Deserializes a tile in [b3dm](https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification/TileFormats/Batched3DModel) format. */ export class B3dmReader extends GltfReader { _range; _isLeaf; _batchTableLength; _transformToRoot; _batchTableJson; _pseudoRtcBias; _batchIdRemap = new Map(); _colors; _modelId; static create(stream, iModel, modelId, is3d, range, system, yAxisUp, isLeaf, tileCenter, transformToRoot, isCanceled, idMap, deduplicateVertices = false, tileData) { const header = new B3dmHeader(stream); if (!header.isValid) return undefined; let returnToCenterTransform, pseudoRtcBias; if (header.featureTableJson && Array.isArray(header.featureTableJson.RTC_CENTER)) { returnToCenterTransform = Transform.createTranslationXYZ(header.featureTableJson.RTC_CENTER[0], header.featureTableJson.RTC_CENTER[1], header.featureTableJson.RTC_CENTER[2]); } else { /** * This is a workaround for tiles generated by * context capture which have a large offset from the tileset origin that exceeds the * capacity of 32 bit integers. It is essentially an ad hoc RTC applied at read time only if the tile is far from the * origin and there is no RTC supplied either with the B3DM of the GLTF. * as the vertices are supplied in a quantized format, applying the RTC bias to * quantization origin will make these tiles work correctly. */ pseudoRtcBias = Vector3d.create(tileCenter.x, tileCenter.y, tileCenter.z); } if (undefined !== returnToCenterTransform) transformToRoot = transformToRoot ? transformToRoot.multiplyTransformTransform(returnToCenterTransform) : returnToCenterTransform; const props = GltfReaderProps.create(stream.nextBytes(header.length - stream.curPos), yAxisUp); const batchTableLength = header.featureTableJson ? JsonUtils.asInt(header.featureTableJson.BATCH_LENGTH, 0) : 0; return undefined !== props ? new B3dmReader(props, iModel, modelId, is3d, system, range, isLeaf, batchTableLength, transformToRoot, header.batchTableJson, isCanceled, idMap, pseudoRtcBias, deduplicateVertices, tileData) : undefined; } constructor(props, iModel, modelId, is3d, system, _range, _isLeaf, _batchTableLength, _transformToRoot, _batchTableJson, shouldAbort, _idMap, _pseudoRtcBias, deduplicateVertices = false, tileData) { super({ props, iModel, system, shouldAbort, deduplicateVertices, is2d: !is3d, idMap: _idMap, tileData }); this._range = _range; this._isLeaf = _isLeaf; this._batchTableLength = _batchTableLength; this._transformToRoot = _transformToRoot; this._batchTableJson = _batchTableJson; this._pseudoRtcBias = _pseudoRtcBias; this._modelId = modelId; } async read() { // NB: For reality models with no batch table, we want the model ID in the feature table const featureTable = new FeatureTable(this._batchTableLength ? this._batchTableLength : 1, this._modelId, this._type); if (this._batchTableLength > 0 && this._idMap !== undefined && this._batchTableJson !== undefined) { if (this._batchTableJson.extensions && this._batchTableJson.extensions["3DTILES_batch_table_hierarchy"]) { const hierarchy = this._batchTableJson.extensions["3DTILES_batch_table_hierarchy"]; const { classIds, classes, parentIds, parentCounts, instancesLength } = hierarchy; if (classes !== undefined && classIds !== undefined && instancesLength !== 0) { const classCounts = new Array(classes.length); classCounts.fill(0); const classIndexes = new Uint16Array(instancesLength); for (let i = 0; i < instancesLength; ++i) { const classId = classIds[i]; classIndexes[i] = classCounts[classId]++; } let parentMap; if (parentIds) { parentMap = new Array(); for (let i = 0, parentIndex = 0; i < instancesLength; i++) { const parentCount = parentCounts === undefined ? 1 : parentCounts[i]; parentMap[i] = parentIds.slice(parentIndex, parentIndex += parentCount); } } const getProperties = (instance, instanceIndex) => { const classId = classIds[instanceIndex]; const instanceClass = classes[classId]; const instances = instanceClass.instances; const indexInClass = classIndexes[instanceIndex]; for (const key in instances) { // eslint-disable-line guard-for-in const value = instances[key][indexInClass]; if (value !== undefined && value !== null) instance[key] = value; } if (parentIds !== undefined) { const thisParents = parentMap[instanceIndex]; for (const parentId of thisParents) { if (parentId !== instanceIndex) getProperties(instance, parentId); } } }; for (let batchId = 0; batchId < instancesLength; batchId++) { const instance = {}; getProperties(instance, batchId); this._batchIdRemap.set(batchId, featureTable.insert(new Feature(this._idMap.getBatchId(instance)))); const cesiumColor = instance["cesium#color"]; if (undefined !== cesiumColor) { if (!this._colors) { this._colors = new Array(instancesLength); this._colors.fill(ColorDef.white.tbgr); } this._colors[batchId] = ColorDef.create(cesiumColor).tbgr; } } } } else { for (let i = 0; i < this._batchTableLength; i++) { const feature = {}; for (const key in this._batchTableJson) // eslint-disable-line guard-for-in feature[key] = this._batchTableJson[key][i]; this._batchIdRemap.set(i, featureTable.insert(new Feature(this._idMap.getBatchId(feature)))); } } } if (featureTable.isEmpty) { this._batchIdRemap.set(0, 0); const feature = new Feature(this._modelId); featureTable.insert(feature); } await this.resolveResources(); if (this._isCanceled) return { readStatus: TileReadStatus.Canceled, isLeaf: this._isLeaf }; return this.readGltfAndCreateGraphics(this._isLeaf, featureTable, this._range, this._transformToRoot, this._pseudoRtcBias, undefined); } readBatchTable(mesh, json) { if (mesh.features !== undefined) { if (this._batchTableLength > 0 && undefined !== this._batchTableJson && undefined !== json.attributes) { const view = this.getBufferView(json.attributes, "_BATCHID"); let batchIds; if (undefined !== view && (undefined !== (batchIds = view.toBufferData(GltfDataType.UInt32)) || undefined !== (batchIds = view.toBufferData(GltfDataType.Float)))) { const indices = []; const { colors, colorMap } = mesh; let colorRemap; if (this._colors && this._colors.length === this._batchTableLength) { colorRemap = new Uint32Array(this._batchTableLength); for (let i = 0; i < this._batchTableLength; i++) colorRemap[i] = colorMap.insert(this._colors[i]); } for (let i = 0; i < batchIds.count; i++) { const batchId = batchIds.buffer[i * view.stride]; const remapId = this._batchIdRemap.get(batchId); indices.push(remapId === undefined ? 0 : remapId); if (colorRemap) colors.push(colorRemap[batchId]); } mesh.features.setIndices(indices); } } else { mesh.features.add(new Feature(this._modelId), 1); } } } } //# sourceMappingURL=B3dmReader.js.map