예제 #1
0
        public void Solve(Profile profile, TimeStep step, Vec2 gravity, bool allowSleep)
        {
            Timer timer = new Timer();

            float h = step.dt;

            // Integrate velocities and apply damping. Initialize the body state.
            for (int i = 0; i < m_bodies.Count(); i++)
            {
                Body  b = m_bodies[i];
                Vec2  c = b.m_sweep.c;
                float a = b.m_sweep.a;
                Vec2  v = b.m_linearVelocity;
                float w = b.m_angularVelocity;

                // Store positions for continuous collision.
                b.m_sweep.c0 = b.m_sweep.c;
                b.m_sweep.a0 = b.m_sweep.a;

                if (b.m_type == BodyType._dynamicBody)
                {
                    // Integrate velocities.
                    v += h * (b.m_gravityScale * gravity + b.m_invMass * b.m_force);
                    w += h * b.m_invI * b.m_torque;

                    // Apply damping.
                    // ODE: dv/dt + c * v = 0
                    // Solution: v(t) = v0 * exp(-c * t)
                    // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
                    // v2 = exp(-c * dt) * v1
                    // Taylor expansion:
                    // v2 = (1.0f - c * dt) * v1
                    v *= Utilities.Clamp(1.0f - h * b.m_linearDamping, 0.0f, 1.0f);
                    w *= Utilities.Clamp(1.0f - h * b.m_angularDamping, 0.0f, 1.0f);
                }

                Position pos = new Position();
                pos.c = c;
                pos.a = a;
                m_positions.Add(pos);

                Velocity vel = new Velocity();
                vel.v = v;
                vel.w = w;
                m_velocities.Add(vel);
            }

            timer.Reset();

            // Solver data
            SolverData solverData;

            solverData.step       = step;
            solverData.positions  = m_positions;
            solverData.velocities = m_velocities;

            // Initialize velocity constraints.
            ContactSolverDef contactSolverDef;

            contactSolverDef.step       = step;
            contactSolverDef.contacts   = m_contacts;
            contactSolverDef.positions  = m_positions;
            contactSolverDef.velocities = m_velocities;

            ContactSolver contactSolver = new ContactSolver(contactSolverDef);

            contactSolver.InitializeVelocityConstraints();

            if (step.warmStarting)
            {
                contactSolver.WarmStart();
            }

            for (int i = 0; i < m_joints.Count(); ++i)
            {
                m_joints[i].InitVelocityConstraints(solverData);
            }

            profile.solveInit = timer.GetMilliseconds();

            // Solve velocity constraints
            timer.Reset();
            for (int i = 0; i < step.velocityIterations; ++i)
            {
                for (int j = 0; j < m_joints.Count(); ++j)
                {
                    m_joints[j].SolveVelocityConstraints(solverData);
                }

                contactSolver.SolveVelocityConstraints();
            }

            // Store impulses for warm starting
            contactSolver.StoreImpulses();
            profile.solveVelocity = timer.GetMilliseconds();

            // Integrate positions
            for (int i = 0; i < m_bodies.Count(); ++i)
            {
                Vec2  c = m_positions[i].c;
                float a = m_positions[i].a;
                Vec2  v = m_velocities[i].v;
                float w = m_velocities[i].w;

                // Check for large velocities
                Vec2 translation = h * v;
                if (Utilities.Dot(translation, translation) > Settings._maxTranslationSquared)
                {
                    float ratio = Settings._maxTranslation / translation.Length();
                    v *= ratio;
                }

                float rotation = h * w;
                if (rotation * rotation > Settings._maxRotationSquared)
                {
                    float ratio = Settings._maxRotation / Math.Abs(rotation);
                    w *= ratio;
                }

                // Integrate
                c += h * v;
                a += h * w;

                m_positions[i].c  = c;
                m_positions[i].a  = a;
                m_velocities[i].v = v;
                m_velocities[i].w = w;
            }

            // Solve position constraints
            timer.Reset();
            bool positionSolved = false;

            for (int i = 0; i < step.positionIterations; ++i)
            {
                bool contactsOkay = contactSolver.SolvePositionConstraints();

                bool jointsOkay = true;
                for (int j = 0; j < m_joints.Count; ++j)
                {
                    bool jointOkay = m_joints[j].SolvePositionConstraints(solverData);
                    jointsOkay = jointsOkay && jointOkay;
                }

                if (contactsOkay && jointsOkay)
                {
                    // Exit early if the position errors are small.
                    positionSolved = true;
                    break;
                }
            }

            // Copy state buffers back to the bodies
            for (int i = 0; i < m_bodies.Count(); ++i)
            {
                Body body = m_bodies[i];
                body.m_sweep.c         = m_positions[i].c;
                body.m_sweep.a         = m_positions[i].a;
                body.m_linearVelocity  = m_velocities[i].v;
                body.m_angularVelocity = m_velocities[i].w;
                body.SynchronizeTransform();
            }

            profile.solvePosition = timer.GetMilliseconds();

            Report(contactSolver.m_velocityConstraints);

            if (allowSleep)
            {
                float minSleepTime = Single.MaxValue;

                const float linTolSqr = Settings._linearSleepTolerance * Settings._linearSleepTolerance;
                const float angTolSqr = Settings._angularSleepTolerance * Settings._angularSleepTolerance;

                for (int i = 0; i < m_bodies.Count(); ++i)
                {
                    Body b = m_bodies[i];
                    if (b.GetBodyType() == BodyType._staticBody)
                    {
                        continue;
                    }

                    if ((b.m_flags & Body.BodyFlags.e_autoSleepFlag) == 0 ||
                        b.m_angularVelocity * b.m_angularVelocity > angTolSqr ||
                        Utilities.Dot(b.m_linearVelocity, b.m_linearVelocity) > linTolSqr)
                    {
                        b.m_sleepTime = 0.0f;
                        minSleepTime  = 0.0f;
                    }
                    else
                    {
                        b.m_sleepTime += h;
                        minSleepTime   = Math.Min(minSleepTime, b.m_sleepTime);
                    }
                }

                if (minSleepTime >= Settings._timeToSleep && positionSolved)
                {
                    for (int i = 0; i < m_bodies.Count(); ++i)
                    {
                        Body b = m_bodies[i];
                        b.SetAwake(false);
                    }
                }
            }
        }
예제 #2
0
        // Broad-phase callback.
        public void AddPair(object proxyUserDataA, object proxyUserDataB)
        {
            FixtureProxy proxyA = (FixtureProxy)proxyUserDataA;
            FixtureProxy proxyB = (FixtureProxy)proxyUserDataB;

            Fixture fixtureA = proxyA.fixture;
            Fixture fixtureB = proxyB.fixture;

            int indexA = proxyA.childIndex;
            int indexB = proxyB.childIndex;

            Body bodyA = fixtureA.GetBody();
            Body bodyB = fixtureB.GetBody();

            // Are the fixtures on the same body?
            if (bodyA == bodyB)
            {
                return;
            }

            // TODO_ERIN use a hash table to remove a potential bottleneck when both
            // bodies have a lot of contacts.
            // Does a contact already exist?
            List <ContactEdge> edges = bodyB.GetContactList();

            foreach (ContactEdge edge in edges)
            {
                if (edge.other == bodyA)
                {
                    Fixture fA = edge.contact.FixtureA;
                    Fixture fB = edge.contact.FixtureB;
                    int     iA = edge.contact.GetChildIndexA();
                    int     iB = edge.contact.GetChildIndexB();

                    if (fA == fixtureA && fB == fixtureB && iA == indexA && iB == indexB)
                    {
                        // A contact already exists.
                        return;
                    }

                    if (fA == fixtureB && fB == fixtureA && iA == indexB && iB == indexA)
                    {
                        // A contact already exists.
                        return;
                    }
                }
            }

            // Does a joint override collision? Is at least one body dynamic?
            if (bodyB.ShouldCollide(bodyA) == false)
            {
                return;
            }

            // Check user filtering.
            if ((m_contactFilter != null) && m_contactFilter.ShouldCollide(fixtureA, fixtureB) == false)
            {
                return;
            }

            // Call the factory.
            Contact c = Contact.Create(fixtureA, indexA, fixtureB, indexB);

            if (c == null)
            {
                return;
            }

            // Contact creation may swap fixtures.
            fixtureA = c.FixtureA;
            fixtureB = c.FixtureB;
            indexA   = c.GetChildIndexA();
            indexB   = c.GetChildIndexB();
            bodyA    = fixtureA.GetBody();
            bodyB    = fixtureB.GetBody();

            // Insert into the world.
            m_contactList.Add(c);

            // Connect to island graph.

            // Connect to body A
            c.m_nodeA.contact = c;
            c.m_nodeA.other   = bodyB;
            bodyA.m_contactList.Add(c.m_nodeA);

            // Connect to body B
            c.m_nodeB.contact = c;
            c.m_nodeB.other   = bodyA;

            bodyB.m_contactList.Add(c.m_nodeB);

            // Wake up the bodies
            if (fixtureA.IsSensor == false && fixtureB.IsSensor == false)
            {
                bodyA.SetAwake(true);
                bodyB.SetAwake(true);
            }
        }
예제 #3
0
        // Update the contact manifold and touching status.
        // Note: do not assume the fixture AABBs are overlapping or are valid.
        internal void Update(IContactListener listener)
        {
            Manifold oldManifold = _manifold;

            // Re-enable this contact.
            _flags |= ContactFlags.Enabled;

            bool touching    = false;
            bool wasTouching = (_flags & ContactFlags.Touching) == ContactFlags.Touching;

            bool sensorA = _fixtureA.IsSensor();
            bool sensorB = _fixtureB.IsSensor();
            bool sensor  = sensorA || sensorB;

            Body      bodyA = _fixtureA.GetBody();
            Body      bodyB = _fixtureB.GetBody();
            Transform xfA; bodyA.GetTransform(out xfA);
            Transform xfB; bodyB.GetTransform(out xfB);

            // Is this contact a sensor?
            if (sensor)
            {
                Shape shapeA = _fixtureA.GetShape();
                Shape shapeB = _fixtureB.GetShape();
                touching = AABB.TestOverlap(shapeA, _indexA, shapeB, _indexB, ref xfA, ref xfB);

                // Sensors don't generate manifolds.
                _manifold._pointCount = 0;
            }
            else
            {
                Evaluate(ref _manifold, ref xfA, ref xfB);
                touching = _manifold._pointCount > 0;

                // Match old contact ids to new contact ids and copy the
                // stored impulses to warm start the solver.
                for (int i = 0; i < _manifold._pointCount; ++i)
                {
                    ManifoldPoint mp2 = _manifold._points[i];
                    mp2.NormalImpulse  = 0.0f;
                    mp2.TangentImpulse = 0.0f;
                    ContactID id2   = mp2.Id;
                    bool      found = false;

                    for (int j = 0; j < oldManifold._pointCount; ++j)
                    {
                        ManifoldPoint mp1 = oldManifold._points[j];

                        if (mp1.Id.Key == id2.Key)
                        {
                            mp2.NormalImpulse  = mp1.NormalImpulse;
                            mp2.TangentImpulse = mp1.TangentImpulse;
                            found = true;
                            break;
                        }
                    }
                    if (found == false)
                    {
                        mp2.NormalImpulse  = 0.0f;
                        mp2.TangentImpulse = 0.0f;
                    }

                    _manifold._points[i] = mp2;
                }

                if (touching != wasTouching)
                {
                    bodyA.SetAwake(true);
                    bodyB.SetAwake(true);
                }
            }

            if (touching)
            {
                _flags |= ContactFlags.Touching;
            }
            else
            {
                _flags &= ~ContactFlags.Touching;
            }

            if (wasTouching == false && touching == true && null != listener)
            {
                listener.BeginContact(this);
            }

            if (wasTouching == true && touching == false && null != listener)
            {
                listener.EndContact(this);
            }

            if (sensor == false && null != listener)
            {
                listener.PreSolve(this, ref oldManifold);
            }
        }
예제 #4
0
        public void Solve(ref TimeStep step, Vector2 gravity, bool allowSleep)
        {
            // Integrate velocities and apply damping.
            for (int i = 0; i < _bodyCount; ++i)
            {
                Body b = _bodies[i];

                if (b.GetType() != BodyType.Dynamic)
                {
                    continue;
                }

                // Integrate velocities.
                b._linearVelocity  += step.dt * (gravity + b._invMass * b._force);
                b._angularVelocity += step.dt * b._invI * b._torque;

                // Apply damping.
                // ODE: dv/dt + c * v = 0
                // Solution: v(t) = v0 * exp(-c * t)
                // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
                // v2 = exp(-c * dt) * v1
                // Taylor expansion:
                // v2 = (1.0f - c * dt) * v1
                b._linearVelocity  *= MathUtils.Clamp(1.0f - step.dt * b._linearDamping, 0.0f, 1.0f);
                b._angularVelocity *= MathUtils.Clamp(1.0f - step.dt * b._angularDamping, 0.0f, 1.0f);
            }

            // Partition contacts so that contacts with static bodies are solved last.
            int i1 = -1;

            for (int i2 = 0; i2 < _contactCount; ++i2)
            {
                Fixture fixtureA  = _contacts[i2].GetFixtureA();
                Fixture fixtureB  = _contacts[i2].GetFixtureB();
                Body    bodyA     = fixtureA.GetBody();
                Body    bodyB     = fixtureB.GetBody();
                bool    nonStatic = bodyA.GetType() != BodyType.Static && bodyB.GetType() != BodyType.Static;
                if (nonStatic)
                {
                    ++i1;
                    //b2Swap(_contacts[i1], _contacts[i2]);
                    Contact temp = _contacts[i1];
                    _contacts[i1] = _contacts[i2];
                    _contacts[i2] = temp;
                }
            }

            // Initialize velocity constraints.
            _contactSolver.Reset(_contacts, _contactCount, step.dtRatio);
            _contactSolver.WarmStart();

            for (int i = 0; i < _jointCount; ++i)
            {
                _joints[i].InitVelocityConstraints(ref step);
            }

            // Solve velocity constraints.
            for (int i = 0; i < step.velocityIterations; ++i)
            {
                for (int j = 0; j < _jointCount; ++j)
                {
                    _joints[j].SolveVelocityConstraints(ref step);
                }

                _contactSolver.SolveVelocityConstraints();
            }

            // Post-solve (store impulses for warm starting).
            _contactSolver.StoreImpulses();

            // Integrate positions.
            for (int i = 0; i < _bodyCount; ++i)
            {
                Body b = _bodies[i];

                if (b.GetType() == BodyType.Static)
                {
                    continue;
                }

                // Check for large velocities.
                Vector2 translation = step.dt * b._linearVelocity;
                if (Vector2.Dot(translation, translation) > Settings.b2_maxTranslationSquared)
                {
                    float ratio = Settings.b2_maxTranslation / translation.magnitude;
                    b._linearVelocity *= ratio;
                }

                float rotation = step.dt * b._angularVelocity;
                if (rotation * rotation > Settings.b2_maxRotationSquared)
                {
                    float ratio = Settings.b2_maxRotation / Math.Abs(rotation);
                    b._angularVelocity *= ratio;
                }

                // Store positions for continuous collision.
                b._sweep.c0 = b._sweep.c;
                b._sweep.a0 = b._sweep.a;

                // Integrate
                b._sweep.c += step.dt * b._linearVelocity;
                b._sweep.a += step.dt * b._angularVelocity;

                // Compute new transform
                b.SynchronizeTransform();

                // Note: shapes are synchronized later.
            }

            // Iterate over constraints.
            for (int i = 0; i < step.positionIterations; ++i)
            {
                bool contactsOkay = _contactSolver.SolvePositionConstraints(Settings.b2_contactBaumgarte);

                bool jointsOkay = true;
                for (int j = 0; j < _jointCount; ++j)
                {
                    bool jointOkay = _joints[j].SolvePositionConstraints(Settings.b2_contactBaumgarte);
                    jointsOkay = jointsOkay && jointOkay;
                }

                if (contactsOkay && jointsOkay)
                {
                    // Exit early if the position errors are small.
                    break;
                }
            }

            Report(_contactSolver._constraints);

            if (allowSleep)
            {
                float minSleepTime = Settings.b2_maxFloat;

                const float linTolSqr = Settings.b2_linearSleepTolerance * Settings.b2_linearSleepTolerance;
                const float angTolSqr = Settings.b2_angularSleepTolerance * Settings.b2_angularSleepTolerance;

                for (int i = 0; i < _bodyCount; ++i)
                {
                    Body b = _bodies[i];
                    if (b.GetType() == BodyType.Static)
                    {
                        continue;
                    }

                    if ((b._flags & BodyFlags.AutoSleep) == 0)
                    {
                        b._sleepTime = 0.0f;
                        minSleepTime = 0.0f;
                    }

                    if ((b._flags & BodyFlags.AutoSleep) == 0 ||
                        b._angularVelocity * b._angularVelocity > angTolSqr ||
                        Vector2.Dot(b._linearVelocity, b._linearVelocity) > linTolSqr)
                    {
                        b._sleepTime = 0.0f;
                        minSleepTime = 0.0f;
                    }
                    else
                    {
                        b._sleepTime += step.dt;
                        minSleepTime  = Math.Min(minSleepTime, b._sleepTime);
                    }
                }

                if (minSleepTime >= Settings.b2_timeToSleep)
                {
                    for (int i = 0; i < _bodyCount; ++i)
                    {
                        Body b = _bodies[i];
                        b.SetAwake(false);
                    }
                }
            }
        }
예제 #5
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.
            int stackSize = _bodyCount;

            if (stackSize > stack.Length)
            {
                stack = new Body[Math.Max(stack.Length * 2, stackSize)];
            }

            for (Body seed = _bodyList; seed != null; seed = seed._next)
            {
                if ((seed._flags & (BodyFlags.Island)) != BodyFlags.None)
                {
                    continue;
                }

                if (seed.IsAwake() == false || seed.IsActive() == false)
                {
                    continue;
                }

                // The seed can be dynamic or kinematic.
                if (seed.GetType() == BodyType.Static)
                {
                    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];
                    //Debug.Assert(b.IsActive() == true);
                    _island.Add(b);

                    // Make sure the body is awake.
                    b.SetAwake(true);

                    // To keep islands as small as possible, we don't
                    // propagate islands across static bodies.
                    if (b.GetType() == BodyType.Static)
                    {
                        continue;
                    }

                    // Search all contacts connected to this body.
                    for (ContactEdge ce = b._contactList; ce != null; ce = ce.Next)
                    {
                        Contact contact = ce.Contact;

                        // Has this contact already been added to an island?
                        if ((contact._flags & ContactFlags.Island) != ContactFlags.None)
                        {
                            continue;
                        }

                        // Is this contact solid and touching?
                        if (!ce.Contact.IsEnabled() || !ce.Contact.IsTouching())
                        {
                            continue;
                        }

                        // Skip sensors.
                        bool sensorA = contact._fixtureA._isSensor;
                        bool sensorB = contact._fixtureB._isSensor;
                        if (sensorA || sensorB)
                        {
                            continue;
                        }

                        _island.Add(contact);
                        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;
                        }

                        Body other = je.Other;

                        // Don't simulate joints connected to inactive bodies.
                        if (other.IsActive() == false)
                        {
                            continue;
                        }

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

                        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.GetType() == BodyType.Static)
                    {
                        b._flags &= ~BodyFlags.Island;
                    }
                }
            }

            // Synchronize fixtures, check for out of range bodies.
            for (Body b = _bodyList; b != null; b = b.GetNext())
            {
                // If a body was not in an island then it did not move.
                if ((b._flags & BodyFlags.Island) != BodyFlags.Island)
                {
                    continue;
                }

                if (b.GetType() == BodyType.Static)
                {
                    continue;
                }

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

            // Look for new contacts.
            _contactManager.FindNewContacts();
        }
예제 #6
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.SetAwake(true);
            bodyB.SetAwake(true);

            // 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.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;
                }
            }
        }
예제 #7
0
        private void Solve(TimeStep step)
        {
            m_profile.solveInit     = 0.0f;
            m_profile.solveVelocity = 0.0f;
            m_profile.solvePosition = 0.0f;

            // Size the island for the worst case.
            Island island = new Island(m_contactManager.m_contactListener);

            // Clear all the island flags.
            foreach (Body b in m_bodyList)
            {
                b.m_flags &= ~Body.BodyFlags.e_islandFlag;
            }
            foreach (Contact c in m_contactManager.m_contactList)
            {
                c.m_flags &= ~ContactFlags.e_islandFlag;
            }
            foreach (Joint j in m_jointList)
            {
                j.m_islandFlag = false;
            }

            // Build and simulate all awake islands.
            List <Body> stack = new List <Body>(m_bodyList.Count());

            foreach (Body seed in m_bodyList)
            {
                if (seed.m_flags.HasFlag(Body.BodyFlags.e_islandFlag))
                {
                    continue;
                }

                if (seed.IsAwake() == false || seed.IsActive() == false)
                {
                    continue;
                }

                // The seed can be dynamic or kinematic.
                if (seed.GetBodyType() == BodyType._staticBody)
                {
                    continue;
                }

                // Reset island and stack.
                island.Clear();
                int stackCount = 0;
                stack.Add(seed); stackCount++;
                seed.m_flags |= Body.BodyFlags.e_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.
                    Body b = stack[--stackCount];
                    Utilities.Assert(b.IsActive() == true);
                    island.Add(b);

                    // Make sure the body is awake.
                    b.SetAwake(true);

                    // To keep islands as small as possible, we don't
                    // propagate islands across static bodies.
                    if (b.GetBodyType() == BodyType._staticBody)
                    {
                        continue;
                    }

                    // Search all contacts connected to this body.
                    foreach (ContactEdge ce in b.m_contactList)
                    {
                        Contact contact = ce.contact;

                        // Has this contact already been added to an island?
                        if (contact.m_flags.HasFlag(ContactFlags.e_islandFlag))
                        {
                            continue;
                        }

                        // Is this contact solid and touching?
                        if (contact.IsEnabled() == false ||
                            contact.IsTouching() == false)
                        {
                            continue;
                        }

                        // Skip sensors.
                        bool sensorA = contact.m_fixtureA.m_isSensor;
                        bool sensorB = contact.m_fixtureB.m_isSensor;
                        if (sensorA || sensorB)
                        {
                            continue;
                        }

                        island.Add(contact);
                        contact.m_flags |= ContactFlags.e_islandFlag;

                        Body other = ce.other;

                        // Was the other body already added to this island?
                        if (other.m_flags.HasFlag(Body.BodyFlags.e_islandFlag))
                        {
                            continue;
                        }

                        Utilities.Assert(stackCount < m_bodyList.Count());
                        stack.Add(other); stackCount++;
                        other.m_flags |= Body.BodyFlags.e_islandFlag;
                    }

                    // Search all joints connect to this body.
                    foreach (JointEdge je in b.m_jointList)
                    {
                        if (je.joint.m_islandFlag == true)
                        {
                            continue;
                        }

                        Body other = je.other;

                        // Don't simulate joints connected to inactive bodies.
                        if (other.IsActive() == false)
                        {
                            continue;
                        }

                        island.Add(je.joint);
                        je.joint.m_islandFlag = true;

                        if (other.m_flags.HasFlag(Body.BodyFlags.e_islandFlag))
                        {
                            continue;
                        }

                        stack.Add(other); stackCount++;
                        other.m_flags |= Body.BodyFlags.e_islandFlag;
                    }
                }

                Profile profile = new Profile();
                island.Solve(profile, step, m_gravity, m_allowSleep);
                m_profile.solveInit     += profile.solveInit;
                m_profile.solveVelocity += profile.solveVelocity;
                m_profile.solvePosition += profile.solvePosition;

                // Post solve cleanup.
                for (int i = 0; i < island.m_bodies.Count(); ++i)
                {
                    // Allow static bodies to participate in other islands.
                    Body b = island.m_bodies[i];
                    if (b.GetBodyType() == BodyType._staticBody)
                    {
                        b.m_flags &= ~Body.BodyFlags.e_islandFlag;
                    }
                }
            }

            {
                Timer timer = new Timer();
                // Synchronize fixtures, check for out of range bodies.
                foreach (Body b in m_bodyList)
                {
                    // If a body was not in an island then it did not move.
                    if ((b.m_flags & Body.BodyFlags.e_islandFlag) == 0)
                    {
                        continue;
                    }

                    if (b.GetBodyType() == BodyType._staticBody)
                    {
                        continue;
                    }

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

                // Look for new contacts.
                m_contactManager.FindNewContacts();
                m_profile.broadphase = timer.GetMilliseconds();
            }
        }
예제 #8
0
        private void SolveTOI(TimeStep step)
        {
            Island island = new Island(m_contactManager.m_contactListener);

            if (m_stepComplete)
            {
                foreach (Body b in m_bodyList)
                {
                    b.m_flags       &= ~Body.BodyFlags.e_islandFlag;
                    b.m_sweep.alpha0 = 0.0f;
                }

                foreach (Contact c in m_contactManager.m_contactList)
                {
                    // Invalidate TOI
                    c.m_flags   &= ~(ContactFlags.e_toiFlag | ContactFlags.e_islandFlag);
                    c.m_toiCount = 0;
                    c.m_toi      = 1.0f;
                }
            }

            Fixture fA = null;
            Fixture fB = null;
            Body    bA = null;
            Body    bB = null;

            // Find TOI events and solve them.
            for (;;)
            {
                // Find the first TOI.
                Contact minContact = null;
                float   minAlpha   = 1.0f;

                foreach (Contact c in m_contactManager.m_contactList)
                {
                    // Is this contact disabled?
                    if (c.IsEnabled() == false)
                    {
                        continue;
                    }

                    // Prevent excessive sub-stepping.
                    if (c.m_toiCount > Settings._maxSubSteps)
                    {
                        continue;
                    }



                    float alpha = 1.0f;
                    if (c.m_flags.HasFlag(ContactFlags.e_toiFlag))
                    {
                        // This contact has a valid cached TOI.
                        alpha = c.m_toi;
                    }
                    else
                    {
                        fA = c.FixtureA;
                        fB = c.FixtureB;

                        // Is there a sensor?
                        if (fA.IsSensor || fB.IsSensor)
                        {
                            continue;
                        }

                        bA = fA.GetBody();
                        bB = fB.GetBody();

                        BodyType typeA = bA.m_type;
                        BodyType typeB = bB.m_type;
                        Utilities.Assert(typeA == BodyType._dynamicBody || typeB == BodyType._dynamicBody);

                        bool activeA = bA.IsAwake() && typeA != BodyType._staticBody;
                        bool activeB = bB.IsAwake() && typeB != BodyType._staticBody;

                        // Is at least one body active (awake and dynamic or kinematic)?
                        if (activeA == false && activeB == false)
                        {
                            continue;
                        }

                        bool collideA = bA.IsBullet() || typeA != BodyType._dynamicBody;
                        bool collideB = bB.IsBullet() || typeB != BodyType._dynamicBody;

                        // 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.
                        float alpha0 = bA.m_sweep.alpha0;

                        if (bA.m_sweep.alpha0 < bB.m_sweep.alpha0)
                        {
                            alpha0 = bB.m_sweep.alpha0;
                            bA.m_sweep.Advance(alpha0);
                        }
                        else if (bB.m_sweep.alpha0 < bA.m_sweep.alpha0)
                        {
                            alpha0 = bA.m_sweep.alpha0;
                            bB.m_sweep.Advance(alpha0);
                        }

                        Utilities.Assert(alpha0 < 1.0f);

                        int indexA = c.GetChildIndexA();
                        int indexB = c.GetChildIndexB();

                        // Compute the time of impact in interval [0, minTOI]
                        TOIInput input = new TOIInput();
                        input.proxyA.Set(fA.GetShape(), indexA);
                        input.proxyB.Set(fB.GetShape(), indexB);
                        input.sweepA = bA.m_sweep;
                        input.sweepB = bB.m_sweep;
                        input.tMax   = 1.0f;

                        TOIOutput output;
                        Utilities.TimeOfImpact(out output, input);

                        // Beta is the fraction of the remaining portion of the .
                        float beta = output.t;
                        if (output.state == TOIOutput.State.e_touching)
                        {
                            alpha = Math.Min(alpha0 + (1.0f - alpha0) * beta, 1.0f);
                        }
                        else
                        {
                            alpha = 1.0f;
                        }

                        c.m_toi    = alpha;
                        c.m_flags |= ContactFlags.e_toiFlag;
                    }

                    if (alpha < minAlpha)
                    {
                        // This is the minimum TOI found so far.
                        minContact = c;
                        minAlpha   = alpha;
                    }
                }

                if (minContact == null || 1.0f - 10.0f * Single.Epsilon < minAlpha)
                {
                    // No more TOI events. Done!
                    m_stepComplete = true;
                    break;
                }

                // Advance the bodies to the TOI.
                fA = minContact.FixtureA;
                fB = minContact.FixtureB;
                bA = fA.GetBody();
                bB = fB.GetBody();

                Sweep backup1 = bA.m_sweep;
                Sweep backup2 = bB.m_sweep;

                bA.Advance(minAlpha);
                bB.Advance(minAlpha);

                // The TOI contact likely has some new contact points.
                minContact.Update(m_contactManager.m_contactListener);
                minContact.m_flags &= ~ContactFlags.e_toiFlag;
                ++minContact.m_toiCount;

                // Is the contact solid?
                if (minContact.IsEnabled() == false || minContact.IsTouching() == false)
                {
                    // Restore the sweeps.
                    minContact.SetEnabled(false);
                    bA.m_sweep = backup1;
                    bB.m_sweep = backup2;
                    bA.SynchronizeTransform();
                    bB.SynchronizeTransform();
                    continue;
                }

                bA.SetAwake(true);
                bB.SetAwake(true);

                // Build the island
                island.Clear();
                island.Add(bA);
                island.Add(bB);
                island.Add(minContact);

                bA.m_flags         |= Body.BodyFlags.e_islandFlag;
                bB.m_flags         |= Body.BodyFlags.e_islandFlag;
                minContact.m_flags |= ContactFlags.e_islandFlag;

                // Get contacts on bodyA and bodyB.
                Body[] bodies = { bA, bB };
                for (int i = 0; i < 2; ++i)
                {
                    Body body = bodies[i];
                    if (body.m_type == BodyType._dynamicBody)
                    {
                        foreach (ContactEdge ce in body.m_contactList)
                        {
                            throw new NotImplementedException();

                            //if (island.m_bodies.Count() == island.m_bodyCapacity)
                            //{
                            //    break;
                            //}

                            //if (island.m_bodies.Count() == island.m_contactCapacity)
                            //{
                            //    break;
                            //}

                            //Contact* contact = ce.contact;

                            //// Has this contact already been added to the island?
                            //if (contact.m_flags & ContactFlags.e_islandFlag)
                            //{
                            //    continue;
                            //}

                            //// Only add static, kinematic, or bullet bodies.
                            //Body* other = ce.other;
                            //if (other.m_type == _dynamicBody &&
                            //    body.IsBullet() == false && other.IsBullet() == false)
                            //{
                            //    continue;
                            //}

                            //// Skip sensors.
                            //bool sensorA = contact.m_fixtureA.m_isSensor;
                            //bool sensorB = contact.m_fixtureB.m_isSensor;
                            //if (sensorA || sensorB)
                            //{
                            //    continue;
                            //}

                            //// Tentatively advance the body to the TOI.
                            //Sweep backup = other.m_sweep;
                            //if ((other.m_flags & Body.BodyFlags.e_islandFlag) == 0)
                            //{
                            //    other.Advance(minAlpha);
                            //}

                            //// Update the contact points
                            //contact.Update(m_contactManager.m_contactListener);

                            //// Was the contact disabled by the user?
                            //if (contact.IsEnabled() == false)
                            //{
                            //    other.m_sweep = backup;
                            //    other.SynchronizeTransform();
                            //    continue;
                            //}

                            //// Are there contact points?
                            //if (contact.IsTouching() == false)
                            //{
                            //    other.m_sweep = backup;
                            //    other.SynchronizeTransform();
                            //    continue;
                            //}

                            //// Add the contact to the island
                            //contact.m_flags |= ContactFlags.e_islandFlag;
                            //island.Add(contact);

                            //// Has the other body already been added to the island?
                            //if (other.m_flags & Body.BodyFlags.e_islandFlag)
                            //{
                            //    continue;
                            //}

                            //// Add the other body to the island.
                            //other.m_flags |= Body.BodyFlags.e_islandFlag;

                            //if (other.m_type != _staticBody)
                            //{
                            //    other.SetAwake(true);
                            //}

                            //island.Add(other);
                        }
                    }
                }

                TimeStep subStep;
                subStep.dt                 = (1.0f - minAlpha) * step.dt;
                subStep.inv_dt             = 1.0f / subStep.dt;
                subStep.dtRatio            = 1.0f;
                subStep.positionIterations = 20;
                subStep.velocityIterations = step.velocityIterations;
                subStep.warmStarting       = false;
                island.SolveTOI(subStep, bA.m_islandIndex, bB.m_islandIndex);

                // Reset island flags and synchronize broad-phase proxies.
                for (int i = 0; i < island.m_bodies.Count(); ++i)
                {
                    throw new NotImplementedException();
                    //Body* body = island.m_bodies[i];
                    //body.m_flags &= ~Body.BodyFlags.e_islandFlag;

                    //if (body.m_type != _dynamicBody)
                    //{
                    //    continue;
                    //}

                    //body.SynchronizeFixtures();

                    //// Invalidate all contact TOIs on this displaced body.
                    //for (ContactEdge* ce = body.m_contactList; ce; ce = ce.next)
                    //{
                    //    ce.contact.m_flags &= ~(ContactFlags.e_toiFlag | ContactFlags.e_islandFlag);
                    //}
                }

                // Commit fixture proxy movements to the broad-phase so that new contacts are created.
                // Also, some contacts can be destroyed.
                m_contactManager.FindNewContacts();

                if (m_subStepping)
                {
                    m_stepComplete = false;
                    break;
                }
            }
        }
예제 #9
0
        // Update the contact manifold and touching status.
        // Note: do not assume the fixture AABBs are overlapping or are valid.
        internal void Update(ContactListener listener)
        {
            Manifold oldManifold = m_manifold;

            // Re-enable this contact.
            m_flags |= ContactFlags.e_enabledFlag;

            bool touching    = false;
            bool wasTouching = (m_flags & ContactFlags.e_touchingFlag) == ContactFlags.e_touchingFlag;

            bool sensorA = m_fixtureA.IsSensor;
            bool sensorB = m_fixtureB.IsSensor;
            bool sensor  = sensorA || sensorB;

            Body      bodyA = m_fixtureA.GetBody();
            Body      bodyB = m_fixtureB.GetBody();
            Transform xfA   = bodyA.GetTransform();
            Transform xfB   = bodyB.GetTransform();

            // Is this contact a sensor?
            if (sensor)
            {
                Shape shapeA = m_fixtureA.GetShape();
                Shape shapeB = m_fixtureB.GetShape();
                touching = Collision.TestOverlap(shapeA, m_indexA, shapeB, m_indexB, xfA, xfB);

                // Sensors don't generate manifolds.
                m_manifold.points.Clear();
            }
            else
            {
                Evaluate(out m_manifold, xfA, xfB);
                touching = m_manifold.points.Count() > 0;

                // Match old contact ids to new contact ids and copy the
                // stored impulses to warm start the solver.
                for (int i = 0; i < m_manifold.points.Count(); ++i)
                {
                    ManifoldPoint mp2 = m_manifold.points[i];
                    mp2.normalImpulse  = 0.0f;
                    mp2.tangentImpulse = 0.0f;
                    ContactID id2 = mp2.id;

                    for (int j = 0; j < oldManifold.points.Count(); ++j)
                    {
                        ManifoldPoint mp1 = oldManifold.points[j];

                        if (mp1.id.key == id2.key)
                        {
                            mp2.normalImpulse  = mp1.normalImpulse;
                            mp2.tangentImpulse = mp1.tangentImpulse;
                            break;
                        }
                    }
                }

                if (touching != wasTouching)
                {
                    bodyA.SetAwake(true);
                    bodyB.SetAwake(true);
                }
            }

            if (touching)
            {
                m_flags |= ContactFlags.e_touchingFlag;
            }
            else
            {
                m_flags &= ~ContactFlags.e_touchingFlag;
            }

            if (wasTouching == false && touching == true && listener != null)
            {
                listener.BeginContact(this);
            }

            if (wasTouching == true && touching == false && listener != null)
            {
                listener.EndContact(this);
            }

            if (sensor == false && touching && listener != null)
            {
                listener.PreSolve(this, oldManifold);
            }
        }