mirror of
https://github.com/1bardesign/batteries.git
synced 2024-11-26 15:44:35 +00:00
reworked intersect.lua - accepting that continuous collision is out of scope, reworking docs
This commit is contained in:
parent
6ef19cfde0
commit
4cea23e2e8
162
intersect.lua
162
intersect.lua
@ -1,20 +1,22 @@
|
|||||||
--[[
|
--[[
|
||||||
geometric intersection routines
|
geometric intersection routines
|
||||||
|
|
||||||
from simple point tests to shape vs shape tests
|
from simple point tests to shape vs shape tests
|
||||||
|
|
||||||
optimised pretty well in most places.
|
optimised pretty well in most places
|
||||||
|
|
||||||
options for boolean or minimum separating vector results
|
tests provided:
|
||||||
|
overlap
|
||||||
continuous sweeps (where provided) also return the
|
boolean "is overlapping"
|
||||||
time-domain position of first intersection
|
collide
|
||||||
|
nil for no collision
|
||||||
TODO: refactor vector storage to be pooled where needed
|
minimum separating vector on collision
|
||||||
|
provided in the direction of the first object
|
||||||
|
optional output parameters to avoid garbage generation
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local path = (...):gsub("intersect", "")
|
local path = (...):gsub("intersect", "")
|
||||||
local vec2 = require(path .. "vec2")
|
local vec2 = require(path .. "vec2")
|
||||||
local functional = require(path .. "functional")
|
|
||||||
|
|
||||||
--module storage
|
--module storage
|
||||||
local intersect = {}
|
local intersect = {}
|
||||||
@ -190,11 +192,6 @@ function intersect.line_line_collide(a_start, a_end, a_rad, b_start, b_end, b_ra
|
|||||||
intersected = "a"
|
intersected = "a"
|
||||||
else
|
else
|
||||||
intersected = "both"
|
intersected = "both"
|
||||||
--collision point =
|
|
||||||
--[[vec2:xy(
|
|
||||||
a_start.x + mua * dx1,
|
|
||||||
a_start.y + mua * dy1,
|
|
||||||
)]]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -219,9 +216,14 @@ function intersect.line_line_collide(a_start, a_end, a_rad, b_start, b_end, b_ra
|
|||||||
table.insert(search_tab, {intersect._line_to_point(a_start, a_end, b_end), -1})
|
table.insert(search_tab, {intersect._line_to_point(a_start, a_end, b_end), -1})
|
||||||
end
|
end
|
||||||
|
|
||||||
local best = functional.find_best(search_tab, function(v)
|
local best = nil
|
||||||
return -(v[1]:length_squared())
|
local best_len = nil
|
||||||
end)
|
for _, v in ipairs(search_tab) do
|
||||||
|
local len = v[1]:length_squared()
|
||||||
|
if not best_len or len < best_len then
|
||||||
|
best = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--fix direction
|
--fix direction
|
||||||
into:vset(best[1]):smuli(best[2])
|
into:vset(best[1]):smuli(best[2])
|
||||||
@ -298,129 +300,6 @@ function intersect.aabb_aabb_collide(a_pos, a_hs, b_pos, b_hs, into)
|
|||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
--return normal and fraction of dt encountered on collision, false otherwise
|
|
||||||
--TODO: re-pool storage here
|
|
||||||
function intersect.aabb_aabb_collide_continuous(
|
|
||||||
a_startpos, a_endpos, a_hs,
|
|
||||||
b_startpos, b_endpos, b_hs,
|
|
||||||
into
|
|
||||||
)
|
|
||||||
if not into then into = vec2:zero() end
|
|
||||||
|
|
||||||
--compute delta motion
|
|
||||||
local _self_delta_motion = a_endpos:vsub(a_startpos)
|
|
||||||
local _other_delta_motion = b_endpos:vsub(b_startpos)
|
|
||||||
|
|
||||||
--cheap "is this even possible" early-out
|
|
||||||
do
|
|
||||||
local _self_half_delta = _self_delta_motion:smul(0.5)
|
|
||||||
local _self_bounds_pos = _self_half_delta:vadd(a_endpos)
|
|
||||||
local _self_bounds_hs = _self_half_delta:vadd(a_hs)
|
|
||||||
|
|
||||||
local _other_half_delta = _other_delta_motion:smul(0.5)
|
|
||||||
local _other_bounds_pos = _other_half_delta:vadd(b_endpos)
|
|
||||||
local _other_bounds_hs = _other_half_delta:vadd(b_hs)
|
|
||||||
|
|
||||||
if not body._overlap_raw(
|
|
||||||
_self_bounds_pos, _self_bounds_hs,
|
|
||||||
_other_bounds_pos, _other_bounds_hs
|
|
||||||
) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--get ccd minkowski box
|
|
||||||
--this is a relative-space box
|
|
||||||
local _relative_delta_motion = _self_delta_motion:vsub(_other_delta_motion)
|
|
||||||
local _minkowski_halfsize = a_hs:vadd(b_hs)
|
|
||||||
local _minkowski_pos = b_startpos:vsub(a_startpos)
|
|
||||||
|
|
||||||
--if a line seg from our relative motion hits the minkowski box, we're in luck
|
|
||||||
--slab raycast is speedy
|
|
||||||
|
|
||||||
--alias
|
|
||||||
local _rmx = _relative_delta_motion.x
|
|
||||||
local _rmy = _relative_delta_motion.y
|
|
||||||
|
|
||||||
local _inv_x = math.huge
|
|
||||||
if _rmx ~= 0 then _inv_x = 1 / _rmx end
|
|
||||||
local _inv_y = math.huge
|
|
||||||
if _rmy ~= 0 then _inv_y = 1 / _rmy end
|
|
||||||
|
|
||||||
local _minkowski_tl = _minkowski_pos:vsub(_minkowski_halfsize)
|
|
||||||
local _minkowski_br = _minkowski_pos:vadd(_minkowski_halfsize)
|
|
||||||
|
|
||||||
--clip x
|
|
||||||
--get edge t along line
|
|
||||||
local tx1 = (_minkowski_tl.x) * _inv_x
|
|
||||||
local tx2 = (_minkowski_br.x) * _inv_x
|
|
||||||
--clip to existing clip space
|
|
||||||
local txmin = math.min(tx1, tx2)
|
|
||||||
local txmax = math.max(tx1, tx2)
|
|
||||||
--clip y
|
|
||||||
--get edge t along line
|
|
||||||
local ty1 = (_minkowski_tl.y) * _inv_y
|
|
||||||
local ty2 = (_minkowski_br.y) * _inv_y
|
|
||||||
--clip to existing clip space
|
|
||||||
local tymin = math.min(ty1, ty2)
|
|
||||||
local tymax = math.max(ty1, ty2)
|
|
||||||
|
|
||||||
--clip space
|
|
||||||
local tmin = math.max(0, txmin, tymin)
|
|
||||||
local tmax = math.min(1, txmax, tymax)
|
|
||||||
|
|
||||||
--still some unclipped? collision!
|
|
||||||
if tmin <= tmax then
|
|
||||||
--"was colliding at start"
|
|
||||||
if tmin == 0 then
|
|
||||||
--todo: maybe collide at old pos, not new pos
|
|
||||||
local msv = self:collide(other, into)
|
|
||||||
if msv then
|
|
||||||
return msv, tmin
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--delta before colliding
|
|
||||||
local _self_collide_pre = _self_delta_motion:smul(tmin)
|
|
||||||
--delta after colliding (to be discarded or projected or whatever)
|
|
||||||
local _self_collide_post = _self_delta_motion:smul(1.0 - tmin)
|
|
||||||
--get the collision normal
|
|
||||||
--(whichever boundary crossed _last_ -> normal)
|
|
||||||
local _self_collide_normal = vec2:zero()
|
|
||||||
if txmin > tymin then
|
|
||||||
_self_collide_normal.x = -math.sign(_self_delta_motion.x)
|
|
||||||
else
|
|
||||||
_self_collide_normal.y = -math.sign(_self_delta_motion.y)
|
|
||||||
end
|
|
||||||
|
|
||||||
--travelling away from normal?
|
|
||||||
if _self_collide_normal:dot(_self_delta_motion) >= 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--just "slide" projection for now
|
|
||||||
_self_collide_post:vreji(_self_collide_normal)
|
|
||||||
|
|
||||||
--combine
|
|
||||||
local _final_delta = _self_collide_pre:vadd(_self_collide_post)
|
|
||||||
|
|
||||||
--construct the target position
|
|
||||||
local _target_pos = a_startpos:vadd(_final_delta)
|
|
||||||
|
|
||||||
--return delta to target pos
|
|
||||||
local msv = _target_pos:vsub(a_endpos)
|
|
||||||
|
|
||||||
if math.abs(msv.x) > COLLIDE_EPS or math.abs(msv.y) > COLLIDE_EPS then
|
|
||||||
into:vset(msv)
|
|
||||||
return into, tmin
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- helper function to clamp point to aabb
|
-- helper function to clamp point to aabb
|
||||||
function intersect.aabb_point_clamp(pos, hs, v, into)
|
function intersect.aabb_point_clamp(pos, hs, v, into)
|
||||||
local v_min = pos:pooled_copy():vsubi(hs)
|
local v_min = pos:pooled_copy():vsubi(hs)
|
||||||
@ -449,7 +328,7 @@ function intersect.aabb_circle_collide(a_pos, a_hs, b_pos, b_rad, into)
|
|||||||
--
|
--
|
||||||
local result
|
local result
|
||||||
if like_aabb then
|
if like_aabb then
|
||||||
local pretend_hs = vec2:pooled():sset(b_rad)
|
local pretend_hs = vec2:pooled():sset()
|
||||||
result = intersect.aabb_aabb_collide(a_pos, a_hs, b_pos, pretend_hs, into)
|
result = intersect.aabb_aabb_collide(a_pos, a_hs, b_pos, pretend_hs, into)
|
||||||
pretend_hs:release()
|
pretend_hs:release()
|
||||||
else
|
else
|
||||||
@ -553,8 +432,7 @@ function intersect.mutual_bounce(velocity_a, velocity_b, normal, conservation)
|
|||||||
velocity_a:fmai(b_remaining, conservation)
|
velocity_a:fmai(b_remaining, conservation)
|
||||||
velocity_b:fmai(a_remaining, conservation)
|
velocity_b:fmai(a_remaining, conservation)
|
||||||
--clean up
|
--clean up
|
||||||
a_remaining:release()
|
vec2.release(a_remaining, b_remaining)
|
||||||
b_remaining:release()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return intersect
|
return intersect
|
||||||
|
Loading…
Reference in New Issue
Block a user