/// <summary> /// Initializes a new instance of the <see cref="World" /> class. /// </summary> public World(Vector2 gravity) { Island = new Island(); Enabled = true; ControllerList = new List <Controller>(); BreakableBodyList = new List <BreakableBody>(); BodyList = new List <Body>(32); JointList = new List <Joint>(32); _queryAABBCallbackWrapper = QueryAABBCallbackWrapper; _rayCastCallbackWrapper = RayCastCallbackWrapper; ContactManager = new ContactManager(new DynamicTreeBroadPhase()); Gravity = gravity; }
private void SolveTOI(ref TimeStep step) { Island.Reset(2 * Settings.MaxTOIContacts, Settings.MaxTOIContacts, 0, ContactManager); if (_stepComplete) { for (var i = 0; i < BodyList.Count; i++) { BodyList[i]._flags &= ~BodyFlags.IslandFlag; BodyList[i]._sweep.Alpha0 = 0.0f; } for (var i = 0; i < ContactManager.ContactList.Count; i++) { var c = ContactManager.ContactList[i]; // Invalidate TOI c._flags &= ~ContactFlags.IslandFlag; c._flags &= ~ContactFlags.TOIFlag; c._toiCount = 0; c._toi = 1.0f; } } // Find TOI events and solve them. for (;;) { // Find the first TOI. Contact minContact = null; var minAlpha = 1.0f; for (var i = 0; i < ContactManager.ContactList.Count; i++) { var c = ContactManager.ContactList[i]; // Is this contact disabled? if (c.Enabled == false) { continue; } // Prevent excessive sub-stepping. if (c._toiCount > Settings.MaxSubSteps) { continue; } float alpha; if (c.TOIFlag) { // This contact has a valid cached TOI. alpha = c._toi; } else { var fA = c.FixtureA; var fB = c.FixtureB; // Is there a sensor? if (fA.IsSensor || fB.IsSensor) { continue; } var bA = fA.Body; var bB = fB.Body; var typeA = bA.BodyType; var typeB = bB.BodyType; Debug.Assert(typeA == BodyType.Dynamic || typeB == BodyType.Dynamic); var activeA = bA.Awake && typeA != BodyType.Static; var activeB = bB.Awake && typeB != BodyType.Static; // Is at least one body active (awake and dynamic or kinematic)? if (activeA == false && activeB == false) { continue; } var collideA = (bA.IsBullet || typeA != BodyType.Dynamic) && (fA.IgnoreCCDWith & fB.CollisionCategories) == 0 && !bA.IgnoreCCD; var collideB = (bB.IsBullet || typeB != BodyType.Dynamic) && (fB.IgnoreCCDWith & fA.CollisionCategories) == 0 && !bB.IgnoreCCD; // Are these two non-bullet dynamic bodies? if (collideA == false && collideB == false) { continue; } // Compute the TOI for this contact. // Put the sweeps onto the same time interval. var alpha0 = bA._sweep.Alpha0; if (bA._sweep.Alpha0 < bB._sweep.Alpha0) { alpha0 = bB._sweep.Alpha0; bA._sweep.Advance(alpha0); } else if (bB._sweep.Alpha0 < bA._sweep.Alpha0) { alpha0 = bA._sweep.Alpha0; bB._sweep.Advance(alpha0); } Debug.Assert(alpha0 < 1.0f); // Compute the time of impact in interval [0, minTOI] var input = new TOIInput(); input.ProxyA = new DistanceProxy(fA.Shape, c.ChildIndexA); input.ProxyB = new DistanceProxy(fB.Shape, c.ChildIndexB); input.SweepA = bA._sweep; input.SweepB = bB._sweep; input.TMax = 1.0f; TOIOutput output; TimeOfImpact.CalculateTimeOfImpact(ref input, out output); // Beta is the fraction of the remaining portion of the . var beta = output.T; if (output.State == TOIOutputState.Touching) { alpha = Mathf.Min(alpha0 + (1.0f - alpha0) * beta, 1.0f); } else { alpha = 1.0f; } c._toi = alpha; c._flags &= ~ContactFlags.TOIFlag; } if (alpha < minAlpha) { // This is the minimum TOI found so far. minContact = c; minAlpha = alpha; } } if (minContact == null || 1.0f - 10.0f * Settings.Epsilon < minAlpha) { // No more TOI events. Done! _stepComplete = true; break; } // Advance the bodies to the TOI. var fA1 = minContact.FixtureA; var fB1 = minContact.FixtureB; var bA0 = fA1.Body; var bB0 = fB1.Body; var backup1 = bA0._sweep; var backup2 = bB0._sweep; bA0.Advance(minAlpha); bB0.Advance(minAlpha); // The TOI contact likely has some new contact points. minContact.Update(ContactManager); minContact._flags &= ~ContactFlags.TOIFlag; ++minContact._toiCount; // Is the contact solid? if (minContact.Enabled == false || minContact.IsTouching == false) { // Restore the sweeps. minContact._flags &= ~ContactFlags.EnabledFlag; bA0._sweep = backup1; bB0._sweep = backup2; bA0.SynchronizeTransform(); bB0.SynchronizeTransform(); continue; } bA0.Awake = true; bB0.Awake = true; // Build the island Island.Clear(); Island.Add(bA0); Island.Add(bB0); Island.Add(minContact); bA0._flags |= BodyFlags.IslandFlag; bB0._flags |= BodyFlags.IslandFlag; minContact._flags &= ~ContactFlags.IslandFlag; // Get contacts on bodyA and bodyB. Body[] bodies = { bA0, bB0 }; for (var i = 0; i < 2; ++i) { var body = bodies[i]; if (body.BodyType == BodyType.Dynamic) { for (var ce = body.ContactList; ce != null; ce = ce.Next) { var contact = ce.Contact; if (Island.BodyCount == Island.BodyCapacity) { break; } if (Island.ContactCount == Island.ContactCapacity) { break; } // Has this contact already been added to the island? if (contact.IslandFlag) { continue; } // Only add static, kinematic, or bullet bodies. var other = ce.Other; if (other.BodyType == BodyType.Dynamic && body.IsBullet == false && other.IsBullet == false) { continue; } // Skip sensors. if (contact.FixtureA.IsSensor || contact.FixtureB.IsSensor) { continue; } // Tentatively advance the body to the TOI. var backup = other._sweep; if (!other.IsIsland) { other.Advance(minAlpha); } // Update the contact points contact.Update(ContactManager); // Was the contact disabled by the user? if (contact.Enabled == false) { other._sweep = backup; other.SynchronizeTransform(); continue; } // Are there contact points? if (contact.IsTouching == false) { other._sweep = backup; other.SynchronizeTransform(); continue; } // Add the contact to the island minContact._flags |= ContactFlags.IslandFlag; Island.Add(contact); // Has the other body already been added to the island? if (other.IsIsland) { continue; } // Add the other body to the island. other._flags |= BodyFlags.IslandFlag; if (other.BodyType != BodyType.Static) { other.Awake = true; } Island.Add(other); } } } TimeStep subStep; subStep.dt = (1.0f - minAlpha) * step.dt; subStep.inv_dt = 1.0f / subStep.dt; subStep.dtRatio = 1.0f; Island.SolveTOI(ref subStep, bA0.IslandIndex, bB0.IslandIndex); // Reset island flags and synchronize broad-phase proxies. for (var i = 0; i < Island.BodyCount; ++i) { var body = Island.Bodies[i]; body._flags &= ~BodyFlags.IslandFlag; if (body.BodyType != BodyType.Dynamic) { continue; } body.SynchronizeFixtures(); // Invalidate all contact TOIs on this displaced body. for (var ce = body.ContactList; ce != null; ce = ce.Next) { ce.Contact._flags &= ~ContactFlags.TOIFlag; ce.Contact._flags &= ~ContactFlags.IslandFlag; } } // Commit fixture proxy movements to the broad-phase so that new contacts are created. // Also, some contacts can be destroyed. ContactManager.FindNewContacts(); if (Settings.EnableSubStepping) { _stepComplete = false; break; } } }
private void Solve(ref TimeStep step) { // Size the island for the worst case. Island.Reset(BodyList.Count, ContactManager.ContactList.Count, JointList.Count, ContactManager); // Clear all the island flags. foreach (var b in BodyList) { b._flags &= ~BodyFlags.IslandFlag; } foreach (var c in ContactManager.ContactList) { c._flags &= ~ContactFlags.IslandFlag; } foreach (var j in JointList) { j.IslandFlag = false; } // Build and simulate all awake islands. var stackSize = BodyList.Count; if (stackSize > _stack.Length) { _stack = new Body[Mathf.Max(_stack.Length * 2, stackSize)]; } for (var index = BodyList.Count - 1; index >= 0; index--) { var seed = BodyList[index]; if ((seed._flags & BodyFlags.IslandFlag) == BodyFlags.IslandFlag) { continue; } if (seed.Awake == false || seed.Enabled == false) { continue; } // The seed can be dynamic or kinematic. if (seed.BodyType == BodyType.Static) { continue; } // Reset island and stack. Island.Clear(); var stackCount = 0; _stack[stackCount++] = seed; seed._flags |= BodyFlags.IslandFlag; // Perform a depth first search (DFS) on the constraint graph. while (stackCount > 0) { // Grab the next body off the stack and add it to the island. var b = _stack[--stackCount]; Debug.Assert(b.Enabled); Island.Add(b); // Make sure the body is awake (without resetting sleep timer). b._flags |= BodyFlags.AwakeFlag; // To keep islands as small as possible, we don't // propagate islands across static bodies. if (b.BodyType == BodyType.Static) { continue; } // Search all contacts connected to this body. for (var ce = b.ContactList; ce != null; ce = ce.Next) { var contact = ce.Contact; // Has this contact already been added to an island? if (contact.IslandFlag) { continue; } // Is this contact solid and touching? if (ce.Contact.Enabled == false || ce.Contact.IsTouching == false) { continue; } // Skip sensors. var sensorA = contact.FixtureA.IsSensor; var sensorB = contact.FixtureB.IsSensor; if (sensorA || sensorB) { continue; } Island.Add(contact); contact._flags |= ContactFlags.IslandFlag; var other = ce.Other; // Was the other body already added to this island? if (other.IsIsland) { continue; } Debug.Assert(stackCount < stackSize); _stack[stackCount++] = other; other._flags |= BodyFlags.IslandFlag; } // Search all joints connect to this body. for (var je = b.JointList; je != null; je = je.Next) { if (je.Joint.IslandFlag) { continue; } var other = je.Other; // WIP David //Enter here when it's a non-fixed joint. Non-fixed joints have a other body. if (other != null) { // Don't simulate joints connected to inactive bodies. if (other.Enabled == false) { continue; } Island.Add(je.Joint); je.Joint.IslandFlag = true; if (other.IsIsland) { continue; } Debug.Assert(stackCount < stackSize); _stack[stackCount++] = other; other._flags |= BodyFlags.IslandFlag; } else { Island.Add(je.Joint); je.Joint.IslandFlag = true; } } } Island.Solve(ref step, ref Gravity); // Post solve cleanup. for (var i = 0; i < Island.BodyCount; ++i) { // Allow static bodies to participate in other islands. var b = Island.Bodies[i]; if (b.BodyType == BodyType.Static) { b._flags &= ~BodyFlags.IslandFlag; } } } // Synchronize fixtures, check for out of range bodies. foreach (var b in BodyList) { // If a body was not in an island then it did not move. if (!b.IsIsland) { continue; } if (b.BodyType == BodyType.Static) { continue; } // Update fixtures (for broad-phase). b.SynchronizeFixtures(); } // Look for new contacts. ContactManager.FindNewContacts(); }