Skip to content

Instantly share code, notes, and snippets.

@z0ero
Created August 18, 2023 06:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save z0ero/02dc6f83f0a1336efe815c93caa79c81 to your computer and use it in GitHub Desktop.
Save z0ero/02dc6f83f0a1336efe815c93caa79c81 to your computer and use it in GitHub Desktop.
// レイと球のコリジョン
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