Bullet Physics Engine(物理引擎)中使用約束
阿新 • • 發佈:2019-02-18
Bullet(version 2.77)中提供了6中基本的約束:
- 點點約束btPoint2PointConstraint
- 鉸鏈約束btHingeConstraint
- 滑動約束btSliderConstraint
- 錐形約束btConeTwistConstraint
- 通用的6自由度約束btGeneric6DofConstraint
- 接觸點約束btContactConstraint
全部繼承自btTypedConstraint。
前4個約束在使用上都比較簡單,其功能也容易顧名思義,可以參考SDK帶的例子ConstraintDemo。
btGeneric6DofConstraint的6自由度分別是表示平移的3個分量和表示旋轉的尤拉角的
btContactConstraint貌似是一個已經被廢棄的約束,現在並沒有被使用,而且實現是空的。
在btSequentialImpulseConstraintSolver中將碰撞資訊建立成了btSolverConstraint,而它沒有繼承自btTypedConstraint。
接下來主要討論btGeneric6DofConstraint的用法及其使用上的限制和繞過限制的方法。
先看一個簡單的例子:
btVector3 pivotInA(0,5,0);btTransform trans(btTransform::getIdentity());
trans.setOrigin(pivotInA);
btTypedConstraint
相當於等價的用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));
}
效果如圖: