UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

110 lines (87 loc) 3.67 kB
// 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; }