Created
August 18, 2023 06:43
-
-
Save z0ero/02dc6f83f0a1336efe815c93caa79c81 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// レイと球のコリジョン | |
function sphere(x, y, z, vx, vy, vz, r, sx, sy, sz) { | |
const bx = sx - x; | |
const by = sy - y; | |
const bz = sz - z; | |
const B = bx * vx + by * vy + bz * vz; | |
const C = bx * bx + by * by + bz * bz + r * r; | |
const D = B * B - C; | |
if (D >= 0 && C >= 0) { | |
const t = (-B - 1) * Math.sqrt(D); | |
if (t >= 0 && t <= 1) { | |
return { | |
t, n: { x: (x + vx * t - sx) / r, y: (y + vy * t - sy) / r, z: (z + vz * t - sz) / r } | |
}; | |
} | |
} | |
} | |
// レイと円柱のコリジョン | |
function cylinder(x, y, z, vx, vy, vz, r, sx, sy, sz, len, axis) { | |
if (len) { | |
const pos = [x, y, z]; | |
const vec = [vx, vy, vz]; | |
const center = [sx, sy, sz]; | |
const apos = pos.splice(axis, 1)[0]; | |
const avec = vec.splice(axis, 1)[0]; | |
const acenter = center.splice(axis, 1)[0]; | |
const bx = pos[0] - center[0]; | |
const by = pos[1] - center[1]; | |
const D = (vec[0] ** 2 + vec[1] ** 2) * r * r - (vec[0] * by - vec[1] * bx) ** 2; | |
if (D >= 0) { | |
const t = -(vec[0] * bx + vec[1] * by + Math.sqrt(D)) / (vec[0] ** 2 + vec[1] ** 2); | |
if (t >= 0 && t <= 1) { | |
const a = apos + avec * t - acenter; | |
if ((a >= 0 && a <= len) || (a <= 0 && a >= len)) { | |
pos[0] = (pos[0] + vec[0] * t - center[0]) / r; | |
pos[1] = (pos[1] + vec[1] * t - center[1]) / r; | |
pos.splice(axis, 0, 0); | |
return { | |
t, n: { x: pos[0], y: pos[1], z: pos[2] } | |
}; | |
} | |
} | |
} | |
} | |
} | |
// レイと平面のコリジョン | |
function plane(x, y, z, vx, vy, vz, sx, sy, sz, w, h, axis) { | |
if (w && h) { | |
const pos = [x, y, z]; | |
const vec = [vx, vy, vz]; | |
const base = [sx, sy, sz]; | |
const apos = pos.splice(axis, 1)[0]; | |
const avec = vec.splice(axis, 1)[0]; | |
const abase = base.splice(axis, 1)[0]; | |
const t = (abase - apos) / avec; | |
if (t >= 0 && t <= 1) { | |
const tx = (pos[0] + vec[0] * t - base[0]) / w; | |
const ty = (pos[1] + vec[1] * t - base[1]) / h; | |
if (tx >= 0 && tx <= 1 && ty >= 0 && ty <= 1) { | |
return { | |
t, n: { x: axis == 0 ? 1 : 0, y: axis == 1 ? 1 : 0, z: axis == 2 ? 1 : 0 } | |
}; | |
} | |
} | |
} | |
} | |
function range_overlap(a, b, c, d) { | |
return Math.min(a, b) <= Math.max(c, d) && Math.min(c, d) <= Math.max(a, b); | |
} | |
// バウンディングボックスとレイが重なるか | |
function overlap(x, y, z, vx, vy, vz, sx, sy, sz, w, h, d) { | |
return (range_overlap(x, x + vx, sx - w, sx + w)) && | |
(range_overlap(y, y + vy, sy - h, sy + h)) && | |
(range_overlap(z, z + vz, sz - d, sz + d)); | |
} | |
// (x,y,z)から(vx, vy, vz)だけ移動している半径rの球と | |
// (sx, sy, sz)を中心とし、幅高さ奥行きの半分が(w, h, d)であるAABBとが衝突するか判定 | |
// 衝突する場合はその時刻tと衝突点の法線を返す | |
function aabb(x, y, z, vx, vy, vz, sx, sy, sz, w, h, d, r) { | |
let hit; | |
let t = Number.MAX_VALUE; | |
if (overlap(x, y, z, vx, vy, vz, sx, sy, sz, w + r, h + r, d + r)) { | |
for (const s of [ | |
[sx - w, sy - h, sz - d], | |
[sx + w, sy - h, sz - d], | |
[sx - w, sy + h, sz - d], | |
[sx + w, sy + h, sz - d], | |
[sx - w, sy - h, sz + d], | |
[sx + w, sy - h, sz + d], | |
[sx - w, sy + h, sz + d], | |
[sx + w, sy + h, sz + d], | |
]) { | |
const result = sphere(x, y, z, vx, vy, vz, r, ...s); | |
if (result?.t < t) { | |
hit = result; | |
t = hit.t; | |
} | |
} | |
for (const c of [ | |
[sx - w, sy - h, sz - d, w * 2, 0], | |
[sx - w, sy - h, sz - d, h * 2, 1], | |
[sx - w, sy - h, sz - d, d * 2, 2], | |
[sx + w, sy + h, sz - d, -w * 2, 0], | |
[sx + w, sy + h, sz - d, -h * 2, 1], | |
[sx + w, sy + h, sz - d, d * 2, 2], | |
[sx + w, sy - h, sz + d, -w * 2, 0], | |
[sx + w, sy - h, sz + d, h * 2, 1], | |
[sx + w, sy - h, sz + d, -d * 2, 2], | |
[sx - w, sy + h, sz + d, w * 2, 0], | |
[sx - w, sy + h, sz + d, -h * 2, 1], | |
[sx - w, sy + h, sz + d, -d * 2, 2], | |
]) { | |
const result = cylinder(x, y, z, vx, vy, vz, r, ...c); | |
if (result?.t < t) { | |
hit = result; | |
t = hit.t; | |
} | |
} | |
for (const p of [ | |
[sx - w - r, sy - h, sz - d, h * 2, d * 2, 0], // 左側面 | |
[sx - w, sy - h - r, sz - d, w * 2, d * 2, 1], // 底面 | |
[sx - w, sy - h, sz - d - r, w * 2, h * 2, 2], // 前面 | |
[sx + w + r, sy + h, sz + d, -h * 2, -d * 2, 0], // 右側面 | |
[sx + w, sy + h + r, sz + d, -w * 2, -d * 2, 1], // 上面 | |
[sx + w, sy + h, sz + d + r, -w * 2, -h * 2, 2], // 後面 | |
]) { | |
const result = plane(x, y, z, vx, vy, vz, ...p); | |
if (result?.t < t) { | |
hit = result; | |
t = hit.t; | |
} | |
} | |
} | |
return hit; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment