intersect.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. --- Various geometric intersections
  2. -- @module intersect
  3. local modules = (...):gsub('%.[^%.]+$', '') .. "."
  4. local constants = require(modules .. "constants")
  5. local mat4 = require(modules .. "mat4")
  6. local vec3 = require(modules .. "vec3")
  7. local utils = require(modules .. "utils")
  8. local DBL_EPSILON = constants.DBL_EPSILON
  9. local sqrt = math.sqrt
  10. local abs = math.abs
  11. local min = math.min
  12. local max = math.max
  13. local intersect = {}
  14. -- https://blogs.msdn.microsoft.com/rezanour/2011/08/07/barycentric-coordinates-and-point-in-triangle-tests/
  15. -- point is a vec3
  16. -- triangle[1] is a vec3
  17. -- triangle[2] is a vec3
  18. -- triangle[3] is a vec3
  19. function intersect.point_triangle(point, triangle)
  20. local u = triangle[2] - triangle[1]
  21. local v = triangle[3] - triangle[1]
  22. local w = point - triangle[1]
  23. local vw = v:cross(w)
  24. local vu = v:cross(u)
  25. if vw:dot(vu) < 0 then
  26. return false
  27. end
  28. local uw = u:cross(w)
  29. local uv = u:cross(v)
  30. if uw:dot(uv) < 0 then
  31. return false
  32. end
  33. local d = uv:len()
  34. local r = vw:len() / d
  35. local t = uw:len() / d
  36. return r + t <= 1
  37. end
  38. -- point is a vec3
  39. -- aabb.min is a vec3
  40. -- aabb.max is a vec3
  41. function intersect.point_aabb(point, aabb)
  42. return
  43. aabb.min.x <= point.x and
  44. aabb.max.x >= point.x and
  45. aabb.min.y <= point.y and
  46. aabb.max.y >= point.y and
  47. aabb.min.z <= point.z and
  48. aabb.max.z >= point.z
  49. end
  50. -- point is a vec3
  51. -- frustum.left is a plane { a, b, c, d }
  52. -- frustum.right is a plane { a, b, c, d }
  53. -- frustum.bottom is a plane { a, b, c, d }
  54. -- frustum.top is a plane { a, b, c, d }
  55. -- frustum.near is a plane { a, b, c, d }
  56. -- frustum.far is a plane { a, b, c, d }
  57. function intersect.point_frustum(point, frustum)
  58. local x, y, z = point:unpack()
  59. local planes = {
  60. frustum.left,
  61. frustum.right,
  62. frustum.bottom,
  63. frustum.top,
  64. frustum.near,
  65. frustum.far or false
  66. }
  67. -- Skip the last test for infinite projections, it'll never fail.
  68. if not planes[6] then
  69. table.remove(planes)
  70. end
  71. local dot
  72. for i = 1, #planes do
  73. dot = planes[i].a * x + planes[i].b * y + planes[i].c * z + planes[i].d
  74. if dot <= 0 then
  75. return false
  76. end
  77. end
  78. return true
  79. end
  80. -- http://www.lighthouse3d.com/tutorials/maths/ray-triangle-intersection/
  81. -- ray.position is a vec3
  82. -- ray.direction is a vec3
  83. -- triangle[1] is a vec3
  84. -- triangle[2] is a vec3
  85. -- triangle[3] is a vec3
  86. function intersect.ray_triangle(ray, triangle)
  87. local e1 = triangle[2] - triangle[1]
  88. local e2 = triangle[3] - triangle[1]
  89. local h = ray.direction:cross(e2)
  90. local a = h:dot(e1)
  91. -- if a is too close to 0, ray does not intersect triangle
  92. if abs(a) <= DBL_EPSILON then
  93. return false
  94. end
  95. local f = 1 / a
  96. local s = ray.position - triangle[1]
  97. local u = s:dot(h) * f
  98. -- ray does not intersect triangle
  99. if u < 0 or u > 1 then
  100. return false
  101. end
  102. local q = s:cross(e1)
  103. local v = ray.direction:dot(q) * f
  104. -- ray does not intersect triangle
  105. if v < 0 or u + v > 1 then
  106. return false
  107. end
  108. -- at this stage we can compute t to find out where
  109. -- the intersection point is on the line
  110. local t = q:dot(e2) * f
  111. -- return position of intersection and distance from ray origin
  112. if t >= DBL_EPSILON then
  113. return ray.position + ray.direction * t, t
  114. end
  115. -- ray does not intersect triangle
  116. return false
  117. end
  118. -- https://gamedev.stackexchange.com/questions/96459/fast-ray-sphere-collision-code
  119. -- ray.position is a vec3
  120. -- ray.direction is a vec3
  121. -- sphere.position is a vec3
  122. -- sphere.radius is a number
  123. function intersect.ray_sphere(ray, sphere)
  124. local offset = ray.position - sphere.position
  125. local b = offset:dot(ray.direction)
  126. local c = offset:dot(offset) - sphere.radius * sphere.radius
  127. -- ray's position outside sphere (c > 0)
  128. -- ray's direction pointing away from sphere (b > 0)
  129. if c > 0 and b > 0 then
  130. return false
  131. end
  132. local discr = b * b - c
  133. -- negative discriminant
  134. if discr < 0 then
  135. return false
  136. end
  137. -- Clamp t to 0
  138. local t = -b - sqrt(discr)
  139. t = t < 0 and 0 or t
  140. -- Return collision point and distance from ray origin
  141. return ray.position + ray.direction * t, t
  142. end
  143. -- http://gamedev.stackexchange.com/a/18459
  144. -- ray.position is a vec3
  145. -- ray.direction is a vec3
  146. -- aabb.min is a vec3
  147. -- aabb.max is a vec3
  148. function intersect.ray_aabb(ray, aabb)
  149. local dir = ray.direction:normalize()
  150. local dirfrac = vec3(
  151. 1 / dir.x,
  152. 1 / dir.y,
  153. 1 / dir.z
  154. )
  155. local t1 = (aabb.min.x - ray.position.x) * dirfrac.x
  156. local t2 = (aabb.max.x - ray.position.x) * dirfrac.x
  157. local t3 = (aabb.min.y - ray.position.y) * dirfrac.y
  158. local t4 = (aabb.max.y - ray.position.y) * dirfrac.y
  159. local t5 = (aabb.min.z - ray.position.z) * dirfrac.z
  160. local t6 = (aabb.max.z - ray.position.z) * dirfrac.z
  161. local tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6))
  162. local tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6))
  163. -- ray is intersecting AABB, but whole AABB is behind us
  164. if tmax < 0 then
  165. return false
  166. end
  167. -- ray does not intersect AABB
  168. if tmin > tmax then
  169. return false
  170. end
  171. -- Return collision point and distance from ray origin
  172. return ray.position + ray.direction * tmin, tmin
  173. end
  174. -- http://stackoverflow.com/a/23976134/1190664
  175. -- ray.position is a vec3
  176. -- ray.direction is a vec3
  177. -- plane.position is a vec3
  178. -- plane.normal is a vec3
  179. function intersect.ray_plane(ray, plane)
  180. local denom = plane.normal:dot(ray.direction)
  181. -- ray does not intersect plane
  182. if abs(denom) < DBL_EPSILON then
  183. return false
  184. end
  185. -- distance of direction
  186. local d = plane.position - ray.position
  187. local t = d:dot(plane.normal) / denom
  188. if t < DBL_EPSILON then
  189. return false
  190. end
  191. -- Return collision point and distance from ray origin
  192. return ray.position + ray.direction * t, t
  193. end
  194. function intersect.ray_capsule(ray, capsule)
  195. local dist2, p1, p2 = intersect.closest_point_segment_segment(
  196. ray.position,
  197. ray.position + ray.direction * 1e10,
  198. capsule.a,
  199. capsule.b
  200. )
  201. if dist2 <= capsule.radius^2 then
  202. return p1
  203. end
  204. return false
  205. end
  206. -- https://web.archive.org/web/20120414063459/http://local.wasp.uwa.edu.au/~pbourke//geometry/lineline3d/
  207. -- a[1] is a vec3
  208. -- a[2] is a vec3
  209. -- b[1] is a vec3
  210. -- b[2] is a vec3
  211. -- e is a number
  212. function intersect.line_line(a, b, e)
  213. -- new points
  214. local p13 = a[1] - b[1]
  215. local p43 = b[2] - b[1]
  216. local p21 = a[2] - a[1]
  217. -- if lengths are negative or too close to 0, lines do not intersect
  218. if p43:len2() < DBL_EPSILON or p21:len2() < DBL_EPSILON then
  219. return false
  220. end
  221. -- dot products
  222. local d1343 = p13:dot(p43)
  223. local d4321 = p43:dot(p21)
  224. local d1321 = p13:dot(p21)
  225. local d4343 = p43:dot(p43)
  226. local d2121 = p21:dot(p21)
  227. local denom = d2121 * d4343 - d4321 * d4321
  228. -- if denom is too close to 0, lines do not intersect
  229. if abs(denom) < DBL_EPSILON then
  230. return false
  231. end
  232. local numer = d1343 * d4321 - d1321 * d4343
  233. local mua = numer / denom
  234. local mub = (d1343 + d4321 * mua) / d4343
  235. -- return positions of intersection on each line
  236. local out1 = a[1] + p21 * mua
  237. local out2 = b[1] + p43 * mub
  238. local dist = out1:dist(out2)
  239. -- if distance of the shortest segment between lines is less than threshold
  240. if e and dist > e then
  241. return false
  242. end
  243. return { out1, out2 }, dist
  244. end
  245. -- a[1] is a vec3
  246. -- a[2] is a vec3
  247. -- b[1] is a vec3
  248. -- b[2] is a vec3
  249. -- e is a number
  250. function intersect.segment_segment(a, b, e)
  251. local c, d = intersect.line_line(a, b, e)
  252. if c and ((
  253. a[1].x <= c[1].x and
  254. a[1].y <= c[1].y and
  255. a[1].z <= c[1].z and
  256. c[1].x <= a[2].x and
  257. c[1].y <= a[2].y and
  258. c[1].z <= a[2].z
  259. ) or (
  260. a[1].x >= c[1].x and
  261. a[1].y >= c[1].y and
  262. a[1].z >= c[1].z and
  263. c[1].x >= a[2].x and
  264. c[1].y >= a[2].y and
  265. c[1].z >= a[2].z
  266. )) and ((
  267. b[1].x <= c[2].x and
  268. b[1].y <= c[2].y and
  269. b[1].z <= c[2].z and
  270. c[2].x <= b[2].x and
  271. c[2].y <= b[2].y and
  272. c[2].z <= b[2].z
  273. ) or (
  274. b[1].x >= c[2].x and
  275. b[1].y >= c[2].y and
  276. b[1].z >= c[2].z and
  277. c[2].x >= b[2].x and
  278. c[2].y >= b[2].y and
  279. c[2].z >= b[2].z
  280. )) then
  281. return c, d
  282. end
  283. -- segments do not intersect
  284. return false
  285. end
  286. -- a.min is a vec3
  287. -- a.max is a vec3
  288. -- b.min is a vec3
  289. -- b.max is a vec3
  290. function intersect.aabb_aabb(a, b)
  291. return
  292. a.min.x <= b.max.x and
  293. a.max.x >= b.min.x and
  294. a.min.y <= b.max.y and
  295. a.max.y >= b.min.y and
  296. a.min.z <= b.max.z and
  297. a.max.z >= b.min.z
  298. end
  299. -- aabb.position is a vec3
  300. -- aabb.extent is a vec3 (half-size)
  301. -- obb.position is a vec3
  302. -- obb.extent is a vec3 (half-size)
  303. -- obb.rotation is a mat4
  304. function intersect.aabb_obb(aabb, obb)
  305. local a = aabb.extent
  306. local b = obb.extent
  307. local T = obb.position - aabb.position
  308. local rot = mat4():transpose(obb.rotation)
  309. local B = {}
  310. local t
  311. for i = 1, 3 do
  312. B[i] = {}
  313. for j = 1, 3 do
  314. assert((i - 1) * 4 + j < 16 and (i - 1) * 4 + j > 0)
  315. B[i][j] = abs(rot[(i - 1) * 4 + j]) + 1e-6
  316. end
  317. end
  318. t = abs(T.x)
  319. if not (t <= (b.x + a.x * B[1][1] + b.y * B[1][2] + b.z * B[1][3])) then return false end
  320. t = abs(T.x * B[1][1] + T.y * B[2][1] + T.z * B[3][1])
  321. if not (t <= (b.x + a.x * B[1][1] + a.y * B[2][1] + a.z * B[3][1])) then return false end
  322. t = abs(T.y)
  323. if not (t <= (a.y + b.x * B[2][1] + b.y * B[2][2] + b.z * B[2][3])) then return false end
  324. t = abs(T.z)
  325. if not (t <= (a.z + b.x * B[3][1] + b.y * B[3][2] + b.z * B[3][3])) then return false end
  326. t = abs(T.x * B[1][2] + T.y * B[2][2] + T.z * B[3][2])
  327. if not (t <= (b.y + a.x * B[1][2] + a.y * B[2][2] + a.z * B[3][2])) then return false end
  328. t = abs(T.x * B[1][3] + T.y * B[2][3] + T.z * B[3][3])
  329. if not (t <= (b.z + a.x * B[1][3] + a.y * B[2][3] + a.z * B[3][3])) then return false end
  330. t = abs(T.z * B[2][1] - T.y * B[3][1])
  331. if not (t <= (a.y * B[3][1] + a.z * B[2][1] + b.y * B[1][3] + b.z * B[1][2])) then return false end
  332. t = abs(T.z * B[2][2] - T.y * B[3][2])
  333. if not (t <= (a.y * B[3][2] + a.z * B[2][2] + b.x * B[1][3] + b.z * B[1][1])) then return false end
  334. t = abs(T.z * B[2][3] - T.y * B[3][3])
  335. if not (t <= (a.y * B[3][3] + a.z * B[2][3] + b.x * B[1][2] + b.y * B[1][1])) then return false end
  336. t = abs(T.x * B[3][1] - T.z * B[1][1])
  337. if not (t <= (a.x * B[3][1] + a.z * B[1][1] + b.y * B[2][3] + b.z * B[2][2])) then return false end
  338. t = abs(T.x * B[3][2] - T.z * B[1][2])
  339. if not (t <= (a.x * B[3][2] + a.z * B[1][2] + b.x * B[2][3] + b.z * B[2][1])) then return false end
  340. t = abs(T.x * B[3][3] - T.z * B[1][3])
  341. if not (t <= (a.x * B[3][3] + a.z * B[1][3] + b.x * B[2][2] + b.y * B[2][1])) then return false end
  342. t = abs(T.y * B[1][1] - T.x * B[2][1])
  343. if not (t <= (a.x * B[2][1] + a.y * B[1][1] + b.y * B[3][3] + b.z * B[3][2])) then return false end
  344. t = abs(T.y * B[1][2] - T.x * B[2][2])
  345. if not (t <= (a.x * B[2][2] + a.y * B[1][2] + b.x * B[3][3] + b.z * B[3][1])) then return false end
  346. t = abs(T.y * B[1][3] - T.x * B[2][3])
  347. if not (t <= (a.x * B[2][3] + a.y * B[1][3] + b.x * B[3][2] + b.y * B[3][1])) then return false end
  348. -- https://gamedev.stackexchange.com/questions/24078/which-side-was-hit
  349. -- Minkowski Sum
  350. local wy = (aabb.extent * 2 + obb.extent * 2) * (aabb.position.y - obb.position.y)
  351. local hx = (aabb.extent * 2 + obb.extent * 2) * (aabb.position.x - obb.position.x)
  352. if wy.x > hx.x and wy.y > hx.y and wy.z > hx.z then
  353. if wy.x > -hx.x and wy.y > -hx.y and wy.z > -hx.z then
  354. return vec3(obb.rotation * { 0, -1, 0, 1 })
  355. else
  356. return vec3(obb.rotation * { -1, 0, 0, 1 })
  357. end
  358. else
  359. if wy.x > -hx.x and wy.y > -hx.y and wy.z > -hx.z then
  360. return vec3(obb.rotation * { 1, 0, 0, 1 })
  361. else
  362. return vec3(obb.rotation * { 0, 1, 0, 1 })
  363. end
  364. end
  365. end
  366. -- http://stackoverflow.com/a/4579069/1190664
  367. -- aabb.min is a vec3
  368. -- aabb.max is a vec3
  369. -- sphere.position is a vec3
  370. -- sphere.radius is a number
  371. local axes = { "x", "y", "z" }
  372. function intersect.aabb_sphere(aabb, sphere)
  373. local dist2 = sphere.radius ^ 2
  374. for _, axis in ipairs(axes) do
  375. local pos = sphere.position[axis]
  376. local amin = aabb.min[axis]
  377. local amax = aabb.max[axis]
  378. if pos < amin then
  379. dist2 = dist2 - (pos - amin) ^ 2
  380. elseif pos > amax then
  381. dist2 = dist2 - (pos - amax) ^ 2
  382. end
  383. end
  384. return dist2 > 0
  385. end
  386. -- aabb.min is a vec3
  387. -- aabb.max is a vec3
  388. -- frustum.left is a plane { a, b, c, d }
  389. -- frustum.right is a plane { a, b, c, d }
  390. -- frustum.bottom is a plane { a, b, c, d }
  391. -- frustum.top is a plane { a, b, c, d }
  392. -- frustum.near is a plane { a, b, c, d }
  393. -- frustum.far is a plane { a, b, c, d }
  394. function intersect.aabb_frustum(aabb, frustum)
  395. -- Indexed for the 'index trick' later
  396. local box = {
  397. aabb.min,
  398. aabb.max
  399. }
  400. -- We have 6 planes defining the frustum, 5 if infinite.
  401. local planes = {
  402. frustum.left,
  403. frustum.right,
  404. frustum.bottom,
  405. frustum.top,
  406. frustum.near,
  407. frustum.far or false
  408. }
  409. -- Skip the last test for infinite projections, it'll never fail.
  410. if not planes[6] then
  411. table.remove(planes)
  412. end
  413. for i = 1, #planes do
  414. -- This is the current plane
  415. local p = planes[i]
  416. -- p-vertex selection (with the index trick)
  417. -- According to the plane normal we can know the
  418. -- indices of the positive vertex
  419. local px = p.a > 0.0 and 2 or 1
  420. local py = p.b > 0.0 and 2 or 1
  421. local pz = p.c > 0.0 and 2 or 1
  422. -- project p-vertex on plane normal
  423. -- (How far is p-vertex from the origin)
  424. local dot = (p.a * box[px].x) + (p.b * box[py].y) + (p.c * box[pz].z)
  425. -- Doesn't intersect if it is behind the plane
  426. if dot < -p.d then
  427. return false
  428. end
  429. end
  430. return true
  431. end
  432. -- outer.min is a vec3
  433. -- outer.max is a vec3
  434. -- inner.min is a vec3
  435. -- inner.max is a vec3
  436. function intersect.encapsulate_aabb(outer, inner)
  437. return
  438. outer.min.x <= inner.min.x and
  439. outer.max.x >= inner.max.x and
  440. outer.min.y <= inner.min.y and
  441. outer.max.y >= inner.max.y and
  442. outer.min.z <= inner.min.z and
  443. outer.max.z >= inner.max.z
  444. end
  445. -- a.position is a vec3
  446. -- a.radius is a number
  447. -- b.position is a vec3
  448. -- b.radius is a number
  449. function intersect.circle_circle(a, b)
  450. return a.position:dist(b.position) <= a.radius + b.radius
  451. end
  452. -- a.position is a vec3
  453. -- a.radius is a number
  454. -- b.position is a vec3
  455. -- b.radius is a number
  456. function intersect.sphere_sphere(a, b)
  457. return intersect.circle_circle(a, b)
  458. end
  459. -- http://realtimecollisiondetection.net/blog/?p=103
  460. -- sphere.position is a vec3
  461. -- sphere.radius is a number
  462. -- triangle[1] is a vec3
  463. -- triangle[2] is a vec3
  464. -- triangle[3] is a vec3
  465. function intersect.sphere_triangle(sphere, triangle)
  466. -- Sphere is centered at origin
  467. local A = triangle[1] - sphere.position
  468. local B = triangle[2] - sphere.position
  469. local C = triangle[3] - sphere.position
  470. -- Compute normal of triangle plane
  471. local V = (B - A):cross(C - A)
  472. -- Test if sphere lies outside triangle plane
  473. local rr = sphere.radius * sphere.radius
  474. local d = A:dot(V)
  475. local e = V:dot(V)
  476. local s1 = d * d > rr * e
  477. -- Test if sphere lies outside triangle vertices
  478. local aa = A:dot(A)
  479. local ab = A:dot(B)
  480. local ac = A:dot(C)
  481. local bb = B:dot(B)
  482. local bc = B:dot(C)
  483. local cc = C:dot(C)
  484. local s2 = (aa > rr) and (ab > aa) and (ac > aa)
  485. local s3 = (bb > rr) and (ab > bb) and (bc > bb)
  486. local s4 = (cc > rr) and (ac > cc) and (bc > cc)
  487. -- Test is sphere lies outside triangle edges
  488. local AB = B - A
  489. local BC = C - B
  490. local CA = A - C
  491. local d1 = ab - aa
  492. local d2 = bc - bb
  493. local d3 = ac - cc
  494. local e1 = AB:dot(AB)
  495. local e2 = BC:dot(BC)
  496. local e3 = CA:dot(CA)
  497. local Q1 = A * e1 - d1 * AB
  498. local Q2 = B * e2 - d2 * BC
  499. local Q3 = C * e3 - d3 * CA
  500. local QC = C * e1 - Q1
  501. local QA = A * e2 - Q2
  502. local QB = B * e3 - Q3
  503. local s5 = (Q1:dot(Q1) > rr * e1 * e1) and (Q1:dot(QC) > 0)
  504. local s6 = (Q2:dot(Q2) > rr * e2 * e2) and (Q2:dot(QA) > 0)
  505. local s7 = (Q3:dot(Q3) > rr * e3 * e3) and (Q3:dot(QB) > 0)
  506. -- Return whether or not any of the tests passed
  507. return s1 or s2 or s3 or s4 or s5 or s6 or s7
  508. end
  509. -- sphere.position is a vec3
  510. -- sphere.radius is a number
  511. -- frustum.left is a plane { a, b, c, d }
  512. -- frustum.right is a plane { a, b, c, d }
  513. -- frustum.bottom is a plane { a, b, c, d }
  514. -- frustum.top is a plane { a, b, c, d }
  515. -- frustum.near is a plane { a, b, c, d }
  516. -- frustum.far is a plane { a, b, c, d }
  517. function intersect.sphere_frustum(sphere, frustum)
  518. local x, y, z = sphere.position:unpack()
  519. local planes = {
  520. frustum.left,
  521. frustum.right,
  522. frustum.bottom,
  523. frustum.top,
  524. frustum.near
  525. }
  526. if frustum.far then
  527. table.insert(planes, frustum.far, 5)
  528. end
  529. local dot
  530. for i = 1, #planes do
  531. dot = planes[i].a * x + planes[i].b * y + planes[i].c * z + planes[i].d
  532. if dot <= -sphere.radius then
  533. return false
  534. end
  535. end
  536. -- dot + radius is the distance of the object from the near plane.
  537. -- make sure that the near plane is the last test!
  538. return dot + sphere.radius
  539. end
  540. function intersect.capsule_capsule(c1, c2)
  541. local dist2, p1, p2 = intersect.closest_point_segment_segment(c1.a, c1.b, c2.a, c2.b)
  542. local radius = c1.radius + c2.radius
  543. if dist2 <= radius * radius then
  544. return p1, p2
  545. end
  546. return false
  547. end
  548. function intersect.closest_point_segment_segment(p1, p2, p3, p4)
  549. local s -- Distance of intersection along segment 1
  550. local t -- Distance of intersection along segment 2
  551. local c1 -- Collision point on segment 1
  552. local c2 -- Collision point on segment 2
  553. local d1 = p2 - p1 -- Direction of segment 1
  554. local d2 = p4 - p3 -- Direction of segment 2
  555. local r = p1 - p3
  556. local a = d1:dot(d1)
  557. local e = d2:dot(d2)
  558. local f = d2:dot(r)
  559. -- Check if both segments degenerate into points
  560. if a <= DBL_EPSILON and e <= DBL_EPSILON then
  561. s = 0
  562. t = 0
  563. c1 = p1
  564. c2 = p3
  565. return (c1 - c2):dot(c1 - c2), s, t, c1, c2
  566. end
  567. -- Check if segment 1 degenerates into a point
  568. if a <= DBL_EPSILON then
  569. s = 0
  570. t = utils.clamp(f / e, 0, 1)
  571. else
  572. local c = d1:dot(r)
  573. -- Check is segment 2 degenerates into a point
  574. if e <= DBL_EPSILON then
  575. t = 0
  576. s = utils.clamp(-c / a, 0, 1)
  577. else
  578. local b = d1:dot(d2)
  579. local denom = a * e - b * b
  580. if abs(denom) > 0 then
  581. s = utils.clamp((b * f - c * e) / denom, 0, 1)
  582. else
  583. s = 0
  584. end
  585. t = (b * s + f) / e
  586. if t < 0 then
  587. t = 0
  588. s = utils.clamp(-c / a, 0, 1)
  589. elseif t > 1 then
  590. t = 1
  591. s = utils.clamp((b - c) / a, 0, 1)
  592. end
  593. end
  594. end
  595. c1 = p1 + d1 * s
  596. c2 = p3 + d2 * t
  597. return (c1 - c2):dot(c1 - c2), c1, c2, s, t
  598. end
  599. return intersect