@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
161 lines (128 loc) • 4.47 kB
JavaScript
import { v3_dot } from "../../vec3/v3_dot.js";
import { assert } from "../../../assert.js";
/**
* NOTE: adapted from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
* @source https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm (Möller and Trumbore, « Fast, Minimum Storage Ray-Triangle Intersection », Journal of Graphics Tools, vol. 2, 1997, p. 21–28)
* @param {SurfacePoint3} result
* @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} ax
* @param {number} ay
* @param {number} az
* @param {number} bx
* @param {number} by
* @param {number} bz
* @param {number} cx
* @param {number} cy
* @param {number} cz
* @returns {boolean}
*/
export function computeTriangleRayIntersection(
result,
origin_x, origin_y, origin_z,
direction_x, direction_y, direction_z,
ax, ay, az,
bx, by, bz,
cx, cy, cz
) {
assert.isNumber(ax, 'ax');
assert.isNumber(ay, 'ay');
assert.isNumber(az, 'az');
assert.isNumber(bx, 'bx');
assert.isNumber(by, 'by');
assert.isNumber(bz, 'bz');
assert.isNumber(cx, 'cx');
assert.isNumber(cy, 'cy');
assert.isNumber(cz, 'cz');
// nan checks
assert.notNaN(ax, 'ax');
assert.notNaN(ay, 'ay');
assert.notNaN(az, 'az');
assert.notNaN(bx, 'bx');
assert.notNaN(by, 'by');
assert.notNaN(bz, 'bz');
assert.notNaN(cx, 'cx');
assert.notNaN(cy, 'cy');
assert.notNaN(cz, 'cz');
// finate number check
assert.isFiniteNumber(ax, 'ax');
assert.isFiniteNumber(ay, 'ay');
assert.isFiniteNumber(az, 'az');
assert.isFiniteNumber(bx, 'bx');
assert.isFiniteNumber(by, 'by');
assert.isFiniteNumber(bz, 'bz');
assert.isFiniteNumber(cx, 'cx');
assert.isFiniteNumber(cy, 'cy');
assert.isFiniteNumber(cz, 'cz');
// edge1 = a - b
const edge1_x = bx - ax;
const edge1_y = by - ay;
const edge1_z = bz - az;
// edge2 = c - a
const edge2_x = cx - ax;
const edge2_y = cy - ay;
const edge2_z = cz - az;
// Compute triangle normal
// normal = edge1 x edge2
const normal_x = edge1_y * edge2_z - edge1_z * edge2_y;
const normal_y = edge1_z * edge2_x - edge1_x * edge2_z;
const normal_z = edge1_x * edge2_y - edge1_y * edge2_x
let DdN = v3_dot(direction_x, direction_y, direction_z, normal_x, normal_y, normal_z);
let sign = 1;
if (DdN === 0) {
// ray and the triangle normal are orthogonal, means that the ray can not pass penetrate triangle
return false;
} else if (DdN < 0) {
sign = -1;
DdN = -DdN;
}
// diff = origin - a
const diff_x = origin_x - ax;
const diff_y = origin_y - ay;
const diff_z = origin_z - az;
// edge3 = diff x edge2
const edge3_x = diff_y * edge2_z - diff_z * edge2_y;
const edge3_y = diff_z * edge2_x - diff_x * edge2_z;
const edge3_z = diff_x * edge2_y - diff_y * edge2_x;
// DdQxE2 = sign * ( direction . edge3)
const DdQxE2 = sign * v3_dot(direction_x, direction_y, direction_z, edge3_x, edge3_y, edge3_z);
if (DdQxE2 < 0) {
// b1 < 0, no intersection
return false;
}
const edge4_x = edge1_y * diff_z - edge1_z * diff_y;
const edge4_y = edge1_z * diff_x - edge1_x * diff_z;
const edge4_z = edge1_x * diff_y - edge1_y * diff_x;
const DdE1xQ = sign * v3_dot(direction_x, direction_y, direction_z, edge4_x, edge4_y, edge4_z);
if (DdE1xQ < 0) {
// b2 < 0, no intersection
return false;
}
if (DdQxE2 + DdE1xQ > DdN) {
// b1+b2 > 1, no intersection
return false;
}
// Line intersects triangle, check if ray does.
const QdN = -sign * v3_dot(diff_x, diff_y, diff_z, normal_x, normal_y, normal_z);
if (QdN < 0) {
// t < 0, no intersection
return false;
}
// Ray intersects triangle.
const t = QdN / DdN;
result.normal.set(
normal_x,
normal_y,
normal_z
);
result.position.set(
direction_x * t + origin_x,
direction_y * t + origin_y,
direction_z * t + origin_z,
);
return true;
}