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的用法及其使用上的限制和繞過限制的方法。
先看一個簡單的例子:
btTransform trans(btTransform::getIdentity());
trans.setOrigin(pivotInA);
btTypedConstraint* p2p = new btGeneric6DofConstraint(*body0, trans, true);
相當於等價的用btPoint2PointConstraint創建出的如下約束:
btTypedConstraint* p2p = new btPoint2PointConstraint(*body0, pivotInA);
btGeneric6DofConstraint(
btRigidBody& rbB, //被約束的剛體
const btTransform& frameInB, //對約束剛體的變換,約束條件是建立在變換後的剛體上
bool useLinearReferenceFrameB); //true表示約束條件參考由frameInB定義的座標系否則參考世界座標系
在創建出btGeneric6DofConstraint之後還應該使用如下函數設置6個自由度的約束條件
void setLinearUpperLimit(const btVector3& linearUpper);
void setAngularLowerLimit(const btVector3& angularLower);
void setAngularUpperLimit(const btVector3& angularUpper);
Lowerlimit > Upperlimit -> axis is free
Lowerlimit < Upperlimit -> axis it limited in that range
如果不設置約束條件,默認情況平移將被鎖住,而旋轉是自由的。
所以上面的例子創建的通用6自由度約束的意義是:將剛體“向上”平移5個單位後將平移鎖死而允許自由旋轉。
約束剛體的變換 在剛體的worldTransform之前作用與剛體,所以這裏的“向上”是不對剛體進行worldTransform時的向上。
這就是爲什麼它等價於上述的btPoint2PointConstraint約束。
上面旋轉是自由的,當然我們可以進行限制,比如只允許剛體繞某個軸進行旋轉,下面的例子中我們分別限制只允許繞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