posts - 206, comments - 61, trackbacks - 0, articles - 0
   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

在Bullet Physics Engine中使用约束

Posted on 2011-11-24 17:44 魔のkyo 阅读(2746) 评论(0)  编辑 收藏 引用

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


只有注册用户登录后才能发表评论。