com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver Maven / Gradle / Ivy
Show all versions of jbullet Show documentation
/*
* Java port of Bullet (c) 2008 Martin Dvorak
*
* Bullet Continuous Collision Detection and Physics Library
* Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.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.
*/
package com.bulletphysics.dynamics.constraintsolver;
import com.bulletphysics.BulletGlobals;
import com.bulletphysics.BulletStats;
import com.bulletphysics.ContactDestroyedCallback;
import com.bulletphysics.util.ObjectPool;
import com.bulletphysics.collision.broadphase.Dispatcher;
import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.narrowphase.ManifoldPoint;
import com.bulletphysics.collision.narrowphase.PersistentManifold;
import com.bulletphysics.dynamics.RigidBody;
import com.bulletphysics.linearmath.IDebugDraw;
import com.bulletphysics.linearmath.MiscUtil;
import com.bulletphysics.linearmath.Transform;
import com.bulletphysics.linearmath.TransformUtil;
import com.bulletphysics.util.IntArrayList;
import com.bulletphysics.util.ObjectArrayList;
import cz.advel.stack.Stack;
import cz.advel.stack.StaticAlloc;
import javax.vecmath.Matrix3f;
import javax.vecmath.Vector3f;
/**
* SequentialImpulseConstraintSolver uses a Propagation Method and Sequentially applies impulses.
* The approach is the 3D version of Erin Catto's GDC 2006 tutorial. See http://www.gphysics.com
*
* Although Sequential Impulse is more intuitive, it is mathematically equivalent to Projected
* Successive Overrelaxation (iterative LCP).
*
* Applies impulses for combined restitution and penetration recovery and to simulate friction.
*
* @author jezek2
*/
public class SequentialImpulseConstraintSolver extends ConstraintSolver {
private static final int MAX_CONTACT_SOLVER_TYPES = ContactConstraintEnum.MAX_CONTACT_SOLVER_TYPES.ordinal();
private static final int SEQUENTIAL_IMPULSE_MAX_SOLVER_POINTS = 16384;
private OrderIndex[] gOrder = new OrderIndex[SEQUENTIAL_IMPULSE_MAX_SOLVER_POINTS];
private int totalCpd = 0;
{
for (int i=0; i bodiesPool = ObjectPool.get(SolverBody.class);
private final ObjectPool constraintsPool = ObjectPool.get(SolverConstraint.class);
private final ObjectPool jacobiansPool = ObjectPool.get(JacobianEntry.class);
private final ObjectArrayList tmpSolverBodyPool = new ObjectArrayList();
private final ObjectArrayList tmpSolverConstraintPool = new ObjectArrayList();
private final ObjectArrayList tmpSolverFrictionConstraintPool = new ObjectArrayList();
private final IntArrayList orderTmpConstraintPool = new IntArrayList();
private final IntArrayList orderFrictionConstraintPool = new IntArrayList();
protected final ContactSolverFunc[][] contactDispatch = new ContactSolverFunc[MAX_CONTACT_SOLVER_TYPES][MAX_CONTACT_SOLVER_TYPES];
protected final ContactSolverFunc[][] frictionDispatch = new ContactSolverFunc[MAX_CONTACT_SOLVER_TYPES][MAX_CONTACT_SOLVER_TYPES];
// btSeed2 is used for re-arranging the constraint rows. improves convergence/quality of friction
protected long btSeed2 = 0L;
public SequentialImpulseConstraintSolver() {
BulletGlobals.setContactDestroyedCallback(new ContactDestroyedCallback() {
public boolean contactDestroyed(Object userPersistentData) {
assert (userPersistentData != null);
ConstraintPersistentData cpd = (ConstraintPersistentData) userPersistentData;
//btAlignedFree(cpd);
totalCpd--;
//printf("totalCpd = %i. DELETED Ptr %x\n",totalCpd,userPersistentData);
return true;
}
});
// initialize default friction/contact funcs
int i, j;
for (i = 0; i < MAX_CONTACT_SOLVER_TYPES; i++) {
for (j = 0; j < MAX_CONTACT_SOLVER_TYPES; j++) {
contactDispatch[i][j] = ContactConstraint.resolveSingleCollision;
frictionDispatch[i][j] = ContactConstraint.resolveSingleFriction;
}
}
}
public long rand2() {
btSeed2 = (1664525L * btSeed2 + 1013904223L) & 0xffffffff;
return btSeed2;
}
// See ODE: adam's all-int straightforward(?) dRandInt (0..n-1)
public int randInt2(int n) {
// seems good; xor-fold and modulus
long un = n;
long r = rand2();
// note: probably more aggressive than it needs to be -- might be
// able to get away without one or two of the innermost branches.
if (un <= 0x00010000L) {
r ^= (r >>> 16);
if (un <= 0x00000100L) {
r ^= (r >>> 8);
if (un <= 0x00000010L) {
r ^= (r >>> 4);
if (un <= 0x00000004L) {
r ^= (r >>> 2);
if (un <= 0x00000002L) {
r ^= (r >>> 1);
}
}
}
}
}
// TODO: check modulo C vs Java mismatch
return (int) Math.abs(r % un);
}
private void initSolverBody(SolverBody solverBody, CollisionObject collisionObject) {
RigidBody rb = RigidBody.upcast(collisionObject);
if (rb != null) {
rb.getAngularVelocity(solverBody.angularVelocity);
solverBody.centerOfMassPosition.set(collisionObject.getWorldTransform(Stack.alloc(Transform.class)).origin);
solverBody.friction = collisionObject.getFriction();
solverBody.invMass = rb.getInvMass();
rb.getLinearVelocity(solverBody.linearVelocity);
solverBody.originalBody = rb;
solverBody.angularFactor = rb.getAngularFactor();
}
else {
solverBody.angularVelocity.set(0f, 0f, 0f);
solverBody.centerOfMassPosition.set(collisionObject.getWorldTransform(Stack.alloc(Transform.class)).origin);
solverBody.friction = collisionObject.getFriction();
solverBody.invMass = 0f;
solverBody.linearVelocity.set(0f, 0f, 0f);
solverBody.originalBody = null;
solverBody.angularFactor = 1f;
}
solverBody.pushVelocity.set(0f, 0f, 0f);
solverBody.turnVelocity.set(0f, 0f, 0f);
}
private float restitutionCurve(float rel_vel, float restitution) {
float rest = restitution * -rel_vel;
return rest;
}
private void resolveSplitPenetrationImpulseCacheFriendly(
SolverBody body1,
SolverBody body2,
SolverConstraint contactConstraint,
ContactSolverInfo solverInfo) {
if (contactConstraint.penetration < solverInfo.splitImpulsePenetrationThreshold) {
BulletStats.gNumSplitImpulseRecoveries++;
float normalImpulse;
// Optimized version of projected relative velocity, use precomputed cross products with normal
// body1.getVelocityInLocalPoint(contactConstraint.m_rel_posA,vel1);
// body2.getVelocityInLocalPoint(contactConstraint.m_rel_posB,vel2);
// btVector3 vel = vel1 - vel2;
// btScalar rel_vel = contactConstraint.m_contactNormal.dot(vel);
float rel_vel;
float vel1Dotn = contactConstraint.contactNormal.dot(body1.pushVelocity) + contactConstraint.relpos1CrossNormal.dot(body1.turnVelocity);
float vel2Dotn = contactConstraint.contactNormal.dot(body2.pushVelocity) + contactConstraint.relpos2CrossNormal.dot(body2.turnVelocity);
rel_vel = vel1Dotn - vel2Dotn;
float positionalError = -contactConstraint.penetration * solverInfo.erp2 / solverInfo.timeStep;
// btScalar positionalError = contactConstraint.m_penetration;
float velocityError = contactConstraint.restitution - rel_vel;// * damping;
float penetrationImpulse = positionalError * contactConstraint.jacDiagABInv;
float velocityImpulse = velocityError * contactConstraint.jacDiagABInv;
normalImpulse = penetrationImpulse + velocityImpulse;
// See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse
float oldNormalImpulse = contactConstraint.appliedPushImpulse;
float sum = oldNormalImpulse + normalImpulse;
contactConstraint.appliedPushImpulse = 0f > sum ? 0f : sum;
normalImpulse = contactConstraint.appliedPushImpulse - oldNormalImpulse;
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.scale(body1.invMass, contactConstraint.contactNormal);
body1.internalApplyPushImpulse(tmp, contactConstraint.angularComponentA, normalImpulse);
tmp.scale(body2.invMass, contactConstraint.contactNormal);
body2.internalApplyPushImpulse(tmp, contactConstraint.angularComponentB, -normalImpulse);
}
}
/**
* velocity + friction
* response between two dynamic objects with friction
*/
private float resolveSingleCollisionCombinedCacheFriendly(
SolverBody body1,
SolverBody body2,
SolverConstraint contactConstraint,
ContactSolverInfo solverInfo) {
float normalImpulse;
{
// Optimized version of projected relative velocity, use precomputed cross products with normal
// body1.getVelocityInLocalPoint(contactConstraint.m_rel_posA,vel1);
// body2.getVelocityInLocalPoint(contactConstraint.m_rel_posB,vel2);
// btVector3 vel = vel1 - vel2;
// btScalar rel_vel = contactConstraint.m_contactNormal.dot(vel);
float rel_vel;
float vel1Dotn = contactConstraint.contactNormal.dot(body1.linearVelocity) + contactConstraint.relpos1CrossNormal.dot(body1.angularVelocity);
float vel2Dotn = contactConstraint.contactNormal.dot(body2.linearVelocity) + contactConstraint.relpos2CrossNormal.dot(body2.angularVelocity);
rel_vel = vel1Dotn - vel2Dotn;
float positionalError = 0.f;
if (!solverInfo.splitImpulse || (contactConstraint.penetration > solverInfo.splitImpulsePenetrationThreshold)) {
positionalError = -contactConstraint.penetration * solverInfo.erp / solverInfo.timeStep;
}
float velocityError = contactConstraint.restitution - rel_vel;// * damping;
float penetrationImpulse = positionalError * contactConstraint.jacDiagABInv;
float velocityImpulse = velocityError * contactConstraint.jacDiagABInv;
normalImpulse = penetrationImpulse + velocityImpulse;
// See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse
float oldNormalImpulse = contactConstraint.appliedImpulse;
float sum = oldNormalImpulse + normalImpulse;
contactConstraint.appliedImpulse = 0f > sum ? 0f : sum;
normalImpulse = contactConstraint.appliedImpulse - oldNormalImpulse;
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.scale(body1.invMass, contactConstraint.contactNormal);
body1.internalApplyImpulse(tmp, contactConstraint.angularComponentA, normalImpulse);
tmp.scale(body2.invMass, contactConstraint.contactNormal);
body2.internalApplyImpulse(tmp, contactConstraint.angularComponentB, -normalImpulse);
}
return normalImpulse;
}
private float resolveSingleFrictionCacheFriendly(
SolverBody body1,
SolverBody body2,
SolverConstraint contactConstraint,
ContactSolverInfo solverInfo,
float appliedNormalImpulse) {
float combinedFriction = contactConstraint.friction;
float limit = appliedNormalImpulse * combinedFriction;
if (appliedNormalImpulse > 0f) //friction
{
float j1;
{
float rel_vel;
float vel1Dotn = contactConstraint.contactNormal.dot(body1.linearVelocity) + contactConstraint.relpos1CrossNormal.dot(body1.angularVelocity);
float vel2Dotn = contactConstraint.contactNormal.dot(body2.linearVelocity) + contactConstraint.relpos2CrossNormal.dot(body2.angularVelocity);
rel_vel = vel1Dotn - vel2Dotn;
// calculate j that moves us to zero relative velocity
j1 = -rel_vel * contactConstraint.jacDiagABInv;
//#define CLAMP_ACCUMULATED_FRICTION_IMPULSE 1
//#ifdef CLAMP_ACCUMULATED_FRICTION_IMPULSE
float oldTangentImpulse = contactConstraint.appliedImpulse;
contactConstraint.appliedImpulse = oldTangentImpulse + j1;
if (limit < contactConstraint.appliedImpulse) {
contactConstraint.appliedImpulse = limit;
}
else {
if (contactConstraint.appliedImpulse < -limit) {
contactConstraint.appliedImpulse = -limit;
}
}
j1 = contactConstraint.appliedImpulse - oldTangentImpulse;
// #else
// if (limit < j1)
// {
// j1 = limit;
// } else
// {
// if (j1 < -limit)
// j1 = -limit;
// }
// #endif
//GEN_set_min(contactConstraint.m_appliedImpulse, limit);
//GEN_set_max(contactConstraint.m_appliedImpulse, -limit);
}
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.scale(body1.invMass, contactConstraint.contactNormal);
body1.internalApplyImpulse(tmp, contactConstraint.angularComponentA, j1);
tmp.scale(body2.invMass, contactConstraint.contactNormal);
body2.internalApplyImpulse(tmp, contactConstraint.angularComponentB, -j1);
}
return 0f;
}
@StaticAlloc
protected void addFrictionConstraint(Vector3f normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, ManifoldPoint cp, Vector3f rel_pos1, Vector3f rel_pos2, CollisionObject colObj0, CollisionObject colObj1, float relaxation) {
RigidBody body0 = RigidBody.upcast(colObj0);
RigidBody body1 = RigidBody.upcast(colObj1);
SolverConstraint solverConstraint = constraintsPool.get();
tmpSolverFrictionConstraintPool.add(solverConstraint);
solverConstraint.contactNormal.set(normalAxis);
solverConstraint.solverBodyIdA = solverBodyIdA;
solverConstraint.solverBodyIdB = solverBodyIdB;
solverConstraint.constraintType = SolverConstraintType.SOLVER_FRICTION_1D;
solverConstraint.frictionIndex = frictionIndex;
solverConstraint.friction = cp.combinedFriction;
solverConstraint.originalContactPoint = null;
solverConstraint.appliedImpulse = 0f;
solverConstraint.appliedPushImpulse = 0f;
solverConstraint.penetration = 0f;
Vector3f ftorqueAxis1 = Stack.alloc(Vector3f.class);
Matrix3f tmpMat = Stack.alloc(Matrix3f.class);
{
ftorqueAxis1.cross(rel_pos1, solverConstraint.contactNormal);
solverConstraint.relpos1CrossNormal.set(ftorqueAxis1);
if (body0 != null) {
solverConstraint.angularComponentA.set(ftorqueAxis1);
body0.getInvInertiaTensorWorld(tmpMat).transform(solverConstraint.angularComponentA);
}
else {
solverConstraint.angularComponentA.set(0f, 0f, 0f);
}
}
{
ftorqueAxis1.cross(rel_pos2, solverConstraint.contactNormal);
solverConstraint.relpos2CrossNormal.set(ftorqueAxis1);
if (body1 != null) {
solverConstraint.angularComponentB.set(ftorqueAxis1);
body1.getInvInertiaTensorWorld(tmpMat).transform(solverConstraint.angularComponentB);
}
else {
solverConstraint.angularComponentB.set(0f, 0f, 0f);
}
}
//#ifdef COMPUTE_IMPULSE_DENOM
// btScalar denom0 = rb0->computeImpulseDenominator(pos1,solverConstraint.m_contactNormal);
// btScalar denom1 = rb1->computeImpulseDenominator(pos2,solverConstraint.m_contactNormal);
//#else
Vector3f vec = Stack.alloc(Vector3f.class);
float denom0 = 0f;
float denom1 = 0f;
if (body0 != null) {
vec.cross(solverConstraint.angularComponentA, rel_pos1);
denom0 = body0.getInvMass() + normalAxis.dot(vec);
}
if (body1 != null) {
vec.cross(solverConstraint.angularComponentB, rel_pos2);
denom1 = body1.getInvMass() + normalAxis.dot(vec);
}
//#endif //COMPUTE_IMPULSE_DENOM
float denom = relaxation / (denom0 + denom1);
solverConstraint.jacDiagABInv = denom;
}
public float solveGroupCacheFriendlySetup(ObjectArrayList bodies, int numBodies, ObjectArrayList manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer/*,btStackAlloc* stackAlloc*/) {
BulletStats.pushProfile("solveGroupCacheFriendlySetup");
try {
if ((numConstraints + numManifolds) == 0) {
// printf("empty\n");
return 0f;
}
PersistentManifold manifold = null;
CollisionObject colObj0 = null, colObj1 = null;
//btRigidBody* rb0=0,*rb1=0;
// //#ifdef FORCE_REFESH_CONTACT_MANIFOLDS
//
// BEGIN_PROFILE("refreshManifolds");
//
// int i;
//
//
//
// for (i=0;igetBody1();
// rb0 = (btRigidBody*)manifold->getBody0();
//
// manifold->refreshContactPoints(rb0->getCenterOfMassTransform(),rb1->getCenterOfMassTransform());
//
// }
//
// END_PROFILE("refreshManifolds");
// //#endif //FORCE_REFESH_CONTACT_MANIFOLDS
Transform tmpTrans = Stack.alloc(Transform.class);
//int sizeofSB = sizeof(btSolverBody);
//int sizeofSC = sizeof(btSolverConstraint);
//if (1)
{
//if m_stackAlloc, try to pack bodies/constraints to speed up solving
// btBlock* sablock;
// sablock = stackAlloc->beginBlock();
// int memsize = 16;
// unsigned char* stackMemory = stackAlloc->allocate(memsize);
// todo: use stack allocator for this temp memory
//int minReservation = numManifolds * 2;
//m_tmpSolverBodyPool.reserve(minReservation);
//don't convert all bodies, only the one we need so solver the constraints
/*
{
for (int i=0;igetIslandTag() >= 0))
{
btAssert(rb->getCompanionId() < 0);
int solverBodyId = m_tmpSolverBodyPool.size();
btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
initSolverBody(&solverBody,rb);
rb->setCompanionId(solverBodyId);
}
}
}
*/
//m_tmpSolverConstraintPool.reserve(minReservation);
//m_tmpSolverFrictionConstraintPool.reserve(minReservation);
{
int i;
Vector3f rel_pos1 = Stack.alloc(Vector3f.class);
Vector3f rel_pos2 = Stack.alloc(Vector3f.class);
Vector3f pos1 = Stack.alloc(Vector3f.class);
Vector3f pos2 = Stack.alloc(Vector3f.class);
Vector3f vel = Stack.alloc(Vector3f.class);
Vector3f torqueAxis0 = Stack.alloc(Vector3f.class);
Vector3f torqueAxis1 = Stack.alloc(Vector3f.class);
Vector3f vel1 = Stack.alloc(Vector3f.class);
Vector3f vel2 = Stack.alloc(Vector3f.class);
Vector3f frictionDir1 = Stack.alloc(Vector3f.class);
Vector3f frictionDir2 = Stack.alloc(Vector3f.class);
Vector3f vec = Stack.alloc(Vector3f.class);
Matrix3f tmpMat = Stack.alloc(Matrix3f.class);
for (i = 0; i < numManifolds; i++) {
manifold = manifoldPtr.getQuick(manifold_offset+i);
colObj0 = (CollisionObject) manifold.getBody0();
colObj1 = (CollisionObject) manifold.getBody1();
int solverBodyIdA = -1;
int solverBodyIdB = -1;
if (manifold.getNumContacts() != 0) {
if (colObj0.getIslandTag() >= 0) {
if (colObj0.getCompanionId() >= 0) {
// body has already been converted
solverBodyIdA = colObj0.getCompanionId();
}
else {
solverBodyIdA = tmpSolverBodyPool.size();
SolverBody solverBody = bodiesPool.get();
tmpSolverBodyPool.add(solverBody);
initSolverBody(solverBody, colObj0);
colObj0.setCompanionId(solverBodyIdA);
}
}
else {
// create a static body
solverBodyIdA = tmpSolverBodyPool.size();
SolverBody solverBody = bodiesPool.get();
tmpSolverBodyPool.add(solverBody);
initSolverBody(solverBody, colObj0);
}
if (colObj1.getIslandTag() >= 0) {
if (colObj1.getCompanionId() >= 0) {
solverBodyIdB = colObj1.getCompanionId();
}
else {
solverBodyIdB = tmpSolverBodyPool.size();
SolverBody solverBody = bodiesPool.get();
tmpSolverBodyPool.add(solverBody);
initSolverBody(solverBody, colObj1);
colObj1.setCompanionId(solverBodyIdB);
}
}
else {
// create a static body
solverBodyIdB = tmpSolverBodyPool.size();
SolverBody solverBody = bodiesPool.get();
tmpSolverBodyPool.add(solverBody);
initSolverBody(solverBody, colObj1);
}
}
float relaxation;
for (int j = 0; j < manifold.getNumContacts(); j++) {
ManifoldPoint cp = manifold.getContactPoint(j);
if (cp.getDistance() <= 0f) {
cp.getPositionWorldOnA(pos1);
cp.getPositionWorldOnB(pos2);
rel_pos1.sub(pos1, colObj0.getWorldTransform(tmpTrans).origin);
rel_pos2.sub(pos2, colObj1.getWorldTransform(tmpTrans).origin);
relaxation = 1f;
float rel_vel;
int frictionIndex = tmpSolverConstraintPool.size();
{
SolverConstraint solverConstraint = constraintsPool.get();
tmpSolverConstraintPool.add(solverConstraint);
RigidBody rb0 = RigidBody.upcast(colObj0);
RigidBody rb1 = RigidBody.upcast(colObj1);
solverConstraint.solverBodyIdA = solverBodyIdA;
solverConstraint.solverBodyIdB = solverBodyIdB;
solverConstraint.constraintType = SolverConstraintType.SOLVER_CONTACT_1D;
solverConstraint.originalContactPoint = cp;
torqueAxis0.cross(rel_pos1, cp.normalWorldOnB);
if (rb0 != null) {
solverConstraint.angularComponentA.set(torqueAxis0);
rb0.getInvInertiaTensorWorld(tmpMat).transform(solverConstraint.angularComponentA);
}
else {
solverConstraint.angularComponentA.set(0f, 0f, 0f);
}
torqueAxis1.cross(rel_pos2, cp.normalWorldOnB);
if (rb1 != null) {
solverConstraint.angularComponentB.set(torqueAxis1);
rb1.getInvInertiaTensorWorld(tmpMat).transform(solverConstraint.angularComponentB);
}
else {
solverConstraint.angularComponentB.set(0f, 0f, 0f);
}
{
//#ifdef COMPUTE_IMPULSE_DENOM
//btScalar denom0 = rb0->computeImpulseDenominator(pos1,cp.m_normalWorldOnB);
//btScalar denom1 = rb1->computeImpulseDenominator(pos2,cp.m_normalWorldOnB);
//#else
float denom0 = 0f;
float denom1 = 0f;
if (rb0 != null) {
vec.cross(solverConstraint.angularComponentA, rel_pos1);
denom0 = rb0.getInvMass() + cp.normalWorldOnB.dot(vec);
}
if (rb1 != null) {
vec.cross(solverConstraint.angularComponentB, rel_pos2);
denom1 = rb1.getInvMass() + cp.normalWorldOnB.dot(vec);
}
//#endif //COMPUTE_IMPULSE_DENOM
float denom = relaxation / (denom0 + denom1);
solverConstraint.jacDiagABInv = denom;
}
solverConstraint.contactNormal.set(cp.normalWorldOnB);
solverConstraint.relpos1CrossNormal.cross(rel_pos1, cp.normalWorldOnB);
solverConstraint.relpos2CrossNormal.cross(rel_pos2, cp.normalWorldOnB);
if (rb0 != null) {
rb0.getVelocityInLocalPoint(rel_pos1, vel1);
}
else {
vel1.set(0f, 0f, 0f);
}
if (rb1 != null) {
rb1.getVelocityInLocalPoint(rel_pos2, vel2);
}
else {
vel2.set(0f, 0f, 0f);
}
vel.sub(vel1, vel2);
rel_vel = cp.normalWorldOnB.dot(vel);
solverConstraint.penetration = Math.min(cp.getDistance() + infoGlobal.linearSlop, 0f);
//solverConstraint.m_penetration = cp.getDistance();
solverConstraint.friction = cp.combinedFriction;
solverConstraint.restitution = restitutionCurve(rel_vel, cp.combinedRestitution);
if (solverConstraint.restitution <= 0f) {
solverConstraint.restitution = 0f;
}
float penVel = -solverConstraint.penetration / infoGlobal.timeStep;
if (solverConstraint.restitution > penVel) {
solverConstraint.penetration = 0f;
}
Vector3f tmp = Stack.alloc(Vector3f.class);
// warm starting (or zero if disabled)
if ((infoGlobal.solverMode & SolverMode.SOLVER_USE_WARMSTARTING) != 0) {
solverConstraint.appliedImpulse = cp.appliedImpulse * infoGlobal.warmstartingFactor;
if (rb0 != null) {
tmp.scale(rb0.getInvMass(), solverConstraint.contactNormal);
tmpSolverBodyPool.getQuick(solverConstraint.solverBodyIdA).internalApplyImpulse(tmp, solverConstraint.angularComponentA, solverConstraint.appliedImpulse);
}
if (rb1 != null) {
tmp.scale(rb1.getInvMass(), solverConstraint.contactNormal);
tmpSolverBodyPool.getQuick(solverConstraint.solverBodyIdB).internalApplyImpulse(tmp, solverConstraint.angularComponentB, -solverConstraint.appliedImpulse);
}
}
else {
solverConstraint.appliedImpulse = 0f;
}
solverConstraint.appliedPushImpulse = 0f;
solverConstraint.frictionIndex = tmpSolverFrictionConstraintPool.size();
if (!cp.lateralFrictionInitialized) {
cp.lateralFrictionDir1.scale(rel_vel, cp.normalWorldOnB);
cp.lateralFrictionDir1.sub(vel, cp.lateralFrictionDir1);
float lat_rel_vel = cp.lateralFrictionDir1.lengthSquared();
if (lat_rel_vel > BulletGlobals.FLT_EPSILON)//0.0f)
{
cp.lateralFrictionDir1.scale(1f / (float) Math.sqrt(lat_rel_vel));
addFrictionConstraint(cp.lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
cp.lateralFrictionDir2.cross(cp.lateralFrictionDir1, cp.normalWorldOnB);
cp.lateralFrictionDir2.normalize(); //??
addFrictionConstraint(cp.lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
}
else {
// re-calculate friction direction every frame, todo: check if this is really needed
TransformUtil.planeSpace1(cp.normalWorldOnB, cp.lateralFrictionDir1, cp.lateralFrictionDir2);
addFrictionConstraint(cp.lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
addFrictionConstraint(cp.lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
}
cp.lateralFrictionInitialized = true;
}
else {
addFrictionConstraint(cp.lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
addFrictionConstraint(cp.lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
}
{
SolverConstraint frictionConstraint1 = tmpSolverFrictionConstraintPool.getQuick(solverConstraint.frictionIndex);
if ((infoGlobal.solverMode & SolverMode.SOLVER_USE_WARMSTARTING) != 0) {
frictionConstraint1.appliedImpulse = cp.appliedImpulseLateral1 * infoGlobal.warmstartingFactor;
if (rb0 != null) {
tmp.scale(rb0.getInvMass(), frictionConstraint1.contactNormal);
tmpSolverBodyPool.getQuick(solverConstraint.solverBodyIdA).internalApplyImpulse(tmp, frictionConstraint1.angularComponentA, frictionConstraint1.appliedImpulse);
}
if (rb1 != null) {
tmp.scale(rb1.getInvMass(), frictionConstraint1.contactNormal);
tmpSolverBodyPool.getQuick(solverConstraint.solverBodyIdB).internalApplyImpulse(tmp, frictionConstraint1.angularComponentB, -frictionConstraint1.appliedImpulse);
}
}
else {
frictionConstraint1.appliedImpulse = 0f;
}
}
{
SolverConstraint frictionConstraint2 = tmpSolverFrictionConstraintPool.getQuick(solverConstraint.frictionIndex + 1);
if ((infoGlobal.solverMode & SolverMode.SOLVER_USE_WARMSTARTING) != 0) {
frictionConstraint2.appliedImpulse = cp.appliedImpulseLateral2 * infoGlobal.warmstartingFactor;
if (rb0 != null) {
tmp.scale(rb0.getInvMass(), frictionConstraint2.contactNormal);
tmpSolverBodyPool.getQuick(solverConstraint.solverBodyIdA).internalApplyImpulse(tmp, frictionConstraint2.angularComponentA, frictionConstraint2.appliedImpulse);
}
if (rb1 != null) {
tmp.scale(rb1.getInvMass(), frictionConstraint2.contactNormal);
tmpSolverBodyPool.getQuick(solverConstraint.solverBodyIdB).internalApplyImpulse(tmp, frictionConstraint2.angularComponentB, -frictionConstraint2.appliedImpulse);
}
}
else {
frictionConstraint2.appliedImpulse = 0f;
}
}
}
}
}
}
}
}
// TODO: btContactSolverInfo info = infoGlobal;
{
int j;
for (j = 0; j < numConstraints; j++) {
TypedConstraint constraint = constraints.getQuick(constraints_offset+j);
constraint.buildJacobian();
}
}
int numConstraintPool = tmpSolverConstraintPool.size();
int numFrictionPool = tmpSolverFrictionConstraintPool.size();
// todo: use stack allocator for such temporarily memory, same for solver bodies/constraints
MiscUtil.resize(orderTmpConstraintPool, numConstraintPool, 0);
MiscUtil.resize(orderFrictionConstraintPool, numFrictionPool, 0);
{
int i;
for (i = 0; i < numConstraintPool; i++) {
orderTmpConstraintPool.set(i, i);
}
for (i = 0; i < numFrictionPool; i++) {
orderFrictionConstraintPool.set(i, i);
}
}
return 0f;
}
finally {
BulletStats.popProfile();
}
}
public float solveGroupCacheFriendlyIterations(ObjectArrayList bodies, int numBodies, ObjectArrayList manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer/*,btStackAlloc* stackAlloc*/) {
BulletStats.pushProfile("solveGroupCacheFriendlyIterations");
try {
int numConstraintPool = tmpSolverConstraintPool.size();
int numFrictionPool = tmpSolverFrictionConstraintPool.size();
// should traverse the contacts random order...
int iteration;
{
for (iteration = 0; iteration < infoGlobal.numIterations; iteration++) {
int j;
if ((infoGlobal.solverMode & SolverMode.SOLVER_RANDMIZE_ORDER) != 0) {
if ((iteration & 7) == 0) {
for (j = 0; j < numConstraintPool; ++j) {
int tmp = orderTmpConstraintPool.get(j);
int swapi = randInt2(j + 1);
orderTmpConstraintPool.set(j, orderTmpConstraintPool.get(swapi));
orderTmpConstraintPool.set(swapi, tmp);
}
for (j = 0; j < numFrictionPool; ++j) {
int tmp = orderFrictionConstraintPool.get(j);
int swapi = randInt2(j + 1);
orderFrictionConstraintPool.set(j, orderFrictionConstraintPool.get(swapi));
orderFrictionConstraintPool.set(swapi, tmp);
}
}
}
for (j = 0; j < numConstraints; j++) {
TypedConstraint constraint = constraints.getQuick(constraints_offset+j);
// todo: use solver bodies, so we don't need to copy from/to btRigidBody
if ((constraint.getRigidBodyA().getIslandTag() >= 0) && (constraint.getRigidBodyA().getCompanionId() >= 0)) {
tmpSolverBodyPool.getQuick(constraint.getRigidBodyA().getCompanionId()).writebackVelocity();
}
if ((constraint.getRigidBodyB().getIslandTag() >= 0) && (constraint.getRigidBodyB().getCompanionId() >= 0)) {
tmpSolverBodyPool.getQuick(constraint.getRigidBodyB().getCompanionId()).writebackVelocity();
}
constraint.solveConstraint(infoGlobal.timeStep);
if ((constraint.getRigidBodyA().getIslandTag() >= 0) && (constraint.getRigidBodyA().getCompanionId() >= 0)) {
tmpSolverBodyPool.getQuick(constraint.getRigidBodyA().getCompanionId()).readVelocity();
}
if ((constraint.getRigidBodyB().getIslandTag() >= 0) && (constraint.getRigidBodyB().getCompanionId() >= 0)) {
tmpSolverBodyPool.getQuick(constraint.getRigidBodyB().getCompanionId()).readVelocity();
}
}
{
int numPoolConstraints = tmpSolverConstraintPool.size();
for (j = 0; j < numPoolConstraints; j++) {
SolverConstraint solveManifold = tmpSolverConstraintPool.getQuick(orderTmpConstraintPool.get(j));
resolveSingleCollisionCombinedCacheFriendly(tmpSolverBodyPool.getQuick(solveManifold.solverBodyIdA),
tmpSolverBodyPool.getQuick(solveManifold.solverBodyIdB), solveManifold, infoGlobal);
}
}
{
int numFrictionPoolConstraints = tmpSolverFrictionConstraintPool.size();
for (j = 0; j < numFrictionPoolConstraints; j++) {
SolverConstraint solveManifold = tmpSolverFrictionConstraintPool.getQuick(orderFrictionConstraintPool.get(j));
float totalImpulse = tmpSolverConstraintPool.getQuick(solveManifold.frictionIndex).appliedImpulse +
tmpSolverConstraintPool.getQuick(solveManifold.frictionIndex).appliedPushImpulse;
resolveSingleFrictionCacheFriendly(tmpSolverBodyPool.getQuick(solveManifold.solverBodyIdA),
tmpSolverBodyPool.getQuick(solveManifold.solverBodyIdB), solveManifold, infoGlobal,
totalImpulse);
}
}
}
if (infoGlobal.splitImpulse) {
for (iteration = 0; iteration < infoGlobal.numIterations; iteration++) {
{
int numPoolConstraints = tmpSolverConstraintPool.size();
int j;
for (j = 0; j < numPoolConstraints; j++) {
SolverConstraint solveManifold = tmpSolverConstraintPool.getQuick(orderTmpConstraintPool.get(j));
resolveSplitPenetrationImpulseCacheFriendly(tmpSolverBodyPool.getQuick(solveManifold.solverBodyIdA),
tmpSolverBodyPool.getQuick(solveManifold.solverBodyIdB), solveManifold, infoGlobal);
}
}
}
}
}
return 0f;
}
finally {
BulletStats.popProfile();
}
}
public float solveGroupCacheFriendly(ObjectArrayList bodies, int numBodies, ObjectArrayList manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer/*,btStackAlloc* stackAlloc*/) {
solveGroupCacheFriendlySetup(bodies, numBodies, manifoldPtr, manifold_offset, numManifolds, constraints, constraints_offset, numConstraints, infoGlobal, debugDrawer/*, stackAlloc*/);
solveGroupCacheFriendlyIterations(bodies, numBodies, manifoldPtr, manifold_offset, numManifolds, constraints, constraints_offset, numConstraints, infoGlobal, debugDrawer/*, stackAlloc*/);
int numPoolConstraints = tmpSolverConstraintPool.size();
for (int j=0; j bodies, int numBodies, ObjectArrayList manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer, Dispatcher dispatcher) {
BulletStats.pushProfile("solveGroup");
try {
// TODO: solver cache friendly
if ((infoGlobal.solverMode & SolverMode.SOLVER_CACHE_FRIENDLY) != 0) {
// you need to provide at least some bodies
// SimpleDynamicsWorld needs to switch off SOLVER_CACHE_FRIENDLY
assert (bodies != null);
assert (numBodies != 0);
float value = solveGroupCacheFriendly(bodies, numBodies, manifoldPtr, manifold_offset, numManifolds, constraints, constraints_offset, numConstraints, infoGlobal, debugDrawer/*,stackAlloc*/);
return value;
}
ContactSolverInfo info = new ContactSolverInfo(infoGlobal);
int numiter = infoGlobal.numIterations;
int totalPoints = 0;
{
short j;
for (j = 0; j < numManifolds; j++) {
PersistentManifold manifold = manifoldPtr.getQuick(manifold_offset+j);
prepareConstraints(manifold, info, debugDrawer);
for (short p = 0; p < manifoldPtr.getQuick(manifold_offset+j).getNumContacts(); p++) {
gOrder[totalPoints].manifoldIndex = j;
gOrder[totalPoints].pointIndex = p;
totalPoints++;
}
}
}
{
int j;
for (j = 0; j < numConstraints; j++) {
TypedConstraint constraint = constraints.getQuick(constraints_offset+j);
constraint.buildJacobian();
}
}
// should traverse the contacts random order...
int iteration;
{
for (iteration = 0; iteration < numiter; iteration++) {
int j;
if ((infoGlobal.solverMode & SolverMode.SOLVER_RANDMIZE_ORDER) != 0) {
if ((iteration & 7) == 0) {
for (j = 0; j < totalPoints; ++j) {
// JAVA NOTE: swaps references instead of copying values (but that's fine in this context)
OrderIndex tmp = gOrder[j];
int swapi = randInt2(j + 1);
gOrder[j] = gOrder[swapi];
gOrder[swapi] = tmp;
}
}
}
for (j = 0; j < numConstraints; j++) {
TypedConstraint constraint = constraints.getQuick(constraints_offset+j);
constraint.solveConstraint(info.timeStep);
}
for (j = 0; j < totalPoints; j++) {
PersistentManifold manifold = manifoldPtr.getQuick(manifold_offset+gOrder[j].manifoldIndex);
solve((RigidBody) manifold.getBody0(),
(RigidBody) manifold.getBody1(), manifold.getContactPoint(gOrder[j].pointIndex), info, iteration, debugDrawer);
}
for (j = 0; j < totalPoints; j++) {
PersistentManifold manifold = manifoldPtr.getQuick(manifold_offset+gOrder[j].manifoldIndex);
solveFriction((RigidBody) manifold.getBody0(),
(RigidBody) manifold.getBody1(), manifold.getContactPoint(gOrder[j].pointIndex), info, iteration, debugDrawer);
}
}
}
return 0f;
}
finally {
BulletStats.popProfile();
}
}
protected void prepareConstraints(PersistentManifold manifoldPtr, ContactSolverInfo info, IDebugDraw debugDrawer) {
RigidBody body0 = (RigidBody) manifoldPtr.getBody0();
RigidBody body1 = (RigidBody) manifoldPtr.getBody1();
// only necessary to refresh the manifold once (first iteration). The integration is done outside the loop
{
//#ifdef FORCE_REFESH_CONTACT_MANIFOLDS
//manifoldPtr->refreshContactPoints(body0->getCenterOfMassTransform(),body1->getCenterOfMassTransform());
//#endif //FORCE_REFESH_CONTACT_MANIFOLDS
int numpoints = manifoldPtr.getNumContacts();
BulletStats.gTotalContactPoints += numpoints;
Vector3f tmpVec = Stack.alloc(Vector3f.class);
Matrix3f tmpMat3 = Stack.alloc(Matrix3f.class);
Vector3f pos1 = Stack.alloc(Vector3f.class);
Vector3f pos2 = Stack.alloc(Vector3f.class);
Vector3f rel_pos1 = Stack.alloc(Vector3f.class);
Vector3f rel_pos2 = Stack.alloc(Vector3f.class);
Vector3f vel1 = Stack.alloc(Vector3f.class);
Vector3f vel2 = Stack.alloc(Vector3f.class);
Vector3f vel = Stack.alloc(Vector3f.class);
Vector3f totalImpulse = Stack.alloc(Vector3f.class);
Vector3f torqueAxis0 = Stack.alloc(Vector3f.class);
Vector3f torqueAxis1 = Stack.alloc(Vector3f.class);
Vector3f ftorqueAxis0 = Stack.alloc(Vector3f.class);
Vector3f ftorqueAxis1 = Stack.alloc(Vector3f.class);
for (int i = 0; i < numpoints; i++) {
ManifoldPoint cp = manifoldPtr.getContactPoint(i);
if (cp.getDistance() <= 0f) {
cp.getPositionWorldOnA(pos1);
cp.getPositionWorldOnB(pos2);
rel_pos1.sub(pos1, body0.getCenterOfMassPosition(tmpVec));
rel_pos2.sub(pos2, body1.getCenterOfMassPosition(tmpVec));
// this jacobian entry is re-used for all iterations
Matrix3f mat1 = body0.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis;
mat1.transpose();
Matrix3f mat2 = body1.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis;
mat2.transpose();
JacobianEntry jac = jacobiansPool.get();
jac.init(mat1, mat2,
rel_pos1, rel_pos2, cp.normalWorldOnB,
body0.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)), body0.getInvMass(),
body1.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)), body1.getInvMass());
float jacDiagAB = jac.getDiagonal();
jacobiansPool.release(jac);
ConstraintPersistentData cpd = (ConstraintPersistentData) cp.userPersistentData;
if (cpd != null) {
// might be invalid
cpd.persistentLifeTime++;
if (cpd.persistentLifeTime != cp.getLifeTime()) {
//printf("Invalid: cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd->m_persistentLifeTime,cp.getLifeTime());
//new (cpd) btConstraintPersistentData;
cpd.reset();
cpd.persistentLifeTime = cp.getLifeTime();
}
else {
//printf("Persistent: cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd->m_persistentLifeTime,cp.getLifeTime());
}
}
else {
// todo: should this be in a pool?
//void* mem = btAlignedAlloc(sizeof(btConstraintPersistentData),16);
//cpd = new (mem)btConstraintPersistentData;
cpd = new ConstraintPersistentData();
//assert(cpd != null);
totalCpd++;
//printf("totalCpd = %i Created Ptr %x\n",totalCpd,cpd);
cp.userPersistentData = cpd;
cpd.persistentLifeTime = cp.getLifeTime();
//printf("CREATED: %x . cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd,cpd->m_persistentLifeTime,cp.getLifeTime());
}
assert (cpd != null);
cpd.jacDiagABInv = 1f / jacDiagAB;
// Dependent on Rigidbody A and B types, fetch the contact/friction response func
// perhaps do a similar thing for friction/restutution combiner funcs...
cpd.frictionSolverFunc = frictionDispatch[body0.frictionSolverType][body1.frictionSolverType];
cpd.contactSolverFunc = contactDispatch[body0.contactSolverType][body1.contactSolverType];
body0.getVelocityInLocalPoint(rel_pos1, vel1);
body1.getVelocityInLocalPoint(rel_pos2, vel2);
vel.sub(vel1, vel2);
float rel_vel;
rel_vel = cp.normalWorldOnB.dot(vel);
float combinedRestitution = cp.combinedRestitution;
cpd.penetration = cp.getDistance(); ///btScalar(info.m_numIterations);
cpd.friction = cp.combinedFriction;
cpd.restitution = restitutionCurve(rel_vel, combinedRestitution);
if (cpd.restitution <= 0f) {
cpd.restitution = 0f;
}
// restitution and penetration work in same direction so
// rel_vel
float penVel = -cpd.penetration / info.timeStep;
if (cpd.restitution > penVel) {
cpd.penetration = 0f;
}
float relaxation = info.damping;
if ((info.solverMode & SolverMode.SOLVER_USE_WARMSTARTING) != 0) {
cpd.appliedImpulse *= relaxation;
}
else {
cpd.appliedImpulse = 0f;
}
// for friction
cpd.prevAppliedImpulse = cpd.appliedImpulse;
// re-calculate friction direction every frame, todo: check if this is really needed
TransformUtil.planeSpace1(cp.normalWorldOnB, cpd.frictionWorldTangential0, cpd.frictionWorldTangential1);
//#define NO_FRICTION_WARMSTART 1
//#ifdef NO_FRICTION_WARMSTART
cpd.accumulatedTangentImpulse0 = 0f;
cpd.accumulatedTangentImpulse1 = 0f;
//#endif //NO_FRICTION_WARMSTART
float denom0 = body0.computeImpulseDenominator(pos1, cpd.frictionWorldTangential0);
float denom1 = body1.computeImpulseDenominator(pos2, cpd.frictionWorldTangential0);
float denom = relaxation / (denom0 + denom1);
cpd.jacDiagABInvTangent0 = denom;
denom0 = body0.computeImpulseDenominator(pos1, cpd.frictionWorldTangential1);
denom1 = body1.computeImpulseDenominator(pos2, cpd.frictionWorldTangential1);
denom = relaxation / (denom0 + denom1);
cpd.jacDiagABInvTangent1 = denom;
//btVector3 totalImpulse =
// //#ifndef NO_FRICTION_WARMSTART
// //cpd->m_frictionWorldTangential0*cpd->m_accumulatedTangentImpulse0+
// //cpd->m_frictionWorldTangential1*cpd->m_accumulatedTangentImpulse1+
// //#endif //NO_FRICTION_WARMSTART
// cp.normalWorldOnB*cpd.appliedImpulse;
totalImpulse.scale(cpd.appliedImpulse, cp.normalWorldOnB);
///
{
torqueAxis0.cross(rel_pos1, cp.normalWorldOnB);
cpd.angularComponentA.set(torqueAxis0);
body0.getInvInertiaTensorWorld(tmpMat3).transform(cpd.angularComponentA);
torqueAxis1.cross(rel_pos2, cp.normalWorldOnB);
cpd.angularComponentB.set(torqueAxis1);
body1.getInvInertiaTensorWorld(tmpMat3).transform(cpd.angularComponentB);
}
{
ftorqueAxis0.cross(rel_pos1, cpd.frictionWorldTangential0);
cpd.frictionAngularComponent0A.set(ftorqueAxis0);
body0.getInvInertiaTensorWorld(tmpMat3).transform(cpd.frictionAngularComponent0A);
}
{
ftorqueAxis1.cross(rel_pos1, cpd.frictionWorldTangential1);
cpd.frictionAngularComponent1A.set(ftorqueAxis1);
body0.getInvInertiaTensorWorld(tmpMat3).transform(cpd.frictionAngularComponent1A);
}
{
ftorqueAxis0.cross(rel_pos2, cpd.frictionWorldTangential0);
cpd.frictionAngularComponent0B.set(ftorqueAxis0);
body1.getInvInertiaTensorWorld(tmpMat3).transform(cpd.frictionAngularComponent0B);
}
{
ftorqueAxis1.cross(rel_pos2, cpd.frictionWorldTangential1);
cpd.frictionAngularComponent1B.set(ftorqueAxis1);
body1.getInvInertiaTensorWorld(tmpMat3).transform(cpd.frictionAngularComponent1B);
}
///
// apply previous frames impulse on both bodies
body0.applyImpulse(totalImpulse, rel_pos1);
tmpVec.negate(totalImpulse);
body1.applyImpulse(tmpVec, rel_pos2);
}
}
}
}
public float solveCombinedContactFriction(RigidBody body0, RigidBody body1, ManifoldPoint cp, ContactSolverInfo info, int iter, IDebugDraw debugDrawer) {
float maxImpulse = 0f;
{
if (cp.getDistance() <= 0f) {
{
//btConstraintPersistentData* cpd = (btConstraintPersistentData*) cp.m_userPersistentData;
float impulse = ContactConstraint.resolveSingleCollisionCombined(body0, body1, cp, info);
if (maxImpulse < impulse) {
maxImpulse = impulse;
}
}
}
}
return maxImpulse;
}
protected float solve(RigidBody body0, RigidBody body1, ManifoldPoint cp, ContactSolverInfo info, int iter, IDebugDraw debugDrawer) {
float maxImpulse = 0f;
{
if (cp.getDistance() <= 0f) {
{
ConstraintPersistentData cpd = (ConstraintPersistentData) cp.userPersistentData;
float impulse = cpd.contactSolverFunc.resolveContact(body0, body1, cp, info);
if (maxImpulse < impulse) {
maxImpulse = impulse;
}
}
}
}
return maxImpulse;
}
protected float solveFriction(RigidBody body0, RigidBody body1, ManifoldPoint cp, ContactSolverInfo info, int iter, IDebugDraw debugDrawer) {
{
if (cp.getDistance() <= 0f) {
ConstraintPersistentData cpd = (ConstraintPersistentData) cp.userPersistentData;
cpd.frictionSolverFunc.resolveContact(body0, body1, cp, info);
}
}
return 0f;
}
@Override
public void reset() {
btSeed2 = 0;
}
/**
* Advanced: Override the default contact solving function for contacts, for certain types of rigidbody
* See RigidBody.contactSolverType and RigidBody.frictionSolverType
*/
public void setContactSolverFunc(ContactSolverFunc func, int type0, int type1) {
contactDispatch[type0][type1] = func;
}
/**
* Advanced: Override the default friction solving function for contacts, for certain types of rigidbody
* See RigidBody.contactSolverType and RigidBody.frictionSolverType
*/
public void setFrictionSolverFunc(ContactSolverFunc func, int type0, int type1) {
frictionDispatch[type0][type1] = func;
}
public void setRandSeed(long seed) {
btSeed2 = seed;
}
public long getRandSeed() {
return btSeed2;
}
////////////////////////////////////////////////////////////////////////////
private static class OrderIndex {
public int manifoldIndex;
public int pointIndex;
}
}