Hey, i'm trying to implement SAT-collision detection and response and was wondering if someone could take a look at it and come with ideas on how to improve the resolving of the entity and get it more clean. Right now it doesn't quite work because it detects collision when e1 is close to (but outside) the corners of e2. and the TranslateEntity() just sets e1 on top of e2.
e1 is a isometric tile.
e2 is a plain square AABB tile.
Entity struct:
struct Entity
{
V4f_COL col;
V2f texCoord;
V2f texDimension;
V2f points[4] = {};
void CreateQuad(float x, float y, float w, float h)
{
points[pTop].x = x;
points[pTop].y = y;
points[pLeft].x = x;
points[pLeft].y = y + h;
points[pRight].x = x + w;
points[pRight].y = y;
points[pBottom].x = x + w;
points[pBottom].y = y + h;
}
void CreateIsometricQuad(float x, float y, float w, float h)
{
points[pTop].x = x;
points[pTop].y = y;
points[pLeft].x = x - (w / 2.0f);
points[pLeft].y = y + (h / 2.0f);
points[pRight].x = x + (w / 2.0f);
points[pRight].y = y + (h / 2.0f);
points[pBottom].x = x;
points[pBottom].y = y + h;
}
void Move(V2f movement)
{
for(i32 i = 0; i < 4; i++)
{
points[i] += movement;
}
}
void SetSolidColor(float _r, float _g, float _b, float _a)
{
col.r = _r;
col.g = _g;
col.b = _b;
col.a = _a;
}
void SetTextureCoordsToFullMap()
{
texCoord.x = 0.0f;
texCoord.y = 0.0f;
texDimension.x = 1.0f;
texDimension.y = 1.0f;
}
void SelectTextureFragment(float x, float y, float w, float h)
{
texCoord.x = x;
texCoord.y = y;
texDimension.x = w;
texDimension.y = h;
}
};
CollisionInfo struct:
struct CollisionInfo
{
bool collision;
float minPenetration;
V2f mtv;
};
Here's is where im using it:
CollisionInfo ci_0 = {};
CollisionInfo ci_1 = {};
// COLLISION CHECKING
ci_1 = Collision_Entity_SAT(e[tile_00], e[tile_00 + 1]);
ci_0 = Collision_Entity_SAT(e[tile_00], e[ghostSorc]);
if(ci_1.collision || ci_0.collision)
{
if(ci_1.collision)
{
printf("c_1.overlap: %f\n", ci_1.minPenetration);
printf("c_1.mtv: ( %f, %f )\n\n", ci_1.mtv.x, ci_1.mtv.y);
tilePos += ci_1.mtv;
e[tile_00].SetSolidColor(0.0f, 0.5f, 0.0f, 1.0f);
e[tile_00].Move(ci_1.mtv);
}
if(ci_0.collision)
{
printf("c_0.overlap: %f\n", ci_0.minPenetration);
printf("c_0.mtv: ( %f, %f )\n\n", ci_0.mtv.x, ci_0.mtv.y);
tilePos += ci_0.mtv;
e[tile_00].SetSolidColor(0.0f, 0.5f, 0.0f, 1.0f);
e[tile_00].Move(ci_0.mtv);
}
}
else
{
e[tile_00].SetSolidColor(0.2f, 0.0f, 0.2f, 1.0f);
}
Collision_Entity_SAT();
CollisionInfo Collision_Entity_SAT(Entity e1, Entity e2)
{
CollisionInfo result = {};
float dot_e1[4] = {};
float dot_e2[4] = {};
V2f normals_e1[4] = {};
V2f normals_e2[4] = {};
V2f mtv = {};
float minDot_e1 = 0.0f;
float maxDot_e1 = 0.0f;
float minDot_e2 = 0.0f;
float maxDot_e2 = 0.0f;
float overlap = 100000000.0f;
float tempOverlap = 0.0f;
// Get normals_e1 of all sides and make them unit vectors
for(i32 i = 0; i < 4; i++)
{
if(i == 3)
{
normals_e1[i] = Unit_V2f(Normal_V2f(e1.points[i], e1.points[(i - 3)]));
}
else
{
normals_e1[i] = Unit_V2f(Normal_V2f(e1.points[i], e1.points[(i + 1)]));
}
}
// Get normals_e2 of all sides and make them unit vectors
for(i32 i = 0; i < 4; i++)
{
if(i == 3)
{
normals_e2[i] = Unit_V2f(Normal_V2f(e2.points[i], e2.points[(i - 3)]));
}
else
{
normals_e2[i] = Unit_V2f(Normal_V2f(e2.points[i], e2.points[(i + 1)]));
}
}
for(i32 j = 0, i = 0; j < 4; j++)
{
for(i = 0; i < 4; i++)
{
// calc dot products of all points one normal at a time
dot_e1[i] = DotProduct_V2f(normals_e1[j], e1.points[i]);
dot_e2[i] = DotProduct_V2f(normals_e1[j], e2.points[i]);
}
// Get min/max Dotproducts of each entity against 1 normal at a time
minDot_e1 = Min_f(dot_e1, sizeof(dot_e1)/sizeof(float));
maxDot_e1 = Max_f(dot_e1, sizeof(dot_e1)/sizeof(float));
minDot_e2 = Min_f(dot_e2, sizeof(dot_e2)/sizeof(float));
maxDot_e2 = Max_f(dot_e2, sizeof(dot_e2)/sizeof(float));
// Check for overlapp
tempOverlap = OverLap(minDot_e1, maxDot_e1, minDot_e2, maxDot_e2);
// No overlapp = no collision
if(tempOverlap <= 0.0f)
{
result.collision = false;
result.mtv = v2f(0.0f, 0.0f);
result.minPenetration = 0.0f;
return result;
}
// some overlapp occured = collision on the axis currently under control
else
{
if(tempOverlap < overlap)
{
overlap = tempOverlap;
mtv = normals_e1[j];
result.minPenetration = overlap;
}
}
}
// Do it all again but check against the normals on the other entity
for(i32 j = 0, i = 0; j < 4; j++)
{
for(i = 0; i < 4; i++)
{
// calc dot products of all points one normal at a time
dot_e1[i] = DotProduct_V2f(normals_e2[j], e1.points[i]);
dot_e2[i] = DotProduct_V2f(normals_e2[j], e2.points[i]);
}
// get min/max DP's
minDot_e1 = Min_f(dot_e1, sizeof(dot_e1)/sizeof(float));
maxDot_e1 = Max_f(dot_e1, sizeof(dot_e1)/sizeof(float));
minDot_e2 = Min_f(dot_e2, sizeof(dot_e2)/sizeof(float));
maxDot_e2 = Max_f(dot_e2, sizeof(dot_e2)/sizeof(float));
// Check for overlapp
tempOverlap = OverLap(minDot_e1, maxDot_e1, minDot_e2, maxDot_e2);
if(tempOverlap <= 0.0f)
{
result.collision = false;
result.mtv = v2f(0.0f, 0.0f);
result.minPenetration = 0.0f;
return result;
}
else
{
if(tempOverlap < overlap)
{
overlap = tempOverlap;
mtv = normals_e2[j];
result.minPenetration = overlap;
}
}
}
// Collision occured and we obtain the minimum penetration magnitude and the corresponding axis
result.mtv = Scalar_V2f(result.minPenetration, mtv);
result.collision = true;
return result;
}