@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
110 lines (87 loc) • 3.67 kB
JavaScript
//
import { max2 } from "../../../math/max2.js";
import { min2 } from "../../../math/min2.js";
import { v3_length } from "../../vec3/v3_length.js";
/**
*
* @param {number[]|ArrayLike<number>|Float32Array} result hit 6-tuple is written here as follows: [hit_position_x, hit_position_y, hit_position_z, hit_normal_x, hit_normal_y, hit_normal_z]
* @param {number} result_offset offset into result array where to write the hit data
* @param {number} x0
* @param {number} y0
* @param {number} z0
* @param {number} x1
* @param {number} y1
* @param {number} z1
* @param {number} origin_x ray origin
* @param {number} origin_y ray origin
* @param {number} origin_z ray origin
* @param {number} direction_x ray direction
* @param {number} direction_y ray direction
* @param {number} direction_z ray direction
* @returns {boolean} true if ray hit the box, false otherwise
*
*
* @see https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
* @see https://gdbooks.gitbooks.io/3dcollisions/content/Chapter3/raycast_aabb.html
* @see https://blog.johnnovak.net/2016/10/22/the-nim-ray-tracer-project-part-4-calculating-box-normals/
*/
export function aabb3_raycast(
result, result_offset,
x0, y0, z0, x1, y1, z1,
origin_x, origin_y, origin_z,
direction_x, direction_y, direction_z
) {
// first find intersection
const dir_inv_x = 1 / direction_x;
const dir_inv_y = 1 / direction_y;
const dir_inv_z = 1 / direction_z;
const t1_x = (x0 - origin_x) * dir_inv_x;
const t2_x = (x1 - origin_x) * dir_inv_x;
const t1_y = (y0 - origin_y) * dir_inv_y;
const t2_y = (y1 - origin_y) * dir_inv_y;
const t1_z = (z0 - origin_z) * dir_inv_z;
const t2_z = (z1 - origin_z) * dir_inv_z;
let tMax = max2(t1_x, t2_x);
tMax = min2(tMax, max2(t1_y, t2_y));
tMax = min2(tMax, max2(t1_z, t2_z));
if (tMax < 0) {
// if tMax < 0, AABB is behind the ray origin in the opposite direction to the ray's
return false;
}
let tMin = min2(t1_x, t2_x);
tMin = max2(tMin, min2(t1_y, t2_y));
tMin = max2(tMin, min2(t1_z, t2_z));
if (tMin > tMax) {
// if tMin > tMax, ray does not intersect the AABB
return false;
}
// ray hit detected, figure out offset along the ray from origin to hit
const ray_offset = tMin < 0 ? tMax : tMin;
const hit_x = origin_x + direction_x * ray_offset;
const hit_y = origin_y + direction_y * ray_offset;
const hit_z = origin_z + direction_z * ray_offset;
// figure out normal
const center_x = (x0 + x1) * 0.5;
const center_y = (y0 + y1) * 0.5;
const center_z = (z0 + z1) * 0.5;
const p_x = hit_x - center_x;
const p_y = hit_y - center_y;
const p_z = hit_z - center_z;
const d_x = (x0 - x1) * 0.5;
const d_y = (y0 - y1) * 0.5;
const d_z = (z0 - z1) * 0.5;
// bias is necessary to deal with rounding errors
const bias = 1.000001;
const normal_x = (p_x / Math.abs(d_x) * bias) | 0;
const normal_y = (p_y / Math.abs(d_y) * bias) | 0;
const normal_z = (p_z / Math.abs(d_z) * bias) | 0;
// normalize surface normal vector
const normal_inv_length = 1 / v3_length(normal_x, normal_y, normal_z);
result[result_offset] = hit_x;
result[result_offset + 1] = hit_y;
result[result_offset + 2] = hit_z;
result[result_offset + 3] = normal_x * normal_inv_length;
result[result_offset + 4] = normal_y * normal_inv_length;
result[result_offset + 5] = normal_z * normal_inv_length;
return true;
}