Bullet(version 2.77)中提供了6中基本的约束:
- 点点约束 btPoint2PointConstraint
- 铰链约束 btHingeConstraint
- 滑动约束 btSliderConstraint
- 锥形约束 btConeTwistConstraint
- 通用的6自由度约束 btGeneric6DofConstraint
- 接触点约束 btContactConstraint
全部继承自btTypedConstraint。
前4个约束在使用上都比较简单,其功能也容易顾名思义,可以参考SDK带的例子ConstraintDemo。
btGeneric6DofConstraint的6自由度分别是表示平移的3个分量和表示旋转的欧拉角的3个分量,欧拉角使用Roll-Yaw-Pitch的旋转顺序,即相当于对X的旋转矩阵Y的旋转矩阵Z的旋转矩阵的复合。
(欧拉角:http://www.cnitblog.com/luckydmz/archive/2010/09/07/68674.html)
btContactConstraint貌似是一个已经被废弃的约束,现在并没有被使用,而且实现是空的。
在btSequentialImpulseConstraintSolver中将碰撞信息创建成了btSolverConstraint,而它没有继承自btTypedConstraint。
接下来主要讨论btGeneric6DofConstraint的用法及其使用上的限制和绕过限制的方法。
先看一个简单的例子:
btVector3 pivotInA(0,5,0);
btTransform trans(btTransform::getIdentity());
trans.setOrigin(pivotInA);
btTypedConstraint* p2p = new btGeneric6DofConstraint(*body0, trans, true);
这段代码使用btGeneric6DofConstraint创建了一个点点约束,body0被约束到保持到它上方5个单位处的定点的距离不变,而可以绕该定点任意旋转。
相当于等价的
用btPoint2PointConstraint创建出的如下约束:
btVector3 pivotInA(0,5,0);
btTypedConstraint* p2p = new btPoint2PointConstraint(*body0, pivotInA);
上例中btGeneric6DofConstraint的构造函数参数的意义如下:
btGeneric6DofConstraint(
btRigidBody& rbB, //被约束的刚体
const btTransform& frameInB, //对约束刚体的变换,约束条件是建立在变换后的刚体上
bool useLinearReferenceFrameB); //true表示约束条件参考由frameInB定义的坐标系否则参考世界坐标系
在创建出
btGeneric6DofConstraint之后还应该使用如下函数设置6个自由度的约束条件
void setLinearLowerLimit(const btVector3& linearLower);
void setLinearUpperLimit(const btVector3& linearUpper);
void setAngularLowerLimit(const btVector3& angularLower);
void setAngularUpperLimit(const btVector3& angularUpper);
Lowerlimit == Upperlimit -> axis is locked.
Lowerlimit > Upperlimit -> axis is free
Lowerlimit < Upperlimit -> axis it limited in that range
如果不设置约束条件,默认情况平移将被锁住,而旋转是自由的。
所以上面的例子创建的通用6自由度约束的意义是:将刚体“向上”平移5个单位后将平移锁死而允许自由旋转。
约束刚体的变换 在刚体的worldTransform之前作用与刚体,所以这里的“向上”是不对刚体进行worldTransform时的向上。
这就是为什么它等价于上述的
btPoint2PointConstraint约束。
上面旋转是自由的,当然我们可以进行限制,比如只允许刚体绕某个轴进行旋转,下面的例子中我们分别限制只允许绕X轴、Y轴、Z轴旋转。
{ //允许绕X轴自由旋转,将Y轴、Z轴锁死
trans.setOrigin(btVector3(-10,0,0));
d6body0 = localCreateRigidBody( mass,trans,shape);
d6body0->setActivationState(DISABLE_DEACTIVATION);
spSlider6Dof = new btGeneric6DofConstraint(*d6body0,btTransform::getIdentity(),true);
spSlider6Dof->setAngularLowerLimit(btVector3(1.0, 0, 0));
spSlider6Dof->setAngularUpperLimit(btVector3(-1.0,0, 0));
m_dynamicsWorld->addConstraint(spSlider6Dof);
spSlider6Dof->setDbgDrawSize(btScalar(5.f));
}
{ //允许绕Y轴自由旋转,将X轴、Z轴锁死
trans.setOrigin(btVector3(0,0,0));
d6body0 = localCreateRigidBody( mass,trans,shape);
d6body0->setActivationState(DISABLE_DEACTIVATION);
spSlider6Dof = new btGeneric6DofConstraint(*d6body0,btTransform::getIdentity(),true);
spSlider6Dof->setAngularLowerLimit(btVector3(0, 1.0, 0));
spSlider6Dof->setAngularUpperLimit(btVector3(0, -1.0, 0));
m_dynamicsWorld->addConstraint(spSlider6Dof);
spSlider6Dof->setDbgDrawSize(btScalar(5.f));
}
{ //允许绕Z轴自由旋转,将X轴、Y轴锁死
trans.setOrigin(btVector3(10,0,0));
d6body0 = localCreateRigidBody( mass,trans,shape);
d6body0->setActivationState(DISABLE_DEACTIVATION);
spSlider6Dof = new btGeneric6DofConstraint(*d6body0,btTransform::getIdentity(),true);
spSlider6Dof->setAngularLowerLimit(btVector3(0, 0, 1.0));
spSlider6Dof->setAngularUpperLimit(btVector3(0, 0,-1.0));
m_dynamicsWorld->addConstraint(spSlider6Dof);
spSlider6Dof->setDbgDrawSize(btScalar(5.f));
}
你可以下载附件中的代码替换ConstraintDemo中的同名文件来观看上面代码的效果
ConstraintDemo.rar
可以看到绕X轴自由旋转和绕Z轴自由旋转的约束都是正确的,而绕Y轴自由旋转的约束出现了异常。
从btGeneric6DofConstraint的注释中我们可以发现对转角的约束是有限制的
AXIS |
MIN ANGLE |
MAX ANGLE |
X |
-PI |
PI |
Y |
-PI/2 |
PI/2 |
Z |
-PI |
PI |
这个限制的存在应该和欧拉角的唯一性有关。(一个相似的例子是经纬度)
当定义超过限制的约束时,约束会变得十分诡异,另外,限制使得对Y轴的约束只能是locked或limited而不能是free
当我们想创建一个不会翻的车子,我们需要让Y轴自由旋转,而X轴和Z轴有一定限制,这时候怎么办?
一个解决办法如下:
{
trans.setOrigin(btVector3(0,0,0));
d6body0 = localCreateRigidBody( mass,trans,shape);
d6body0->setActivationState(DISABLE_DEACTIVATION);
btRigidBody* _bt_balancer_body = new btRigidBody(0,0,0);
m_dynamicsWorld->addRigidBody(_bt_balancer_body);
// must use X axis as Y axis because 6dof wont spin freely on Y
btTransform rotateZ( btTransform::getIdentity() );
rotateZ.getBasis().setEulerZYX( 0, 0, SIMD_HALF_PI );
spSlider6Dof = new btGeneric6DofConstraint(*d6body0, *_bt_balancer_body, rotateZ, rotateZ,true);
// 这里的约束条件是参照rotateZ表示的坐标系,是经过绕Z轴旋转的坐标系,这里的X轴是世界坐标系的Y轴,所以只需要设置旋转的X自由,而锁死Y,Z即可绕过对Y轴不能设置自由的限制。
spSlider6Dof->setAngularLowerLimit(btVector3(1.0, 0, 0));
spSlider6Dof->setAngularUpperLimit(btVector3(-1.0, 0, 0));
m_dynamicsWorld->addConstraint(spSlider6Dof);
spSlider6Dof->setDbgDrawSize(btScalar(5.f));
}
效果如图:
附件为错误和正确限制示例的可执行文件 AppConstraintDemo_exe.rar