This repository has been archived on 2025-02-04. You can view files and clone it, but cannot push or open issues or pull requests.
helpless/gjk.c

159 lines
5.5 KiB
C
Raw Permalink Normal View History

2024-08-29 17:49:37 +01:00
// Created by Igor Kroitor on 29/12/15, Modifiied by Abdulmujeeb Raji on 29/08/2024
2024-08-29 14:32:56 +01:00
//-----------------------------------------------------------------------------
// Gilbert-Johnson-Keerthi (GJK) collision detection algorithm in 2D
// http://www.dyn4j.org/2010/04/gjk-gilbert-johnson-keerthi/
// http://mollyrocket.com/849
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Basic vector arithmetic operations
Vector2 v2_negate(Vector2 v) { v.x = -v.x; v.y = -v.y; return v; }
Vector2 v2_perpendicular(Vector2 v) { Vector2 p = { v.y, -v.x }; return p; }
float v2_squared_length(Vector2 v) { return v.x * v.x + v.y * v.y; }
//-----------------------------------------------------------------------------
// Triple product expansion is used to calculate perpendicular normal vectors
// which kinda 'prefer' pointing towards the Origin in Minkowski space
Vector2 v2_triple_product(Vector2 a, Vector2 b, Vector2 c) {
Vector2 r;
float ac = a.x * c.x + a.y * c.y; // perform a.dot(c)
float bc = b.x * c.x + b.y * c.y; // perform b.dot(c)
// perform b * a.dot(c) - a * b.dot(c)
r.x = b.x * ac - a.x * bc;
r.y = b.y * ac - a.y * bc;
return r;
}
//-----------------------------------------------------------------------------
// This is to compute average center (roughly). It might be different from
// Center of Gravity, especially for bodies with nonuniform density,
// but this is ok as initial direction of simplex search in GJK.
Vector2 average_point (const Vector2 * vertices, size_t count) {
Vector2 avg = { 0.f, 0.f };
for (size_t i = 0; i < count; i++) {
avg.x += vertices[i].x;
avg.y += vertices[i].y;
}
avg.x /= count;
avg.y /= count;
return avg;
}
//-----------------------------------------------------------------------------
// Get furthest vertex along a certain direction
size_t furthest_point_index (const Vector2 * vertices, size_t count, Vector2 d) {
float maxProduct = v2_dot (d, vertices[0]);
size_t index = 0;
for (size_t i = 1; i < count; i++) {
float product = v2_dot (d, vertices[i]);
if (product > maxProduct) {
maxProduct = product;
index = i;
}
}
return index;
}
//-----------------------------------------------------------------------------
// Minkowski sum support function for GJK
Vector2 support (const Vector2 * vertices1, size_t count1,
const Vector2 * vertices2, size_t count2, Vector2 d) {
// get furthest point of first body along an arbitrary direction
size_t i = furthest_point_index (vertices1, count1, d);
// get furthest point of second body along the opposite direction
size_t j = furthest_point_index (vertices2, count2, v2_negate (d));
// subtract (Minkowski sum) the two points to see if bodies 'overlap'
return v2_sub (vertices1[i], vertices2[j]);
}
//-----------------------------------------------------------------------------
// The GJK yes/no test
int iter_count = 0;
bool gjk (const Vector2 * vertices1, size_t count1,
const Vector2 * vertices2, size_t count2) {
size_t index = 0; // index of current vertex of simplex
Vector2 a, b, c, d, ao, ab, ac, abperp, acperp, simplex[3];
Vector2 position1 = average_point (vertices1, count1); // not a CoG but
Vector2 position2 = average_point (vertices2, count2); // it's ok for GJK )
// initial direction from the center of 1st body to the center of 2nd body
d = v2_sub (position1, position2);
// if initial direction is zero set it to any arbitrary axis (we choose X)
if ((d.x == 0) && (d.y == 0))
d.x = 1.f;
// set the first support as initial point of the new simplex
a = simplex[0] = support (vertices1, count1, vertices2, count2, d);
if (v2_dot (a, d) <= 0)
return false; // no collision
d = v2_negate (a); // The next search direction is always towards the origin, so the next search direction is v2_negate(a)
while (1) {
iter_count++;
a = simplex[++index] = support (vertices1, count1, vertices2, count2, d);
if (v2_dot (a, d) <= 0)
return 0; // no collision
ao = v2_negate (a); // from point A to Origin is just negative A
// simplex has 2 points (a line segment, not a triangle yet)
if (index < 2) {
b = simplex[0];
ab = v2_sub (b, a); // from point A to B
d = v2_triple_product (ab, ao, ab); // normal to AB towards Origin
if (v2_squared_length (d) == 0)
d = v2_perpendicular (ab);
continue; // skip to next iteration
}
b = simplex[1];
c = simplex[0];
ab = v2_sub (b, a); // from point A to B
ac = v2_sub (c, a); // from point A to C
acperp = v2_triple_product (ab, ac, ac);
if (v2_dot (acperp, ao) >= 0) {
d = acperp; // new direction is normal to AC towards Origin
} else {
abperp = v2_triple_product (ac, ab, ab);
if (v2_dot (abperp, ao) < 0)
return true; // collision
simplex[0] = simplex[1]; // swap first element (point C)
d = abperp; // new direction is normal to AB towards Origin
}
simplex[1] = simplex[2]; // swap element in the middle (point B)
--index;
}
return false;
}