@itwin/core-frontend
Version:
iTwin.js frontend components
178 lines • 6.22 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 } 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