UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

279 lines (228 loc) • 6.44 kB
import { assert } from "../../../assert.js"; import { array_copy } from "../../../collection/array/array_copy.js"; import { array_range_equal_strict } from "../../../collection/array/array_range_equal_strict.js"; import { computeHashFloatArray } from "../../../math/hash/computeHashFloatArray.js"; import { v3_array_matrix4_rotate } from "../../vec3/v3_array_matrix4_rotate.js"; import { v3_array_normalize } from "../../vec3/v3_array_normalize.js"; import { v3_dot } from "../../vec3/v3_dot.js"; import { v3_matrix4_multiply } from "../../vec3/v3_matrix4_multiply.js"; import { ray3_interval_array_apply_matrix4 } from "./ray3_interval_array_apply_matrix4.js"; /** * 3D ray * Optimized representation for faster memory access */ export class Ray3 extends Float32Array { constructor() { super(7); // make ray unbounded this.tMax = Infinity; } get tMax() { return this[6]; } set tMax(v) { this[6] = v; } get origin() { return new Float32Array(this.buffer, this.byteOffset, 3); } /** * * @param {number[]|Float32Array} v */ set origin(v) { array_copy(v, 0, this, 0, 3); } get origin_x() { return this[0]; } get origin_y() { return this[1]; } get origin_z() { return this[2]; } /** * * @param {number} x * @param {number} y * @param {number} z */ setOrigin(x, y, z) { assert.isNumber(x, 'x'); assert.isNumber(y, 'y'); assert.isNumber(z, 'z'); this[0] = x; this[1] = y; this[2] = z; } get direction() { return new Float32Array(this.buffer, this.byteOffset + 3 * 4, 3); } /** * * @param {number[]|Float32Array} v */ set direction(v) { array_copy(v, 0, this, 3, 3); } get direction_x() { return this[3]; } get direction_y() { return this[4]; } get direction_z() { return this[5]; } /** * * @param {number} x * @param {number} y * @param {number} z */ setDirection(x, y, z) { assert.isNumber(x, 'x'); assert.isNumber(y, 'y'); assert.isNumber(z, 'z'); this[3] = x; this[4] = y; this[5] = z; } normalizeDirection() { v3_array_normalize(this, 3, this, 3); } /** * * @param {number} x normalized coordinates in -1,1 range * @param {number} y normalized coordinates in -1,1 range * @param {number} z normalized coordinates in -1,1 range, typically 0.5 * @param {number[]|Float32Array|mat4} transform * @param {number[]|Float32Array|mat4} projection_inverse */ projectFromPerspectiveCamera( x, y, z, transform, projection_inverse ) { assert.isNumber(x, 'x'); assert.isNumber(y, 'y'); assert.isNumber(z, 'z'); // extract position from transform matrix const origin_x = transform[12]; const origin_y = transform[13]; const origin_z = transform[14]; this.setOrigin(origin_x, origin_y, origin_z); this.setDirection(x, y, z); v3_matrix4_multiply(this, 3, this, 3, projection_inverse); v3_matrix4_multiply(this, 3, this, 3, transform); // subtract origin this[3] -= origin_x; this[4] -= origin_y; this[5] -= origin_z; // normalize this.normalizeDirection(); } /** * * @param {number} distance */ shiftForward(distance) { this[0] += this[3] * distance; this[1] += this[4] * distance; this[2] += this[5] * distance; } /** * * @param {number[]|mat4|Float32Array} m4 */ applyMatrix4(m4) { if (this[6] !== Infinity) { ray3_interval_array_apply_matrix4(this, 0, this, 0, m4); } else { // special case v3_matrix4_multiply(this, 0, this, 0, m4); v3_array_matrix4_rotate(this, 3, this, 3, m4); } } /** * * @returns {Ray3} */ clone() { const r = new Ray3(); r.copy(this); return r; } /** * * @param {Ray3} other */ copy(other) { this.set(other); } hash() { return computeHashFloatArray(this); } /** * * @param {Ray3} other * @returns {boolean} */ equals(other) { return array_range_equal_strict(this, 0, other, 0, 7); } /** * Compute `t` value for a given point in 3d space. * This is the distance along the direction from origin of the ray * Value will be negative if the ray is pointing away from the point * PRECONDITION: ray direction must be normalized * @param {number} x * @param {number} y * @param {number} z * @returns {number} */ computeSignedDistance(x, y, z) { assert.isNumber(x, 'x'); assert.isNumber(y, 'y'); assert.isNumber(z, 'z'); // localize point to ray's origin const _x = x - this.origin_x; const _y = y - this.origin_y; const _z = z - this.origin_z; // we assume that direction is normalized return v3_dot(_x, _y, _z, this.direction_x, this.direction_y, this.direction_z); } /** * * @param {number} origin_x * @param {number} origin_y * @param {number} origin_z * @param {number} direction_x * @param {number} direction_y * @param {number} direction_z * @param {number} t_max * @return {Ray3} */ static from( origin_x, origin_y, origin_z, direction_x, direction_y, direction_z, t_max = Infinity ) { const ray = new Ray3(); ray[0] = origin_x; ray[1] = origin_y; ray[2] = origin_z; ray[3] = direction_x; ray[4] = direction_y; ray[5] = direction_z; ray[6] = t_max; return ray; } } /** * Useful for type checking * @readonly * @type {boolean} */ Ray3.prototype.isRay3 = true;