Qt Quick + OpenGL + Bullet初次测试
目前Qt的Quick模块已经表现得非常出色,而且可以预留接口来渲染OpenGL场景。一般来说,已经能够满足大部分编程需要了。这次呢,尝试使用结合一些技术,来做一些有趣儿的事情——将Bullet整合进来,并且进行测试。
蒋彩阳原创文章,首发地址:http://blog.csdn.net/gamesdev/article/details/44284317。欢迎同行前来探讨。
有关Bullet的选择,其实也是有一番讲究的。目前Bullet的2.82版本,暂时没有更新了,而Bullet 3.x,则是还少有应用。如果我们去看Bullet的新的代码库——github,就会发现,在“Requirementsfor Bullet 2”,作者表示几乎任何编译器都能够编译它的代码,而在“Requirements for Bullet 3”的介绍中,只是说能在高端的显卡中运行,低端显卡或者是移动设备可能和Bullet 3无缘了。所以在这种情况下,我就选择Bullet 2.82来制作这个例子。
为了完成这个例子,我参考了前辈们的一些例子,比如说Qt在诺基亚时期,就写了一个很棒的Bullet + Qt的例子,叫做BulletDice。它的github地址在这里。这边儿比较简单,容易上手,再加上Bullet有一份Manual,两者结合起来看,方便了许多。花了一周的时间,终于模仿并且制作出来了这样的效果:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiM2ATMzkzMzITNxMDM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
一开始立方体在平面的上面位置,随后根据重力,呈自由落体速度,慢慢地往下落。最终落在了地面上。下图表示落下来时候的样子:
怎么样?很简单吧。这里也是我对Bullet的初步认识。在编写这个例子的过程中,遇到了很多困难,也有很多地方值得推敲。所以我在这里先作一个笔记,给以后作参考。
首先为了验证物理引擎可用,我在类中写了一个函数叫debugShow(),每次仿真采样的时候,输出立方体的位置。在验证这样是没有问题的基础上,开始研究怎样让数据与渲染相结合。下面的代码片描述了与Bullet相关的一些知识:
void DynamicsWorldPrivate::initializePhysics( void )
{
// 初始化物理引擎
m_broadPhase = new btDbvtBroadphase;
m_conf = new btDefaultCollisionConfiguration;
m_dispatcher = new btCollisionDispatcher( m_conf );
m_solver = new btSequentialImpulseConstraintSolver;
// 应用重力
m_world = new btDiscreteDynamicsWorld( m_dispatcher,
m_broadPhase,
m_solver,
m_conf );
m_world->setGravity( btVector3( 0, -9.81, 0 ) );
// 创建一个平面刚体
createPlane( );
// 创建一个立方体刚体
createCube( );
}
void DynamicsWorldPrivate::releasePhysics( void )
{
delete m_world; m_world = Q_NULLPTR;
delete m_solver; m_solver = Q_NULLPTR;
delete m_conf; m_conf = Q_NULLPTR;
delete m_dispatcher; m_dispatcher = Q_NULLPTR;
delete m_broadPhase; m_broadPhase = Q_NULLPTR;
qDeleteAll( m_shapes );
qDeleteAll( m_bodies );
m_shapes.clear( );
m_bodies.clear( );
}
void DynamicsWorldPrivate::createPlane( void )
{
btStaticPlaneShape* planeShape = new btStaticPlaneShape(
btVector3( 0, 1, 0 ), // 平面法线
0.0 ); // 平面的相对原点的距离
btTransform originTransform;
originTransform.setFromOpenGLMatrix( m_planeModelMatrix->data( ) );
btDefaultMotionState* motionState = new btDefaultMotionState(
originTransform, // 开始的变换
btTransform::getIdentity( ) ); // 中心平移量
btRigidBody::btRigidBodyConstructionInfo planeInfo(
0, // 质量
motionState, // 运动状态
planeShape, // 碰撞的形状
btVector3( 0, 0, 0 ) ); // 本地的惯性
m_planeBody = new btRigidBody( planeInfo );
m_world->addRigidBody( m_planeBody );
m_shapes.append( planeShape );
m_bodies.append( m_planeBody );
}
void DynamicsWorldPrivate::createCube( void )
{
qreal semi = m_cubeLength / 2.0;
btBoxShape* cubeShape = new btBoxShape(
btVector3( semi, semi, semi ) );// 半个立方体的大小
btTransform originTransform;
originTransform.setFromOpenGLMatrix( m_cubeModelMatrix->data( ) );
btDefaultMotionState* motionState = new btDefaultMotionState(
originTransform, // 开始的变换
btTransform::getIdentity( ) ); // 中心平移量
btRigidBody::btRigidBodyConstructionInfo cubeInfo(
0.8f, // 质量
motionState, // 运动状态
cubeShape, // 碰撞的形状
btVector3( 0, 0, 0 ) ); // 本地的惯性
cubeInfo.m_friction = 0.3; // 摩擦力
cubeInfo.m_restitution = 0.1; // 反弹力(恢复力)
m_cubeBody = new btRigidBody( cubeInfo );
m_world->addRigidBody( m_cubeBody );
m_shapes.append( cubeShape );
m_bodies.append( m_cubeBody );
}
void DynamicsWorldPrivate::simulate( void )
{
QTime curTime = QTime::currentTime( );
int stepTime = m_lastTime.msecsTo( curTime );
m_lastTime = curTime;
m_world->stepSimulation( btScalar( stepTime ) * 0.001 );
}
void DynamicsWorldPrivate::debugShow( void )
{
btTransform worldTransform;
m_cubeBody->getMotionState( )->getWorldTransform( worldTransform );
btVector3 position = worldTransform * btVector3( 0, 0, 0 );
qDebug( "position is: ( %.2f, %.2f, %.2f )",
position.x( ), position.y( ), position.z( ) );
}
随后,可能要考虑,在这个模型中,只有一个施力物体,也就是“地球”,它对立方体施以重力,让立方体做自由落体运动,运动过程中采样的轨迹通过body中motionState中的worldTransform来表述。如果和渲染引擎相结合的话,我这边的做法是物理引擎最终只修改物体的modelMatrix,模型自己的形态则控制着本原位置,摄像机的参数控制着viewMatrix和projectionMatrix。只有这样,一切看起来概念才清晰。
这里可能有一个比较棘手的问题,有时候,需要对modelMatrix进行手动控制,来改变模型的变换信息,但是呢,modelMatrix又是受物理引擎控制的,所以对于modelMatrix受手动控制和物理引擎控制的切换,是比较难以处理的,下次我再看看随着学习的深入,能不能厘清他们之前的关系。
最后,我也在我的Android平板电脑上测试成功。说明Bullet是完全可以做到结合Qt来跨平台的。