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的旋轉矩陣的複合。

(歐拉角: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.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));
    }

效果如圖:

附件爲錯誤和正確限制示例的可執行文件 AppConstraintDemo_exe.rar

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章