Пример #1
0
        /// Set the contact filtering data. This is an expensive operation and should
        /// not be called frequently. This will not update contacts until the next time
        /// step when either parent body is awake.
        public void SetFilterData(ref Filter filter)
        {
            _filter = filter;

            if (_body == null)
            {
                return;
            }

            // Flag associated contacts for filtering.
            ContactEdge edge = _body.GetConactList();

            while (edge != null)
            {
                Contact contact  = edge.Contact;
                Fixture fixtureA = contact.GetFixtureA();
                Fixture fixtureB = contact.GetFixtureB();
                if (fixtureA == this || fixtureB == this)
                {
                    contact.FlagForFiltering();
                }
            }
        }
Пример #2
0
        /// Compute the mass properties from the attached fixture. You typically call this
        /// after adding all the fixtures. If you add or remove fixtures later, you may want
        /// to call this again. Note that this changes the center of mass position.
        public void SetMassFromShapes()
        {
            Debug.Assert(_world.IsLocked == false);
            if (_world.IsLocked == true)
            {
                return;
            }

            // Compute mass data from shapes. Each shape has its own density.
            _mass    = 0.0f;
            _invMass = 0.0f;
            _I       = 0.0f;
            _invI    = 0.0f;

            Vector2 center = Vector2.Zero;

            for (Fixture f = _fixtureList; f != null; f = f._next)
            {
                MassData massData;
                f.ComputeMass(out massData);
                _mass  += massData.mass;
                center += massData.mass * massData.center;
                _I     += massData.i;
            }

            // Compute center of mass, and shift the origin to the COM.
            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 * Vector2.Dot(center, center);
                Debug.Assert(_I > 0.0f);
                _invI = 1.0f / _I;
            }
            else
            {
                _I    = 0.0f;
                _invI = 0.0f;
            }

            // Move center of mass.
            _sweep.localCenter = center;
            _sweep.c0          = _sweep.c = MathUtils.Multiply(ref _xf, _sweep.localCenter);

            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();
                }
            }
        }
Пример #3
0
        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();
            }
        }
Пример #4
0
        void Solve(ref TimeStep step)
        {
            // Size the island for the worst case.
            _island.Reset(_bodyCount,
                          _contactManager._contactCount,
                          _jointCount,
                          _contactManager.ContactListener);

            // Clear all the island flags.
            for (Body b = _bodyList; b != null; b = b._next)
            {
                b._flags &= ~BodyFlags.Island;
            }
            for (Contact c = _contactManager._contactList; c != null; c = c._next)
            {
                c._flags &= ~ContactFlags.Island;
            }
            for (Joint j = _jointList; j != null; j = j._next)
            {
                j._islandFlag = false;
            }

            // Build and simulate all awake islands.
//#warning Remove extra allocs

            int stackSize = _bodyCount;

            Body[] stack = new Body[_bodyCount];
            for (Body seed = _bodyList; seed != null; seed = seed._next)
            {
                if ((seed._flags & (BodyFlags.Island | BodyFlags.Sleep | BodyFlags.Frozen)) != BodyFlags.None)
                {
                    continue;
                }

                if (seed.IsStatic)
                {
                    continue;
                }

                // Reset island and stack.
                _island.Clear();
                int stackCount = 0;
                stack[stackCount++] = seed;
                seed._flags        |= BodyFlags.Island;

                // Perform a depth first search (DFS) on the raint graph.
                while (stackCount > 0)
                {
                    // Grab the next body off the stack and add it to the island.
                    Body b = stack[--stackCount];
                    _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 ce = b._contactList; ce != null; ce = ce.Next)
                    {
                        // Has this contact already been added to an island?
                        // Is this contact non-solid (involves a sensor).
                        if ((ce.Contact._flags & (ContactFlags.Island | ContactFlags.NonSolid)) != ContactFlags.None)
                        {
                            continue;
                        }

                        // Is this contact touching?
                        if ((ce.Contact._flags & ContactFlags.Touch) == ContactFlags.None)
                        {
                            continue;
                        }

                        _island.Add(ce.Contact);
                        ce.Contact._flags |= ContactFlags.Island;

                        Body other = ce.Other;

                        // Was the other body already added to this island?
                        if ((other._flags & BodyFlags.Island) != BodyFlags.None)
                        {
                            continue;
                        }

                        Debug.Assert(stackCount < stackSize);
                        stack[stackCount++] = other;
                        other._flags       |= BodyFlags.Island;
                    }

                    // Search all joints connect to this body.
                    for (JointEdge je = b._jointList; je != null; je = je.Next)
                    {
                        if (je.Joint._islandFlag == true)
                        {
                            continue;
                        }

                        _island.Add(je.Joint);
                        je.Joint._islandFlag = true;

                        Body other = je.Other;
                        if ((other._flags & BodyFlags.Island) != BodyFlags.None)
                        {
                            continue;
                        }

                        Debug.Assert(stackCount < stackSize);
                        stack[stackCount++] = other;
                        other._flags       |= BodyFlags.Island;
                    }
                }

                _island.Solve(ref step, Gravity, _allowSleep);

                // Post solve cleanup.
                for (int i = 0; i < _island._bodyCount; ++i)
                {
                    // Allow static bodies to participate in other islands.
                    Body b = _island._bodies[i];
                    if (b.IsStatic)
                    {
                        b._flags &= ~BodyFlags.Island;
                    }
                }
            }

            // Synchronize fixtures, check for out of range bodies.
            for (Body b = _bodyList; b != null; b = b.GetNext())
            {
                if ((b._flags & (BodyFlags.Sleep | BodyFlags.Frozen)) != BodyFlags.None)
                {
                    continue;
                }

                if (b.IsStatic)
                {
                    continue;
                }

                // Update fixtures (for broad-phase).
                b.SynchronizeFixtures();
            }

            // Look for new contacts.
            _contactManager.FindNewContacts();
        }
Пример #5
0
        /// Destroy a joint. This may cause the connected bodies to begin colliding.
        /// @warning This function is locked during callbacks.
        public void DestroyJoint(Joint j)
        {
            Debug.Assert(!IsLocked);
            if (IsLocked)
            {
                return;
            }

            bool collideConnected = j._collideConnected;

            // Remove from the doubly linked list.
            if (j._prev != null)
            {
                j._prev._next = j._next;
            }

            if (j._next != null)
            {
                j._next._prev = j._prev;
            }

            if (j == _jointList)
            {
                _jointList = j._next;
            }

            // Disconnect from island graph.
            Body bodyA = j._bodyA;
            Body bodyB = j._bodyB;

            // Wake up connected bodies.
            bodyA.WakeUp();
            bodyB.WakeUp();

            // Remove from body 1.
            if (j._edgeA.Prev != null)
            {
                j._edgeA.Prev.Next = j._edgeA.Next;
            }

            if (j._edgeA.Next != null)
            {
                j._edgeA.Next.Prev = j._edgeA.Prev;
            }

            if (j._edgeA == bodyA._jointList)
            {
                bodyA._jointList = j._edgeA.Next;
            }

            j._edgeA.Prev = null;
            j._edgeA.Next = null;

            // Remove from body 2
            if (j._edgeB.Prev != null)
            {
                j._edgeB.Prev.Next = j._edgeB.Next;
            }

            if (j._edgeB.Next != null)
            {
                j._edgeB.Next.Prev = j._edgeB.Prev;
            }

            if (j._edgeB == bodyB._jointList)
            {
                bodyB._jointList = j._edgeB.Next;
            }

            j._edgeB.Prev = null;
            j._edgeB.Next = null;

            Debug.Assert(_jointCount > 0);
            --_jointCount;

            // If the joint prevents collisions, then flag any contacts for filtering.
            if (collideConnected == false)
            {
                ContactEdge edge = bodyB.GetConactList();
                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;
                }
            }
        }
Пример #6
0
        /// Create a joint to rain 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.
        public Joint CreateJoint(JointDef def)
        {
            Debug.Assert(!IsLocked);
            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)
                {
                    MathUtils.Swap(ref bodyA, ref bodyB);
                }

                ContactEdge edge = bodyB.GetConactList();
                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);
        }
Пример #7
0
        /// Destroy a rigid body given a definition. No reference to the definition
        /// is retained. This function is locked during callbacks.
        /// @warning This automatically deletes all associated shapes and joints.
        /// @warning This function is locked during callbacks.
        public void DestroyBody(Body b)
        {
            Debug.Assert(_bodyCount > 0);
            Debug.Assert(!IsLocked);
            if (IsLocked)
            {
                return;
            }

            // Delete the attached joints.
            JointEdge je = b._jointList;

            while (je != null)
            {
                JointEdge je0 = je;
                je = je.Next;

                if (DestructionListener != null)
                {
                    DestructionListener.SayGoodbye(je0.Joint);
                }

                DestroyJoint(je0.Joint);
            }
            b._jointList = null;

            // Delete the attached contacts.
            ContactEdge ce = b._contactList;

            while (ce != null)
            {
                ContactEdge ce0 = ce;
                ce = ce.Next;
                _contactManager.Destroy(ce0.Contact);
            }
            b._contactList = null;

            // Delete the attached fixtures. This destroys broad-phase proxies.
            Fixture f = b._fixtureList;

            while (f != null)
            {
                Fixture f0 = f;
                f = f._next;

                if (DestructionListener != null)
                {
                    DestructionListener.SayGoodbye(f0);
                }

                f0.Destroy(_contactManager._broadPhase);
            }
            b._fixtureList  = null;
            b._fixtureCount = 0;

            // Remove world body list.
            if (b._prev != null)
            {
                b._prev._next = b._next;
            }

            if (b._next != null)
            {
                b._next._prev = b._prev;
            }

            if (b == _bodyList)
            {
                _bodyList = b._next;
            }

            --_bodyCount;
        }
Пример #8
0
        // 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;
        }