@itwin/core-frontend
Version:
iTwin.js frontend components
260 lines • 15.8 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RealityMeshGeometry = exports.RealityMeshGeometryParams = void 0;
/** @packageDocumentation
* @module WebGL
*/
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const core_geometry_1 = require("@itwin/core-geometry");
const GraphicBranch_1 = require("../../../render/GraphicBranch");
const AttributeBuffers_1 = require("./AttributeBuffers");
const AttributeMap_1 = require("./AttributeMap");
const CachedGeometry_1 = require("./CachedGeometry");
const GL_1 = require("./GL");
const Primitive_1 = require("./Primitive");
const System_1 = require("./System");
const RenderTerrain_1 = require("../RenderTerrain");
const internal_1 = require("../../../tile/internal");
const MapLayerParams_1 = require("./MapLayerParams");
const scratchOverlapRange = core_geometry_1.Range2d.createNull();
/** @internal */
class RealityMeshGeometryParams extends CachedGeometry_1.IndexedGeometryParams {
uvParams;
featureID;
normals;
numBytesPerIndex;
constructor(positions, normals, uvParams, indices, numIndices, numBytesPerIndex, featureID) {
super(positions, indices, numIndices);
this.numBytesPerIndex = numBytesPerIndex;
let attrParams = AttributeMap_1.AttributeMap.findAttribute("a_uvParam", 7 /* TechniqueId.RealityMesh */, false);
(0, core_bentley_1.assert)(attrParams !== undefined);
this.buffers.addBuffer(uvParams, [AttributeBuffers_1.BufferParameters.create(attrParams.location, 2, GL_1.GL.DataType.UnsignedShort, false, 0, 0, false)]);
this.uvParams = uvParams;
if (undefined !== normals) {
attrParams = AttributeMap_1.AttributeMap.findAttribute("a_norm", 7 /* TechniqueId.RealityMesh */, false);
(0, core_bentley_1.assert)(attrParams !== undefined);
if (normals.bytesUsed > 0)
this.buffers.addBuffer(normals, [AttributeBuffers_1.BufferParameters.create(attrParams.location, 2, GL_1.GL.DataType.UnsignedByte, false, 0, 0, false)]);
this.normals = normals;
}
this.featureID = featureID;
}
static createFromBuffers(posBuf, uvParamBuf, indices, normBuf, featureID) {
const indBuf = AttributeBuffers_1.BufferHandle.createBuffer(GL_1.GL.Buffer.Target.ElementArrayBuffer, indices);
if (undefined === indBuf)
return undefined;
const bytesPerIndex = indices.BYTES_PER_ELEMENT;
(0, core_bentley_1.assert)(1 === bytesPerIndex || 2 === bytesPerIndex || 4 === bytesPerIndex);
return new RealityMeshGeometryParams(posBuf, normBuf, uvParamBuf, indBuf, indices.length, bytesPerIndex, featureID);
}
static fromRealityMesh(params) {
const posBuf = AttributeBuffers_1.QBufferHandle3d.create(params.positions.params, params.positions.points);
const uvParamBuf = AttributeBuffers_1.QBufferHandle2d.create(params.uvs.params, params.uvs.points);
const normalBuf = params.normals ? AttributeBuffers_1.BufferHandle.createArrayBuffer(params.normals) : undefined;
return (undefined === posBuf || undefined === uvParamBuf) ? undefined : this.createFromBuffers(posBuf, uvParamBuf, params.indices, normalBuf, params.featureID ?? 0);
}
get isDisposed() {
return super.isDisposed && this.uvParams.isDisposed;
}
get bytesUsed() { return this.positions.bytesUsed + (undefined === this.normals ? 0 : this.normals.bytesUsed) + this.uvParams.bytesUsed + this.indices.bytesUsed; }
[Symbol.dispose]() {
super[Symbol.dispose]();
(0, core_bentley_1.dispose)(this.uvParams);
}
}
exports.RealityMeshGeometryParams = RealityMeshGeometryParams;
/** @internal */
class RealityMeshGeometry extends CachedGeometry_1.IndexedGeometry {
renderGeometryType = "reality-mesh";
isInstanceable = false;
noDispose = false;
hasTextures;
get asRealityMesh() { return this; }
get isDisposed() { return this._realityMeshParams.isDisposed; }
get uvQParams() { return this._realityMeshParams.uvParams.params; }
get hasFeatures() { return this._realityMeshParams.featureID !== undefined; }
get supportsThematicDisplay() { return true; }
get overrideColorMix() { return .5; } // This could be a setting from either the mesh or the override if required.
get transform() { return this._transform; }
_realityMeshParams;
_indexType;
textureParams;
_transform;
baseColor;
_baseIsTransparent;
_isTerrain;
_disableTextureDisposal;
constructor(props) {
super(props.realityMeshParams);
this._realityMeshParams = props.realityMeshParams;
this.textureParams = props.textureParams;
this._transform = props.transform;
this.baseColor = props.baseColor;
this._baseIsTransparent = props.baseIsTransparent;
this._isTerrain = props.isTerrain;
this._disableTextureDisposal = props.disableTextureDisposal;
this.hasTextures = undefined !== this.textureParams && this.textureParams.params.some((x) => undefined !== x.texture);
const bytesPerIndex = props.realityMeshParams.numBytesPerIndex;
this._indexType = 1 === bytesPerIndex ? GL_1.GL.DataType.UnsignedByte : (2 === bytesPerIndex ? GL_1.GL.DataType.UnsignedShort : GL_1.GL.DataType.UnsignedInt);
}
[Symbol.dispose]() {
if (this.noDispose) {
return;
}
super[Symbol.dispose]();
(0, core_bentley_1.dispose)(this._realityMeshParams);
if (true !== this._disableTextureDisposal)
(0, core_bentley_1.dispose)(this.textureParams);
}
static createForTerrain(mesh, transform, disableTextureDisposal = false) {
const params = RealityMeshGeometryParams.fromRealityMesh(mesh);
if (!params)
return undefined;
return new RealityMeshGeometry({
realityMeshParams: params,
transform,
baseIsTransparent: false,
isTerrain: true,
disableTextureDisposal,
});
}
static createFromRealityMesh(realityMesh, disableTextureDisposal = false) {
const params = RealityMeshGeometryParams.fromRealityMesh(realityMesh);
if (!params)
return undefined;
const { texture: meshTexture, featureID } = realityMesh;
const tile = realityMesh.tileData;
const layerClassifiers = tile?.layerClassifiers;
const texture = meshTexture ? new RenderTerrain_1.TerrainTexture(meshTexture, featureID ?? 0, core_geometry_1.Vector2d.create(1.0, -1.0), core_geometry_1.Vector2d.create(0.0, 1.0), core_geometry_1.Range2d.createXYXY(0, 0, 1, 1), 0, 0) : undefined;
if (!layerClassifiers?.size || !tile)
return new RealityMeshGeometry({ realityMeshParams: params, textureParams: texture ? MapLayerParams_1.LayerTextureParams.create([texture]) : undefined, baseIsTransparent: false, isTerrain: false, disableTextureDisposal });
const transformECEF = tile.ecefTransform;
const tileEcefRange = transformECEF.multiplyRange(tile.range);
const cartographicRange = new core_common_1.CartographicRange(tileEcefRange, transformECEF);
const boundingBox = cartographicRange.getLongitudeLatitudeBoundingBox();
const mapCartoRectangle = internal_1.MapCartoRectangle.fromRadians(boundingBox.low.x, boundingBox.low.y, boundingBox.high.x, boundingBox.high.y);
const corners = tile.range.corners();
const normal = core_geometry_1.Vector3d.createCrossProductToPoints(corners[0], corners[1], corners[2])?.normalize();
if (!normal) {
return new RealityMeshGeometry({ realityMeshParams: params, textureParams: texture ? MapLayerParams_1.LayerTextureParams.create([texture]) : undefined, baseIsTransparent: false, isTerrain: false, disableTextureDisposal });
}
const chordHeight = corners[0].distance(corners[3]) / 2;
const realityPlanarTilePatch = new internal_1.PlanarTilePatch(corners, normal, chordHeight);
const realityProjection = new internal_1.PlanarProjection(realityPlanarTilePatch);
const realityMeshParams = {
projection: realityProjection,
tileRectangle: mapCartoRectangle,
tileId: undefined,
baseColor: undefined,
baseTransparent: false,
layerClassifiers
};
const layerTextures = texture ? [texture] : [];
layerClassifiers?.forEach((layerClassifier, layerIndex) => layerTextures[layerIndex] = new MapLayerParams_1.ProjectedTexture(layerClassifier, realityMeshParams, realityMeshParams.tileRectangle));
return new RealityMeshGeometry({ realityMeshParams: params, textureParams: layerTextures.length > 0 ? MapLayerParams_1.LayerTextureParams.create(layerTextures) : undefined, baseIsTransparent: false, isTerrain: false, disableTextureDisposal });
}
getRange() {
return core_geometry_1.Range3d.createXYZXYZ(this.qOrigin[0], this.qOrigin[1], this.qOrigin[2], this.qOrigin[0] + core_common_1.Quantization.rangeScale16 * this.qScale[0], this.qOrigin[1] + core_common_1.Quantization.rangeScale16 * this.qScale[1], this.qOrigin[2] + core_common_1.Quantization.rangeScale16 * this.qScale[2]);
}
static createGraphic(system, params, disableTextureDisposal = false) {
const meshes = [];
const textures = params.textures ?? [];
const realityMesh = params.realityMesh;
const { baseColor, baseTransparent, featureTable, tileId, layerClassifiers } = params;
const texturesPerMesh = System_1.System.instance.maxRealityImageryLayers;
const layers = new Array();
// Collate the textures and classifiers layers into a single array.
for (const texture of textures) {
const layer = layers[texture.layerIndex];
if (layer) {
layer.push(texture);
}
else {
layers[texture.layerIndex] = [texture];
}
}
params.layerClassifiers?.forEach((layerClassifier, layerIndex) => layers[layerIndex] = [new MapLayerParams_1.ProjectedTexture(layerClassifier, params, params.tileRectangle)]);
if (layers.length < 2 && !layerClassifiers?.size && textures.length < texturesPerMesh) {
// If only there is not more than one layer then we can group all of the textures into a single draw call.
meshes.push(new RealityMeshGeometry({ realityMeshParams: realityMesh._realityMeshParams, textureParams: MapLayerParams_1.LayerTextureParams.create(textures), transform: realityMesh._transform, baseColor, baseIsTransparent: baseTransparent, isTerrain: realityMesh._isTerrain, disableTextureDisposal }));
}
else {
let primaryLayer;
while (primaryLayer === undefined)
primaryLayer = layers.shift();
if (!primaryLayer)
return undefined;
for (const primaryTexture of primaryLayer) {
const targetRectangle = primaryTexture.targetRectangle;
const overlapMinimum = 1.0E-5 * (targetRectangle.high.x - targetRectangle.low.x) * (targetRectangle.high.y - targetRectangle.low.y);
let layerTextures = [primaryTexture];
for (const secondaryLayer of layers) {
if (!secondaryLayer)
continue;
for (const secondaryTexture of secondaryLayer) {
if (secondaryTexture instanceof MapLayerParams_1.ProjectedTexture) {
layerTextures.push(secondaryTexture.clone(targetRectangle));
}
else {
const secondaryRectangle = secondaryTexture.targetRectangle;
const overlap = targetRectangle.intersect(secondaryRectangle, scratchOverlapRange);
if (!overlap.isNull && (overlap.high.x - overlap.low.x) * (overlap.high.y - overlap.low.y) > overlapMinimum) {
const textureRange = core_geometry_1.Range2d.createXYXY(overlap.low.x, overlap.low.y, overlap.high.x, overlap.high.y);
secondaryRectangle.worldToLocal(textureRange.low, textureRange.low);
secondaryRectangle.worldToLocal(textureRange.high, textureRange.high);
if (secondaryTexture.clipRectangle)
textureRange.intersect(secondaryTexture.clipRectangle, textureRange);
if (!textureRange.isNull && textureRange) {
layerTextures.push(secondaryTexture.cloneWithClip(textureRange));
}
}
}
}
}
while (layerTextures.length > texturesPerMesh) {
meshes.push(new RealityMeshGeometry({ realityMeshParams: realityMesh._realityMeshParams, textureParams: MapLayerParams_1.LayerTextureParams.create(layerTextures.slice(0, texturesPerMesh)), transform: realityMesh._transform, baseColor, baseIsTransparent: baseTransparent, isTerrain: realityMesh._isTerrain, disableTextureDisposal }));
layerTextures = layerTextures.slice(texturesPerMesh);
}
meshes.push(new RealityMeshGeometry({ realityMeshParams: realityMesh._realityMeshParams, textureParams: MapLayerParams_1.LayerTextureParams.create(layerTextures), transform: realityMesh._transform, baseColor, baseIsTransparent: baseTransparent, isTerrain: realityMesh._isTerrain, disableTextureDisposal }));
}
}
if (meshes.length === 0)
return undefined;
const branch = new GraphicBranch_1.GraphicBranch(true);
for (const mesh of meshes) {
const primitive = Primitive_1.Primitive.create(mesh);
if (featureTable) {
branch.add(system.createBatch(primitive, featureTable, mesh.getRange(), { tileId }));
}
}
return system.createBranch(branch, realityMesh._transform ? realityMesh._transform : core_geometry_1.Transform.createIdentity(), { disableClipStyle: params.disableClipStyle });
}
collectStatistics(stats) {
this._isTerrain ? stats.addTerrain(this._realityMeshParams.bytesUsed) : stats.addRealityMesh(this._realityMeshParams.bytesUsed);
if (this.textureParams?.params) {
for (const param of this.textureParams.params) {
if (param.texture?.bytesUsed)
stats.addTexture(param.texture.bytesUsed);
}
}
}
get techniqueId() { return 7 /* TechniqueId.RealityMesh */; }
getPass(target) {
if (this._baseIsTransparent || (target.wantThematicDisplay && target.uniforms.thematic.wantIsoLines))
return "translucent";
return "opaque";
}
get renderOrder() { return 3 /* RenderOrder.UnlitSurface */; }
draw() {
this._params.buffers.bind();
System_1.System.instance.context.drawElements(GL_1.GL.PrimitiveType.Triangles, this._params.numIndices, this._indexType, 0);
this._params.buffers.unbind();
}
}
exports.RealityMeshGeometry = RealityMeshGeometry;
//# sourceMappingURL=RealityMesh.js.map