123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- /*
- * Farseer Physics Engine based on Box2D.XNA port:
- * Copyright (c) 2010 Ian Qvist
- *
- * Box2D.XNA port of Box2D:
- * Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
- *
- * Original source Box2D:
- * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- */
- using System;
- using System.Diagnostics;
- using FarseerPhysics.Common;
- using FarseerPhysics.Dynamics.Contacts;
- using FarseerPhysics.Dynamics.Joints;
- using Microsoft.Xna.Framework;
- namespace FarseerPhysics.Dynamics
- {
- /// <summary>
- /// This is an internal class.
- /// </summary>
- public class Island
- {
- public Body[] Bodies;
- public int BodyCount;
- public int ContactCount;
- public int JointCount;
- private int _bodyCapacity;
- private int _contactCapacity;
- private ContactManager _contactManager;
- private ContactSolver _contactSolver = new ContactSolver();
- private Contact[] _contacts;
- private int _jointCapacity;
- private Joint[] _joints;
- public float JointUpdateTime;
- private const float LinTolSqr = Settings.LinearSleepTolerance * Settings.LinearSleepTolerance;
- private const float AngTolSqr = Settings.AngularSleepTolerance * Settings.AngularSleepTolerance;
- #if (!SILVERLIGHT)
- private Stopwatch _watch = new Stopwatch();
- #endif
- public void Reset(int bodyCapacity, int contactCapacity, int jointCapacity, ContactManager contactManager)
- {
- _bodyCapacity = bodyCapacity;
- _contactCapacity = contactCapacity;
- _jointCapacity = jointCapacity;
- BodyCount = 0;
- ContactCount = 0;
- JointCount = 0;
- _contactManager = contactManager;
- if (Bodies == null || Bodies.Length < bodyCapacity)
- {
- Bodies = new Body[bodyCapacity];
- }
- if (_contacts == null || _contacts.Length < contactCapacity)
- {
- _contacts = new Contact[contactCapacity * 2];
- }
- if (_joints == null || _joints.Length < jointCapacity)
- {
- _joints = new Joint[jointCapacity * 2];
- }
- }
- public void Clear()
- {
- BodyCount = 0;
- ContactCount = 0;
- JointCount = 0;
- }
- private float _tmpTime;
- public void Solve(ref TimeStep step, ref Vector2 gravity)
- {
- // Integrate velocities and apply damping.
- for (int i = 0; i < BodyCount; ++i)
- {
- Body b = Bodies[i];
- if (b.BodyType != BodyType.Dynamic)
- {
- continue;
- }
- // Integrate velocities.
- // FPE 3 only - Only apply gravity if the body wants it.
- if (b.IgnoreGravity)
- {
- b.LinearVelocityInternal.X += step.dt * (b.InvMass * b.Force.X);
- b.LinearVelocityInternal.Y += step.dt * (b.InvMass * b.Force.Y);
- b.AngularVelocityInternal += step.dt * b.InvI * b.Torque;
- }
- else
- {
- b.LinearVelocityInternal.X += step.dt * (gravity.X + b.InvMass * b.Force.X);
- b.LinearVelocityInternal.Y += step.dt * (gravity.Y + b.InvMass * b.Force.Y);
- b.AngularVelocityInternal += 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.LinearVelocityInternal *= MathUtils.Clamp(1.0f - step.dt * b.LinearDamping, 0.0f, 1.0f);
- b.AngularVelocityInternal *= 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].FixtureA;
- Fixture fixtureB = _contacts[i2].FixtureB;
- Body bodyA = fixtureA.Body;
- Body bodyB = fixtureB.Body;
- bool nonStatic = bodyA.BodyType != BodyType.Static && bodyB.BodyType != BodyType.Static;
- if (nonStatic)
- {
- ++i1;
- //TODO: Only swap if they are not the same? see http://code.google.com/p/box2d/issues/detail?id=162
- Contact tmp = _contacts[i1];
- _contacts[i1] = _contacts[i2];
- _contacts[i2] = tmp;
- }
- }
- // Initialize velocity constraints.
- _contactSolver.Reset(_contacts, ContactCount, step.dtRatio, Settings.EnableWarmstarting);
- _contactSolver.InitializeVelocityConstraints();
- if (Settings.EnableWarmstarting)
- {
- _contactSolver.WarmStart();
- }
- #if (!SILVERLIGHT)
- if (Settings.EnableDiagnostics)
- {
- _watch.Start();
- _tmpTime = 0;
- }
- #endif
- for (int i = 0; i < JointCount; ++i)
- {
- if (_joints[i].Enabled)
- _joints[i].InitVelocityConstraints(ref step);
- }
- #if (!SILVERLIGHT)
- if (Settings.EnableDiagnostics)
- {
- _tmpTime += _watch.ElapsedTicks;
- }
- #endif
- // Solve velocity constraints.
- for (int i = 0; i < Settings.VelocityIterations; ++i)
- {
- #if (!SILVERLIGHT)
- if (Settings.EnableDiagnostics)
- _watch.Start();
- #endif
- for (int j = 0; j < JointCount; ++j)
- {
- Joint joint = _joints[j];
- if (!joint.Enabled)
- continue;
- joint.SolveVelocityConstraints(ref step);
- joint.Validate(step.inv_dt);
- }
- #if (!SILVERLIGHT)
- if (Settings.EnableDiagnostics)
- {
- _watch.Stop();
- _tmpTime += _watch.ElapsedTicks;
- _watch.Reset();
- }
- #endif
- _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.BodyType == BodyType.Static)
- {
- continue;
- }
- // Check for large velocities.
- float translationX = step.dt * b.LinearVelocityInternal.X;
- float translationY = step.dt * b.LinearVelocityInternal.Y;
- float result = translationX * translationX + translationY * translationY;
- if (result > Settings.MaxTranslationSquared)
- {
- float sq = (float)Math.Sqrt(result);
- float ratio = Settings.MaxTranslation / sq;
- b.LinearVelocityInternal.X *= ratio;
- b.LinearVelocityInternal.Y *= ratio;
- }
- float rotation = step.dt * b.AngularVelocityInternal;
- if (rotation * rotation > Settings.MaxRotationSquared)
- {
- float ratio = Settings.MaxRotation / Math.Abs(rotation);
- b.AngularVelocityInternal *= ratio;
- }
- // Store positions for continuous collision.
- b.Sweep.C0.X = b.Sweep.C.X;
- b.Sweep.C0.Y = b.Sweep.C.Y;
- b.Sweep.A0 = b.Sweep.A;
- // Integrate
- b.Sweep.C.X += step.dt * b.LinearVelocityInternal.X;
- b.Sweep.C.Y += step.dt * b.LinearVelocityInternal.Y;
- b.Sweep.A += step.dt * b.AngularVelocityInternal;
- // Compute new transform
- b.SynchronizeTransform();
- // Note: shapes are synchronized later.
- }
- // Iterate over constraints.
- for (int i = 0; i < Settings.PositionIterations; ++i)
- {
- bool contactsOkay = _contactSolver.SolvePositionConstraints(Settings.ContactBaumgarte);
- bool jointsOkay = true;
- #if (!SILVERLIGHT)
- if (Settings.EnableDiagnostics)
- _watch.Start();
- #endif
- for (int j = 0; j < JointCount; ++j)
- {
- Joint joint = _joints[j];
- if (!joint.Enabled)
- continue;
- bool jointOkay = joint.SolvePositionConstraints();
- jointsOkay = jointsOkay && jointOkay;
- }
- #if (!SILVERLIGHT)
- if (Settings.EnableDiagnostics)
- {
- _watch.Stop();
- _tmpTime += _watch.ElapsedTicks;
- _watch.Reset();
- }
- #endif
- if (contactsOkay && jointsOkay)
- {
- // Exit early if the position errors are small.
- break;
- }
- }
- #if (!SILVERLIGHT)
- if (Settings.EnableDiagnostics)
- {
- JointUpdateTime = _tmpTime;
- }
- #endif
- Report(_contactSolver.Constraints);
- if (Settings.AllowSleep)
- {
- float minSleepTime = Settings.MaxFloat;
- for (int i = 0; i < BodyCount; ++i)
- {
- Body b = Bodies[i];
- if (b.BodyType == BodyType.Static)
- {
- continue;
- }
- if ((b.Flags & BodyFlags.AutoSleep) == 0)
- {
- b.SleepTime = 0.0f;
- minSleepTime = 0.0f;
- }
- if ((b.Flags & BodyFlags.AutoSleep) == 0 ||
- b.AngularVelocityInternal * b.AngularVelocityInternal > AngTolSqr ||
- Vector2.Dot(b.LinearVelocityInternal, b.LinearVelocityInternal) > LinTolSqr)
- {
- b.SleepTime = 0.0f;
- minSleepTime = 0.0f;
- }
- else
- {
- b.SleepTime += step.dt;
- minSleepTime = Math.Min(minSleepTime, b.SleepTime);
- }
- }
- if (minSleepTime >= Settings.TimeToSleep)
- {
- for (int i = 0; i < BodyCount; ++i)
- {
- Body b = Bodies[i];
- b.Awake = false;
- }
- }
- }
- }
- internal void SolveTOI(ref TimeStep subStep)
- {
- _contactSolver.Reset(_contacts, ContactCount, subStep.dtRatio, false);
- // Solve position constraints.
- const float kTOIBaumgarte = 0.75f;
- for (int i = 0; i < Settings.TOIPositionIterations; ++i)
- {
- bool contactsOkay = _contactSolver.SolvePositionConstraints(kTOIBaumgarte);
- if (contactsOkay)
- {
- break;
- }
- if (i == Settings.TOIPositionIterations - 1)
- {
- i += 0;
- }
- }
- // Leap of faith to new safe state.
- for (int i = 0; i < BodyCount; ++i)
- {
- Body body = Bodies[i];
- body.Sweep.A0 = body.Sweep.A;
- body.Sweep.C0 = body.Sweep.C;
- }
- // No warm starting is needed for TOI events because warm
- // starting impulses were applied in the discrete solver.
- _contactSolver.InitializeVelocityConstraints();
- // Solve velocity constraints.
- for (int i = 0; i < Settings.TOIVelocityIterations; ++i)
- {
- _contactSolver.SolveVelocityConstraints();
- }
- // Don't store the TOI contact forces for warm starting
- // because they can be quite large.
- // Integrate positions.
- for (int i = 0; i < BodyCount; ++i)
- {
- Body b = Bodies[i];
- if (b.BodyType == BodyType.Static)
- {
- continue;
- }
- // Check for large velocities.
- float translationx = subStep.dt * b.LinearVelocityInternal.X;
- float translationy = subStep.dt * b.LinearVelocityInternal.Y;
- float dot = translationx * translationx + translationy * translationy;
- if (dot > Settings.MaxTranslationSquared)
- {
- float norm = 1f / (float)Math.Sqrt(dot);
- float value = Settings.MaxTranslation * subStep.inv_dt;
- b.LinearVelocityInternal.X = value * (translationx * norm);
- b.LinearVelocityInternal.Y = value * (translationy * norm);
- }
- float rotation = subStep.dt * b.AngularVelocity;
- if (rotation * rotation > Settings.MaxRotationSquared)
- {
- if (rotation < 0.0)
- {
- b.AngularVelocityInternal = -subStep.inv_dt * Settings.MaxRotation;
- }
- else
- {
- b.AngularVelocityInternal = subStep.inv_dt * Settings.MaxRotation;
- }
- }
- // Integrate
- b.Sweep.C.X += subStep.dt * b.LinearVelocityInternal.X;
- b.Sweep.C.Y += subStep.dt * b.LinearVelocityInternal.Y;
- b.Sweep.A += subStep.dt * b.AngularVelocityInternal;
- // Compute new transform
- b.SynchronizeTransform();
- // Note: shapes are synchronized later.
- }
- Report(_contactSolver.Constraints);
- }
- public void Add(Body body)
- {
- Debug.Assert(BodyCount < _bodyCapacity);
- Bodies[BodyCount++] = body;
- }
- public void Add(Contact contact)
- {
- Debug.Assert(ContactCount < _contactCapacity);
- _contacts[ContactCount++] = contact;
- }
- public void Add(Joint joint)
- {
- Debug.Assert(JointCount < _jointCapacity);
- _joints[JointCount++] = joint;
- }
- private void Report(ContactConstraint[] constraints)
- {
- if (_contactManager == null)
- return;
- for (int i = 0; i < ContactCount; ++i)
- {
- Contact c = _contacts[i];
- if (c.FixtureA.AfterCollision != null)
- c.FixtureA.AfterCollision(c.FixtureA, c.FixtureB, c);
- if (c.FixtureB.AfterCollision != null)
- c.FixtureB.AfterCollision(c.FixtureB, c.FixtureA, c);
- if (_contactManager.PostSolve != null)
- {
- ContactConstraint cc = constraints[i];
- _contactManager.PostSolve(c, cc);
- }
- }
- }
- }
- }
|