@itwin/core-frontend
Version:
iTwin.js frontend components
169 lines • 9.74 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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