UNPKG

@itwin/core-frontend

Version:
178 lines 6.22 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 WebGL */ import { assert } from "@itwin/core-bentley"; import { Point3d, Transform, Vector3d, } from "@itwin/core-geometry"; import { RenderClipVolume } from "../../../render/RenderClipVolume"; const scratch = { normal: new Vector3d(), dir: new Vector3d(), pos: new Point3d(), v0: new Vector3d(), }; /** Maintains an ArrayBuffer to serve as texture data for a ClipVector. * The clip planes are in view coordinates, so the data must be updated whenever the view * matrix changes. */ class ClipPlanesBuffer { /** Most recently-applied view matrix. */ _viewMatrix = Transform.createZero(); /** For writing to the ArrayBuffer. */ _view; /** For inspecting the data in the ArrayBuffer. */ _data; /** The current write position. */ _curPos = 0; _clips; _append; /** The number of rows of data. Each row corresponds to a clipping plane, or to mark a boundary between two ClipPlaneSets or UnionOfConvexClipPlaneSets. * The final row is *always* a union boundary, to enable multiple clips to be concatenated - this is how nested clip volumes work. */ numRows; getData(viewMatrix) { if (!viewMatrix.isAlmostEqual(this._viewMatrix)) this.updateData(viewMatrix); return this._data; } get byteLength() { return this._view.buffer.byteLength; } static create(clips, numRows) { assert(numRows > 1); // at least one plane, plus a union boundary. return new ClipPlanesBuffer(clips, numRows); } constructor(clips, numRows) { this._data = new Uint8Array(numRows * 4 * 4); this._view = new DataView(this._data.buffer); this._clips = clips; this.numRows = numRows; this._append = (value) => this.appendFloat(value); } appendFloat(value) { this._view.setFloat32(this._curPos, value, true); this.advance(4); } appendUint8(value) { this._view.setUint8(this._curPos, value); this.advance(1); } appendValues(a, b, c, d) { this._append(a); this._append(b); this._append(c); this._append(d); } appendPlane(normal, distance) { this.appendValues(normal.x, normal.y, normal.z, distance); } appendSetBoundary() { this.appendValues(0, 0, 0, 0); } appendUnionBoundary() { this.appendValues(2, 2, 2, 0); } appendEncodedFloat(value) { const sign = value < 0 ? 1 : 0; value = Math.abs(value); const exponent = Math.floor(Math.log10(value)) + 1; value = value / Math.pow(10, exponent); const bias = 38; let temp = value * 256; const b0 = Math.floor(temp); temp = (temp - b0) * 256; const b1 = Math.floor(temp); temp = (temp - b1) * 256; const b2 = Math.floor(temp); const b3 = (exponent + bias) * 2 + sign; this.appendUint8(b0); this.appendUint8(b1); this.appendUint8(b2); this.appendUint8(b3); } advance(numBytes) { this._curPos += numBytes; } reset() { this._curPos = 0; } updateData(transform) { this.reset(); transform.clone(this._viewMatrix); const { normal, dir, pos, v0 } = { ...scratch }; for (const clip of this._clips) { for (let j = 0; j < clip.convexSets.length; j++) { const set = clip.convexSets[j]; if (0 === set.planes.length) continue; if (j > 0) this.appendSetBoundary(); for (const plane of set.planes) { plane.inwardNormalRef.clone(normal); let distance = plane.distance; const norm = normal; transform.matrix.multiplyVector(norm, dir); dir.normalizeInPlace(); transform.multiplyPoint3d(norm.scale(distance, v0), pos); v0.setFromPoint3d(pos); normal.set(dir.x, dir.y, dir.z); distance = -v0.dotProduct(dir); this.appendPlane(normal, distance); } } this.appendUnionBoundary(); } } } /** A ClipVector encoded for transmission to the GPU as a texture. * @internal */ export class ClipVolume extends RenderClipVolume { _buffer; get numRows() { return this._buffer.numRows; } get byteLength() { return this._buffer.byteLength; } getData(viewMatrix) { return this._buffer.getData(viewMatrix); } static create(clip) { if (!clip.isValid) return undefined; // Compute how many rows of data we need. const unions = []; let numRows = 0; for (const primitive of clip.clips) { const union = primitive.fetchClipPlanesRef(); if (!union) continue; let numSets = 0; for (const set of union.convexSets) { const setLength = set.planes.length; if (setLength > 0) { ++numSets; numRows += setLength; } } if (numSets > 0) { unions.push(union); numRows += numSets - 1; // Additional boundary rows between sets. } } if (unions.length === 0) return undefined; numRows += unions.length; // Additional boundary row after each union - *including* the last union. const buffer = ClipPlanesBuffer.create(unions, numRows); return new ClipVolume(clip, buffer); } constructor(clip, buffer) { super(clip); this._buffer = buffer; } } //# sourceMappingURL=ClipVolume.js.map