@itwin/core-frontend
Version:
iTwin.js frontend components
127 lines • 5.7 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 { assert, dispose, expectDefined, Id64, OrderedId64Iterable } from "@itwin/core-bentley";
import { ContourDisplay, PackedFeature } from "@itwin/core-common";
import { GL } from "./GL";
import { TextureUnit } from "./RenderFlags";
import { System } from "./System";
import { Texture2DDataUpdater, TextureHandle } from "./Texture";
import { computeDimensions } from "../../../common/internal/render/VertexTable";
const scratchPackedFeature = PackedFeature.createWithIndex();
/** @internal */
export class Contours {
target;
_options;
_contours;
_lut;
_lutWidth = 0;
_numFeatures = 0;
get byteLength() { return undefined !== this._lut ? this._lut.bytesUsed : 0; }
matchesTargetAndFeatureCount(target, map) {
// checking for target change or texture size requirement change
return target === this.target && this._numFeatures === map.numFeatures;
}
matchesSubCategories() {
if (this._contours === undefined && this.target.currentContours === undefined)
return true;
if (this._contours === undefined || this.target.currentContours === undefined)
return false;
if (this._contours.groups.length !== this.target.currentContours.groups.length)
return false;
for (let index = 0, len = this._contours.groups.length; index < len && index < ContourDisplay.maxContourGroups; ++index) {
if (!this._contours.groups[index].subCategoriesEqual(this.target.currentContours.groups[index]))
return false;
}
return true;
}
_initialize(map) {
assert(0 < map.numFeatures);
this._numFeatures = map.numFeatures;
const dims = computeDimensions(this._numFeatures, 1 / 8, 0, System.instance.maxTextureSize);
const width = dims.width;
const height = dims.height;
assert(width * height * 8 >= this._numFeatures);
const data = new Uint8Array(width * height * 4);
const creator = new Texture2DDataUpdater(data);
this.buildLookupTable(creator, map, expectDefined(this.target.currentContours));
this._lut = TextureHandle.createForData(width, height, data, true, GL.Texture.WrapMode.ClampToEdge);
this._lutWidth = width;
}
_update(map, lut) {
assert(this._numFeatures === map.numFeatures);
const updater = new Texture2DDataUpdater(expectDefined(lut.dataBytes));
this.buildLookupTable(updater, map, expectDefined(this.target.currentContours));
lut.update(updater);
}
buildLookupTable(data, map, contours) {
// setup an efficient way to compare feature subcategories with lists in terrains
const subCatMap = new Id64.Uint32Map();
let defaultNdx = 0xf; // default for unmatched subcategories is to not show contours
// NB: index also has to be a max of 14 - has to fit in 4 bits with value 15 reserved for no terrain def
for (let index = 0, len = contours.groups.length; index < len && index < ContourDisplay.maxContourGroups; ++index) {
const subCats = contours.groups[index].subCategories;
if (OrderedId64Iterable.isEmptySet(subCats)) {
defaultNdx = index; // change default for unmatched subcategories to this definition
}
else {
for (const subCat of subCats)
subCatMap.setById(subCat, index);
}
}
// NB: We currently use 1/2 of one component of RGBA value per feature as follows:
// [0] R/G/B/A = index pair - lower 4 bits = ndx n, upper 4 bits = ndx n+1
let even = false;
let byteOut = 0;
let dataIndex = 0;
for (const feature of map.iterable(scratchPackedFeature)) {
dataIndex = Math.floor(feature.index * 0.5);
even = (feature.index & 1) === 0;
const terrainNdx = subCatMap.get(feature.subCategoryId.lower, feature.subCategoryId.upper) ?? defaultNdx;
if (even)
byteOut = terrainNdx;
else
data.setByteAtIndex(dataIndex, (terrainNdx << 4) | byteOut);
}
if (even) // not written
data.setByteAtIndex(dataIndex, byteOut);
}
constructor(target, options) {
this.target = target;
this._options = options;
this._contours = target.currentContours;
}
static createFromTarget(target, options) {
return new Contours(target, options);
}
get isDisposed() { return undefined === this._lut; }
[Symbol.dispose]() {
this._lut = dispose(this._lut);
return undefined;
}
initFromMap(map) {
this._lut = dispose(this._lut);
this._initialize(map);
}
update(features) {
if (this.matchesSubCategories())
return;
this._contours = this.target.currentContours;
// _lut can be undefined if context was lost, (gl.createTexture returns null)
if (this._lut) {
this._update(features, this._lut);
}
}
bindContourLUTWidth(uniform) {
uniform.setUniform1ui(this._lutWidth);
}
bindContourLUT(uniform) {
if (this._lut)
this._lut.bindSampler(uniform, TextureUnit.Contours);
}
}
//# sourceMappingURL=Contours.js.map