/*
|
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
|
*
|
* 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<p>
|
*
|
* Although Sequential Impulse is more intuitive, it is mathematically equivalent to Projected
|
* Successive Overrelaxation (iterative LCP).<p>
|
*
|
* 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<gOrder.length; i++) {
|
gOrder[i] = new OrderIndex();
|
}
|
}
|
|
////////////////////////////////////////////////////////////////////////////
|
|
private final ObjectPool<SolverBody> bodiesPool = ObjectPool.get(SolverBody.class);
|
private final ObjectPool<SolverConstraint> constraintsPool = ObjectPool.get(SolverConstraint.class);
|
private final ObjectPool<JacobianEntry> jacobiansPool = ObjectPool.get(JacobianEntry.class);
|
|
private final ObjectArrayList<SolverBody> tmpSolverBodyPool = new ObjectArrayList<SolverBody>();
|
private final ObjectArrayList<SolverConstraint> tmpSolverConstraintPool = new ObjectArrayList<SolverConstraint>();
|
private final ObjectArrayList<SolverConstraint> tmpSolverFrictionConstraintPool = new ObjectArrayList<SolverConstraint>();
|
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<CollisionObject> bodies, int numBodies, ObjectArrayList<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList<TypedConstraint> 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;i<numManifolds;i++)
|
// {
|
// manifold = manifoldPtr[i];
|
// rb1 = (btRigidBody*)manifold->getBody1();
|
// 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;i<numBodies;i++)
|
{
|
btRigidBody* rb = btRigidBody::upcast(bodies[i]);
|
if (rb && (rb->getIslandTag() >= 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<CollisionObject> bodies, int numBodies, ObjectArrayList<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList<TypedConstraint> 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<CollisionObject> bodies, int numBodies, ObjectArrayList<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList<TypedConstraint> 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<numPoolConstraints; j++) {
|
|
SolverConstraint solveManifold = tmpSolverConstraintPool.getQuick(j);
|
ManifoldPoint pt = (ManifoldPoint) solveManifold.originalContactPoint;
|
assert (pt != null);
|
pt.appliedImpulse = solveManifold.appliedImpulse;
|
pt.appliedImpulseLateral1 = tmpSolverFrictionConstraintPool.getQuick(solveManifold.frictionIndex).appliedImpulse;
|
pt.appliedImpulseLateral1 = tmpSolverFrictionConstraintPool.getQuick(solveManifold.frictionIndex + 1).appliedImpulse;
|
|
// do a callback here?
|
}
|
|
if (infoGlobal.splitImpulse) {
|
for (int i=0; i<tmpSolverBodyPool.size(); i++) {
|
tmpSolverBodyPool.getQuick(i).writebackVelocity(infoGlobal.timeStep);
|
bodiesPool.release(tmpSolverBodyPool.getQuick(i));
|
}
|
}
|
else {
|
for (int i=0; i<tmpSolverBodyPool.size(); i++) {
|
tmpSolverBodyPool.getQuick(i).writebackVelocity();
|
bodiesPool.release(tmpSolverBodyPool.getQuick(i));
|
}
|
}
|
|
// printf("m_tmpSolverConstraintPool.size() = %i\n",m_tmpSolverConstraintPool.size());
|
|
/*
|
printf("m_tmpSolverBodyPool.size() = %i\n",m_tmpSolverBodyPool.size());
|
printf("m_tmpSolverConstraintPool.size() = %i\n",m_tmpSolverConstraintPool.size());
|
printf("m_tmpSolverFrictionConstraintPool.size() = %i\n",m_tmpSolverFrictionConstraintPool.size());
|
printf("m_tmpSolverBodyPool.capacity() = %i\n",m_tmpSolverBodyPool.capacity());
|
printf("m_tmpSolverConstraintPool.capacity() = %i\n",m_tmpSolverConstraintPool.capacity());
|
printf("m_tmpSolverFrictionConstraintPool.capacity() = %i\n",m_tmpSolverFrictionConstraintPool.capacity());
|
*/
|
|
tmpSolverBodyPool.clear();
|
|
for (int i=0; i<tmpSolverConstraintPool.size(); i++) {
|
constraintsPool.release(tmpSolverConstraintPool.getQuick(i));
|
}
|
tmpSolverConstraintPool.clear();
|
|
for (int i=0; i<tmpSolverFrictionConstraintPool.size(); i++) {
|
constraintsPool.release(tmpSolverFrictionConstraintPool.getQuick(i));
|
}
|
tmpSolverFrictionConstraintPool.clear();
|
|
return 0f;
|
}
|
|
/**
|
* Sequentially applies impulses.
|
*/
|
@Override
|
public float solveGroup(ObjectArrayList<CollisionObject> bodies, int numBodies, ObjectArrayList<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, ObjectArrayList<TypedConstraint> 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<br>
|
* 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<br>
|
* 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 implements java.io.Serializable {
|
public int manifoldIndex;
|
public int pointIndex;
|
}
|
|
}
|