gpu-curtains
Version:
gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.
195 lines (192 loc) • 7.48 kB
JavaScript
import { Mat4 } from '../../math/Mat4.mjs';
import { Vec3 } from '../../math/Vec3.mjs';
import { Camera } from './Camera.mjs';
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
var _fov;
class PerspectiveCamera extends Camera {
/**
* PerspectiveCamera constructor
* @param parameters - {@link PerspectiveCameraParams} used to create our {@link PerspectiveCamera}.
*/
constructor({
fov = 50,
near = 0.1,
far = 150,
width = 1,
height = 1,
pixelRatio = 1,
forceAspect = false,
onMatricesChanged = () => {
}
} = {}) {
super({ near, far, pixelRatio, onMatricesChanged });
/** @ignore */
__privateAdd(this, _fov);
this.forceAspect = forceAspect;
this.position.set(0, 0, 10);
this.size = {
width: 1,
height: 1
};
this.setPerspective({ fov, near, far, width, height, pixelRatio });
}
/**
* Get the {@link PerspectiveCamera.fov | field of view}.
*/
get fov() {
return __privateGet(this, _fov);
}
/**
* Set the {@link PerspectiveCamera.fov | field of view}. Update the {@link projectionMatrix} only if the field of view actually changed.
* @param fov - New field of view.
*/
set fov(fov) {
fov = Math.max(1, Math.min(fov ?? this.fov, 179));
if (fov !== this.fov) {
__privateSet(this, _fov, fov);
this.shouldUpdateProjectionMatrices();
}
this.setVisibleSize();
this.setCSSPerspective();
}
/**
* Set the {@link PerspectiveCamera} {@link RectSize.width | width} and {@link RectSize.height | height}. Update the {@link projectionMatrix} only if the width or height actually changed.
* @param size - New width and height values to use.
*/
setSize({ width, height }) {
if (width !== this.size.width || height !== this.size.height) {
this.shouldUpdateProjectionMatrices();
}
this.size.width = width;
this.size.height = height;
this.setVisibleSize();
this.setCSSPerspective();
}
/**
* Sets the {@link PerspectiveCamera} perspective projection settings. Update the {@link projectionMatrix} if needed.
* @param parameters - {@link PerspectiveCameraOptions} to use for the perspective projection.
*/
setPerspective({
fov = this.fov,
near = this.near,
far = this.far,
width = this.size.width,
height = this.size.height,
pixelRatio = this.pixelRatio
} = {}) {
this.setSize({ width, height });
this.pixelRatio = pixelRatio;
this.fov = fov;
this.near = near;
this.far = far;
}
/**
* Sets a {@link CSSPerspective} property based on {@link size}, {@link pixelRatio} and {@link fov}.
*
* Used to translate planes along the Z axis using pixel units as CSS would do.
*
* {@link https://stackoverflow.com/questions/22421439/convert-field-of-view-value-to-css3d-perspective-value | See reference}
*/
setCSSPerspective() {
this.CSSPerspective = Math.pow(
Math.pow(this.size.width / (2 * this.pixelRatio), 2) + Math.pow(this.size.height / (2 * this.pixelRatio), 2),
0.5
) / Math.tan(this.fov * 0.5 * Math.PI / 180);
}
/**
* Get visible width / height at a given z-depth from our {@link PerspectiveCamera} parameters.
*
* {@link https://discourse.threejs.org/t/functions-to-calculate-the-visible-width-height-at-a-given-z-depth-from-a-perspective-camera/269 | See reference}.
* @param depth - Depth to use for calculations.
* @returns - Visible width and height at given depth.
*/
getVisibleSizeAtDepth(depth = 0) {
const cameraOffset = this.position.z;
if (depth < cameraOffset) {
depth -= cameraOffset;
} else {
depth += cameraOffset;
}
const vFOV = this.fov * Math.PI / 180;
const height = 2 * Math.tan(vFOV / 2) * Math.abs(depth);
return {
width: height * this.size.width / this.size.height,
height
};
}
/**
* Updates the {@link PerspectiveCamera} {@link projectionMatrix}.
*/
updateProjectionMatrix() {
this.projectionMatrix.makePerspective({
fov: this.fov,
aspect: this.size.width / this.size.height,
near: this.near,
far: this.far
});
}
/**
* Get the current {@link PerspectiveCamera} frustum planes in the [left, right, top, bottom, near, far] order, based on its {@link projectionMatrix} and {@link viewMatrix}.
* @returns - Frustum planes as an array of 6 faces in the [left, right, top, bottom, near, far] order, made of {@link Float32Array} of length 4.
* @readonly
*/
get frustumPlanes() {
const tempCamMat4 = new Mat4();
const tempCamVec3 = new Vec3();
tempCamMat4.copy(this.projectionMatrix).multiply(this.viewMatrix);
const { elements } = tempCamMat4;
const frustumPlanes = [
new Float32Array(4),
new Float32Array(4),
new Float32Array(4),
new Float32Array(4),
new Float32Array(4),
new Float32Array(4)
];
tempCamVec3.set(elements[3] + elements[0], elements[7] + elements[4], elements[11] + elements[8]);
let l = tempCamVec3.length();
frustumPlanes[0][0] = tempCamVec3.x / l;
frustumPlanes[0][1] = tempCamVec3.y / l;
frustumPlanes[0][2] = tempCamVec3.z / l;
frustumPlanes[0][3] = (elements[15] + elements[12]) / l;
tempCamVec3.set(elements[3] - elements[0], elements[7] - elements[4], elements[11] - elements[8]);
l = tempCamVec3.length();
frustumPlanes[1][0] = tempCamVec3.x / l;
frustumPlanes[1][1] = tempCamVec3.y / l;
frustumPlanes[1][2] = tempCamVec3.z / l;
frustumPlanes[1][3] = (elements[15] - elements[12]) / l;
tempCamVec3.set(elements[3] - elements[1], elements[7] - elements[5], elements[11] - elements[9]);
l = tempCamVec3.length();
frustumPlanes[2][0] = tempCamVec3.x / l;
frustumPlanes[2][1] = tempCamVec3.y / l;
frustumPlanes[2][2] = tempCamVec3.z / l;
frustumPlanes[2][3] = (elements[15] - elements[13]) / l;
tempCamVec3.set(elements[3] + elements[1], elements[7] + elements[5], elements[11] + elements[9]);
l = tempCamVec3.length();
frustumPlanes[3][0] = tempCamVec3.x / l;
frustumPlanes[3][1] = tempCamVec3.y / l;
frustumPlanes[3][2] = tempCamVec3.z / l;
frustumPlanes[3][3] = (elements[15] + elements[13]) / l;
tempCamVec3.set(elements[2], elements[6], elements[10]);
l = tempCamVec3.length();
frustumPlanes[4][0] = tempCamVec3.x / l;
frustumPlanes[4][1] = tempCamVec3.y / l;
frustumPlanes[4][2] = tempCamVec3.z / l;
frustumPlanes[4][3] = elements[14] / l;
tempCamVec3.set(elements[3] - elements[2], elements[7] - elements[6], elements[11] - elements[10]);
l = tempCamVec3.length();
frustumPlanes[5][0] = tempCamVec3.x / l;
frustumPlanes[5][1] = tempCamVec3.y / l;
frustumPlanes[5][2] = tempCamVec3.z / l;
frustumPlanes[5][3] = (elements[15] - elements[14]) / l;
return frustumPlanes;
}
}
_fov = new WeakMap();
export { PerspectiveCamera };