@itwin/core-frontend
Version:
iTwin.js frontend components
204 lines • 11 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.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module WebGL
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FrustumUniforms = void 0;
exports.fromSumOf = fromSumOf;
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const IModelFrameLifecycle_1 = require("./IModelFrameLifecycle");
const Matrix_1 = require("./Matrix");
const Sync_1 = require("./Sync");
/** Represents a Target's frustum for use in glsl as a pair of uniforms.
* Do not modify fields of exposed objects directly. e.g., do not directly manipulate the projection or view matrices - use the appropriate APIs.
* @internal
*/
class FrustumUniforms {
// CPU state. Do not modify - use APIs.
planFrustum = new core_common_1.Frustum();
_planFraction = 0;
_nearPlaneCenter = new core_geometry_1.Point3d();
viewMatrix = core_geometry_1.Transform.createIdentity();
projectionMatrix = core_geometry_1.Matrix4d.createIdentity();
_worldUpVector = core_geometry_1.Vector3d.unitZ();
_viewUpVector = core_geometry_1.Vector3d.unitZ();
// GPU state
_planeData = new Float32Array(4);
_frustumData = new Float32Array(3);
_worldFrustumZRange = new Float32Array(2);
projectionMatrix32 = new Matrix_1.Matrix4();
_logZData = new Float32Array(2);
_viewUpVector32 = new Float32Array(3);
// SyncTarget
syncKey = 0;
// Scratch variables
_scratch = {
point3d: new core_geometry_1.Point3d(),
vec3d: new core_geometry_1.Vector3d(),
viewX: new core_geometry_1.Vector3d(),
viewY: new core_geometry_1.Vector3d(),
viewZ: new core_geometry_1.Vector3d(),
range: new core_geometry_1.Range3d(),
};
constructor() {
}
bindProjectionMatrix(uniform) {
if (!(0, Sync_1.sync)(this, uniform))
uniform.setMatrix4(this.projectionMatrix32);
}
bindUpVector(uniform) {
if (!(0, Sync_1.sync)(this, uniform))
uniform.setUniform3fv(this._viewUpVector32);
}
// uniform vec4 u_frustumPlanes; // { top, bottom, left, right }
get planes() { return this._planeData; }
// uniform vec3 u_frustum; // { near, far, type }
get frustum() { return this._frustumData; }
// uniform vec2 u_worldFrustumZRange // { min, max }
get worldFrustumZRange() { return this._worldFrustumZRange; }
get nearPlane() { return this._frustumData[0 /* FrustumData.kNear */]; }
get farPlane() { return this._frustumData[1 /* FrustumData.kFar */]; }
get type() { return this.frustum[2 /* FrustumData.kType */]; }
get is2d() { return 0 /* FrustumUniformType.TwoDee */ === this.type; }
get planFraction() { return this._planFraction; }
// uniform vec2 u_logZ where x = 1/near and y = log(far/near)
get logZ() { return this._logZData; }
changeFrustum(newFrustum, newFraction, is3d) {
if (newFraction === this._planFraction && is3d !== this.is2d && newFrustum.equals(this.planFrustum))
return;
(0, Sync_1.desync)(this);
newFrustum.clone(this.planFrustum);
const range = newFrustum.toRange(this._scratch.range);
this._worldFrustumZRange[0] = range.low.z;
this._worldFrustumZRange[1] = range.high.z;
const farLowerLeft = newFrustum.getCorner(core_common_1.Npc.LeftBottomRear);
const farLowerRight = newFrustum.getCorner(core_common_1.Npc.RightBottomRear);
const farUpperLeft = newFrustum.getCorner(core_common_1.Npc.LeftTopRear);
const farUpperRight = newFrustum.getCorner(core_common_1.Npc.RightTopRear);
const nearLowerLeft = newFrustum.getCorner(core_common_1.Npc.LeftBottomFront);
const nearLowerRight = newFrustum.getCorner(core_common_1.Npc.RightBottomFront);
const nearUpperLeft = newFrustum.getCorner(core_common_1.Npc.LeftTopFront);
const nearUpperRight = newFrustum.getCorner(core_common_1.Npc.RightTopFront);
const nearCenter = nearLowerLeft.interpolate(0.5, nearUpperRight, this._scratch.point3d);
const viewX = normalizedDifference(nearLowerRight, nearLowerLeft, this._scratch.viewX);
const viewY = normalizedDifference(nearUpperLeft, nearLowerLeft, this._scratch.viewY);
const viewZ = viewX.crossProduct(viewY, this._scratch.viewZ).normalize();
this._planFraction = newFraction;
if (!is3d || newFraction > 0.999) { // ortho or 2d
const halfWidth = core_geometry_1.Vector3d.createStartEnd(farLowerRight, farLowerLeft, this._scratch.vec3d).magnitude() * 0.5;
const halfHeight = core_geometry_1.Vector3d.createStartEnd(farLowerRight, farUpperRight).magnitude() * 0.5;
const depth = core_geometry_1.Vector3d.createStartEnd(farLowerLeft, nearLowerLeft, this._scratch.vec3d).magnitude();
lookIn(nearCenter, viewX, viewY, viewZ, this.viewMatrix);
ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, 0, depth, this.projectionMatrix);
this._nearPlaneCenter.setFrom(nearLowerLeft);
this._nearPlaneCenter.interpolate(0.5, nearUpperRight, this._nearPlaneCenter);
this.setPlanes(halfHeight, -halfHeight, -halfWidth, halfWidth);
this.setFrustum(0, depth, is3d ? 1 /* FrustumUniformType.Orthographic */ : 0 /* FrustumUniformType.TwoDee */);
}
else { // perspective
const scale = 1.0 / (1.0 - newFraction);
const zVec = core_geometry_1.Vector3d.createStartEnd(farLowerLeft, nearLowerLeft, this._scratch.vec3d);
const cameraPosition = fromSumOf(farLowerLeft, zVec, scale, this._scratch.point3d);
const frustumLeft = dotDifference(farLowerLeft, cameraPosition, viewX) * newFraction;
const frustumRight = dotDifference(farLowerRight, cameraPosition, viewX) * newFraction;
const frustumBottom = dotDifference(farLowerLeft, cameraPosition, viewY) * newFraction;
const frustumTop = dotDifference(farUpperLeft, cameraPosition, viewY) * newFraction;
const frustumFront = -dotDifference(nearLowerLeft, cameraPosition, viewZ);
const frustumBack = -dotDifference(farLowerLeft, cameraPosition, viewZ);
lookIn(cameraPosition, viewX, viewY, viewZ, this.viewMatrix);
frustum(frustumLeft, frustumRight, frustumBottom, frustumTop, frustumFront, frustumBack, this.projectionMatrix);
IModelFrameLifecycle_1.IModelFrameLifecycle.onChangeCameraView.raiseEvent({
cameraPosition,
viewX,
viewY,
viewZ,
});
IModelFrameLifecycle_1.IModelFrameLifecycle.onChangeCameraFrustum.raiseEvent({
type: 2 /* FrustumUniformType.Perspective */,
left: frustumLeft,
right: frustumRight,
bottom: frustumBottom,
top: frustumTop,
front: frustumFront,
back: frustumBack,
});
this._nearPlaneCenter.setFrom(nearLowerLeft);
this._nearPlaneCenter.interpolate(0.5, nearUpperRight, this._nearPlaneCenter);
this.setPlanes(frustumTop, frustumBottom, frustumLeft, frustumRight);
this.setFrustum(frustumFront, frustumBack, 2 /* FrustumUniformType.Perspective */);
}
this.viewMatrix.matrix.inverseState = core_geometry_1.InverseMatrixState.unknown;
this.viewMatrix.matrix.multiplyVector(this._worldUpVector, this._viewUpVector);
this._viewUpVector.normalizeInPlace();
this._viewUpVector32[0] = this._viewUpVector.x;
this._viewUpVector32[1] = this._viewUpVector.y;
this._viewUpVector32[2] = this._viewUpVector.z;
this.projectionMatrix32.initFromMatrix4d(this.projectionMatrix);
}
changeProjectionMatrix(newMatrix) {
(0, Sync_1.desync)(this);
this.projectionMatrix.setFrom(newMatrix);
this.projectionMatrix32.initFromMatrix4d(this.projectionMatrix);
}
setPlanes(top, bottom, left, right) {
this._planeData[0 /* Plane.kTop */] = top;
this._planeData[1 /* Plane.kBottom */] = bottom;
this._planeData[2 /* Plane.kLeft */] = left;
this._planeData[3 /* Plane.kRight */] = right;
}
setFrustum(nearPlane, farPlane, type) {
this._frustumData[0 /* FrustumData.kNear */] = nearPlane;
this._frustumData[1 /* FrustumData.kFar */] = farPlane;
this._frustumData[2 /* FrustumData.kType */] = type;
// If nearPlane is zero, we don't have a camera (or got very unlucky); in that case shader will compute linear depth.
this._logZData[0] = 0 !== nearPlane ? 1 / nearPlane : 0;
this._logZData[1] = 0 !== nearPlane ? Math.log(farPlane / nearPlane) : farPlane;
}
}
exports.FrustumUniforms = FrustumUniforms;
function normalizedDifference(p0, p1, out) {
const result = undefined !== out ? out : new core_geometry_1.Vector3d();
result.x = p0.x - p1.x;
result.y = p0.y - p1.y;
result.z = p0.z - p1.z;
result.normalizeInPlace();
return result;
}
/** @internal */
function fromSumOf(p, v, scale, out) {
const result = undefined !== out ? out : new core_geometry_1.Point3d();
result.x = p.x + v.x * scale;
result.y = p.y + v.y * scale;
result.z = p.z + v.z * scale;
return result;
}
function dotDifference(pt, origin, vec) {
return (pt.x - origin.x) * vec.x + (pt.y - origin.y) * vec.y + (pt.z - origin.z) * vec.z;
}
function lookIn(eye, viewX, viewY, viewZ, result) {
const rot = result.matrix.coffs;
rot[0] = viewX.x;
rot[1] = viewX.y;
rot[2] = viewX.z;
rot[3] = viewY.x;
rot[4] = viewY.y;
rot[5] = viewY.z;
rot[6] = viewZ.x;
rot[7] = viewZ.y;
rot[8] = viewZ.z;
result.origin.x = -viewX.dotProduct(eye);
result.origin.y = -viewY.dotProduct(eye);
result.origin.z = -viewZ.dotProduct(eye);
}
function ortho(left, right, bottom, top, near, far, result) {
core_geometry_1.Matrix4d.createRowValues(2.0 / (right - left), 0.0, 0.0, -(right + left) / (right - left), 0.0, 2.0 / (top - bottom), 0.0, -(top + bottom) / (top - bottom), 0.0, 0.0, -2.0 / (far - near), -(far + near) / (far - near), 0.0, 0.0, 0.0, 1.0, result);
}
function frustum(left, right, bottom, top, near, far, result) {
core_geometry_1.Matrix4d.createRowValues((2.0 * near) / (right - left), 0.0, (right + left) / (right - left), 0.0, 0.0, (2.0 * near) / (top - bottom), (top + bottom) / (top - bottom), 0.0, 0.0, 0.0, -(far + near) / (far - near), -(2.0 * far * near) / (far - near), 0.0, 0.0, -1.0, 0.0, result);
}
//# sourceMappingURL=FrustumUniforms.js.map