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

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的用法及其使用上的限制和绕过限制的方法。

先看一个简单的例子:

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.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的注释中我们可以发现对转角的约束是有限制的

AXISMIN ANGLEMAX ANGLE
X-PIPI
Y-PI/2PI/2
Z-PIPI

这个限制的存在应该和欧拉角的唯一性有关。(一个相似的例子是经纬度)
当定义超过限制的约束时,约束会变得十分诡异,另外,限制使得对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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值