UNPKG

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.

404 lines (403 loc) 12 kB
import { Quat } from "./Quat.mjs"; //#region src/math/Vec3.ts /** * Really basic 3D vector class used for vector calculations. * @see https://github.com/mrdoob/three.js/blob/dev/src/math/Vector3.js * @see http://glmatrix.net/docs/vec3.js.html */ var Vec3 = class Vec3 { /** * Vec3 constructor * @param x - X component of our {@link Vec3}. * @param y - Y component of our {@link Vec3}. * @param z - Z component of our {@link Vec3}. */ constructor(x = 0, y = x, z = x) { this.type = "Vec3"; this._x = x; this._y = y; this._z = z; } /** * Get the X component of the {@link Vec3}. */ get x() { return this._x; } /** * Set the X component of the {@link Vec3}. * Can trigger {@link onChange} callback. * @param value - X component to set. */ set x(value) { const changed = value !== this._x; this._x = value; changed && this._onChangeCallback && this._onChangeCallback(); } /** * Get the Y component of the {@link Vec3}. */ get y() { return this._y; } /** * Set the Y component of the {@link Vec3}. * Can trigger {@link onChange} callback. * @param value - Y component to set. */ set y(value) { const changed = value !== this._y; this._y = value; changed && this._onChangeCallback && this._onChangeCallback(); } /** * Get the Z component of the {@link Vec3}. */ get z() { return this._z; } /** * Set the Z component of the {@link Vec3}. * Can trigger {@link onChange} callback. * @param value - Z component to set. */ set z(value) { const changed = value !== this._z; this._z = value; changed && this._onChangeCallback && this._onChangeCallback(); } /** * Called when at least one component of the {@link Vec3} has changed. * @param callback - Callback to run when at least one component of the {@link Vec3} has changed. * @returns - Our {@link Vec3}. */ onChange(callback) { if (callback) this._onChangeCallback = callback; return this; } /** * Set the {@link Vec3} from values. * @param x - New X component to set. * @param y - New Y component to set. * @param z - New Z component to set. * @returns - This {@link Vec3} after being set. */ set(x = 0, y = x, z = x) { this.x = x; this.y = y; this.z = z; return this; } /** * Add a {@link Vec3} to this {@link Vec3}. * @param vector - {@link Vec3} to add. * @returns - This {@link Vec3} after addition. */ add(vector = new Vec3()) { this.x += vector.x; this.y += vector.y; this.z += vector.z; return this; } /** * Add a scalar to all the components of this {@link Vec3}. * @param value - Number to add. * @returns - This {@link Vec3} after addition. */ addScalar(value = 0) { this.x += value; this.y += value; this.z += value; return this; } /** * Subtract a {@link Vec3} from this {@link Vec3}. * @param vector - {@link Vec3} to subtract. * @returns - This {@link Vec3} after subtraction. */ sub(vector = new Vec3()) { this.x -= vector.x; this.y -= vector.y; this.z -= vector.z; return this; } /** * Subtract a scalar to all the components of this {@link Vec3}. * @param value - Number to subtract. * @returns - This {@link Vec3} after subtraction. */ subScalar(value = 0) { this.x -= value; this.y -= value; this.z -= value; return this; } /** * Multiply a {@link Vec3} with this {@link Vec3}. * @param vector - {@link Vec3} to multiply with. * @returns - This {@link Vec3} after multiplication. */ multiply(vector = new Vec3(1)) { this.x *= vector.x; this.y *= vector.y; this.z *= vector.z; return this; } /** * Multiply all components of this {@link Vec3} with a scalar. * @param value - Number to multiply with. * @returns - This {@link Vec3} after multiplication. */ multiplyScalar(value = 1) { this.x *= value; this.y *= value; this.z *= value; return this; } /** * Divide a {@link Vec3} with this {@link Vec3}. * @param vector - {@link Vec3} to divide with. * @returns - This {@link Vec3} after division. */ divide(vector = new Vec3(1)) { this.x /= vector.x; this.y /= vector.y; this.z /= vector.z; return this; } /** * Divide all components of this {@link Vec3} with a scalar. * @param value - number to divide with. * @returns - This {@link Vec3} after division. */ divideScalar(value = 1) { this.x /= value; this.y /= value; this.z /= value; return this; } /** * Copy a {@link Vec3} into this {@link Vec3}. * @param vector - {@link Vec3} to copy. * @returns - This {@link Vec3} after copy. */ copy(vector = new Vec3()) { this.x = vector.x; this.y = vector.y; this.z = vector.z; return this; } /** * Clone this {@link Vec3}. * @returns - Cloned {@link Vec3}. */ clone() { return new Vec3(this.x, this.y, this.z); } /** * Apply max values to this {@link Vec3} components. * @param vector - {@link Vec3} representing max values. * @returns - {@link Vec3} with max values applied. */ max(vector = new Vec3()) { this.x = Math.max(this.x, vector.x); this.y = Math.max(this.y, vector.y); this.z = Math.max(this.z, vector.z); return this; } /** * Apply min values to this {@link Vec3} components. * @param vector - {@link Vec3} representing min values. * @returns - {@link Vec3} with min values applied. */ min(vector = new Vec3()) { this.x = Math.min(this.x, vector.x); this.y = Math.min(this.y, vector.y); this.z = Math.min(this.z, vector.z); return this; } /** * Clamp this {@link Vec3} components by min and max {@link Vec3} vectors. * @param min - Minimum {@link Vec3} components to compare with. * @param max - Maximum {@link Vec3} components to compare with. * @returns - Clamped {@link Vec3}. */ clamp(min = new Vec3(), max = new Vec3()) { this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } /** * Check if 2 {@link Vec3} are equal. * @param vector - {@link Vec3} to compare. * @returns - Whether the {@link Vec3} are equals or not. */ equals(vector = new Vec3()) { return this.x === vector.x && this.y === vector.y && this.z === vector.z; } /** * Get the square length of this {@link Vec3}. * @returns - Square length of this {@link Vec3}. */ lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } /** * Get the length of this {@link Vec3}. * @returns - Length of this {@link Vec3}. */ length() { return Math.sqrt(this.lengthSq()); } /** * Get the euclidian distance between this {@link Vec3} and another {@link Vec3}. * @param vector - {@link Vec3} to use for distance calculation. * @returns - Euclidian distance. */ distance(vector = new Vec3()) { return Math.hypot(vector.x - this.x, vector.y - this.y, vector.z - this.z); } /** * Normalize this {@link Vec3}. * @returns - Normalized {@link Vec3}. */ normalize() { let len = this.lengthSq(); if (len > 0) len = 1 / Math.sqrt(len); this.x *= len; this.y *= len; this.z *= len; return this; } /** * Calculate the dot product of 2 {@link Vec3}. * @param vector - {@link Vec3} to use for dot product. * @returns - Dot product of the 2 {@link Vec3}. */ dot(vector = new Vec3()) { return this.x * vector.x + this.y * vector.y + this.z * vector.z; } /** * Get the cross product of this {@link Vec3} with another {@link Vec3}. * @param vector - {@link Vec3} to use for cross product. * @returns - This {@link Vec3} after cross product. */ cross(vector = new Vec3()) { return this.crossVectors(this, vector); } /** * Set this {@link Vec3} as the result of the cross product of two {@link Vec3}. * @param a - First {@link Vec3} to use for cross product. * @param b - Second {@link Vec3} to use for cross product. * @returns - This {@link Vec3} after cross product. */ crossVectors(a = new Vec3(), b = new Vec3()) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } /** * Calculate the linear interpolation of this {@link Vec3} by given {@link Vec3} and alpha, where alpha is the percent distance along the line. * @param vector - {@link Vec3} to interpolate towards. * @param alpha - Interpolation factor in the [0, 1] interval. * @returns - This {@link Vec3} after linear interpolation. */ lerp(vector = new Vec3(), alpha = 1) { this.x += (vector.x - this.x) * alpha; this.y += (vector.y - this.y) * alpha; this.z += (vector.z - this.z) * alpha; return this; } /** * Apply a {@link Mat4 | matrix} to a {@link Vec3}. * Useful to convert a position {@link Vec3} from plane local world to webgl space using projection view matrix for example. * Source code from: http://glmatrix.net/docs/vec3.js.html * @param matrix - {@link Mat4 | matrix} to use. * @returns - This {@link Vec3} after {@link Mat4 | matrix} application. */ applyMat4(matrix) { const x = this._x, y = this._y, z = this._z; const mArray = matrix.elements; let w = mArray[3] * x + mArray[7] * y + mArray[11] * z + mArray[15]; w = w || 1; this.x = (mArray[0] * x + mArray[4] * y + mArray[8] * z + mArray[12]) / w; this.y = (mArray[1] * x + mArray[5] * y + mArray[9] * z + mArray[13]) / w; this.z = (mArray[2] * x + mArray[6] * y + mArray[10] * z + mArray[14]) / w; return this; } /** * Set this {@link Vec3} to the translation component of a {@link Mat4 | matrix}. * @param matrix - {@link Mat4 | matrix} to use. * @returns - This {@link Vec3} after {@link Mat4 | matrix} application. */ setFromMatrixPosition(matrix) { const e = matrix.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } /** * Apply a {@link Quat | quaternion} (rotation in 3D space) to this {@link Vec3}. * @param quaternion - {@link Quat | quaternion} to use. * @returns - This {@link Vec3} with the transformation applied. */ applyQuat(quaternion = new Quat()) { const x = this.x, y = this.y, z = this.z; const qx = quaternion.elements[0], qy = quaternion.elements[1], qz = quaternion.elements[2], qw = quaternion.elements[3]; const tx = 2 * (qy * z - qz * y); const ty = 2 * (qz * x - qx * z); const tz = 2 * (qx * y - qy * x); this.x = x + qw * tx + qy * tz - qz * ty; this.y = y + qw * ty + qz * tx - qx * tz; this.z = z + qw * tz + qx * ty - qy * tx; return this; } /** * Rotate a {@link Vec3} around and axis by a given angle. * @param axis - Normalized {@link Vec3} around which to rotate. * @param angle - Angle (in radians) to rotate. * @param quaternion - Optional {@link Quat | quaternion} to use for rotation computations. * @returns - This {@link Vec3} with the rotation applied. */ applyAxisAngle(axis = new Vec3(), angle = 0, quaternion = new Quat()) { return this.applyQuat(quaternion.setFromAxisAngle(axis, angle)); } /** * Transforms the direction of this vector by a {@link Mat4} (the upper left 3 x 3 subset) and then normalizes the result. * @param matrix - {@link Mat4} to use for transformation. * @returns - This {@link Vec3} with the transformation applied. */ transformDirection(matrix) { const x = this.x, y = this.y, z = this.z; const e = matrix.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } /** * Project a 3D coordinate {@link Vec3} to a 2D coordinate {@link Vec3}. * @param camera - {@link Camera} to use for projection. * @returns - Projected {@link Vec3}. */ project(camera) { this.applyMat4(camera.viewMatrix).applyMat4(camera.projectionMatrix); return this; } /** * Unproject a 2D coordinate {@link Vec3} to 3D coordinate {@link Vec3}. * @param camera - {@link Camera} to use for projection. * @returns - Unprojected {@link Vec3}. */ unproject(camera) { this.applyMat4(camera.projectionMatrix.getInverse()).applyMat4(camera.modelMatrix); return this; } }; //#endregion export { Vec3 };