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?