public void SetAsBox(float hx, float hy, Vec2 center, float angle) { VertexCount = 4; Vertices[0].Set(-hx, -hy); Vertices[1].Set(hx, -hy); Vertices[2].Set(hx, hy); Vertices[3].Set(-hx, hy); Normals[0].Set(0.0f, -1.0f); Normals[1].Set(1.0f, 0.0f); Normals[2].Set(0.0f, 1.0f); Normals[3].Set(-1.0f, 0.0f); Centroid = center; Transform xf = new Transform(); xf.Position = center; xf.R.Set(angle); // Transform vertices and normals. for (int i = 0; i < VertexCount; ++i) { Vertices[i] = Math.Mul(xf, Vertices[i]); Normals[i] = Math.Mul(xf.R, Normals[i]); } }
public override bool TestPoint(Transform transform, Vec2 p) { Vec2 center = transform.Position + Math.Mul(transform.R, _p); Vec2 d = p - center; return(Vec2.Dot(d, d) <= _radius * _radius); }
private void DrawShape(Fixture fixture, Transform xf, Color color) { Color coreColor = new Color(0.9f, 0.6f, 0.6f); switch (fixture.GetType()) { case ShapeType.CircleShape: { CircleShape circle = (CircleShape)fixture.GetShape(); Vec2 center = Math.Mul(xf, circle._p); float radius = circle._radius; Vec2 axis = xf.R.Col1; _debugDraw.DrawSolidCircle(center, radius, axis, color); } break; case ShapeType.PolygonShape: { PolygonShape poly = (PolygonShape)fixture.GetShape(); int vertexCount = poly.VertexCount; Box2DXDebug.Assert(vertexCount <= Settings.MaxPolygonVertices); Vec2[] vertices = new Vec2[Settings.MaxPolygonVertices]; for (int i = 0; i < vertexCount; ++i) { vertices[i] = Math.Mul(xf, poly.Vertices[i]); } _debugDraw.DrawSolidPolygon(vertices, vertexCount, color); } break; } }
internal Body(BodyDef bd, World world) { _flags = 0; if (bd.IsBullet) { _flags |= BodyFlags.Bullet; } if (bd.FixedRotation) { _flags |= BodyFlags.FixedRotation; } if (bd.AllowSleep) { _flags |= BodyFlags.AllowSleep; } if (bd.IsSleeping) { _flags |= BodyFlags.Sleep; } _world = world; _xf.Position = bd.Position; _xf.R.Set(bd.Angle); _sweep.LocalCenter.SetZero(); _sweep.T0 = 1.0f; _sweep.A0 = _sweep.A = bd.Angle; _sweep.C0 = _sweep.C = Math.Mul(_xf, _sweep.LocalCenter); _jointList = null; _contactList = null; _prev = null; _next = null; _linearVelocity = bd.LinearVelocity; _angularVelocity = bd.AngularVelocity; _linearDamping = bd.LinearDamping; _angularDamping = bd.AngularDamping; _force.Set(0.0f, 0.0f); _torque = 0.0f; _sleepTime = 0.0f; _mass = 0; _invMass = 0.0f; _I = 0.0f; _invI = 0.0f; _type = BodyType.Static; _userData = bd.UserData; _fixtureList = null; _fixtureCount = 0; }
public override void ComputeAABB(out AABB aabb, ref Transform transform) { aabb = new AABB(); Vec2 p = transform.Position + Math.Mul(transform.R, _p); aabb.LowerBound.Set(p.X - _radius, p.Y - _radius); aabb.UpperBound.Set(p.X + _radius, p.Y + _radius); }
/// Set the mass properties to override the mass properties of the fixtures. /// Note that this changes the center of mass position. You can make the body /// static by using zero mass. /// Note that creating or destroying fixtures can also alter the mass. /// @warning The supplied rotational inertia is assumed to be relative to the center of mass. /// @param massData the mass properties. // TODO ERIN adjust linear velocity and torque to account for movement of center. public void SetMassData(MassData massData) { Box2DXDebug.Assert(_world.IsLocked() == false); if (_world.IsLocked() == true) { return; } _invMass = 0.0f; _I = 0.0f; _invI = 0.0f; _mass = massData.Mass; if (_mass > 0.0f) { _invMass = 1.0f / _mass; } if (massData.I > 0.0f && (_flags & BodyFlags.FixedRotation) == 0) { _I = massData.I - _mass * Vec2.Dot(massData.Center, massData.Center); _invI = 1.0f / _I; } // Move center of mass. Vec2 oldCenter = _sweep.C; _sweep.LocalCenter = massData.Center; _sweep.C0 = _sweep.C = Math.Mul(_xf, _sweep.LocalCenter); // Update center of mass velocity. _linearVelocity += Vec2.Cross(_angularVelocity, _sweep.C - oldCenter); BodyType oldType = _type; if (_invMass == 0.0f && _invI == 0.0f) { _type = BodyType.Static; } else { _type = BodyType.Dynamic; } // If the body type changed, we need to flag contacts for filtering. if (oldType != _type) { for (ContactEdge ce = _contactList; ce != null; ce = ce.Next) { ce.Contact.FlagForFiltering(); } } }
//TODO: uncomment //public void Rebalance(int iterations) //{ // if (_root == NullNode) // { // return; // } // for (int i = 0; i < iterations; ++i) // { // int node = _root; // uint bit = 0; // while (_nodes[node].IsLeaf() == false) // { // int children = _nodes[node].Child1; // node = children[(_path >> bit) & 1]; // bit = (bit + 1) & (8 * sizeof(uint) - 1); // } // ++_path; // RemoveLeaf(node); // InsertLeaf(node); // } //} // Compute the height of a sub-tree. public int ComputeHeight(int nodeId) { if (nodeId == NullNode) { return(0); } Box2DXDebug.Assert(0 <= nodeId && nodeId < _nodeCapacity); DynamicTreeNode node = _nodes[nodeId]; int height1 = ComputeHeight(node.Child1); int height2 = ComputeHeight(node.Child2); return(1 + Math.Max(height1, height2)); }
internal void SynchronizeFixtures() { Transform xf1 = new Transform(); xf1.R.Set(_sweep.A0); xf1.Position = _sweep.C0 - Math.Mul(xf1.R, _sweep.LocalCenter); BroadPhase broadPhase = _world._contactManager._broadPhase; for (Fixture f = _fixtureList; f != null; f = f._next) { f.Synchronize(broadPhase, xf1, _xf); } }
public override void Step(Settings settings) { base.Step(settings); DistanceInput input = new DistanceInput(); input.TransformA = _transformA; input.TransformB = _transformB; input.UseRadii = true; SimplexCache cache = new SimplexCache(); cache.Count = 0; DistanceOutput output; Collision.Distance(out output, ref cache, ref input, _polygonA, _polygonB); StringBuilder strBld = new StringBuilder(); strBld.AppendFormat("distance = {0}", new object[] { output.Distance }); OpenGLDebugDraw.DrawString(5, _textLine, strBld.ToString()); _textLine += 15; strBld = new StringBuilder(); strBld.AppendFormat("iterations = {0}", new object[] { output.Iterations }); OpenGLDebugDraw.DrawString(5, _textLine, strBld.ToString()); _textLine += 15; { Color color = new Color(0.9f, 0.9f, 0.9f); int i; for (i = 0; i < _polygonA.VertexCount; ++i) { _dv[i] = Math.Mul(_transformA, _polygonA.Vertices[i]); } _debugDraw.DrawPolygon(_dv, _polygonA.VertexCount, color); for (i = 0; i < _polygonB.VertexCount; ++i) { _dv[i] = Math.Mul(_transformB, _polygonB.Vertices[i]); } _debugDraw.DrawPolygon(_dv, _polygonB.VertexCount, color); } Vec2 x1 = output.PointA; Vec2 x2 = output.PointB; OpenGLDebugDraw.DrawPoint(x1, 4.0f, new Color(1, 0, 0)); OpenGLDebugDraw.DrawSegment(x1, x2, new Color(1, 1, 0)); OpenGLDebugDraw.DrawPoint(x2, 4.0f, new Color(1, 0, 0)); }
public override bool TestPoint(Transform xf, Vec2 p) { Vec2 pLocal = Math.MulT(xf.R, p - xf.Position); for (int i = 0; i < VertexCount; ++i) { float dot = Vec2.Dot(Normals[i], pLocal - Vertices[i]); if (dot > 0.0f) { return(false); } } return(true); }
public override void ComputeAABB(out AABB aabb, ref Transform xf) { Vec2 lower = Math.Mul(xf, Vertices[0]); Vec2 upper = lower; for (int i = 1; i < VertexCount; ++i) { Vec2 v = Math.Mul(xf, Vertices[i]); lower = Math.Min(lower, v); upper = Math.Max(upper, v); } Vec2 r = new Vec2(_radius, _radius); aabb.LowerBound = lower - r; aabb.UpperBound = upper + r; }
public override void Step(TimeStep step) { //B2_NOT_USED(step); if (InvSqr) { for (ControllerEdge i = _bodyList; i != null; i = i.nextBody) { Body body1 = i.body; for (ControllerEdge j = _bodyList; j != i; j = j.nextBody) { Body body2 = j.body; Vector2 d = body2.GetWorldCenter() - body1.GetWorldCenter(); float r2 = d.LengthSquared; if (r2 < Settings.FLT_EPSILON) { continue; } Vector2 f = G / r2 / Math.Sqrt(r2) * body1.GetMass() * body2.GetMass() * d; body1.ApplyForce(f, body1.GetWorldCenter()); body2.ApplyForce(-1.0f * f, body2.GetWorldCenter()); } } } else { for (ControllerEdge i = _bodyList; i != null; i = i.nextBody) { Body body1 = i.body; for (ControllerEdge j = _bodyList; j != i; j = j.nextBody) { Body body2 = j.body; Vector2 d = body2.GetWorldCenter() - body1.GetWorldCenter(); float r2 = d.LengthSquared; if (r2 < Settings.FLT_EPSILON) { continue; } Vector2 f = G / r2 * body1.GetMass() * body2.GetMass() * d; body1.ApplyForce(f, body1.GetWorldCenter()); body2.ApplyForce(-1.0f * f, body2.GetWorldCenter()); } } } }
// Collision Detection in Interactive 3D Environments by Gino van den Bergen // From Section 3.1.2 // x = s + a * r // norm(x) = radius public override void RayCast(out RayCastOutput output, ref RayCastInput input, Transform transform) { output = new RayCastOutput(); Vec2 position = transform.Position + Math.Mul(transform.R, _p); Vec2 s = input.P1 - position; float b = Vec2.Dot(s, s) - _radius * _radius; // Solve quadratic equation. Vec2 r = input.P2 - input.P1; float c = Vec2.Dot(s, r); float rr = Vec2.Dot(r, r); float sigma = c * c - rr * b; // Check for negative discriminant and short segment. if (sigma < 0.0f || rr < Settings.FLT_EPSILON) { output.Hit = false; return; } // Find the point of intersection of the line with the circle. float a = -(c + Math.Sqrt(sigma)); // Is the intersection point on the segment? if (0.0f <= a && a <= input.MaxFraction * rr) { a /= rr; output.Hit = true; output.Fraction = a; output.Normal = s + a * r; output.Normal.Normalize(); return; } output.Hit = false; return; }
public void SetTransform(Vec2 position, float angle) { Box2DXDebug.Assert(_world.IsLocked() == false); if (_world.IsLocked() == true) { return; } _xf.R.Set(angle); _xf.Position = position; _sweep.C0 = _sweep.C = Math.Mul(_xf, _sweep.LocalCenter); _sweep.A0 = _sweep.A = angle; BroadPhase broadPhase = _world._contactManager._broadPhase; for (Fixture f = _fixtureList; f != null; f = f._next) { f.Synchronize(broadPhase, _xf, _xf); } _world._contactManager.FindNewContacts(); }
/// <summary> /// Create a joint to constrain bodies together. No reference to the definition /// is retained. This may cause the connected bodies to cease colliding. /// @warning This function is locked during callbacks. /// </summary> /// <param name="def"></param> /// <returns></returns> public Joint CreateJoint(JointDef def) { Box2DXDebug.Assert(IsLocked() == false); if (IsLocked()) { return(null); } Joint j = Joint.Create(def); // Connect to the world list. j._prev = null; j._next = _jointList; if (_jointList != null) { _jointList._prev = j; } _jointList = j; ++_jointCount; // Connect to the bodies' doubly linked lists. j._edgeA.Joint = j; j._edgeA.Other = j._bodyB; j._edgeA.Prev = null; j._edgeA.Next = j._bodyA._jointList; if (j._bodyA._jointList != null) { j._bodyA._jointList.Prev = j._edgeA; } j._bodyA._jointList = j._edgeA; j._edgeB.Joint = j; j._edgeB.Other = j._bodyA; j._edgeB.Prev = null; j._edgeB.Next = j._bodyB._jointList; if (j._bodyB._jointList != null) { j._bodyB._jointList.Prev = j._edgeB; } j._bodyB._jointList = j._edgeB; Body bodyA = def.Body1; Body bodyB = def.Body2; bool staticA = bodyA.IsStatic(); bool staticB = bodyB.IsStatic(); // If the joint prevents collisions, then flag any contacts for filtering. if (def.CollideConnected == false && (staticA == false || staticB == false)) { // Ensure we iterate over contacts on a dynamic body (usually have less contacts // than a static body). Ideally we will have a contact count on both bodies. if (staticB) { Math.Swap(ref bodyA, ref bodyB); } ContactEdge edge = bodyB.GetContactList(); while (edge != null) { if (edge.Other == bodyA) { // Flag the contact for filtering at the next time step (where either // body is awake). edge.Contact.FlagForFiltering(); } edge = edge.Next; } } // Note: creating a joint doesn't wake the bodies. return(j); }
public override void RayCast(out RayCastOutput output, ref RayCastInput input, Transform xf) { output = new RayCastOutput(); float lower = 0.0f, upper = input.MaxFraction; // Put the ray into the polygon's frame of reference. Vec2 p1 = Math.MulT(xf.R, input.P1 - xf.Position); Vec2 p2 = Math.MulT(xf.R, input.P2 - xf.Position); Vec2 d = p2 - p1; int index = -1; output.Hit = false; for (int i = 0; i < VertexCount; ++i) { // p = p1 + a * d // dot(normal, p - v) = 0 // dot(normal, p1 - v) + a * dot(normal, d) = 0 float numerator = Vec2.Dot(Normals[i], Vertices[i] - p1); float denominator = Vec2.Dot(Normals[i], d); if (denominator == 0.0f) { if (numerator < 0.0f) { return; } } else { // Note: we want this predicate without division: // lower < numerator / denominator, where denominator < 0 // Since denominator < 0, we have to flip the inequality: // lower < numerator / denominator <==> denominator * lower > numerator. if (denominator < 0.0f && numerator < lower * denominator) { // Increase lower. // The segment enters this half-space. lower = numerator / denominator; index = i; } else if (denominator > 0.0f && numerator < upper * denominator) { // Decrease upper. // The segment exits this half-space. upper = numerator / denominator; } } if (upper < lower) { return; } } Box2DXDebug.Assert(0.0f <= lower && lower <= input.MaxFraction); if (index >= 0) { output.Hit = true; output.Fraction = lower; output.Normal = Math.Mul(xf.R, Normals[index]); return; } }
public void InsertLeaf(int leaf) { ++_insertionCount; if (_root == NullNode) { _root = leaf; _nodes[_root].Parent = NullNode; return; } // Find the best sibling for this node. Vec2 center = _nodes[leaf].Aabb.GetCenter(); int sibling = _root; if (_nodes[sibling].IsLeaf() == false) { do { int child1 = _nodes[sibling].Child1; int child2 = _nodes[sibling].Child2; Vec2 delta1 = Math.Abs(_nodes[child1].Aabb.GetCenter() - center); Vec2 delta2 = Math.Abs(_nodes[child2].Aabb.GetCenter() - center); float norm1 = delta1.X + delta1.Y; float norm2 = delta2.X + delta2.Y; if (norm1 < norm2) { sibling = child1; } else { sibling = child2; } }while (_nodes[sibling].IsLeaf() == false); } // Create a parent for the siblings. int node1 = _nodes[sibling].Parent; int node2 = AllocateNode(); _nodes[node2].Parent = node1; _nodes[node2].UserData = null; _nodes[node2].Aabb.Combine(_nodes[leaf].Aabb, _nodes[sibling].Aabb); if (node1 != NullNode) { if (_nodes[_nodes[sibling].Parent].Child1 == sibling) { _nodes[node1].Child1 = node2; } else { _nodes[node1].Child2 = node2; } _nodes[node2].Child1 = sibling; _nodes[node2].Child2 = leaf; _nodes[sibling].Parent = node2; _nodes[leaf].Parent = node2; do { if (_nodes[node1].Aabb.Contains(_nodes[node2].Aabb)) { break; } _nodes[node1].Aabb.Combine(_nodes[_nodes[node1].Child1].Aabb, _nodes[_nodes[node1].Child2].Aabb); node2 = node1; node1 = _nodes[node1].Parent; }while (node1 != NullNode); } else { _nodes[node2].Child1 = sibling; _nodes[node2].Child2 = leaf; _nodes[sibling].Parent = node2; _nodes[leaf].Parent = node2; _root = node2; } }
internal void SynchronizeTransform() { _xf.R.Set(_sweep.A); _xf.Position = _sweep.C - Math.Mul(_xf.R, _sweep.LocalCenter); }
/// Ray-cast against the proxies in the tree. This relies on the callback /// to perform a exact ray-cast in the case were the proxy contains a shape. /// The callback also performs the any collision filtering. This has performance /// roughly equal to k * log(n), where k is the number of collisions and n is the /// number of proxies in the tree. /// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). /// @param callback a callback class that is called for each proxy that is hit by the ray. public void RayCast(IRayCastEnabled callback, RayCastInput input) { Vec2 p1 = input.P1; Vec2 p2 = input.P2; Vec2 r = p2 - p1; Box2DXDebug.Assert(r.LengthSquared() > 0.0f); r.Normalize(); // v is perpendicular to the segment. Vec2 v = Vec2.Cross(1.0f, r); Vec2 abs_v = Math.Abs(v); // Separating axis for segment (Gino, p80). // |dot(v, p1 - c)| > dot(|v|, h) float maxFraction = input.MaxFraction; // Build a bounding box for the segment. AABB segmentAABB = new AABB(); { Vec2 t = p1 + maxFraction * (p2 - p1); segmentAABB.LowerBound = Math.Min(p1, t); segmentAABB.UpperBound = Math.Max(p1, t); } const int k_stackSize = 128; int[] stack = new int[k_stackSize]; int count = 0; stack[count++] = _root; while (count > 0) { int nodeId = stack[--count]; if (nodeId == NullNode) { continue; } DynamicTreeNode node = _nodes[nodeId]; if (Collision.TestOverlap(node.Aabb, segmentAABB) == false) { continue; } // Separating axis for segment (Gino, p80). // |dot(v, p1 - c)| > dot(|v|, h) Vec2 c = node.Aabb.GetCenter(); Vec2 h = node.Aabb.GetExtents(); float separation = Math.Abs(Vec2.Dot(v, p1 - c)) - Vec2.Dot(abs_v, h); if (separation > 0.0f) { continue; } if (node.IsLeaf()) { RayCastInput subInput = new RayCastInput(); subInput.P1 = input.P1; subInput.P2 = input.P2; subInput.MaxFraction = maxFraction; maxFraction = callback.RayCastCallback(subInput, nodeId); if (maxFraction == 0.0f) { return; } // Update segment bounding box. { Vec2 t = p1 + maxFraction * (p2 - p1); segmentAABB.LowerBound = Math.Min(p1, t); segmentAABB.UpperBound = Math.Max(p1, t); } } else { Box2DXDebug.Assert(count + 1 < k_stackSize); stack[count++] = node.Child1; stack[count++] = node.Child2; } } }
/// This resets the mass properties to the sum of the mass properties of the fixtures. /// This normally does not need to be called unless you called SetMassData to override /// the mass and you later want to reset the mass. public void ResetMass() { // Compute mass data from shapes. Each shape has its own density. _mass = 0.0f; _invMass = 0.0f; _I = 0.0f; _invI = 0.0f; Vec2 center = Vec2.Zero; for (Fixture f = _fixtureList; f != null; f = f._next) { MassData massData = f.GetMassData(); _mass += massData.Mass; center += massData.Mass * massData.Center; _I += massData.I; } // Compute center of mass. if (_mass > 0.0f) { _invMass = 1.0f / _mass; center *= _invMass; } if (_I > 0.0f && (_flags & BodyFlags.FixedRotation) == 0) { // Center the inertia about the center of mass. _I -= _mass * Vec2.Dot(center, center); Box2DXDebug.Assert(_I > 0.0f); _invI = 1.0f / _I; } else { _I = 0.0f; _invI = 0.0f; } // Move center of mass. Vec2 oldCenter = _sweep.C; _sweep.LocalCenter = center; _sweep.C0 = _sweep.C = Math.Mul(_xf, _sweep.LocalCenter); // Update center of mass velocity. _linearVelocity += Vec2.Cross(_angularVelocity, _sweep.C - oldCenter); // Determine the new body type. BodyType oldType = _type; if (_invMass == 0.0f && _invI == 0.0f) { _type = BodyType.Static; } else { _type = BodyType.Dynamic; } // If the body type changed, we need to flag contacts for filtering. if (oldType != _type) { for (ContactEdge ce = _contactList; ce != null; ce = ce.Next) { ce.Contact.FlagForFiltering(); } } }
// Find TOI contacts and solve them. private void SolveTOI(TimeStep step) { // Reserve an island and a queue for TOI island solution. Island island = new Island(_bodyCount, Settings.MaxTOIContactsPerIsland, Settings.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; int queueCapacity = _bodyCount; Body[] queue = new Body[queueCapacity]; for (Body b = _bodyList; b != null; b = b._next) { b._flags &= ~Body.BodyFlags.Island; b._sweep.T0 = 0.0f; } for (Contact c = _contactManager._contactList; c != null; c = c.Next) { // Invalidate TOI c.Flags &= ~(ContactFlag.ToiFlag | ContactFlag.IslandFlag); } 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) { // Can this contact generate a solid TOI contact? if (c.IsSolid() == false || c.IsContinuous() == false) { continue; } // TODO_ERIN keep a counter on the contact, only respond to M TOIs per contact. float toi = 1.0f; if ((c.Flags & ContactFlag.ToiFlag) != 0) { // 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); } Box2DXDebug.Assert(t0 < 1.0f); // Compute the time of impact. toi = c.ComputeTOI(b1._sweep, b2._sweep); Box2DXDebug.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 |= ContactFlag.ToiFlag; } if (Settings.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.FLT_EPSILON < minTOI) { // No more TOI events. Done! break; } // Advance the bodies to the TOI. Fixture f1 = minContact.GetFixtureA(); Fixture f2 = minContact.GetFixtureB(); Body b3 = f1.GetBody(); Body b4 = f2.GetBody(); Sweep backup1 = b3._sweep; Sweep backup2 = b4._sweep; b3.Advance(minTOI); b4.Advance(minTOI); // The TOI contact likely has some new contact points. minContact.Update(_contactManager._contactListener); minContact.Flags &= ~ContactFlag.ToiFlag; // Is the contact solid? if (minContact.IsSolid() == false) { // Restore the sweeps. b3._sweep = backup1; b4._sweep = backup2; b3.SynchronizeTransform(); b4.SynchronizeTransform(); continue; } // Did numerical issues prevent a contact point from being generated? if (minContact.IsTouching() == false) { // Give up on this TOI. continue; } // Build the TOI island. We need a dynamic seed. Body seed = b3; if (seed.IsStatic()) { seed = b4; } // 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 |= Body.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(ref b); // Make sure the body is awake. b._flags &= ~Body.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.ContactCount == island.ContactCapacity) { break; } // Has this contact already been added to an island? Skip slow or non-solid contacts. if ((cEdge.Contact.Flags & ContactFlag.IslandFlag) != 0) { continue; } // Is this contact touching? For performance we are not updating this contact. if (cEdge.Contact.IsSolid() == false || cEdge.Contact.IsTouching() == false) { continue; } island.Add(ref cEdge.Contact); cEdge.Contact.Flags |= ContactFlag.IslandFlag; // Update other body. Body other = cEdge.Other; // Was the other body already added to this island? if ((other._flags & Body.BodyFlags.Island) != 0) { continue; } // March forward, this can do no harm since this is the min TOI. if (other.IsStatic() == false) { other.Advance(minTOI); other.WakeUp(); } Box2DXDebug.Assert(queueStart + queueSize < queueCapacity); queue[queueStart + queueSize] = other; ++queueSize; other._flags |= Body.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 & Body.BodyFlags.Island) != 0) { continue; } if (!other.IsStatic()) { other.Advance(minTOI); other.WakeUp(); } Box2DXDebug.Assert(queueStart + queueSize < queueCapacity); queue[queueStart + queueSize] = other; ++queueSize; other._flags |= Body.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 &= ~Body.BodyFlags.Island; if ((b._flags & Body.BodyFlags.Sleep) != 0) { continue; } if (b.IsStatic()) { continue; } 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 &= ~ContactFlag.ToiFlag; } } for (int i = 0; i < island.ContactCount; ++i) { // Allow contacts to participate in future TOI islands. Contact c = island.Contacts[i]; c.Flags &= ~(ContactFlag.ToiFlag | ContactFlag.IslandFlag); } 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(); } queue = null; }