1554 lines
58 KiB
C++
1554 lines
58 KiB
C++
/*
|
|
Bullet Continuous Collision Detection and Physics Library
|
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
|
|
|
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.
|
|
*/
|
|
|
|
#include "btSequentialImpulseConstraintSolverMt.h"
|
|
|
|
#include "LinearMath/btQuickprof.h"
|
|
|
|
#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h"
|
|
|
|
#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
|
|
#include "BulletDynamics/Dynamics/btRigidBody.h"
|
|
|
|
bool btSequentialImpulseConstraintSolverMt::s_allowNestedParallelForLoops = false; // some task schedulers don't like nested loops
|
|
int btSequentialImpulseConstraintSolverMt::s_minimumContactManifoldsForBatching = 250;
|
|
int btSequentialImpulseConstraintSolverMt::s_minBatchSize = 50;
|
|
int btSequentialImpulseConstraintSolverMt::s_maxBatchSize = 100;
|
|
btBatchedConstraints::BatchingMethod btSequentialImpulseConstraintSolverMt::s_contactBatchingMethod = btBatchedConstraints::BATCHING_METHOD_SPATIAL_GRID_2D;
|
|
btBatchedConstraints::BatchingMethod btSequentialImpulseConstraintSolverMt::s_jointBatchingMethod = btBatchedConstraints::BATCHING_METHOD_SPATIAL_GRID_2D;
|
|
|
|
btSequentialImpulseConstraintSolverMt::btSequentialImpulseConstraintSolverMt()
|
|
{
|
|
m_numFrictionDirections = 1;
|
|
m_useBatching = false;
|
|
m_useObsoleteJointConstraints = false;
|
|
}
|
|
|
|
btSequentialImpulseConstraintSolverMt::~btSequentialImpulseConstraintSolverMt()
|
|
{
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::setupBatchedContactConstraints()
|
|
{
|
|
BT_PROFILE("setupBatchedContactConstraints");
|
|
m_batchedContactConstraints.setup(&m_tmpSolverContactConstraintPool,
|
|
m_tmpSolverBodyPool,
|
|
s_contactBatchingMethod,
|
|
s_minBatchSize,
|
|
s_maxBatchSize,
|
|
&m_scratchMemory);
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::setupBatchedJointConstraints()
|
|
{
|
|
BT_PROFILE("setupBatchedJointConstraints");
|
|
m_batchedJointConstraints.setup(&m_tmpSolverNonContactConstraintPool,
|
|
m_tmpSolverBodyPool,
|
|
s_jointBatchingMethod,
|
|
s_minBatchSize,
|
|
s_maxBatchSize,
|
|
&m_scratchMemory);
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalSetupContactConstraints(int iContactConstraint, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
btSolverConstraint& contactConstraint = m_tmpSolverContactConstraintPool[iContactConstraint];
|
|
|
|
btVector3 rel_pos1;
|
|
btVector3 rel_pos2;
|
|
btScalar relaxation;
|
|
|
|
int solverBodyIdA = contactConstraint.m_solverBodyIdA;
|
|
int solverBodyIdB = contactConstraint.m_solverBodyIdB;
|
|
|
|
btSolverBody* solverBodyA = &m_tmpSolverBodyPool[solverBodyIdA];
|
|
btSolverBody* solverBodyB = &m_tmpSolverBodyPool[solverBodyIdB];
|
|
|
|
btRigidBody* colObj0 = solverBodyA->m_originalBody;
|
|
btRigidBody* colObj1 = solverBodyB->m_originalBody;
|
|
|
|
btManifoldPoint& cp = *static_cast<btManifoldPoint*>(contactConstraint.m_originalContactPoint);
|
|
|
|
const btVector3& pos1 = cp.getPositionWorldOnA();
|
|
const btVector3& pos2 = cp.getPositionWorldOnB();
|
|
|
|
rel_pos1 = pos1 - solverBodyA->getWorldTransform().getOrigin();
|
|
rel_pos2 = pos2 - solverBodyB->getWorldTransform().getOrigin();
|
|
|
|
btVector3 vel1;
|
|
btVector3 vel2;
|
|
|
|
solverBodyA->getVelocityInLocalPointNoDelta(rel_pos1, vel1);
|
|
solverBodyB->getVelocityInLocalPointNoDelta(rel_pos2, vel2);
|
|
|
|
btVector3 vel = vel1 - vel2;
|
|
btScalar rel_vel = cp.m_normalWorldOnB.dot(vel);
|
|
|
|
setupContactConstraint(contactConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal, relaxation, rel_pos1, rel_pos2);
|
|
|
|
// setup rolling friction constraints
|
|
int rollingFrictionIndex = m_rollingFrictionIndexTable[iContactConstraint];
|
|
if (rollingFrictionIndex >= 0)
|
|
{
|
|
btSolverConstraint& spinningFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[rollingFrictionIndex];
|
|
btAssert(spinningFrictionConstraint.m_frictionIndex == iContactConstraint);
|
|
setupTorsionalFrictionConstraint(spinningFrictionConstraint,
|
|
cp.m_normalWorldOnB,
|
|
solverBodyIdA,
|
|
solverBodyIdB,
|
|
cp,
|
|
cp.m_combinedSpinningFriction,
|
|
rel_pos1,
|
|
rel_pos2,
|
|
colObj0,
|
|
colObj1,
|
|
relaxation,
|
|
0.0f,
|
|
0.0f);
|
|
btVector3 axis[2];
|
|
btPlaneSpace1(cp.m_normalWorldOnB, axis[0], axis[1]);
|
|
axis[0].normalize();
|
|
axis[1].normalize();
|
|
|
|
applyAnisotropicFriction(colObj0, axis[0], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
|
|
applyAnisotropicFriction(colObj1, axis[0], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
|
|
applyAnisotropicFriction(colObj0, axis[1], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
|
|
applyAnisotropicFriction(colObj1, axis[1], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
|
|
// put the largest axis first
|
|
if (axis[1].length2() > axis[0].length2())
|
|
{
|
|
btSwap(axis[0], axis[1]);
|
|
}
|
|
const btScalar kRollingFrictionThreshold = 0.001f;
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
int iRollingFric = rollingFrictionIndex + 1 + i;
|
|
btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[iRollingFric];
|
|
btAssert(rollingFrictionConstraint.m_frictionIndex == iContactConstraint);
|
|
btVector3 dir = axis[i];
|
|
if (dir.length() > kRollingFrictionThreshold)
|
|
{
|
|
setupTorsionalFrictionConstraint(rollingFrictionConstraint,
|
|
dir,
|
|
solverBodyIdA,
|
|
solverBodyIdB,
|
|
cp,
|
|
cp.m_combinedRollingFriction,
|
|
rel_pos1,
|
|
rel_pos2,
|
|
colObj0,
|
|
colObj1,
|
|
relaxation,
|
|
0.0f,
|
|
0.0f);
|
|
}
|
|
else
|
|
{
|
|
rollingFrictionConstraint.m_frictionIndex = -1; // disable constraint
|
|
}
|
|
}
|
|
}
|
|
|
|
// setup friction constraints
|
|
// setupFrictionConstraint(solverConstraint, normalAxis, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, desiredVelocity, cfmSlip);
|
|
{
|
|
///Bullet has several options to set the friction directions
|
|
///By default, each contact has only a single friction direction that is recomputed automatically very frame
|
|
///based on the relative linear velocity.
|
|
///If the relative velocity it zero, it will automatically compute a friction direction.
|
|
|
|
///You can also enable two friction directions, using the SOLVER_USE_2_FRICTION_DIRECTIONS.
|
|
///In that case, the second friction direction will be orthogonal to both contact normal and first friction direction.
|
|
///
|
|
///If you choose SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION, then the friction will be independent from the relative projected velocity.
|
|
///
|
|
///The user can manually override the friction directions for certain contacts using a contact callback,
|
|
///and set the cp.m_lateralFrictionInitialized to true
|
|
///In that case, you can set the target relative motion in each friction direction (cp.m_contactMotion1 and cp.m_contactMotion2)
|
|
///this will give a conveyor belt effect
|
|
///
|
|
btSolverConstraint* frictionConstraint1 = &m_tmpSolverContactFrictionConstraintPool[contactConstraint.m_frictionIndex];
|
|
btAssert(frictionConstraint1->m_frictionIndex == iContactConstraint);
|
|
|
|
btSolverConstraint* frictionConstraint2 = NULL;
|
|
if (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)
|
|
{
|
|
frictionConstraint2 = &m_tmpSolverContactFrictionConstraintPool[contactConstraint.m_frictionIndex + 1];
|
|
btAssert(frictionConstraint2->m_frictionIndex == iContactConstraint);
|
|
}
|
|
|
|
if (!(infoGlobal.m_solverMode & SOLVER_ENABLE_FRICTION_DIRECTION_CACHING) || !(cp.m_contactPointFlags & BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED))
|
|
{
|
|
cp.m_lateralFrictionDir1 = vel - cp.m_normalWorldOnB * rel_vel;
|
|
btScalar lat_rel_vel = cp.m_lateralFrictionDir1.length2();
|
|
if (!(infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION) && lat_rel_vel > SIMD_EPSILON)
|
|
{
|
|
cp.m_lateralFrictionDir1 *= 1.f / btSqrt(lat_rel_vel);
|
|
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
setupFrictionConstraint(*frictionConstraint1, cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
|
|
|
|
if (frictionConstraint2)
|
|
{
|
|
cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB);
|
|
cp.m_lateralFrictionDir2.normalize(); //??
|
|
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
setupFrictionConstraint(*frictionConstraint2, cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
btPlaneSpace1(cp.m_normalWorldOnB, cp.m_lateralFrictionDir1, cp.m_lateralFrictionDir2);
|
|
|
|
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
setupFrictionConstraint(*frictionConstraint1, cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
|
|
|
|
if (frictionConstraint2)
|
|
{
|
|
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
|
|
setupFrictionConstraint(*frictionConstraint2, cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
|
|
}
|
|
|
|
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) && (infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION))
|
|
{
|
|
cp.m_contactPointFlags |= BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setupFrictionConstraint(*frictionConstraint1, cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion1, cp.m_frictionCFM);
|
|
if (frictionConstraint2)
|
|
{
|
|
setupFrictionConstraint(*frictionConstraint2, cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion2, cp.m_frictionCFM);
|
|
}
|
|
}
|
|
}
|
|
|
|
setFrictionConstraintImpulse(contactConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal);
|
|
}
|
|
|
|
struct SetupContactConstraintsLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btBatchedConstraints* m_bc;
|
|
const btContactSolverInfo* m_infoGlobal;
|
|
|
|
SetupContactConstraintsLoop(btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
m_solver = solver;
|
|
m_bc = bc;
|
|
m_infoGlobal = &infoGlobal;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
BT_PROFILE("SetupContactConstraintsLoop");
|
|
for (int iBatch = iBegin; iBatch < iEnd; ++iBatch)
|
|
{
|
|
const btBatchedConstraints::Range& batch = m_bc->m_batches[iBatch];
|
|
for (int i = batch.begin; i < batch.end; ++i)
|
|
{
|
|
int iContact = m_bc->m_constraintIndices[i];
|
|
m_solver->internalSetupContactConstraints(iContact, *m_infoGlobal);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void btSequentialImpulseConstraintSolverMt::setupAllContactConstraints(const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("setupAllContactConstraints");
|
|
if (m_useBatching)
|
|
{
|
|
const btBatchedConstraints& batchedCons = m_batchedContactConstraints;
|
|
SetupContactConstraintsLoop loop(this, &batchedCons, infoGlobal);
|
|
for (int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase)
|
|
{
|
|
int iPhase = batchedCons.m_phaseOrder[iiPhase];
|
|
const btBatchedConstraints::Range& phase = batchedCons.m_phases[iPhase];
|
|
int grainSize = 1;
|
|
btParallelFor(phase.begin, phase.end, grainSize, loop);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < m_tmpSolverContactConstraintPool.size(); ++i)
|
|
{
|
|
internalSetupContactConstraints(i, infoGlobal);
|
|
}
|
|
}
|
|
}
|
|
|
|
int btSequentialImpulseConstraintSolverMt::getOrInitSolverBodyThreadsafe(btCollisionObject& body, btScalar timeStep)
|
|
{
|
|
//
|
|
// getOrInitSolverBody is threadsafe only for a single thread per solver (with potentially multiple solvers)
|
|
//
|
|
// getOrInitSolverBodyThreadsafe -- attempts to be fully threadsafe (however may affect determinism)
|
|
//
|
|
int solverBodyId = -1;
|
|
bool isRigidBodyType = btRigidBody::upcast(&body) != NULL;
|
|
if (isRigidBodyType && !body.isStaticOrKinematicObject())
|
|
{
|
|
// dynamic body
|
|
// Dynamic bodies can only be in one island, so it's safe to write to the companionId
|
|
solverBodyId = body.getCompanionId();
|
|
if (solverBodyId < 0)
|
|
{
|
|
m_bodySolverArrayMutex.lock();
|
|
// now that we have the lock, check again
|
|
solverBodyId = body.getCompanionId();
|
|
if (solverBodyId < 0)
|
|
{
|
|
solverBodyId = m_tmpSolverBodyPool.size();
|
|
btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
|
|
initSolverBody(&solverBody, &body, timeStep);
|
|
body.setCompanionId(solverBodyId);
|
|
}
|
|
m_bodySolverArrayMutex.unlock();
|
|
}
|
|
}
|
|
else if (isRigidBodyType && body.isKinematicObject())
|
|
{
|
|
//
|
|
// NOTE: must test for kinematic before static because some kinematic objects also
|
|
// identify as "static"
|
|
//
|
|
// Kinematic bodies can be in multiple islands at once, so it is a
|
|
// race condition to write to them, so we use an alternate method
|
|
// to record the solverBodyId
|
|
int uniqueId = body.getWorldArrayIndex();
|
|
const int INVALID_SOLVER_BODY_ID = -1;
|
|
if (m_kinematicBodyUniqueIdToSolverBodyTable.size() <= uniqueId)
|
|
{
|
|
m_kinematicBodyUniqueIdToSolverBodyTableMutex.lock();
|
|
// now that we have the lock, check again
|
|
if (m_kinematicBodyUniqueIdToSolverBodyTable.size() <= uniqueId)
|
|
{
|
|
m_kinematicBodyUniqueIdToSolverBodyTable.resize(uniqueId + 1, INVALID_SOLVER_BODY_ID);
|
|
}
|
|
m_kinematicBodyUniqueIdToSolverBodyTableMutex.unlock();
|
|
}
|
|
solverBodyId = m_kinematicBodyUniqueIdToSolverBodyTable[uniqueId];
|
|
// if no table entry yet,
|
|
if (INVALID_SOLVER_BODY_ID == solverBodyId)
|
|
{
|
|
// need to acquire both locks
|
|
m_kinematicBodyUniqueIdToSolverBodyTableMutex.lock();
|
|
m_bodySolverArrayMutex.lock();
|
|
// now that we have the lock, check again
|
|
solverBodyId = m_kinematicBodyUniqueIdToSolverBodyTable[uniqueId];
|
|
if (INVALID_SOLVER_BODY_ID == solverBodyId)
|
|
{
|
|
// create a table entry for this body
|
|
solverBodyId = m_tmpSolverBodyPool.size();
|
|
btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
|
|
initSolverBody(&solverBody, &body, timeStep);
|
|
m_kinematicBodyUniqueIdToSolverBodyTable[uniqueId] = solverBodyId;
|
|
}
|
|
m_bodySolverArrayMutex.unlock();
|
|
m_kinematicBodyUniqueIdToSolverBodyTableMutex.unlock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// all fixed bodies (inf mass) get mapped to a single solver id
|
|
if (m_fixedBodyId < 0)
|
|
{
|
|
m_bodySolverArrayMutex.lock();
|
|
// now that we have the lock, check again
|
|
if (m_fixedBodyId < 0)
|
|
{
|
|
m_fixedBodyId = m_tmpSolverBodyPool.size();
|
|
btSolverBody& fixedBody = m_tmpSolverBodyPool.expand();
|
|
initSolverBody(&fixedBody, 0, timeStep);
|
|
}
|
|
m_bodySolverArrayMutex.unlock();
|
|
}
|
|
solverBodyId = m_fixedBodyId;
|
|
}
|
|
btAssert(solverBodyId >= 0 && solverBodyId < m_tmpSolverBodyPool.size());
|
|
return solverBodyId;
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalCollectContactManifoldCachedInfo(btContactManifoldCachedInfo* cachedInfoArray, btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("internalCollectContactManifoldCachedInfo");
|
|
for (int i = 0; i < numManifolds; ++i)
|
|
{
|
|
btContactManifoldCachedInfo* cachedInfo = &cachedInfoArray[i];
|
|
btPersistentManifold* manifold = manifoldPtr[i];
|
|
btCollisionObject* colObj0 = (btCollisionObject*)manifold->getBody0();
|
|
btCollisionObject* colObj1 = (btCollisionObject*)manifold->getBody1();
|
|
|
|
int solverBodyIdA = getOrInitSolverBodyThreadsafe(*colObj0, infoGlobal.m_timeStep);
|
|
int solverBodyIdB = getOrInitSolverBodyThreadsafe(*colObj1, infoGlobal.m_timeStep);
|
|
|
|
cachedInfo->solverBodyIds[0] = solverBodyIdA;
|
|
cachedInfo->solverBodyIds[1] = solverBodyIdB;
|
|
cachedInfo->numTouchingContacts = 0;
|
|
|
|
btSolverBody* solverBodyA = &m_tmpSolverBodyPool[solverBodyIdA];
|
|
btSolverBody* solverBodyB = &m_tmpSolverBodyPool[solverBodyIdB];
|
|
|
|
// A contact manifold between 2 static object should not exist!
|
|
// check the collision flags of your objects if this assert fires.
|
|
// Incorrectly set collision object flags can degrade performance in various ways.
|
|
btAssert(!m_tmpSolverBodyPool[solverBodyIdA].m_invMass.isZero() || !m_tmpSolverBodyPool[solverBodyIdB].m_invMass.isZero());
|
|
|
|
int iContact = 0;
|
|
for (int j = 0; j < manifold->getNumContacts(); j++)
|
|
{
|
|
btManifoldPoint& cp = manifold->getContactPoint(j);
|
|
|
|
if (cp.getDistance() <= manifold->getContactProcessingThreshold())
|
|
{
|
|
cachedInfo->contactPoints[iContact] = &cp;
|
|
cachedInfo->contactHasRollingFriction[iContact] = (cp.m_combinedRollingFriction > 0.f);
|
|
iContact++;
|
|
}
|
|
}
|
|
cachedInfo->numTouchingContacts = iContact;
|
|
}
|
|
}
|
|
|
|
struct CollectContactManifoldCachedInfoLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* m_cachedInfoArray;
|
|
btPersistentManifold** m_manifoldPtr;
|
|
const btContactSolverInfo* m_infoGlobal;
|
|
|
|
CollectContactManifoldCachedInfoLoop(btSequentialImpulseConstraintSolverMt* solver, btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* cachedInfoArray, btPersistentManifold** manifoldPtr, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
m_solver = solver;
|
|
m_cachedInfoArray = cachedInfoArray;
|
|
m_manifoldPtr = manifoldPtr;
|
|
m_infoGlobal = &infoGlobal;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalCollectContactManifoldCachedInfo(m_cachedInfoArray + iBegin, m_manifoldPtr + iBegin, iEnd - iBegin, *m_infoGlobal);
|
|
}
|
|
};
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalAllocContactConstraints(const btContactManifoldCachedInfo* cachedInfoArray, int numManifolds)
|
|
{
|
|
BT_PROFILE("internalAllocContactConstraints");
|
|
// possibly parallel part
|
|
for (int iManifold = 0; iManifold < numManifolds; ++iManifold)
|
|
{
|
|
const btContactManifoldCachedInfo& cachedInfo = cachedInfoArray[iManifold];
|
|
int contactIndex = cachedInfo.contactIndex;
|
|
int frictionIndex = contactIndex * m_numFrictionDirections;
|
|
int rollingFrictionIndex = cachedInfo.rollingFrictionIndex;
|
|
for (int i = 0; i < cachedInfo.numTouchingContacts; i++)
|
|
{
|
|
btSolverConstraint& contactConstraint = m_tmpSolverContactConstraintPool[contactIndex];
|
|
contactConstraint.m_solverBodyIdA = cachedInfo.solverBodyIds[0];
|
|
contactConstraint.m_solverBodyIdB = cachedInfo.solverBodyIds[1];
|
|
contactConstraint.m_originalContactPoint = cachedInfo.contactPoints[i];
|
|
|
|
// allocate the friction constraints
|
|
contactConstraint.m_frictionIndex = frictionIndex;
|
|
for (int iDir = 0; iDir < m_numFrictionDirections; ++iDir)
|
|
{
|
|
btSolverConstraint& frictionConstraint = m_tmpSolverContactFrictionConstraintPool[frictionIndex];
|
|
frictionConstraint.m_frictionIndex = contactIndex;
|
|
frictionIndex++;
|
|
}
|
|
|
|
// allocate rolling friction constraints
|
|
if (cachedInfo.contactHasRollingFriction[i])
|
|
{
|
|
m_rollingFrictionIndexTable[contactIndex] = rollingFrictionIndex;
|
|
// allocate 3 (although we may use only 2 sometimes)
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
m_tmpSolverContactRollingFrictionConstraintPool[rollingFrictionIndex].m_frictionIndex = contactIndex;
|
|
rollingFrictionIndex++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// indicate there is no rolling friction for this contact point
|
|
m_rollingFrictionIndexTable[contactIndex] = -1;
|
|
}
|
|
contactIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct AllocContactConstraintsLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* m_cachedInfoArray;
|
|
|
|
AllocContactConstraintsLoop(btSequentialImpulseConstraintSolverMt* solver, btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* cachedInfoArray)
|
|
{
|
|
m_solver = solver;
|
|
m_cachedInfoArray = cachedInfoArray;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalAllocContactConstraints(m_cachedInfoArray + iBegin, iEnd - iBegin);
|
|
}
|
|
};
|
|
|
|
void btSequentialImpulseConstraintSolverMt::allocAllContactConstraints(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("allocAllContactConstraints");
|
|
btAlignedObjectArray<btContactManifoldCachedInfo> cachedInfoArray; // = m_manifoldCachedInfoArray;
|
|
cachedInfoArray.resizeNoInitialize(numManifolds);
|
|
if (/* DISABLES CODE */ (false))
|
|
{
|
|
// sequential
|
|
internalCollectContactManifoldCachedInfo(&cachedInfoArray[0], manifoldPtr, numManifolds, infoGlobal);
|
|
}
|
|
else
|
|
{
|
|
// may alter ordering of bodies which affects determinism
|
|
CollectContactManifoldCachedInfoLoop loop(this, &cachedInfoArray[0], manifoldPtr, infoGlobal);
|
|
int grainSize = 200;
|
|
btParallelFor(0, numManifolds, grainSize, loop);
|
|
}
|
|
|
|
{
|
|
// serial part
|
|
int numContacts = 0;
|
|
int numRollingFrictionConstraints = 0;
|
|
for (int iManifold = 0; iManifold < numManifolds; ++iManifold)
|
|
{
|
|
btContactManifoldCachedInfo& cachedInfo = cachedInfoArray[iManifold];
|
|
cachedInfo.contactIndex = numContacts;
|
|
cachedInfo.rollingFrictionIndex = numRollingFrictionConstraints;
|
|
numContacts += cachedInfo.numTouchingContacts;
|
|
for (int i = 0; i < cachedInfo.numTouchingContacts; ++i)
|
|
{
|
|
if (cachedInfo.contactHasRollingFriction[i])
|
|
{
|
|
numRollingFrictionConstraints += 3;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
BT_PROFILE("allocPools");
|
|
if (m_tmpSolverContactConstraintPool.capacity() < numContacts)
|
|
{
|
|
// if we need to reallocate, reserve some extra so we don't have to reallocate again next frame
|
|
int extraReserve = numContacts / 16;
|
|
m_tmpSolverContactConstraintPool.reserve(numContacts + extraReserve);
|
|
m_rollingFrictionIndexTable.reserve(numContacts + extraReserve);
|
|
m_tmpSolverContactFrictionConstraintPool.reserve((numContacts + extraReserve) * m_numFrictionDirections);
|
|
m_tmpSolverContactRollingFrictionConstraintPool.reserve(numRollingFrictionConstraints + extraReserve);
|
|
}
|
|
m_tmpSolverContactConstraintPool.resizeNoInitialize(numContacts);
|
|
m_rollingFrictionIndexTable.resizeNoInitialize(numContacts);
|
|
m_tmpSolverContactFrictionConstraintPool.resizeNoInitialize(numContacts * m_numFrictionDirections);
|
|
m_tmpSolverContactRollingFrictionConstraintPool.resizeNoInitialize(numRollingFrictionConstraints);
|
|
}
|
|
}
|
|
{
|
|
AllocContactConstraintsLoop loop(this, &cachedInfoArray[0]);
|
|
int grainSize = 200;
|
|
btParallelFor(0, numManifolds, grainSize, loop);
|
|
}
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::convertContacts(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
if (!m_useBatching)
|
|
{
|
|
btSequentialImpulseConstraintSolver::convertContacts(manifoldPtr, numManifolds, infoGlobal);
|
|
return;
|
|
}
|
|
BT_PROFILE("convertContacts");
|
|
if (numManifolds > 0)
|
|
{
|
|
if (m_fixedBodyId < 0)
|
|
{
|
|
m_fixedBodyId = m_tmpSolverBodyPool.size();
|
|
btSolverBody& fixedBody = m_tmpSolverBodyPool.expand();
|
|
initSolverBody(&fixedBody, 0, infoGlobal.m_timeStep);
|
|
}
|
|
allocAllContactConstraints(manifoldPtr, numManifolds, infoGlobal);
|
|
if (m_useBatching)
|
|
{
|
|
setupBatchedContactConstraints();
|
|
}
|
|
setupAllContactConstraints(infoGlobal);
|
|
}
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalInitMultipleJoints(btTypedConstraint** constraints, int iBegin, int iEnd)
|
|
{
|
|
BT_PROFILE("internalInitMultipleJoints");
|
|
for (int i = iBegin; i < iEnd; i++)
|
|
{
|
|
btTypedConstraint* constraint = constraints[i];
|
|
btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i];
|
|
if (constraint->isEnabled())
|
|
{
|
|
constraint->buildJacobian();
|
|
constraint->internalSetAppliedImpulse(0.0f);
|
|
btJointFeedback* fb = constraint->getJointFeedback();
|
|
if (fb)
|
|
{
|
|
fb->m_appliedForceBodyA.setZero();
|
|
fb->m_appliedTorqueBodyA.setZero();
|
|
fb->m_appliedForceBodyB.setZero();
|
|
fb->m_appliedTorqueBodyB.setZero();
|
|
}
|
|
constraint->getInfo1(&info1);
|
|
}
|
|
else
|
|
{
|
|
info1.m_numConstraintRows = 0;
|
|
info1.nub = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct InitJointsLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
btTypedConstraint** m_constraints;
|
|
|
|
InitJointsLoop(btSequentialImpulseConstraintSolverMt* solver, btTypedConstraint** constraints)
|
|
{
|
|
m_solver = solver;
|
|
m_constraints = constraints;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalInitMultipleJoints(m_constraints, iBegin, iEnd);
|
|
}
|
|
};
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalConvertMultipleJoints(const btAlignedObjectArray<JointParams>& jointParamsArray, btTypedConstraint** constraints, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("internalConvertMultipleJoints");
|
|
for (int i = iBegin; i < iEnd; ++i)
|
|
{
|
|
const JointParams& jointParams = jointParamsArray[i];
|
|
int currentRow = jointParams.m_solverConstraint;
|
|
if (currentRow != -1)
|
|
{
|
|
const btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i];
|
|
btAssert(currentRow < m_tmpSolverNonContactConstraintPool.size());
|
|
btAssert(info1.m_numConstraintRows > 0);
|
|
|
|
btSolverConstraint* currentConstraintRow = &m_tmpSolverNonContactConstraintPool[currentRow];
|
|
btTypedConstraint* constraint = constraints[i];
|
|
|
|
convertJoint(currentConstraintRow, constraint, info1, jointParams.m_solverBodyA, jointParams.m_solverBodyB, infoGlobal);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ConvertJointsLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btAlignedObjectArray<btSequentialImpulseConstraintSolverMt::JointParams>& m_jointParamsArray;
|
|
btTypedConstraint** m_srcConstraints;
|
|
const btContactSolverInfo& m_infoGlobal;
|
|
|
|
ConvertJointsLoop(btSequentialImpulseConstraintSolverMt* solver,
|
|
const btAlignedObjectArray<btSequentialImpulseConstraintSolverMt::JointParams>& jointParamsArray,
|
|
btTypedConstraint** srcConstraints,
|
|
const btContactSolverInfo& infoGlobal) : m_jointParamsArray(jointParamsArray),
|
|
m_infoGlobal(infoGlobal)
|
|
{
|
|
m_solver = solver;
|
|
m_srcConstraints = srcConstraints;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalConvertMultipleJoints(m_jointParamsArray, m_srcConstraints, iBegin, iEnd, m_infoGlobal);
|
|
}
|
|
};
|
|
|
|
void btSequentialImpulseConstraintSolverMt::convertJoints(btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
if (!m_useBatching)
|
|
{
|
|
btSequentialImpulseConstraintSolver::convertJoints(constraints, numConstraints, infoGlobal);
|
|
return;
|
|
}
|
|
BT_PROFILE("convertJoints");
|
|
bool parallelJointSetup = true;
|
|
m_tmpConstraintSizesPool.resizeNoInitialize(numConstraints);
|
|
if (parallelJointSetup)
|
|
{
|
|
InitJointsLoop loop(this, constraints);
|
|
int grainSize = 40;
|
|
btParallelFor(0, numConstraints, grainSize, loop);
|
|
}
|
|
else
|
|
{
|
|
internalInitMultipleJoints(constraints, 0, numConstraints);
|
|
}
|
|
|
|
int totalNumRows = 0;
|
|
btAlignedObjectArray<JointParams> jointParamsArray;
|
|
jointParamsArray.resizeNoInitialize(numConstraints);
|
|
|
|
//calculate the total number of contraint rows
|
|
for (int i = 0; i < numConstraints; i++)
|
|
{
|
|
btTypedConstraint* constraint = constraints[i];
|
|
|
|
JointParams& params = jointParamsArray[i];
|
|
const btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i];
|
|
|
|
if (info1.m_numConstraintRows)
|
|
{
|
|
params.m_solverConstraint = totalNumRows;
|
|
params.m_solverBodyA = getOrInitSolverBody(constraint->getRigidBodyA(), infoGlobal.m_timeStep);
|
|
params.m_solverBodyB = getOrInitSolverBody(constraint->getRigidBodyB(), infoGlobal.m_timeStep);
|
|
}
|
|
else
|
|
{
|
|
params.m_solverConstraint = -1;
|
|
}
|
|
totalNumRows += info1.m_numConstraintRows;
|
|
}
|
|
m_tmpSolverNonContactConstraintPool.resizeNoInitialize(totalNumRows);
|
|
|
|
///setup the btSolverConstraints
|
|
if (parallelJointSetup)
|
|
{
|
|
ConvertJointsLoop loop(this, jointParamsArray, constraints, infoGlobal);
|
|
int grainSize = 20;
|
|
btParallelFor(0, numConstraints, grainSize, loop);
|
|
}
|
|
else
|
|
{
|
|
internalConvertMultipleJoints(jointParamsArray, constraints, 0, numConstraints, infoGlobal);
|
|
}
|
|
setupBatchedJointConstraints();
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalConvertBodies(btCollisionObject** bodies, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("internalConvertBodies");
|
|
for (int i = iBegin; i < iEnd; i++)
|
|
{
|
|
btCollisionObject* obj = bodies[i];
|
|
obj->setCompanionId(i);
|
|
btSolverBody& solverBody = m_tmpSolverBodyPool[i];
|
|
initSolverBody(&solverBody, obj, infoGlobal.m_timeStep);
|
|
|
|
btRigidBody* body = btRigidBody::upcast(obj);
|
|
if (body && body->getInvMass())
|
|
{
|
|
btVector3 gyroForce(0, 0, 0);
|
|
if (body->getFlags() & BT_ENABLE_GYROSCOPIC_FORCE_EXPLICIT)
|
|
{
|
|
gyroForce = body->computeGyroscopicForceExplicit(infoGlobal.m_maxGyroscopicForce);
|
|
solverBody.m_externalTorqueImpulse -= gyroForce * body->getInvInertiaTensorWorld() * infoGlobal.m_timeStep;
|
|
}
|
|
if (body->getFlags() & BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_WORLD)
|
|
{
|
|
gyroForce = body->computeGyroscopicImpulseImplicit_World(infoGlobal.m_timeStep);
|
|
solverBody.m_externalTorqueImpulse += gyroForce;
|
|
}
|
|
if (body->getFlags() & BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_BODY)
|
|
{
|
|
gyroForce = body->computeGyroscopicImpulseImplicit_Body(infoGlobal.m_timeStep);
|
|
solverBody.m_externalTorqueImpulse += gyroForce;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ConvertBodiesLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
btCollisionObject** m_bodies;
|
|
int m_numBodies;
|
|
const btContactSolverInfo& m_infoGlobal;
|
|
|
|
ConvertBodiesLoop(btSequentialImpulseConstraintSolverMt* solver,
|
|
btCollisionObject** bodies,
|
|
int numBodies,
|
|
const btContactSolverInfo& infoGlobal) : m_infoGlobal(infoGlobal)
|
|
{
|
|
m_solver = solver;
|
|
m_bodies = bodies;
|
|
m_numBodies = numBodies;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalConvertBodies(m_bodies, iBegin, iEnd, m_infoGlobal);
|
|
}
|
|
};
|
|
|
|
void btSequentialImpulseConstraintSolverMt::convertBodies(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("convertBodies");
|
|
m_kinematicBodyUniqueIdToSolverBodyTable.resize(0);
|
|
|
|
m_tmpSolverBodyPool.resizeNoInitialize(numBodies + 1);
|
|
|
|
m_fixedBodyId = numBodies;
|
|
{
|
|
btSolverBody& fixedBody = m_tmpSolverBodyPool[m_fixedBodyId];
|
|
initSolverBody(&fixedBody, NULL, infoGlobal.m_timeStep);
|
|
}
|
|
|
|
bool parallelBodySetup = true;
|
|
if (parallelBodySetup)
|
|
{
|
|
ConvertBodiesLoop loop(this, bodies, numBodies, infoGlobal);
|
|
int grainSize = 40;
|
|
btParallelFor(0, numBodies, grainSize, loop);
|
|
}
|
|
else
|
|
{
|
|
internalConvertBodies(bodies, 0, numBodies, infoGlobal);
|
|
}
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::solveGroupCacheFriendlySetup(
|
|
btCollisionObject** bodies,
|
|
int numBodies,
|
|
btPersistentManifold** manifoldPtr,
|
|
int numManifolds,
|
|
btTypedConstraint** constraints,
|
|
int numConstraints,
|
|
const btContactSolverInfo& infoGlobal,
|
|
btIDebugDraw* debugDrawer)
|
|
{
|
|
m_numFrictionDirections = (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) ? 2 : 1;
|
|
m_useBatching = false;
|
|
if (numManifolds >= s_minimumContactManifoldsForBatching &&
|
|
(s_allowNestedParallelForLoops || !btThreadsAreRunning()))
|
|
{
|
|
m_useBatching = true;
|
|
m_batchedContactConstraints.m_debugDrawer = debugDrawer;
|
|
m_batchedJointConstraints.m_debugDrawer = debugDrawer;
|
|
}
|
|
btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(bodies,
|
|
numBodies,
|
|
manifoldPtr,
|
|
numManifolds,
|
|
constraints,
|
|
numConstraints,
|
|
infoGlobal,
|
|
debugDrawer);
|
|
return 0.0f;
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactSplitPenetrationImpulseConstraints(const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd)
|
|
{
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiCons = batchBegin; iiCons < batchEnd; ++iiCons)
|
|
{
|
|
int iCons = consIndices[iiCons];
|
|
const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[iCons];
|
|
btSolverBody& bodyA = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA];
|
|
btSolverBody& bodyB = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB];
|
|
btScalar residual = resolveSplitPenetrationImpulse(bodyA, bodyB, solveManifold);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
struct ContactSplitPenetrationImpulseSolverLoop : public btIParallelSumBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btBatchedConstraints* m_bc;
|
|
|
|
ContactSplitPenetrationImpulseSolverLoop(btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc)
|
|
{
|
|
m_solver = solver;
|
|
m_bc = bc;
|
|
}
|
|
btScalar sumLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
BT_PROFILE("ContactSplitPenetrationImpulseSolverLoop");
|
|
btScalar sum = 0;
|
|
for (int iBatch = iBegin; iBatch < iEnd; ++iBatch)
|
|
{
|
|
const btBatchedConstraints::Range& batch = m_bc->m_batches[iBatch];
|
|
sum += m_solver->resolveMultipleContactSplitPenetrationImpulseConstraints(m_bc->m_constraintIndices, batch.begin, batch.end);
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
void btSequentialImpulseConstraintSolverMt::solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer)
|
|
{
|
|
BT_PROFILE("solveGroupCacheFriendlySplitImpulseIterations");
|
|
if (infoGlobal.m_splitImpulse)
|
|
{
|
|
for (int iteration = 0; iteration < infoGlobal.m_numIterations; iteration++)
|
|
{
|
|
btScalar leastSquaresResidual = 0.f;
|
|
if (m_useBatching)
|
|
{
|
|
const btBatchedConstraints& batchedCons = m_batchedContactConstraints;
|
|
ContactSplitPenetrationImpulseSolverLoop loop(this, &batchedCons);
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase)
|
|
{
|
|
int iPhase = batchedCons.m_phaseOrder[iiPhase];
|
|
const btBatchedConstraints::Range& phase = batchedCons.m_phases[iPhase];
|
|
int grainSize = batchedCons.m_phaseGrainSize[iPhase];
|
|
leastSquaresResidual += btParallelSum(phase.begin, phase.end, grainSize, loop);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// non-batched
|
|
leastSquaresResidual = resolveMultipleContactSplitPenetrationImpulseConstraints(m_orderTmpConstraintPool, 0, m_tmpSolverContactConstraintPool.size());
|
|
}
|
|
if (leastSquaresResidual <= infoGlobal.m_leastSquaresResidualThreshold || iteration >= (infoGlobal.m_numIterations - 1))
|
|
{
|
|
#ifdef VERBOSE_RESIDUAL_PRINTF
|
|
printf("residual = %f at iteration #%d\n", leastSquaresResidual, iteration);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::solveSingleIteration(int iteration, btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer)
|
|
{
|
|
if (!m_useBatching)
|
|
{
|
|
return btSequentialImpulseConstraintSolver::solveSingleIteration(iteration, bodies, numBodies, manifoldPtr, numManifolds, constraints, numConstraints, infoGlobal, debugDrawer);
|
|
}
|
|
BT_PROFILE("solveSingleIterationMt");
|
|
btScalar leastSquaresResidual = 0.f;
|
|
|
|
if (infoGlobal.m_solverMode & SOLVER_RANDMIZE_ORDER)
|
|
{
|
|
if (1) // uncomment this for a bit less random ((iteration & 7) == 0)
|
|
{
|
|
randomizeConstraintOrdering(iteration, infoGlobal.m_numIterations);
|
|
}
|
|
}
|
|
|
|
{
|
|
///solve all joint constraints
|
|
leastSquaresResidual += resolveAllJointConstraints(iteration);
|
|
|
|
if (iteration < infoGlobal.m_numIterations)
|
|
{
|
|
// this loop is only used for cone-twist constraints,
|
|
// it would be nice to skip this loop if none of the constraints need it
|
|
if (m_useObsoleteJointConstraints)
|
|
{
|
|
for (int j = 0; j < numConstraints; j++)
|
|
{
|
|
if (constraints[j]->isEnabled())
|
|
{
|
|
int bodyAid = getOrInitSolverBody(constraints[j]->getRigidBodyA(), infoGlobal.m_timeStep);
|
|
int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB(), infoGlobal.m_timeStep);
|
|
btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid];
|
|
btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid];
|
|
constraints[j]->solveConstraintObsolete(bodyA, bodyB, infoGlobal.m_timeStep);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (infoGlobal.m_solverMode & SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS)
|
|
{
|
|
// solve all contact, contact-friction, and rolling friction constraints interleaved
|
|
leastSquaresResidual += resolveAllContactConstraintsInterleaved();
|
|
}
|
|
else //SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS
|
|
{
|
|
// don't interleave them
|
|
// solve all contact constraints
|
|
leastSquaresResidual += resolveAllContactConstraints();
|
|
|
|
// solve all contact friction constraints
|
|
leastSquaresResidual += resolveAllContactFrictionConstraints();
|
|
|
|
// solve all rolling friction constraints
|
|
leastSquaresResidual += resolveAllRollingFrictionConstraints();
|
|
}
|
|
}
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleJointConstraints(const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd, int iteration)
|
|
{
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiCons = batchBegin; iiCons < batchEnd; ++iiCons)
|
|
{
|
|
int iCons = consIndices[iiCons];
|
|
const btSolverConstraint& constraint = m_tmpSolverNonContactConstraintPool[iCons];
|
|
if (iteration < constraint.m_overrideNumSolverIterations)
|
|
{
|
|
btSolverBody& bodyA = m_tmpSolverBodyPool[constraint.m_solverBodyIdA];
|
|
btSolverBody& bodyB = m_tmpSolverBodyPool[constraint.m_solverBodyIdB];
|
|
btScalar residual = resolveSingleConstraintRowGeneric(bodyA, bodyB, constraint);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactConstraints(const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd)
|
|
{
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiCons = batchBegin; iiCons < batchEnd; ++iiCons)
|
|
{
|
|
int iCons = consIndices[iiCons];
|
|
const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[iCons];
|
|
btSolverBody& bodyA = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA];
|
|
btSolverBody& bodyB = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB];
|
|
btScalar residual = resolveSingleConstraintRowLowerLimit(bodyA, bodyB, solveManifold);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactFrictionConstraints(const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd)
|
|
{
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiCons = batchBegin; iiCons < batchEnd; ++iiCons)
|
|
{
|
|
int iContact = consIndices[iiCons];
|
|
btScalar totalImpulse = m_tmpSolverContactConstraintPool[iContact].m_appliedImpulse;
|
|
|
|
// apply sliding friction
|
|
if (totalImpulse > 0.0f)
|
|
{
|
|
int iBegin = iContact * m_numFrictionDirections;
|
|
int iEnd = iBegin + m_numFrictionDirections;
|
|
for (int iFriction = iBegin; iFriction < iEnd; ++iFriction)
|
|
{
|
|
btSolverConstraint& solveManifold = m_tmpSolverContactFrictionConstraintPool[iFriction++];
|
|
btAssert(solveManifold.m_frictionIndex == iContact);
|
|
|
|
solveManifold.m_lowerLimit = -(solveManifold.m_friction * totalImpulse);
|
|
solveManifold.m_upperLimit = solveManifold.m_friction * totalImpulse;
|
|
|
|
btSolverBody& bodyA = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA];
|
|
btSolverBody& bodyB = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB];
|
|
btScalar residual = resolveSingleConstraintRowGeneric(bodyA, bodyB, solveManifold);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
}
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactRollingFrictionConstraints(const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd)
|
|
{
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiCons = batchBegin; iiCons < batchEnd; ++iiCons)
|
|
{
|
|
int iContact = consIndices[iiCons];
|
|
int iFirstRollingFriction = m_rollingFrictionIndexTable[iContact];
|
|
if (iFirstRollingFriction >= 0)
|
|
{
|
|
btScalar totalImpulse = m_tmpSolverContactConstraintPool[iContact].m_appliedImpulse;
|
|
// apply rolling friction
|
|
if (totalImpulse > 0.0f)
|
|
{
|
|
int iBegin = iFirstRollingFriction;
|
|
int iEnd = iBegin + 3;
|
|
for (int iRollingFric = iBegin; iRollingFric < iEnd; ++iRollingFric)
|
|
{
|
|
btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[iRollingFric];
|
|
if (rollingFrictionConstraint.m_frictionIndex != iContact)
|
|
{
|
|
break;
|
|
}
|
|
btScalar rollingFrictionMagnitude = rollingFrictionConstraint.m_friction * totalImpulse;
|
|
if (rollingFrictionMagnitude > rollingFrictionConstraint.m_friction)
|
|
{
|
|
rollingFrictionMagnitude = rollingFrictionConstraint.m_friction;
|
|
}
|
|
|
|
rollingFrictionConstraint.m_lowerLimit = -rollingFrictionMagnitude;
|
|
rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude;
|
|
|
|
btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdA], m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdB], rollingFrictionConstraint);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactConstraintsInterleaved(const btAlignedObjectArray<int>& contactIndices,
|
|
int batchBegin,
|
|
int batchEnd)
|
|
{
|
|
btScalar leastSquaresResidual = 0.f;
|
|
int numPoolConstraints = m_tmpSolverContactConstraintPool.size();
|
|
|
|
for (int iiCons = batchBegin; iiCons < batchEnd; iiCons++)
|
|
{
|
|
btScalar totalImpulse = 0;
|
|
int iContact = contactIndices[iiCons];
|
|
// apply penetration constraint
|
|
{
|
|
const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[iContact];
|
|
btScalar residual = resolveSingleConstraintRowLowerLimit(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
|
|
leastSquaresResidual += residual * residual;
|
|
totalImpulse = solveManifold.m_appliedImpulse;
|
|
}
|
|
|
|
// apply sliding friction
|
|
if (totalImpulse > 0.0f)
|
|
{
|
|
int iBegin = iContact * m_numFrictionDirections;
|
|
int iEnd = iBegin + m_numFrictionDirections;
|
|
for (int iFriction = iBegin; iFriction < iEnd; ++iFriction)
|
|
{
|
|
btSolverConstraint& solveManifold = m_tmpSolverContactFrictionConstraintPool[iFriction];
|
|
btAssert(solveManifold.m_frictionIndex == iContact);
|
|
|
|
solveManifold.m_lowerLimit = -(solveManifold.m_friction * totalImpulse);
|
|
solveManifold.m_upperLimit = solveManifold.m_friction * totalImpulse;
|
|
|
|
btSolverBody& bodyA = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA];
|
|
btSolverBody& bodyB = m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB];
|
|
btScalar residual = resolveSingleConstraintRowGeneric(bodyA, bodyB, solveManifold);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
}
|
|
|
|
// apply rolling friction
|
|
int iFirstRollingFriction = m_rollingFrictionIndexTable[iContact];
|
|
if (totalImpulse > 0.0f && iFirstRollingFriction >= 0)
|
|
{
|
|
int iBegin = iFirstRollingFriction;
|
|
int iEnd = iBegin + 3;
|
|
for (int iRollingFric = iBegin; iRollingFric < iEnd; ++iRollingFric)
|
|
{
|
|
btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[iRollingFric];
|
|
if (rollingFrictionConstraint.m_frictionIndex != iContact)
|
|
{
|
|
break;
|
|
}
|
|
btScalar rollingFrictionMagnitude = rollingFrictionConstraint.m_friction * totalImpulse;
|
|
if (rollingFrictionMagnitude > rollingFrictionConstraint.m_friction)
|
|
{
|
|
rollingFrictionMagnitude = rollingFrictionConstraint.m_friction;
|
|
}
|
|
|
|
rollingFrictionConstraint.m_lowerLimit = -rollingFrictionMagnitude;
|
|
rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude;
|
|
|
|
btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdA], m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdB], rollingFrictionConstraint);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
}
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::randomizeBatchedConstraintOrdering(btBatchedConstraints* batchedConstraints)
|
|
{
|
|
btBatchedConstraints& bc = *batchedConstraints;
|
|
// randomize ordering of phases
|
|
for (int ii = 1; ii < bc.m_phaseOrder.size(); ++ii)
|
|
{
|
|
int iSwap = btRandInt2(ii + 1);
|
|
bc.m_phaseOrder.swap(ii, iSwap);
|
|
}
|
|
|
|
// for each batch,
|
|
for (int iBatch = 0; iBatch < bc.m_batches.size(); ++iBatch)
|
|
{
|
|
// randomize ordering of constraints within the batch
|
|
const btBatchedConstraints::Range& batch = bc.m_batches[iBatch];
|
|
for (int iiCons = batch.begin; iiCons < batch.end; ++iiCons)
|
|
{
|
|
int iSwap = batch.begin + btRandInt2(iiCons - batch.begin + 1);
|
|
btAssert(iSwap >= batch.begin && iSwap < batch.end);
|
|
bc.m_constraintIndices.swap(iiCons, iSwap);
|
|
}
|
|
}
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::randomizeConstraintOrdering(int iteration, int numIterations)
|
|
{
|
|
// randomize ordering of joint constraints
|
|
randomizeBatchedConstraintOrdering(&m_batchedJointConstraints);
|
|
|
|
//contact/friction constraints are not solved more than numIterations
|
|
if (iteration < numIterations)
|
|
{
|
|
randomizeBatchedConstraintOrdering(&m_batchedContactConstraints);
|
|
}
|
|
}
|
|
|
|
struct JointSolverLoop : public btIParallelSumBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btBatchedConstraints* m_bc;
|
|
int m_iteration;
|
|
|
|
JointSolverLoop(btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc, int iteration)
|
|
{
|
|
m_solver = solver;
|
|
m_bc = bc;
|
|
m_iteration = iteration;
|
|
}
|
|
btScalar sumLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
BT_PROFILE("JointSolverLoop");
|
|
btScalar sum = 0;
|
|
for (int iBatch = iBegin; iBatch < iEnd; ++iBatch)
|
|
{
|
|
const btBatchedConstraints::Range& batch = m_bc->m_batches[iBatch];
|
|
sum += m_solver->resolveMultipleJointConstraints(m_bc->m_constraintIndices, batch.begin, batch.end, m_iteration);
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveAllJointConstraints(int iteration)
|
|
{
|
|
BT_PROFILE("resolveAllJointConstraints");
|
|
const btBatchedConstraints& batchedCons = m_batchedJointConstraints;
|
|
JointSolverLoop loop(this, &batchedCons, iteration);
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase)
|
|
{
|
|
int iPhase = batchedCons.m_phaseOrder[iiPhase];
|
|
const btBatchedConstraints::Range& phase = batchedCons.m_phases[iPhase];
|
|
int grainSize = 1;
|
|
leastSquaresResidual += btParallelSum(phase.begin, phase.end, grainSize, loop);
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
struct ContactSolverLoop : public btIParallelSumBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btBatchedConstraints* m_bc;
|
|
|
|
ContactSolverLoop(btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc)
|
|
{
|
|
m_solver = solver;
|
|
m_bc = bc;
|
|
}
|
|
btScalar sumLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
BT_PROFILE("ContactSolverLoop");
|
|
btScalar sum = 0;
|
|
for (int iBatch = iBegin; iBatch < iEnd; ++iBatch)
|
|
{
|
|
const btBatchedConstraints::Range& batch = m_bc->m_batches[iBatch];
|
|
sum += m_solver->resolveMultipleContactConstraints(m_bc->m_constraintIndices, batch.begin, batch.end);
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveAllContactConstraints()
|
|
{
|
|
BT_PROFILE("resolveAllContactConstraints");
|
|
const btBatchedConstraints& batchedCons = m_batchedContactConstraints;
|
|
ContactSolverLoop loop(this, &batchedCons);
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase)
|
|
{
|
|
int iPhase = batchedCons.m_phaseOrder[iiPhase];
|
|
const btBatchedConstraints::Range& phase = batchedCons.m_phases[iPhase];
|
|
int grainSize = batchedCons.m_phaseGrainSize[iPhase];
|
|
leastSquaresResidual += btParallelSum(phase.begin, phase.end, grainSize, loop);
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
struct ContactFrictionSolverLoop : public btIParallelSumBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btBatchedConstraints* m_bc;
|
|
|
|
ContactFrictionSolverLoop(btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc)
|
|
{
|
|
m_solver = solver;
|
|
m_bc = bc;
|
|
}
|
|
btScalar sumLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
BT_PROFILE("ContactFrictionSolverLoop");
|
|
btScalar sum = 0;
|
|
for (int iBatch = iBegin; iBatch < iEnd; ++iBatch)
|
|
{
|
|
const btBatchedConstraints::Range& batch = m_bc->m_batches[iBatch];
|
|
sum += m_solver->resolveMultipleContactFrictionConstraints(m_bc->m_constraintIndices, batch.begin, batch.end);
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveAllContactFrictionConstraints()
|
|
{
|
|
BT_PROFILE("resolveAllContactFrictionConstraints");
|
|
const btBatchedConstraints& batchedCons = m_batchedContactConstraints;
|
|
ContactFrictionSolverLoop loop(this, &batchedCons);
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase)
|
|
{
|
|
int iPhase = batchedCons.m_phaseOrder[iiPhase];
|
|
const btBatchedConstraints::Range& phase = batchedCons.m_phases[iPhase];
|
|
int grainSize = batchedCons.m_phaseGrainSize[iPhase];
|
|
leastSquaresResidual += btParallelSum(phase.begin, phase.end, grainSize, loop);
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
struct InterleavedContactSolverLoop : public btIParallelSumBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btBatchedConstraints* m_bc;
|
|
|
|
InterleavedContactSolverLoop(btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc)
|
|
{
|
|
m_solver = solver;
|
|
m_bc = bc;
|
|
}
|
|
btScalar sumLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
BT_PROFILE("InterleavedContactSolverLoop");
|
|
btScalar sum = 0;
|
|
for (int iBatch = iBegin; iBatch < iEnd; ++iBatch)
|
|
{
|
|
const btBatchedConstraints::Range& batch = m_bc->m_batches[iBatch];
|
|
sum += m_solver->resolveMultipleContactConstraintsInterleaved(m_bc->m_constraintIndices, batch.begin, batch.end);
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveAllContactConstraintsInterleaved()
|
|
{
|
|
BT_PROFILE("resolveAllContactConstraintsInterleaved");
|
|
const btBatchedConstraints& batchedCons = m_batchedContactConstraints;
|
|
InterleavedContactSolverLoop loop(this, &batchedCons);
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase)
|
|
{
|
|
int iPhase = batchedCons.m_phaseOrder[iiPhase];
|
|
const btBatchedConstraints::Range& phase = batchedCons.m_phases[iPhase];
|
|
int grainSize = 1;
|
|
leastSquaresResidual += btParallelSum(phase.begin, phase.end, grainSize, loop);
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
struct ContactRollingFrictionSolverLoop : public btIParallelSumBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btBatchedConstraints* m_bc;
|
|
|
|
ContactRollingFrictionSolverLoop(btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc)
|
|
{
|
|
m_solver = solver;
|
|
m_bc = bc;
|
|
}
|
|
btScalar sumLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
BT_PROFILE("ContactFrictionSolverLoop");
|
|
btScalar sum = 0;
|
|
for (int iBatch = iBegin; iBatch < iEnd; ++iBatch)
|
|
{
|
|
const btBatchedConstraints::Range& batch = m_bc->m_batches[iBatch];
|
|
sum += m_solver->resolveMultipleContactRollingFrictionConstraints(m_bc->m_constraintIndices, batch.begin, batch.end);
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::resolveAllRollingFrictionConstraints()
|
|
{
|
|
BT_PROFILE("resolveAllRollingFrictionConstraints");
|
|
btScalar leastSquaresResidual = 0.f;
|
|
//
|
|
// We do not generate batches for rolling friction constraints. We assume that
|
|
// one of two cases is true:
|
|
//
|
|
// 1. either most bodies in the simulation have rolling friction, in which case we can use the
|
|
// batches for contacts and use a lookup table to translate contact indices to rolling friction
|
|
// (ignoring any contact indices that don't map to a rolling friction constraint). As long as
|
|
// most contacts have a corresponding rolling friction constraint, this should parallelize well.
|
|
//
|
|
// -OR-
|
|
//
|
|
// 2. few bodies in the simulation have rolling friction, so it is not worth trying to use the
|
|
// batches from contacts as most of the contacts won't have corresponding rolling friction
|
|
// constraints and most threads would end up doing very little work. Most of the time would
|
|
// go to threading overhead, so we don't bother with threading.
|
|
//
|
|
int numRollingFrictionPoolConstraints = m_tmpSolverContactRollingFrictionConstraintPool.size();
|
|
if (numRollingFrictionPoolConstraints >= m_tmpSolverContactConstraintPool.size())
|
|
{
|
|
// use batching if there are many rolling friction constraints
|
|
const btBatchedConstraints& batchedCons = m_batchedContactConstraints;
|
|
ContactRollingFrictionSolverLoop loop(this, &batchedCons);
|
|
btScalar leastSquaresResidual = 0.f;
|
|
for (int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase)
|
|
{
|
|
int iPhase = batchedCons.m_phaseOrder[iiPhase];
|
|
const btBatchedConstraints::Range& phase = batchedCons.m_phases[iPhase];
|
|
int grainSize = 1;
|
|
leastSquaresResidual += btParallelSum(phase.begin, phase.end, grainSize, loop);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no batching, also ignores SOLVER_RANDMIZE_ORDER
|
|
for (int j = 0; j < numRollingFrictionPoolConstraints; j++)
|
|
{
|
|
btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[j];
|
|
if (rollingFrictionConstraint.m_frictionIndex >= 0)
|
|
{
|
|
btScalar totalImpulse = m_tmpSolverContactConstraintPool[rollingFrictionConstraint.m_frictionIndex].m_appliedImpulse;
|
|
if (totalImpulse > 0.0f)
|
|
{
|
|
btScalar rollingFrictionMagnitude = rollingFrictionConstraint.m_friction * totalImpulse;
|
|
if (rollingFrictionMagnitude > rollingFrictionConstraint.m_friction)
|
|
rollingFrictionMagnitude = rollingFrictionConstraint.m_friction;
|
|
|
|
rollingFrictionConstraint.m_lowerLimit = -rollingFrictionMagnitude;
|
|
rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude;
|
|
|
|
btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdA], m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdB], rollingFrictionConstraint);
|
|
leastSquaresResidual += residual * residual;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return leastSquaresResidual;
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalWriteBackContacts(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("internalWriteBackContacts");
|
|
writeBackContacts(iBegin, iEnd, infoGlobal);
|
|
//for ( int iContact = iBegin; iContact < iEnd; ++iContact)
|
|
//{
|
|
// const btSolverConstraint& contactConstraint = m_tmpSolverContactConstraintPool[ iContact ];
|
|
// btManifoldPoint* pt = (btManifoldPoint*) contactConstraint.m_originalContactPoint;
|
|
// btAssert( pt );
|
|
// pt->m_appliedImpulse = contactConstraint.m_appliedImpulse;
|
|
// pt->m_appliedImpulseLateral1 = m_tmpSolverContactFrictionConstraintPool[ contactConstraint.m_frictionIndex ].m_appliedImpulse;
|
|
// if ( m_numFrictionDirections == 2 )
|
|
// {
|
|
// pt->m_appliedImpulseLateral2 = m_tmpSolverContactFrictionConstraintPool[ contactConstraint.m_frictionIndex + 1 ].m_appliedImpulse;
|
|
// }
|
|
//}
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalWriteBackJoints(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("internalWriteBackJoints");
|
|
writeBackJoints(iBegin, iEnd, infoGlobal);
|
|
}
|
|
|
|
void btSequentialImpulseConstraintSolverMt::internalWriteBackBodies(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("internalWriteBackBodies");
|
|
writeBackBodies(iBegin, iEnd, infoGlobal);
|
|
}
|
|
|
|
struct WriteContactPointsLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btContactSolverInfo* m_infoGlobal;
|
|
|
|
WriteContactPointsLoop(btSequentialImpulseConstraintSolverMt* solver, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
m_solver = solver;
|
|
m_infoGlobal = &infoGlobal;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalWriteBackContacts(iBegin, iEnd, *m_infoGlobal);
|
|
}
|
|
};
|
|
|
|
struct WriteJointsLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btContactSolverInfo* m_infoGlobal;
|
|
|
|
WriteJointsLoop(btSequentialImpulseConstraintSolverMt* solver, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
m_solver = solver;
|
|
m_infoGlobal = &infoGlobal;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalWriteBackJoints(iBegin, iEnd, *m_infoGlobal);
|
|
}
|
|
};
|
|
|
|
struct WriteBodiesLoop : public btIParallelForBody
|
|
{
|
|
btSequentialImpulseConstraintSolverMt* m_solver;
|
|
const btContactSolverInfo* m_infoGlobal;
|
|
|
|
WriteBodiesLoop(btSequentialImpulseConstraintSolverMt* solver, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
m_solver = solver;
|
|
m_infoGlobal = &infoGlobal;
|
|
}
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
{
|
|
m_solver->internalWriteBackBodies(iBegin, iEnd, *m_infoGlobal);
|
|
}
|
|
};
|
|
|
|
btScalar btSequentialImpulseConstraintSolverMt::solveGroupCacheFriendlyFinish(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
|
|
{
|
|
BT_PROFILE("solveGroupCacheFriendlyFinish");
|
|
|
|
if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING)
|
|
{
|
|
WriteContactPointsLoop loop(this, infoGlobal);
|
|
int grainSize = 500;
|
|
btParallelFor(0, m_tmpSolverContactConstraintPool.size(), grainSize, loop);
|
|
}
|
|
|
|
{
|
|
WriteJointsLoop loop(this, infoGlobal);
|
|
int grainSize = 400;
|
|
btParallelFor(0, m_tmpSolverNonContactConstraintPool.size(), grainSize, loop);
|
|
}
|
|
{
|
|
WriteBodiesLoop loop(this, infoGlobal);
|
|
int grainSize = 100;
|
|
btParallelFor(0, m_tmpSolverBodyPool.size(), grainSize, loop);
|
|
}
|
|
|
|
m_tmpSolverContactConstraintPool.resizeNoInitialize(0);
|
|
m_tmpSolverNonContactConstraintPool.resizeNoInitialize(0);
|
|
m_tmpSolverContactFrictionConstraintPool.resizeNoInitialize(0);
|
|
m_tmpSolverContactRollingFrictionConstraintPool.resizeNoInitialize(0);
|
|
|
|
m_tmpSolverBodyPool.resizeNoInitialize(0);
|
|
return 0.f;
|
|
}
|