SAT with concave/complex polygon for collision response
Posted: Sat Apr 08, 2023 11:40 am
Hello everyone
After having made several more or less successful attempts to solve the problem of collision between any polygons, whether between convex, non-convex or even complex polygons, I would like to know your opinion on the subject and how you would approach this problem.
I have already developed a collision detection function between convex polygons, which returns a direction vector with the penetration distance, based on the SAT algorithm in my polyMan module. If you have any comments to make on this subject, I am also a taker.
However, the results obtained with this function are obviously not suitable for concave or complex polygons. For these types of polygons, my latest attempt was to subdivide them into triangles and calculate, for example, an average of the direction vectors and overlaps This solution is one of the lightest and most convincing that I have found so far, but it is still far from perfect. You will find attached the code and images of my attempts (the test function does not yet use an optimized version of the collision detection function for triangles).
The replacement of the second concave polygon with the function written for this case:
Replace only the first polygon with this same function:
Do you have any suggestions to tackle this problem or even the solution let's be crazy because I already am a little crazy
If I forgot any details that you think are important, sorry in advance, but above all, don't hesitate.
I share with you the demo which is in gif, the `polyInPolyComplex` function is directly used there to replace the first polygon in parameter as it should do.
After having made several more or less successful attempts to solve the problem of collision between any polygons, whether between convex, non-convex or even complex polygons, I would like to know your opinion on the subject and how you would approach this problem.
I have already developed a collision detection function between convex polygons, which returns a direction vector with the penetration distance, based on the SAT algorithm in my polyMan module. If you have any comments to make on this subject, I am also a taker.
Code: Select all
local function polyInPolyConvex(polygon1, polygon2, approximate)
local min_distance = huge
local min_nx, min_ny
local polygon = polygon1
local reiterated
::reiterated::
for i = 1, #polygon, 2 do
-- Calculate the normals for each line of both polygons
local x1, y1, x2, y2
if i == #polygon - 1 then
x1, y1 = polygon[i], polygon[i+1]
x2, y2 = polygon[1], polygon[2]
else
x1, y1 = polygon[i], polygon[i+1]
x2, y2 = polygon[i+2], polygon[i+3]
end
local nx, ny = y2-y1, x1-x2
local length = sqrt(nx * nx + ny * ny)
nx = nx / length
ny = ny / length
-- Checks the minimum distance between the two polygons along each normal
local min1, max1, min2, max2 = huge, -huge, huge, -huge
for j = 1, #polygon1, 2 do
local dot = nx * polygon1[j] + ny * polygon1[j+1]
min1 = min(min1, dot)
max1 = max(max1, dot)
end
for j = 1, #polygon2, 2 do
local dot = nx * polygon2[j] + ny * polygon2[j+1]
min2 = min(min2, dot)
max2 = max(max2, dot)
end
-- If the polygons do not overlap, there is no collision
if min1 > max2 or min2 > max1 then
return false
end
-- Get the penetration distance and keep the results if it is the current smallest
local distance = min(max2 - min1, max1 - min2)
if distance < min_distance then
min_distance = distance
min_nx, min_ny = nx, ny
if max2 - min1 > max1 - min2 then
min_nx = -min_nx
min_ny = -min_ny
end
end
end
if not approximate and not reiterated then
polygon, reiterated = polygon2, true
goto reiterated
end
return true, min_nx, min_ny, min_distance
end
Code: Select all
local function polyInPolyComplex(polygon1, polygon2)
local displacement_x, displacement_y = 0, 0
local num_collisions = 0
-- Triangulate polygons (using love.math.triangulate)
local triangles1 = love.math.triangulate(polygon1)
local triangles2 = love.math.triangulate(polygon2)
-- Check each triangle for collision using polyInPolyConvex (SAT) function
for i, tri1 in ipairs(triangles1) do
for j, tri2 in ipairs(triangles2) do
local collides, nx, ny, dist = polyInPolyConvex(tri1, tri2)
if collides then
-- Calculate displacement needed to separate polygons
local displacement = dist / math.sqrt(nx * nx + ny * ny)
local dx, dy = nx * displacement, ny * displacement
-- Accumulate displacement
displacement_x = displacement_x + dx
displacement_y = displacement_y + dy
num_collisions = num_collisions + 1
end
end
end
if num_collisions > 0 then
-- Calculate average displacement
displacement_x = displacement_x / num_collisions
displacement_y = displacement_y / num_collisions
return true, displacement_x, displacement_y
else
return false, 0, 0
end
end
Replace only the first polygon with this same function:
Do you have any suggestions to tackle this problem or even the solution let's be crazy because I already am a little crazy
If I forgot any details that you think are important, sorry in advance, but above all, don't hesitate.
I share with you the demo which is in gif, the `polyInPolyComplex` function is directly used there to replace the first polygon in parameter as it should do.