/* * JBox2D - A Java Port of Erin Catto's Box2D * * JBox2D homepage: http://jbox2d.sourceforge.net/ * Box2D homepage: http://www.box2d.org * * 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. */ package org.jbox2d.dynamics; import java.util.List; import org.jbox2d.collision.Manifold; import org.jbox2d.collision.ManifoldPoint; import org.jbox2d.common.MathUtils; import org.jbox2d.common.Settings; import org.jbox2d.common.Vec2; import org.jbox2d.common.XForm; import org.jbox2d.dynamics.contacts.Contact; import org.jbox2d.dynamics.contacts.ContactConstraint; import org.jbox2d.dynamics.contacts.ContactConstraintPoint; import org.jbox2d.dynamics.contacts.ContactResult; import org.jbox2d.dynamics.contacts.ContactSolver; import org.jbox2d.dynamics.joints.Joint; import org.jbox2d.pooling.stacks.ContactSolverStack; //Updated to rev. 46->103->142 of b2Island.cpp/.h /** * Handles much of the heavy lifting of physics solving - for internal use. */ public class Island { public Body[] m_bodies; public Contact[] m_contacts; public Joint[] m_joints; public int m_bodyCount; public int m_jointCount; public int m_contactCount; public int m_bodyCapacity; public int m_contactCapacity; public int m_jointCapacity; public static int m_positionIterationCount = 0; public float m_positionError; public ContactListener m_listener; //begin .h methods public void clear() { m_bodyCount = 0; m_contactCount = 0; m_jointCount = 0; } void add(final Body body) { assert m_bodyCount < m_bodyCapacity; m_bodies[m_bodyCount++] = body; } void add(final Contact contact) { assert (m_contactCount < m_contactCapacity); m_contacts[m_contactCount++] = contact; //no clone, botches CCD if cloned! } void add(final Joint joint) { assert (m_jointCount < m_jointCapacity); m_joints[m_jointCount++] = joint; } //end .h methods //begin .cpp methods /** * TODO djm: make this so it isn't created every time step */ public Island(){ } public final void init(final int bodyCapacity, final int contactCapacity, final int jointCapacity, final ContactListener listener) { m_bodyCapacity = bodyCapacity; m_contactCapacity = contactCapacity; m_jointCapacity = jointCapacity; m_bodyCount = 0; m_contactCount = 0; m_jointCount = 0; m_listener = listener; m_bodies = new Body[bodyCapacity]; m_contacts = new Contact[contactCapacity]; m_joints = new Joint[jointCapacity]; m_positionIterationCount = 0; } // djm pooling private static final ContactSolverStack contactSolvers = new ContactSolverStack(); public void solve(final TimeStep step, final Vec2 gravity, final boolean correctPositions, final boolean allowSleep) { // Integrate velocities and apply damping. for (int i = 0; i < m_bodyCount; ++i) { final Body b = m_bodies[i]; if (b.isStatic()) { continue; } // Integrate velocities. b.m_linearVelocity.x += step.dt * (gravity.x + b.m_invMass * b.m_force.x); b.m_linearVelocity.y += step.dt * (gravity.y + b.m_invMass * b.m_force.y); b.m_angularVelocity += step.dt * b.m_invI * b.m_torque; // Reset forces. b.m_force.set(0.0f, 0.0f); b.m_torque = 0.0f; // 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.m_linearVelocity.mulLocal(MathUtils.clamp(1.0f - step.dt * b.m_linearDamping, 0.0f, 1.0f)); b.m_angularVelocity *= MathUtils.clamp(1.0f - step.dt * b.m_angularDamping, 0.0f, 1.0f); // Check for large velocities. if (Vec2.dot(b.m_linearVelocity, b.m_linearVelocity) > Settings.maxLinearVelocitySquared) { b.m_linearVelocity.normalize(); b.m_linearVelocity.mulLocal(Settings.maxLinearVelocity); } if (b.m_angularVelocity * b.m_angularVelocity > Settings.maxAngularVelocitySquared) { if (b.m_angularVelocity < 0.0f) { b.m_angularVelocity = -Settings.maxAngularVelocity; } else { b.m_angularVelocity = Settings.maxAngularVelocity; } } } final ContactSolver contactSolver = contactSolvers.get(); contactSolver.init(step, m_contacts, m_contactCount); // Initialize velocity constraints. contactSolver.initVelocityConstraints(step); for (int i = 0; i < m_jointCount; ++i) { m_joints[i].initVelocityConstraints(step); } // Solve velocity constraints. for (int i = 0; i < step.maxIterations; ++i) { contactSolver.solveVelocityConstraints(); for (int j = 0; j < m_jointCount; ++j) { m_joints[j].solveVelocityConstraints(step); } } // Post-solve (store impulses for warm starting). contactSolver.finalizeVelocityConstraints(); // Integrate positions. for (int i = 0; i < m_bodyCount; ++i) { final Body b = m_bodies[i]; if (b.isStatic()) { continue; } // Store positions for continuous collision. b.m_sweep.c0.set(b.m_sweep.c); b.m_sweep.a0 = b.m_sweep.a; // Integrate b.m_sweep.c.x += step.dt * b.m_linearVelocity.x; b.m_sweep.c.y += step.dt * b.m_linearVelocity.y; b.m_sweep.a += step.dt * b.m_angularVelocity; // Compute new transform b.synchronizeTransform(); // Note: shapes are synchronized later. } if (correctPositions) { // Initialize position constraints. // Contacts don't need initialization. for (int i = 0; i < m_jointCount; ++i) { m_joints[i].initPositionConstraints(); } // Iterate over constraints. for (m_positionIterationCount = 0; m_positionIterationCount < step.maxIterations; ++m_positionIterationCount) { final boolean contactsOkay = contactSolver.solvePositionConstraints(Settings.contactBaumgarte); boolean jointsOkay = true; for (int i = 0; i < m_jointCount; ++i) { final boolean jointOkay = m_joints[i].solvePositionConstraints(); jointsOkay = jointsOkay && jointOkay; } if (contactsOkay && jointsOkay) { break; } } } report(contactSolver.m_constraints); if (allowSleep) { float minSleepTime = Float.MAX_VALUE; final float linTolSqr = Settings.linearSleepTolerance * Settings.linearSleepTolerance; final float angTolSqr = Settings.angularSleepTolerance * Settings.angularSleepTolerance; for (int i = 0; i < m_bodyCount; ++i) { final Body b = m_bodies[i]; if (b.m_invMass == 0.0f) { continue; } /*if ((b.m_flags & Body.e_allowSleepFlag) == 0) { b.m_sleepTime = 0.0f; minSleepTime = 0.0f; djm: we don't need this, as the next if statement takes care of it. thanks Edge! }*/ if ((b.m_flags & Body.e_allowSleepFlag) == 0 || b.m_angularVelocity * b.m_angularVelocity > angTolSqr || Vec2.dot(b.m_linearVelocity, b.m_linearVelocity) > linTolSqr) { b.m_sleepTime = 0.0f; minSleepTime = 0.0f; } else { b.m_sleepTime += step.dt; minSleepTime = MathUtils.min(minSleepTime, b.m_sleepTime); } } if (minSleepTime >= Settings.timeToSleep) { for (int i = 0; i < m_bodyCount; ++i) { final Body b = m_bodies[i]; b.m_flags |= Body.e_sleepFlag; // thanks Edge! b.m_linearVelocity.setZero(); // no new creation = new Vec2(0.0f, 0.0f); b.m_angularVelocity = 0.0f; } } } contactSolvers.recycle(contactSolver); } // djm pooling, from above public void solveTOI(final TimeStep subStep) { final ContactSolver contactSolver = contactSolvers.get(); contactSolver.init(subStep, m_contacts, m_contactCount); // No warm starting needed for TOI contact events. // For joints, initialize with the last full step warm starting values if (Settings.maxTOIJointsPerIsland > 0) { subStep.warmStarting = true; //for (int i=0; i<m_jointCount; ++i) { for (int i = m_jointCount-1; i >= 0; --i) { m_joints[i].initVelocityConstraints(subStep); } // ...but don't update the warm starting value during solving subStep.warmStarting = false; } // Solve velocity constraints. for (int i = 0; i < subStep.maxIterations; ++i) { contactSolver.solveVelocityConstraints(); //for (int j = 0; j < m_jointCount; ++j) { for (int j = m_jointCount-1; j >= 0; --j) { m_joints[j].solveVelocityConstraints(subStep); } } // Don't store the TOI contact forces for warm starting // because they can be quite large. // Integrate positions. for (int i = 0; i < m_bodyCount; ++i) { final Body b = m_bodies[i]; if (b.isStatic()) { continue; } //System.out.println("(Island::SolveTOI 1) :"+b.m_sweep); // Store positions for continuous collision. b.m_sweep.c0.set(b.m_sweep.c); b.m_sweep.a0 = b.m_sweep.a; // Integrate b.m_sweep.c.x += subStep.dt * b.m_linearVelocity.x; b.m_sweep.c.y += subStep.dt * b.m_linearVelocity.y; b.m_sweep.a += subStep.dt * b.m_angularVelocity; //System.out.println("(Island::SolveTOI 2) :"+b.m_sweep); // Compute new transform b.synchronizeTransform(); // System.out.println("(Island::SolveTOI 3) :"+b.m_sweep); // Note: shapes are synchronized later. } // Solve position constraints. final float k_toiBaumgarte = 0.75f; for (int i = 0; i < subStep.maxIterations; ++i) { final boolean contactsOkay = contactSolver.solvePositionConstraints(k_toiBaumgarte); boolean jointsOkay = true; //for (int j = 0; j < m_jointCount; ++j) { for (int j = m_jointCount-1; j >= 0; --j) { final boolean jointOkay = m_joints[j].solvePositionConstraints(); //System.out.println("iter "+i + ": "+j + " " + jointOkay); jointsOkay = jointsOkay && jointOkay; } if (contactsOkay && jointsOkay) { break; } } report(contactSolver.m_constraints); contactSolvers.recycle(contactSolver); } public void report(final List<ContactConstraint> constraints) { //TODO: optimize this, it's crummy final ContactConstraint[] cc = new ContactConstraint[constraints.size()]; for (int i=0; i<cc.length; ++i) { cc[i] = constraints.get(i); } report(cc); } public void report(final ContactConstraint[] constraints) { if (m_listener == null) { return; } for (int i = 0; i < m_contactCount; ++i) { final Contact c = m_contacts[i]; final ContactConstraint cc = constraints[i]; final ContactResult cr = new ContactResult(); cr.shape1 = c.getShape1(); cr.shape2 = c.getShape2(); final Body b1 = cr.shape1.getBody(); final int manifoldCount = c.getManifoldCount(); final List<Manifold> manifolds = c.getManifolds(); for (int j = 0; j < manifoldCount; ++j) { final Manifold manifold = manifolds.get(j); cr.normal.set(manifold.normal); for (int k = 0; k < manifold.pointCount; ++k) { final ManifoldPoint point = manifold.points[k]; final ContactConstraintPoint ccp = cc.points[k]; XForm.mulToOut(b1.getMemberXForm(), point.localPoint1, cr.position); // TOI constraint results are not stored, so get // the result from the constraint. cr.normalImpulse = ccp.normalImpulse; cr.tangentImpulse = ccp.tangentImpulse; cr.id.set(point.id); m_listener.result(cr); } } } } }