天天看點

Qt Quick + OpenGL + Bullet初次測試Qt Quick + OpenGL + Bullet初次測試

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,兩者結合起來看,友善了許多。花了一周的時間,終于模仿并且制作出來了這樣的效果:

Qt Quick + OpenGL + Bullet初次測試Qt Quick + OpenGL + Bullet初次測試

         一開始立方體在平面的上面位置,随後根據重力,呈自由落體速度,慢慢地往下落。最終落在了地面上。下圖表示落下來時候的樣子:

Qt Quick + OpenGL + Bullet初次測試Qt Quick + OpenGL + Bullet初次測試

         怎麼樣?很簡單吧。這裡也是我對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。隻有這樣,一切看起來概念才清晰。

Qt Quick + OpenGL + Bullet初次測試Qt Quick + OpenGL + Bullet初次測試

         這裡可能有一個比較棘手的問題,有時候,需要對modelMatrix進行手動控制,來改變模型的變換資訊,但是呢,modelMatrix又是受實體引擎控制的,是以對于modelMatrix受手動控制和實體引擎控制的切換,是比較難以處理的,下次我再看看随着學習的深入,能不能厘清他們之前的關系。

         最後,我也在我的Android平闆電腦上測試成功。說明Bullet是完全可以做到結合Qt來跨平台的。

Qt Quick + OpenGL + Bullet初次測試Qt Quick + OpenGL + Bullet初次測試

繼續閱讀