@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
279 lines (228 loc) • 6.44 kB
JavaScript
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;