improve rolling friction using anisotropic direction, to avoid resting in an instable position

(for implicit capsule, cylinder and cone shape)
See Bullet/Demos/RollingFrictionDemo for an example
This commit is contained in:
erwin.coumans
2012-09-16 17:01:25 +00:00
parent 26c713423f
commit 22fb7d5c1e
8 changed files with 120 additions and 43 deletions

View File

@@ -281,8 +281,9 @@ void CcdPhysicsDemo::initPhysics()
float mass = 1.f; float mass = 1.f;
btRigidBody* body = localCreateRigidBody(mass,trans,shape); btRigidBody* body = localCreateRigidBody(mass,trans,shape);
body->setAnisotropicFriction(shape->getAnisotropicRollingFrictionDirection(),btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
body->setRollingFriction(0.1); body->setRollingFriction(.3);
///when using m_ccdMode ///when using m_ccdMode
if (m_ccdMode==USE_CCD) if (m_ccdMode==USE_CCD)
{ {

View File

@@ -171,9 +171,21 @@ void RollingFrictionDemo::initPhysics()
{ {
//create a few dynamic rigidbodies //create a few dynamic rigidbodies
// Re-using the same collision is better for memory usage and performance // Re-using the same collision is better for memory usage and performance
#define NUM_SHAPES 10
btCollisionShape* colShape = new btSphereShape(btScalar(1.)); btCollisionShape* colShapes[NUM_SHAPES] = {
m_collisionShapes.push_back(colShape); new btSphereShape(btScalar(1.)),
new btCapsuleShape(0.5,1),
new btCapsuleShapeX(0.5,1),
new btCapsuleShapeZ(0.5,1),
new btConeShape(0.5,1),
new btConeShapeX(0.5,1),
new btConeShapeZ(0.5,1),
new btCylinderShape(btVector3(0.5,1,0.1)),
new btCylinderShapeX(btVector3(1,0.5,0.1)),
new btCylinderShapeZ(btVector3(0.5,0.5,1)),
};
for (int i=0;i<NUM_SHAPES;i++)
m_collisionShapes.push_back(colShapes[i]);
/// Create Dynamic Objects /// Create Dynamic Objects
btTransform startTransform; btTransform startTransform;
@@ -182,36 +194,44 @@ void RollingFrictionDemo::initPhysics()
btScalar mass(1.f); btScalar mass(1.f);
//rigidbody is dynamic if and only if mass is non zero, otherwise static //rigidbody is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0,0,0);
if (isDynamic)
colShape->calculateLocalInertia(mass,localInertia);
float start_x = START_POS_X - ARRAY_SIZE_X/2; float start_x = START_POS_X - ARRAY_SIZE_X/2;
float start_y = START_POS_Y; float start_y = START_POS_Y;
float start_z = START_POS_Z - ARRAY_SIZE_Z/2; float start_z = START_POS_Z - ARRAY_SIZE_Z/2;
for (int k=0;k<ARRAY_SIZE_Y;k++)
{ {
for (int i=0;i<ARRAY_SIZE_X;i++) int shapeIndex = 0;
for (int k=0;k<ARRAY_SIZE_Y;k++)
{ {
for(int j = 0;j<ARRAY_SIZE_Z;j++) for (int i=0;i<ARRAY_SIZE_X;i++)
{ {
startTransform.setOrigin(SCALING*btVector3( for(int j = 0;j<ARRAY_SIZE_Z;j++)
btScalar(2.0*i + start_x), {
btScalar(20+2.0*k + start_y), startTransform.setOrigin(SCALING*btVector3(
btScalar(2.0*j + start_z))); btScalar(2.0*i + start_x),
btScalar(20+2.0*k + start_y),
btScalar(2.0*j + start_z)));
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,colShape,localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
body->setFriction(1.f);
body->setRollingFriction(.3);
m_dynamicsWorld->addRigidBody(body); shapeIndex++;
btCollisionShape* colShape = colShapes[shapeIndex%NUM_SHAPES];
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0,0,0);
if (isDynamic)
colShape->calculateLocalInertia(mass,localInertia);
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,colShape,localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
body->setFriction(1.f);
body->setRollingFriction(.3);
body->setAnisotropicFriction(colShape->getAnisotropicRollingFrictionDirection(),btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
m_dynamicsWorld->addRigidBody(body);
}
} }
} }
} }

View File

@@ -138,6 +138,13 @@ public:
CO_USER_TYPE=32 CO_USER_TYPE=32
}; };
enum AnisotropicFrictionFlags
{
CF_ANISOTROPIC_FRICTION_DISABLED=0,
CF_ANISOTROPIC_FRICTION = 1,
CF_ANISOTROPIC_ROLLING_FRICTION = 2
};
SIMD_FORCE_INLINE bool mergesSimulationIslands() const SIMD_FORCE_INLINE bool mergesSimulationIslands() const
{ {
///static objects, kinematic and object without contact response don't merge islands ///static objects, kinematic and object without contact response don't merge islands
@@ -148,14 +155,15 @@ public:
{ {
return m_anisotropicFriction; return m_anisotropicFriction;
} }
void setAnisotropicFriction(const btVector3& anisotropicFriction) void setAnisotropicFriction(const btVector3& anisotropicFriction, int frictionMode = CF_ANISOTROPIC_FRICTION)
{ {
m_anisotropicFriction = anisotropicFriction; m_anisotropicFriction = anisotropicFriction;
m_hasAnisotropicFriction = (anisotropicFriction[0]!=1.f) || (anisotropicFriction[1]!=1.f) || (anisotropicFriction[2]!=1.f); bool isUnity = (anisotropicFriction[0]!=1.f) || (anisotropicFriction[1]!=1.f) || (anisotropicFriction[2]!=1.f);
m_hasAnisotropicFriction = isUnity?frictionMode : 0;
} }
bool hasAnisotropicFriction() const bool hasAnisotropicFriction(int frictionMode = CF_ANISOTROPIC_FRICTION) const
{ {
return m_hasAnisotropicFriction!=0; return (m_hasAnisotropicFriction&frictionMode)!=0;
} }
///the constraint solver can discard solving contacts, if the distance is above this threshold. 0 by default. ///the constraint solver can discard solving contacts, if the distance is above this threshold. 0 by default.

View File

@@ -104,6 +104,14 @@ public:
} }
virtual btVector3 getAnisotropicRollingFrictionDirection() const
{
btVector3 aniDir(0,0,0);
aniDir[getUpAxis()]=1;
return aniDir;
}
virtual int calculateSerializeBufferSize() const; virtual int calculateSerializeBufferSize() const;
///fills the dataBuffer and returns the struct name (and 0 on failure) ///fills the dataBuffer and returns the struct name (and 0 on failure)

View File

@@ -109,6 +109,13 @@ public:
int getShapeType() const { return m_shapeType; } int getShapeType() const { return m_shapeType; }
///the getAnisotropicRollingFrictionDirection can be used in combination with setAnisotropicFriction
///See Bullet/Demos/RollingFrictionDemo for an example
virtual btVector3 getAnisotropicRollingFrictionDirection() const
{
return btVector3(1,1,1);
}
virtual void setMargin(btScalar margin) = 0; virtual void setMargin(btScalar margin) = 0;
virtual btScalar getMargin() const = 0; virtual btScalar getMargin() const = 0;

View File

@@ -84,6 +84,11 @@ public:
return m_coneIndices[1]; return m_coneIndices[1];
} }
virtual btVector3 getAnisotropicRollingFrictionDirection() const
{
return btVector3 (0,1,0);
}
virtual void setLocalScaling(const btVector3& scaling); virtual void setLocalScaling(const btVector3& scaling);
}; };
@@ -93,6 +98,12 @@ class btConeShapeX : public btConeShape
{ {
public: public:
btConeShapeX(btScalar radius,btScalar height); btConeShapeX(btScalar radius,btScalar height);
virtual btVector3 getAnisotropicRollingFrictionDirection() const
{
return btVector3 (1,0,0);
}
}; };
///btConeShapeZ implements a Cone shape, around the Z axis ///btConeShapeZ implements a Cone shape, around the Z axis
@@ -100,6 +111,12 @@ class btConeShapeZ : public btConeShape
{ {
public: public:
btConeShapeZ(btScalar radius,btScalar height); btConeShapeZ(btScalar radius,btScalar height);
virtual btVector3 getAnisotropicRollingFrictionDirection() const
{
return btVector3 (0,0,1);
}
}; };
#endif //BT_CONE_MINKOWSKI_H #endif //BT_CONE_MINKOWSKI_H

View File

@@ -97,6 +97,13 @@ BT_DECLARE_ALIGNED_ALLOCATOR();
return m_upAxis; return m_upAxis;
} }
virtual btVector3 getAnisotropicRollingFrictionDirection() const
{
btVector3 aniDir(0,0,0);
aniDir[getUpAxis()]=1;
return aniDir;
}
virtual btScalar getRadius() const virtual btScalar getRadius() const
{ {
return getHalfExtentsWithMargin().getX(); return getHalfExtentsWithMargin().getX();

View File

@@ -324,12 +324,12 @@ btScalar btSequentialImpulseConstraintSolver::restitutionCurve(btScalar rel_vel,
static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection); static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection, int frictionMode);
static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection) static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection, int frictionMode)
{ {
if (colObj && colObj->hasAnisotropicFriction()) if (colObj && colObj->hasAnisotropicFriction(frictionMode))
{ {
// transform to local coordinates // transform to local coordinates
btVector3 loc_lateral = frictionDirection * colObj->getWorldTransform().getBasis(); btVector3 loc_lateral = frictionDirection * colObj->getWorldTransform().getBasis();
@@ -807,15 +807,24 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
if (relAngVel.length()>infoGlobal.m_singleAxisRollingFrictionThreshold) if (relAngVel.length()>infoGlobal.m_singleAxisRollingFrictionThreshold)
{ {
relAngVel.normalize(); relAngVel.normalize();
addRollingFrictionConstraint(relAngVel,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); applyAnisotropicFriction(colObj0,relAngVel,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
applyAnisotropicFriction(colObj1,relAngVel,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
if (relAngVel.length()>0.001)
addRollingFrictionConstraint(relAngVel,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);
} else } else
{ {
addRollingFrictionConstraint(cp.m_normalWorldOnB,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); addRollingFrictionConstraint(cp.m_normalWorldOnB,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);
btVector3 axis0,axis1; btVector3 axis0,axis1;
btPlaneSpace1(cp.m_normalWorldOnB,axis0,axis1); btPlaneSpace1(cp.m_normalWorldOnB,axis0,axis1);
addRollingFrictionConstraint(axis0,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); applyAnisotropicFriction(colObj0,axis0,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
addRollingFrictionConstraint(axis1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); applyAnisotropicFriction(colObj1,axis0,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
applyAnisotropicFriction(colObj0,axis1,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
applyAnisotropicFriction(colObj1,axis1,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
if (axis0.length()>0.001)
addRollingFrictionConstraint(axis0,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);
if (axis1.length()>0.001)
addRollingFrictionConstraint(axis1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);
} }
} }
@@ -834,14 +843,14 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
{ {
cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB); cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB);
cp.m_lateralFrictionDir2.normalize();//?? cp.m_lateralFrictionDir2.normalize();//??
applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2); applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2); applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION);
addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);
} }
applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1); applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1); applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION);
addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);
@@ -853,13 +862,13 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS))
{ {
applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2); applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2); applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION);
addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);
} }
applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1); applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1); applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION);
addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation);