/// <summary> /// Returns the contact list from two possibly intersecting Geom's. /// This is the stationary version of this function. It doesn't /// account for linear or angular motion. /// </summary> /// <param name="geomA">The first Geom.</param> /// <param name="geomB">The second Geom.</param> /// <param name="contactList">Set of Contacts between the two Geoms. /// NOTE- this will be empty if no contacts are present.</param> public void Collide(Geom geomA, Geom geomB, ContactList contactList) { PolygonCollisionResult result = PolygonCollision(geomA.WorldVertices, geomB.WorldVertices); float distance = result.MinimumTranslationVector.Length(); int contactsDetected = 0; Vector2 normal = Vector2.Normalize(-result.MinimumTranslationVector); int contactsHandled = 0; if (result.Intersect && distance > 0.001f) { for (int i = 0; i < geomA.WorldVertices.Count; i++) { if (contactsDetected <= PhysicsSimulator.MaxContactsToDetect) { if (InsidePolygon(geomB.WorldVertices, geomA.WorldVertices[i])) { Contact c = new Contact(geomA.WorldVertices[i], normal, -distance, new ContactId(geomA.id, i, geomB.id)); contactList.Add(c); contactsDetected++; contactsHandled++; } } else { break; } } contactsDetected = 0; for (int i = 0; i < geomB.WorldVertices.Count; i++) { if (contactsDetected <= PhysicsSimulator.MaxContactsToDetect) { if (InsidePolygon(geomA.WorldVertices, geomB.WorldVertices[i])) { Contact c = new Contact(geomB.WorldVertices[i], normal, -distance, new ContactId(geomB.id, i, geomA.id)); contactList.Add(c); contactsDetected++; contactsHandled++; } } else { break; } } // No vertices of either polygon are inside the other, despite their intersection. // (Think of an X made of two rectangles of four vertices each.) // So select the vertex that is furthest past the edge forming the // separating axis as the contact point. // - Andrew Russell if (contactsHandled == 0) { int edgeIndex = result.bestEdgeIndex; Geom separatingEdgeOn = geomA; Geom otherPolygon = geomB; if (result.bestEdgeIndex >= geomA.WorldVertices.Count) { edgeIndex -= geomA.WorldVertices.Count; separatingEdgeOn = geomB; otherPolygon = geomA; } Vector2 edge = separatingEdgeOn.WorldVertices.GetEdge(edgeIndex); Vector2 axis = new Vector2(-edge.Y, edge.X); axis.Normalize(); int mostPenetrationIndex = 0; float mostPenetration = Vector2.Dot(axis, otherPolygon.WorldVertices[0]); for (int i = 1; i < otherPolygon.WorldVertices.Count; i++) { float penetration = Vector2.Dot(axis, otherPolygon.WorldVertices[i]); if (penetration < mostPenetration) { mostPenetration = penetration; mostPenetrationIndex = i; } } Contact c = new Contact(otherPolygon.WorldVertices[mostPenetrationIndex], normal, -distance, new ContactId(otherPolygon.id, mostPenetrationIndex, separatingEdgeOn.id)); contactList.Add(c); } } }
/// <summary> /// Finds the contactpoints between the two geometries. /// </summary> /// <param name="geomA">The first geom.</param> /// <param name="geomB">The second geom.</param> /// <param name="contactList">The contact list.</param> public void Collide(Geom geomA, Geom geomB, ContactList contactList) { int vertexIndex = -1; //Lookup distancegrid A data from list DistanceGridData geomAGridData = _distanceGrids[geomA.id]; //Iterate the second geometry vertices for (int i = 0; i < geomB.worldVertices.Count; i++) { if (contactList.Count == PhysicsSimulator.MaxContactsToDetect) { break; } vertexIndex += 1; _vertRef = geomB.WorldVertices[i]; geomA.TransformToLocalCoordinates(ref _vertRef, out _localVertex); //The geometry intersects when distance <= 0 //Continue in the list if the current vector does not intersect if (!geomAGridData.Intersect(ref _localVertex, out _feature)) { continue; } //If the geometries collide, create a new contact and add it to the contact list. if (_feature.Distance < 0f) { geomA.TransformNormalToWorld(ref _feature.Normal, out _feature.Normal); Contact contact = new Contact(geomB.WorldVertices[i], _feature.Normal, _feature.Distance, new ContactId(geomB.id, vertexIndex, geomA.id)); contactList.Add(contact); } } //Lookup distancegrid B data from list DistanceGridData geomBGridData = _distanceGrids[geomB.id]; //Iterate the first geometry vertices for (int i = 0; i < geomA.WorldVertices.Count; i++) { if (contactList.Count == PhysicsSimulator.MaxContactsToDetect) { break; } vertexIndex += 1; _vertRef = geomA.WorldVertices[i]; geomB.TransformToLocalCoordinates(ref _vertRef, out _localVertex); if (!geomBGridData.Intersect(ref _localVertex, out _feature)) { continue; } if (_feature.Distance < 0f) { geomB.TransformNormalToWorld(ref _feature.Normal, out _feature.Normal); _feature.Normal = -_feature.Normal; Contact contact = new Contact(geomA.WorldVertices[i], _feature.Normal, _feature.Distance, new ContactId(geomA.id, vertexIndex, geomB.id)); contactList.Add(contact); } } }