1. 程式人生 > >Bullet Physics Engine(物理引擎)中使用約束

Bullet Physics Engine(物理引擎)中使用約束

Bullet(version 2.77)中提供了6中基本的約束:

  • 點點約束btPoint2PointConstraint
  • 鉸鏈約束btHingeConstraint
  • 滑動約束btSliderConstraint
  • 錐形約束btConeTwistConstraint
  • 通用的6自由度約束btGeneric6DofConstraint
  • 接觸點約束btContactConstraint

全部繼承自btTypedConstraint

4個約束在使用上都比較簡單,其功能也容易顧名思義,可以參考SDK帶的例子ConstraintDemo。

btGeneric6DofConstraint6自由度分別是表示平移的3個分量和表示旋轉的尤拉角的

3個分量,尤拉角使用Roll-Yaw-Pitch的旋轉順序,即相當於對X的旋轉矩陣Y的旋轉矩陣Z的旋轉矩陣的複合。

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.000));
        spSlider6Dof
->setAngularUpperLimit(btVector3(-1.0,00));

        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(01.00));
        spSlider6Dof
->setAngularUpperLimit(btVector3(0-1.00));

        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(001.0));
        spSlider6Dof
->setAngularUpperLimit(btVector3(00,-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( 
00, 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.000));
        spSlider6Dof
->setAngularUpperLimit(btVector3(-1.00,  0));

        m_dynamicsWorld
->addConstraint(spSlider6Dof);
        spSlider6Dof
->setDbgDrawSize(btScalar(5.f));
    }

效果如圖: