@itwin/core-frontend
Version:
iTwin.js frontend components
183 lines (181 loc) • 8.91 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 WebGL
*/
import { Range2d } from "@itwin/core-geometry";
import { TerrainTexture } from "../RenderTerrain";
import { Matrix4 } from "./Matrix";
import { ModelMapLayerDrapeTarget, ModelMapLayerSettings } from "@itwin/core-common";
import { assert, dispose, disposeArray, expectDefined } from "@itwin/core-bentley";
import { System } from "./System";
export class ProjectedTexture {
meshParams;
targetRectangle;
classifier;
constructor(classifier, meshParams, targetRectangle) {
this.meshParams = meshParams;
this.targetRectangle = targetRectangle;
this.classifier = classifier;
}
clone(targetRectangle) {
return new ProjectedTexture(this.classifier, this.meshParams, targetRectangle.clone());
}
}
const scratchBytes = new Uint8Array(4);
const scratchBatchBaseId = new Uint32Array(scratchBytes.buffer);
const scratchRange2d = Range2d.createNull();
class LayerTextureParam {
texture;
_projectedTextureOrMatrix;
constructor(texture, _projectedTextureOrMatrix) {
this.texture = texture;
this._projectedTextureOrMatrix = _projectedTextureOrMatrix;
}
get isProjected() { return this._projectedTextureOrMatrix instanceof ProjectedTexture; }
[Symbol.dispose]() {
this.texture = dispose(this.texture);
}
/* There are two methods of applying a texture to a reality mesh. the first member of "params" denotes which
method is to be used. A value of zero indicates a standard texture and one represents a classified texture.
A standard (nonprojected) texture is generated by multiplying v_textCoord by the scaling and translation packed into the first row
of "matrix". A clip rectangle is packed into second row of "matrix".
A "classified" reality mesh texture is used for map layers. It does not uses v_texCoord, the texture coordinates
are instead generated by a projection of the model position onto the X-Y plane. We only have eye position, not model position
so the matrix in this case is a real transform matrix that contains a mapping from eye to model position
followed by the model to texture projection.
*/
getProjectionMatrix() {
return this._projectedTextureOrMatrix instanceof ProjectedTexture ? this._projectedTextureOrMatrix.classifier.projectionMatrix : undefined;
}
getTerrainMatrix() {
return this._projectedTextureOrMatrix instanceof Matrix4 ? this._projectedTextureOrMatrix : undefined;
}
getParams(result) {
/** Entry 0 is 0 for */
if (this._projectedTextureOrMatrix instanceof ProjectedTexture) {
const projectedTexture = this._projectedTextureOrMatrix;
result.data[0] = 1;
result.data[1] = projectedTexture.classifier.textureImageCount;
result.data[2] = projectedTexture.classifier.sourceTransparency === undefined ? 1.0 : (1.0 - projectedTexture.classifier.sourceTransparency);
scratchBatchBaseId[0] = projectedTexture.classifier.baseBatchId;
result.data[4] = scratchBytes[0];
result.data[5] = scratchBytes[1];
result.data[6] = scratchBytes[2];
result.data[7] = scratchBytes[3];
const points = [];
const meshParams = projectedTexture.meshParams;
// Calculate range in the tiles local coordinates.
const low = expectDefined(meshParams.tileRectangle.worldToLocal(projectedTexture.targetRectangle.low, scratchRange2d.low));
const high = expectDefined(meshParams.tileRectangle.worldToLocal(projectedTexture.targetRectangle.high, scratchRange2d.high));
points.push(meshParams.projection.getGlobalPoint(low.x, low.y, 0));
points.push(meshParams.projection.getGlobalPoint(high.x, low.y, 0));
points.push(meshParams.projection.getGlobalPoint(high.x, high.y, 0));
points.push(meshParams.projection.getGlobalPoint(low.x, high.y, 0));
for (let i = 0, j = 8; i < 4; i++) {
const projectedPoint = projectedTexture.classifier.projectionMatrix.multiplyPoint3dQuietNormalize(points[i]);
result.data[j++] = projectedPoint.x;
result.data[j++] = projectedPoint.y;
}
const x0 = result.data[10] - result.data[8], y0 = result.data[11] - result.data[9];
const x1 = result.data[12] - result.data[8], y1 = result.data[13] - result.data[9];
if (x0 * y1 - x1 * y0 < 0) {
const swap = ((i, j) => {
const temp = result.data[i];
result.data[i] = result.data[j];
result.data[j] = temp;
});
for (let i = 8, j = 14; i <= 10; i += 2, j -= 2) {
swap(i, j);
swap(i + 1, j + 1);
}
}
}
else {
result.data[0] = 0;
}
return result;
}
}
/** @internal */
export class LayerTextureParams {
params;
constructor(params) {
this.params = params;
}
static create(textures) {
const maxTexturesPerMesh = System.instance.maxRealityImageryLayers;
assert(textures.length <= maxTexturesPerMesh);
const textureParams = new Array();
for (const texture of textures) {
if (texture instanceof TerrainTexture) {
const terrainTexture = texture;
const matrix = new Matrix4(); // Published as Mat4.
assert(terrainTexture.texture !== undefined, "Texture not defined in TerrainTextureParams constructor");
matrix.data[0] = terrainTexture.translate.x;
matrix.data[1] = terrainTexture.translate.y;
matrix.data[2] = terrainTexture.scale.x;
matrix.data[3] = terrainTexture.scale.y;
if (terrainTexture.clipRectangle) {
matrix.data[4] = terrainTexture.clipRectangle.low.x;
matrix.data[5] = terrainTexture.clipRectangle.low.y;
matrix.data[6] = terrainTexture.clipRectangle.high.x;
matrix.data[7] = terrainTexture.clipRectangle.high.y;
}
else {
matrix.data[4] = matrix.data[5] = 0;
matrix.data[6] = matrix.data[7] = 1;
}
matrix.data[8] = (1.0 - terrainTexture.transparency);
matrix.data[9] = terrainTexture.featureId;
textureParams.push(new LayerTextureParam(terrainTexture.texture, matrix));
}
else {
const classifier = texture.classifier;
textureParams.push(new LayerTextureParam(classifier.getOrCreateClassifierTexture(), texture));
}
}
for (let i = textures.length; i < maxTexturesPerMesh; i++) {
const matrix = new Matrix4();
matrix.data[0] = matrix.data[1] = 0.0;
matrix.data[2] = matrix.data[3] = 1.0;
matrix.data[4] = matrix.data[5] = 1;
matrix.data[6] = matrix.data[7] = -1;
matrix.data[15] = 0; // Denotes a terrain texture.
textureParams.push(new LayerTextureParam(undefined, matrix));
}
return new LayerTextureParams(textureParams);
}
[Symbol.dispose]() {
disposeArray(this.params);
}
}
/**
* Compares the map layers of two view states, ensuring both the number of layers
* and their order remain unchanged.
* Returns true if the map layers differ in count, order, or model IDs; otherwise, returns false.
*
* @param prevView The previous view state.
* @param newView The new view state.
* @returns {boolean} True if there is any difference in the model layer configuration; false otherwise.
*/
export function compareMapLayer(prevView, newView) {
const getDrapedModelIds = (view) => view.displayStyle
.getMapLayers(false)
.filter((layer) => layer instanceof ModelMapLayerSettings &&
(layer.drapeTarget === ModelMapLayerDrapeTarget.RealityData ||
layer.drapeTarget === ModelMapLayerDrapeTarget.IModel))
.map(layer => layer.modelId);
const prev = getDrapedModelIds(prevView);
const next = getDrapedModelIds(newView);
if (prev.length !== next.length)
return true;
for (let i = 0; i < prev.length; i++) {
if (prev[i] !== next[i])
return true;
}
return false;
}
//# sourceMappingURL=MapLayerParams.js.map