1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | struct physics_object { v2 P, dP, ddP; f32 Mass; collision_boundary *Boundaries; u8 BoundaryCount; }; internal inline v2 GJKSupport(collision_boundary *Boundary, physics_object *Object, v2 Delta, v2 Direction){ v2 Result = {}; switch(Boundary->Type){ case BoundaryType_Rect: { v2 Min = Boundary->Bounds.Min; v2 Max = Boundary->Bounds.Max; v2 Points[4] = { Min, V2(Min.X, Max.Y), Max, V2(Max.X, Min.Y), }; for(u32 I=0; I < 4; I++){ if(Dot(Points[I], Direction) > Dot(Result, Direction)){ Result = Points[I]; } } }break; case BoundaryType_Circle: { f32 DirectionLength = Length(Direction); v2 UnitDirection = Direction/DirectionLength; Result = UnitDirection * Boundary->Radius; }break; case BoundaryType_Wedge: { for(u32 I=0; I < 3; I++){ v2 Point = Boundary->Points[I]; if(Dot(Point, Direction) > Dot(Result, Direction)){ Result = Point; } } }break; default: INVALID_CODE_PATH; } Result += Object->P; if(Dot(Delta, Direction) > 0.0f){ Result += Delta; } return(Result); } internal inline v2 CalculateSupport(physics_object *ObjectA, physics_object *ObjectB, v2 Delta, v2 Direction){ v2 Result = GJKSupport(ObjectA->Boundaries, ObjectA, Delta, Direction) - GJKSupport(ObjectB->Boundaries, ObjectB, V20, -Direction); return(Result); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | struct epa_result {
f32 ActualTimeOfImpact;
f32 WorkingTimeOfImpact;
v2 Normal;
};
internal epa_result
DoVelocityEPA(physics_object *ObjectA, physics_object *ObjectB, v2 Delta, v2 Simplex[3]){
TIMED_FUNCTION();
const f32 Epsilon = 0.0001f;
dynamic_array<v2> Polytope;
DynamicArrayInitialize(&Polytope, 20, &TransientStorageArena);
DynamicArrayPushBack(&Polytope, Simplex[0]);
DynamicArrayPushBack(&Polytope, Simplex[1]);
DynamicArrayPushBack(&Polytope, Simplex[2]);
v2 DeltaNormal = Normalize(Clockwise90(Delta));
epa_result Result = {};
while(true){
b8 FoundEdge = false;
b8 IsColinear = false;
u32 EdgeIndex = 0;
f32 EdgeDistance = 0;
v2 InverseNormal = {};
v2 IntersectionPoint = {};
for(u32 I=0; I < Polytope.Count; I++){
v2 A = Polytope[I];
v2 B = Polytope[(I+1)%Polytope.Count];
f32 AAlongDeltaNormal = Dot(DeltaNormal, A);
f32 BAlongDeltaNormal = Dot(DeltaNormal, B);
if(((-Epsilon <= AAlongDeltaNormal) && (AAlongDeltaNormal <= Epsilon)) && // Collinear
((-Epsilon <= BAlongDeltaNormal) && (BAlongDeltaNormal <= Epsilon))){
FoundEdge = true;
IsColinear = true;
EdgeIndex = I;
InverseNormal = Normalize(TripleProduct(B-A, -A));
EdgeDistance = Dot(InverseNormal, -A);
IntersectionPoint = V20;
}else if(((AAlongDeltaNormal >= 0) && (BAlongDeltaNormal <= 0)) ||
((AAlongDeltaNormal <= 0) && (BAlongDeltaNormal >= 0))){ // The delta line intersects
AAlongDeltaNormal = AbsoluteValue(AAlongDeltaNormal);
BAlongDeltaNormal = AbsoluteValue(BAlongDeltaNormal);
f32 ABPercent = AAlongDeltaNormal/(AAlongDeltaNormal + BAlongDeltaNormal);
v2 Point = A + ABPercent*(B-A);
v2 DeltaDirection = Normalize(Delta);
f32 PointAlongDeltaDirection = Dot(DeltaDirection, Point);
f32 DeltaLength = Dot(DeltaDirection, Delta);
if((-Epsilon <= PointAlongDeltaDirection) &&
(PointAlongDeltaDirection <= DeltaLength + Epsilon)){ // The delta intersects
FoundEdge = true;
EdgeIndex = I;
InverseNormal = Normalize(TripleProduct(B-A, -A));
EdgeDistance = Dot(InverseNormal, -A);
IntersectionPoint = Point;
break;
}else if(-Epsilon <= PointAlongDeltaDirection){ // The delta ray intersects
if(!IsColinear){
FoundEdge = true;
EdgeIndex = I;
InverseNormal = Normalize(TripleProduct(B-A, -A));
EdgeDistance = Dot(InverseNormal, -A);
IntersectionPoint = Point;
}
}
}
}
Assert(FoundEdge); // Catch any potential bugs
f32 Distance;
v2 NewPoint = V20;
if((InverseNormal.X == 0.0f) && (InverseNormal.Y == 0.0f)){
Distance = EdgeDistance;
}else{
v2 Normal = -InverseNormal;
NewPoint = CalculateSupport(ObjectA, ObjectB, Delta, Normal);
Distance = Dot(NewPoint, Normal);
}
if((-Epsilon <= (Distance-EdgeDistance)) && ((Distance-EdgeDistance) <= Epsilon)){
f32 Percent = IntersectionPoint.X/Delta.X;
if((Delta.X == 0.0f) && (Delta.Y == 0.0f)){
Percent = 0.0f;
}else if(Delta.X == 0.0f){
Percent = IntersectionPoint.Y/Delta.Y;
}
Result.WorkingTimeOfImpact = 1.0f - Percent;
if(Result.WorkingTimeOfImpact > 1.0f){
Result.WorkingTimeOfImpact = 1.0f;
}else if(Result.WorkingTimeOfImpact < 0.0f){
Result.WorkingTimeOfImpact = 0.0f;
}
Result.ActualTimeOfImpact = 1.0f - Percent; // This is for penetration correction
Result.Normal = InverseNormal;
break;
}else{
DynamicArrayInsertNewArrayItem(&Polytope, EdgeIndex+1, NewPoint);
}
}
return(Result);
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | epa_result Colliding = {};
Colliding.ActualTimeOfImpact = F32_POSITIVE_INFINITY;
f32 CollidingObjectDistanceAlongDelta = F32_POSITIVE_INFINITY;
for(u32 I = 0; I < CollidingObjects.Count; I++){ // Colliding objects is just an array of objects that GJK has deemed to be colliding
physics_object *ObjectB = CollidingObjects[I];
v2 *Simplex = &CollidingSimplices[I*3];
epa_result EPAResult = DoVelocityEPA(ObjectA, ObjectB, Delta, Simplex);
if(EPAResult.ActualTimeOfImpact < Colliding.ActualTimeOfImpact){
Colliding = EPAResult;
CollidingObjectDistanceAlongDelta = Dot(Delta, ObjectB->P-ObjectA->P);
}else if(EPAResult.ActualTimeOfImpact == Colliding.ActualTimeOfImpact){
f32 ObjectBDistanceAlongDelta = Dot(Delta, ObjectB->P-ObjectA->P);
if(ObjectBDistanceAlongDelta < CollidingObjectDistanceAlongDelta){
Colliding = EPAResult;
CollidingObjectDistanceAlongDelta = ObjectBDistanceAlongDelta;
}
}
}
// I still need to properly implement collision response, I have just finished collision detection
f32 COR = 1.0f;
FrameTimeRemaining -= FrameTimeRemaining*Colliding.WorkingTimeOfImpact;
ObjectA->P += Colliding.ActualTimeOfImpact*Delta;
ObjectA->dP = ObjectA->dP - COR*Dot(Colliding.Normal, ObjectA->dP)*Colliding.Normal;
Delta = Delta - COR*Dot(Colliding.Normal, Delta)*Colliding.Normal;
|
Epicrelyt
How would one go about finding if a certain algorithm has been described somewhere else?