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地址在这里。这边儿比較简单,easy上手。再加上Bullet有一份Manual,两者结合起来看。方便了很多。
花了一周的时间。最终模仿而且制作出来了这种效果:
一開始立方体在平面的上面位置,随后依据重力,呈自由落体速度,慢慢地往下落。
终于落在了地面上。下图表示落下来时候的样子:
怎么样?非常easy吧。
这里也是我对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来跨平台的。