public void Reset(ref TimeStep step, List <Contact> contacts) { _step = step; _contacts = contacts; int contactCount = contacts.Count; int constraintCount = contactCount; _constraints.Clear(); for (int i = 0; i < constraintCount; i++) { _constraints.Add(new ContactConstraint()); } for (int i = 0; i < constraintCount; ++i) { Contact contact = contacts[i]; Fixture fixtureA = contact._fixtureA; Fixture fixtureB = contact._fixtureB; Shape shapeA = fixtureA.GetShape(); Shape shapeB = fixtureB.GetShape(); float radiusA = shapeA._radius; float radiusB = shapeB._radius; Body bodyA = fixtureA.GetBody(); Body bodyB = fixtureB.GetBody(); Manifold manifold; contact.GetManifold(out manifold); float friction = Settings.b2MixFriction(fixtureA.GetFriction(), fixtureB.GetFriction()); float restitution = Settings.b2MixRestitution(fixtureA.GetRestitution(), fixtureB.GetRestitution()); Vector2 vA = bodyA._linearVelocity; Vector2 vB = bodyB._linearVelocity; float wA = bodyA._angularVelocity; float wB = bodyB._angularVelocity; Debug.Assert(manifold._pointCount > 0); WorldManifold worldManifold = new WorldManifold(ref manifold, ref bodyA._xf, radiusA, ref bodyB._xf, radiusB); ContactConstraint cc = _constraints[i]; cc.bodyA = bodyA; cc.bodyB = bodyB; cc.manifold = manifold; cc.normal = worldManifold._normal; cc.pointCount = manifold._pointCount; cc.friction = friction; cc.restitution = restitution; cc.localPlaneNormal = manifold._localPlaneNormal; cc.localPoint = manifold._localPoint; cc.radius = radiusA + radiusB; cc.type = manifold._type; for (int j = 0; j < cc.pointCount; ++j) { ManifoldPoint cp = manifold._points[j]; ContactConstraintPoint ccp = cc.points[j]; ccp.normalImpulse = cp.NormalImpulse; ccp.tangentImpulse = cp.TangentImpulse; ccp.localPoint = cp.LocalPoint; ccp.rA = worldManifold._points[j] - bodyA._sweep.c; ccp.rB = worldManifold._points[j] - bodyB._sweep.c; float rnA = MathUtils.Cross(ccp.rA, cc.normal); float rnB = MathUtils.Cross(ccp.rB, cc.normal); rnA *= rnA; rnB *= rnB; float kNormal = bodyA._invMass + bodyB._invMass + bodyA._invI * rnA + bodyB._invI * rnB; Debug.Assert(kNormal > Settings.b2_FLT_EPSILON); ccp.normalMass = 1.0f / kNormal; float kEqualized = bodyA._mass * bodyA._invMass + bodyB._mass * bodyB._invMass; kEqualized += bodyA._mass * bodyA._invI * rnA + bodyB._mass * bodyB._invI * rnB; Debug.Assert(kEqualized > Settings.b2_FLT_EPSILON); ccp.equalizedMass = 1.0f / kEqualized; Vector2 tangent = MathUtils.Cross(cc.normal, 1.0f); float rtA = MathUtils.Cross(ccp.rA, tangent); float rtB = MathUtils.Cross(ccp.rB, tangent); rtA *= rtA; rtB *= rtB; float kTangent = bodyA._invMass + bodyB._invMass + bodyA._invI * rtA + bodyB._invI * rtB; Debug.Assert(kTangent > Settings.b2_FLT_EPSILON); ccp.tangentMass = 1.0f / kTangent; // Setup a velocity bias for restitution. ccp.velocityBias = 0.0f; float vRel = Vector2.Dot(cc.normal, vB + MathUtils.Cross(wB, ccp.rB) - vA - MathUtils.Cross(wA, ccp.rA)); if (vRel < -Settings.b2_velocityThreshold) { ccp.velocityBias = -cc.restitution * vRel; } cc.points[j] = ccp; } // If we have two points, then prepare the block solver. if (cc.pointCount == 2) { ContactConstraintPoint ccp1 = cc.points[0]; ContactConstraintPoint ccp2 = cc.points[1]; float invMassA = bodyA._invMass; float invIA = bodyA._invI; float invMassB = bodyB._invMass; float invIB = bodyB._invI; float rn1A = MathUtils.Cross(ccp1.rA, cc.normal); float rn1B = MathUtils.Cross(ccp1.rB, cc.normal); float rn2A = MathUtils.Cross(ccp2.rA, cc.normal); float rn2B = MathUtils.Cross(ccp2.rB, cc.normal); float k11 = invMassA + invMassB + invIA * rn1A * rn1A + invIB * rn1B * rn1B; float k22 = invMassA + invMassB + invIA * rn2A * rn2A + invIB * rn2B * rn2B; float k12 = invMassA + invMassB + invIA * rn1A * rn2A + invIB * rn1B * rn2B; // Ensure a reasonable condition number. float k_maxConditionNumber = 100.0f; if (k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) { // K is safe to invert. cc.K = new Mat22(new Vector2(k11, k12), new Vector2(k12, k22)); cc.normalMass = cc.K.GetInverse(); } else { // The constraints are redundant, just use one. // TODO_ERIN use deepest? cc.pointCount = 1; } } _constraints[i] = cc; } }
void SolveTOI(ref TimeStep step) { // Reserve an island and a queue for TOI island solution. _island.Reset(_bodyCount, Settings.b2_maxTOIContactsPerIsland, Settings.b2_maxTOIJointsPerIsland, _contactManager.ContactListener); //Simple one pass queue //Relies on the fact that we're only making one pass //through and each body can only be pushed/popped once. //To push: // queue[queueStart+queueSize++] = newElement; //To pop: // poppedElement = queue[queueStart++]; // --queueSize; //#warning More Body array Allocs int queueCapacity = _bodyCount; Body[] queue = new Body[_bodyCount]; for (Body b = _bodyList; b != null; b = b._next) { b._flags &= ~BodyFlags.Island; b._sweep.t0 = 0.0f; } for (Contact c = _contactManager._contactList; c != null; c = c._next) { // Invalidate TOI c._flags &= ~(ContactFlags.Toi | ContactFlags.Island); } for (Joint j = _jointList; j != null; j = j._next) { j._islandFlag = false; } // Find TOI events and solve them. for (;;) { // Find the first TOI. Contact minContact = null; float minTOI = 1.0f; for (Contact c = _contactManager._contactList; c != null; c = c._next) { if ((c._flags & (ContactFlags.Slow | ContactFlags.NonSolid)) != ContactFlags.None) { continue; } // TODO_ERIN keep a counter on the contact, only respond to M TOIs per contact. float toi = 1.0f; if ((c._flags & ContactFlags.Toi) != ContactFlags.None) { // This contact has a valid cached TOI. toi = c._toi; } else { // Compute the TOI for this contact. Fixture s1 = c.GetFixtureA(); Fixture s2 = c.GetFixtureB(); Body b1 = s1.GetBody(); Body b2 = s2.GetBody(); if ((b1.IsStatic || b1.IsSleeping) && (b2.IsStatic || b2.IsSleeping)) { continue; } // Put the sweeps onto the same time interval. float t0 = b1._sweep.t0; if (b1._sweep.t0 < b2._sweep.t0) { t0 = b2._sweep.t0; b1._sweep.Advance(t0); } else if (b2._sweep.t0 < b1._sweep.t0) { t0 = b1._sweep.t0; b2._sweep.Advance(t0); } Debug.Assert(t0 < 1.0f); // Compute the time of impact. toi = c.ComputeTOI(ref b1._sweep, ref b2._sweep); //CalculateTimeOfImpact(c._fixtureA.GetShape(), b1._sweep, c._fixtureB.GetShape(), b2._sweep); Debug.Assert(0.0f <= toi && toi <= 1.0f); // If the TOI is in range ... if (0.0f < toi && toi < 1.0f) { // Interpolate on the actual range. toi = Math.Min((1.0f - toi) * t0 + toi, 1.0f); } c._toi = toi; c._flags |= ContactFlags.Toi; } if (Settings.b2_FLT_EPSILON < toi && toi < minTOI) { // This is the minimum TOI found so far. minContact = c; minTOI = toi; } } if (minContact == null || 1.0f - 100.0f * Settings.b2_FLT_EPSILON < minTOI) { // No more TOI events. Done! break; } // Advance the bodies to the TOI. Fixture s1_2 = minContact.GetFixtureA(); Fixture s2_2 = minContact.GetFixtureB(); Body b1_2 = s1_2.GetBody(); Body b2_2 = s2_2.GetBody(); b1_2.Advance(minTOI); b2_2.Advance(minTOI); // The TOI contact likely has some new contact points. minContact.Update(_contactManager.ContactListener); minContact._flags &= ~ContactFlags.Toi; if ((minContact._flags & ContactFlags.Touch) == 0) { // This shouldn't happen. Numerical error? //Debug.Assert(false); continue; } // Build the TOI island. We need a dynamic seed. Body seed = b1_2; if (seed.IsStatic) { seed = b2_2; } // Reset island and queue. _island.Clear(); int queueStart = 0; // starting index for queue int queueSize = 0; // elements in queue queue[queueStart + queueSize++] = seed; seed._flags |= BodyFlags.Island; // Perform a breadth first search (BFS) on the contact/joint graph. while (queueSize > 0) { // Grab the next body off the stack and add it to the island. Body b = queue[queueStart++]; --queueSize; _island.Add(b); // Make sure the body is awake. b._flags &= ~BodyFlags.Sleep; // To keep islands as small as possible, we don't // propagate islands across static bodies. if (b.IsStatic) { continue; } // Search all contacts connected to this body. for (ContactEdge cEdge = b._contactList; cEdge != null; cEdge = cEdge.Next) { // Does the TOI island still have space for contacts? if (_island._contacts.Count == _island._contactCapacity) { continue; } // Has this contact already been added to an island? Skip slow or non-solid contacts. if ((cEdge.Contact._flags & (ContactFlags.Island | ContactFlags.Slow | ContactFlags.NonSolid)) != ContactFlags.None) { continue; } // Is this contact touching? For performance we are not updating this contact. if ((cEdge.Contact._flags & ContactFlags.Touch) == 0) { continue; } _island.Add(cEdge.Contact); cEdge.Contact._flags |= ContactFlags.Island; // Update other body. Body other = cEdge.Other; // Was the other body already added to this island? if ((other._flags & BodyFlags.Island) != BodyFlags.None) { continue; } // March forward, this can do no harm since this is the min TOI. if (other.IsStatic == false) { other.Advance(minTOI); other.WakeUp(); } Debug.Assert(queueStart + queueSize < queueCapacity); queue[queueStart + queueSize] = other; ++queueSize; other._flags |= BodyFlags.Island; } for (JointEdge jEdge = b._jointList; jEdge != null; jEdge = jEdge.Next) { if (_island._jointCount == _island._jointCapacity) { continue; } if (jEdge.Joint._islandFlag == true) { continue; } _island.Add(jEdge.Joint); jEdge.Joint._islandFlag = true; Body other = jEdge.Other; if ((other._flags & BodyFlags.Island) != BodyFlags.None) { continue; } if (!other.IsStatic) { other.Advance(minTOI); other.WakeUp(); } Debug.Assert(queueStart + queueSize < queueCapacity); queue[queueStart + queueSize] = other; ++queueSize; other._flags |= BodyFlags.Island; } } TimeStep subStep; subStep.warmStarting = false; subStep.dt = (1.0f - minTOI) * step.dt; subStep.inv_dt = 1.0f / subStep.dt; subStep.dtRatio = 0.0f; subStep.velocityIterations = step.velocityIterations; subStep.positionIterations = step.positionIterations; _island.SolveTOI(ref subStep); // Post solve cleanup. for (int i = 0; i < _island._bodyCount; ++i) { // Allow bodies to participate in future TOI islands. Body b = _island._bodies[i]; b._flags &= ~BodyFlags.Island; if ((b._flags & (BodyFlags.Sleep | BodyFlags.Frozen)) != BodyFlags.None) { continue; } if (b.IsStatic) { continue; } // Update fixtures (for broad-phase). b.SynchronizeFixtures(); // Invalidate all contact TOIs associated with this body. Some of these // may not be in the island because they were not touching. for (ContactEdge ce = b._contactList; ce != null; ce = ce.Next) { ce.Contact._flags &= ~ContactFlags.Toi; } } int contactCount = _island._contacts.Count; for (int i = 0; i < contactCount; ++i) { // Allow contacts to participate in future TOI islands. Contact c = _island._contacts[i]; c._flags &= ~(ContactFlags.Toi | ContactFlags.Island); } for (int i = 0; i < _island._jointCount; ++i) { // Allow joints to participate in future TOI islands. Joint j = _island._joints[i]; j._islandFlag = false; } // Commit fixture proxy movements to the broad-phase so that new contacts are created. // Also, some contacts can be destroyed. _contactManager.FindNewContacts(); } }
internal void Collide() { // Update awake contacts. Contact c = _contactList; while (c != null) { Fixture fixtureA = c.GetFixtureA(); Fixture fixtureB = c.GetFixtureB(); Body bodyA = fixtureA.GetBody(); Body bodyB = fixtureB.GetBody(); if (bodyA.IsSleeping && bodyB.IsSleeping) { c = c.GetNext(); continue; } // Is this contact flagged for filtering? if ((c._flags & ContactFlags.Filter) == ContactFlags.Filter) { // Are both bodies static? if (bodyA.IsStatic && bodyB.IsStatic) { Contact cNuke = c; c = cNuke.GetNext(); Destroy(cNuke); continue; } // Does a joint override collision? if (bodyB.IsConnected(bodyA)) { Contact cNuke = c; c = cNuke.GetNext(); Destroy(cNuke); continue; } // Check user filtering. if (ContactFilter.ShouldCollide(fixtureA, fixtureB) == false) { Contact cNuke = c; c = cNuke.GetNext(); Destroy(cNuke); continue; } // Clear the filtering flag. c._flags &= ~ContactFlags.Filter; } int proxyIdA = fixtureA._proxyId; int proxyIdB = fixtureB._proxyId; AABB aabbA; _broadPhase.GetAABB(proxyIdA, out aabbA); AABB aabbB; _broadPhase.GetAABB(proxyIdB, out aabbB); // Here we cull out contacts that cease to overlap. if (AABB.TestOverlap(ref aabbA, ref aabbB) == false) { Contact cNuke = c; c = cNuke.GetNext(); Destroy(cNuke); continue; } // The contact persists. c.Update(ContactListener); c = c.GetNext(); } }
// Broad-phase callback. internal void AddPair(Fixture proxyUserDataA, Fixture proxyUserDataB) { Fixture fixtureA = proxyUserDataA; Fixture fixtureB = proxyUserDataB; Body bodyA = fixtureA.GetBody(); Body bodyB = fixtureB.GetBody(); // Are the fixtures on the same body? if (bodyA == bodyB) { return; } // Are both bodies static? if (bodyA.IsStatic && bodyB.IsStatic) { return; } // Does a contact already exist? ContactEdge edge = bodyB.GetConactList(); while (edge != null) { if (edge.Other == bodyA) { Fixture fA = edge.Contact.GetFixtureA(); Fixture fB = edge.Contact.GetFixtureB(); if (fA == fixtureA && fB == fixtureB) { // A contact already exists. return; } if (fA == fixtureB && fB == fixtureA) { // A contact already exists. return; } } edge = edge.Next; } // Does a joint override collision? if (bodyB.IsConnected(bodyA)) { return; } // Check user filtering. if (ContactFilter.ShouldCollide(fixtureA, fixtureB) == false) { return; } // Call the factory. Contact c = Contact.Create(fixtureA, fixtureB); // Contact creation may swap fixtures. fixtureA = c.GetFixtureA(); fixtureB = c.GetFixtureB(); bodyA = fixtureA.GetBody(); bodyB = fixtureB.GetBody(); // Insert into the world. c._prev = null; c._next = _contactList; if (_contactList != null) { _contactList._prev = c; } _contactList = c; // Connect to island graph. // Connect to body A c._nodeA.Contact = c; c._nodeA.Other = bodyB; c._nodeA.Prev = null; c._nodeA.Next = bodyA._contactList; if (bodyA._contactList != null) { bodyA._contactList.Prev = c._nodeA; } bodyA._contactList = c._nodeA; // Connect to body B c._nodeB.Contact = c; c._nodeB.Other = bodyA; c._nodeB.Prev = null; c._nodeB.Next = bodyB._contactList; if (bodyB._contactList != null) { bodyB._contactList.Prev = c._nodeB; } bodyB._contactList = c._nodeB; ++_contactCount; }
internal void Destroy(Contact c) { Fixture fixtureA = c.GetFixtureA(); Fixture fixtureB = c.GetFixtureB(); Body bodyA = fixtureA.GetBody(); Body bodyB = fixtureB.GetBody(); if (c._manifold._pointCount > 0) { ContactListener.EndContact(c); } // Remove from the world. if (c._prev != null) { c._prev._next = c._next; } if (c._next != null) { c._next._prev = c._prev; } if (c == _contactList) { _contactList = c._next; } // Remove from body 1 if (c._nodeA.Prev != null) { c._nodeA.Prev.Next = c._nodeA.Next; } if (c._nodeA.Next != null) { c._nodeA.Next.Prev = c._nodeA.Prev; } if (c._nodeA == bodyA._contactList) { bodyA._contactList = c._nodeA.Next; } // Remove from body 2 if (c._nodeB.Prev != null) { c._nodeB.Prev.Next = c._nodeB.Next; } if (c._nodeB.Next != null) { c._nodeB.Next.Prev = c._nodeB.Prev; } if (c._nodeB == bodyB._contactList) { bodyB._contactList = c._nodeB.Next; } --_contactCount; }