ホーム < ゲームつくろー!< 衝突判定編

Havok編
その2 Havok導入!:サンプルプログラム



 衝突判定編Havok編その2「Havok導入!」で説明した内容を踏まえたサンプルプログラムです。実行すると、8つのキューブが100m上空から床に向かって自由落下をし、床に当たると弾けます。


サンプルスクリーンショット。直方体がはじけてます!

○ コンパイル方法

 Visual Studioで新規プロジェクト(Win32アプリケーション)を作成し、main.cppを追加します。次に下記のプログラムをコピペします。「Havok導入!」で説明したヘッダーとライブラリパスをプロジェクトに設定すると実行できます。

#pragma comment(lib, "hkBase.lib")
#pragma comment(lib, "hkInternal.lib")
#pragma comment(lib, "hkpCollide.lib")
#pragma comment(lib, "hkpconstraintsolver.lib")
#pragma comment(lib, "hkpDynamics.lib")
#pragma comment(lib, "hkpinternal.lib")
#pragma comment(lib, "hkputilities.lib")

#include <Common/Base/keycode.cxx>
#include <Common/Base/hkBase.h>
#include <Common/Base/Memory/System/Util/hkMemoryInitUtil.h>

#include <Physics/Dynamics/World/hkpWorld.h>
#include <Physics/Dynamics/Entity/hkpRigidBody.h>
#include <Physics/Dynamics/Entity/hkpRigidBodyCinfo.h>
#include <Physics/Utilities/Dynamics/Inertia/hkpInertiaTensorComputer.h>
#include <Physics/Collide/Shape/Convex/Box/hkpBoxShape.h>
#include <Physics/Collide/Dispatch/hkpAgentRegisterUtil.h>

#include <windows.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <stdio.h>

#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")


// HAvok内でエラーが起きた時によばれます
void HavokErrorReportFunction(const char* s, void* errorReportObject) {}


TCHAR gName[100] = _T("Havokサンプル");


LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam){
    if(mes == WM_DESTROY) {PostQuitMessage(0); return 0;}
    return DefWindowProc(hWnd, mes, wParam, lParam);
}


int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    // アプリケーションの初期化
    MSG msg; HWND hWnd;
    WNDCLASSEX wcex ={sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH)(COLOR_WINDOW+1), NULL, (TCHAR*)gName, NULL};
    if(!RegisterClassEx(&wcex))
        return 0;

    if(!(hWnd = CreateWindow(gName, gName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL)))
        return 0;

    // Direct3Dの初期化
    LPDIRECT3D9 g_pD3D;
    LPDIRECT3DDEVICE9 g_pD3DDev;
    if( !(g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )) ) return 0;

    D3DPRESENT_PARAMETERS d3dpp = {0,0,D3DFMT_UNKNOWN,0,D3DMULTISAMPLE_NONE,0,D3DSWAPEFFECT_DISCARD,NULL,TRUE,0,D3DFMT_UNKNOWN,0,0};
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
    d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
    d3dpp.MultiSampleQuality = 0;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) )
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) ) {
        g_pD3D->Release();
        return 0;
    }

    ShowWindow(hWnd, nCmdShow);


    //////////////////////////////
    // Havokの初期化
    hkMemoryRouter* memoryRouter = hkMemoryInitUtil::initDefault();
    hkBaseSystem::init(memoryRouter, HavokErrorReportFunction);

    hkpWorldCinfo info;
    info.m_simulationType = hkpWorldCinfo::SIMULATION_TYPE_CONTINUOUS;
    info.m_collisionTolerance = 0.001f;
    info.setBroadPhaseWorldSize( 10000.0f );
    info.setupSolverInfo( hkpWorldCinfo::SOLVER_TYPE_8ITERS_HARD );
    info.m_gravity = hkVector4(0.0f, -9.8f, 0.0f);
    hkpWorld *world = new hkpWorld(info);

    hkpAgentRegisterUtil::registerAllAgents( world->getCollisionDispatcher() );


    /////////////////////////////
    // 床を作ってみよう
    const float groundHalfH = 2.0f;
    const hkVector4 halfExtents(2000.0f, groundHalfH, 2000.0f);
    hkpBoxShape* groundShape = new hkpBoxShape(halfExtents);

    hkpRigidBodyCinfo bodyInfo;
    bodyInfo.m_mass = 0.0f;
    bodyInfo.m_shape = groundShape;
    bodyInfo.m_motionType = hkpMotion::MOTION_FIXED;
    bodyInfo.m_position.set(0.0f, -groundHalfH, 0.0f);

    hkpRigidBody* groundBody = new hkpRigidBody(bodyInfo);
    groundShape->removeReference();

    world->addEntity(groundBody);
    groundBody->removeReference();


    //////////////////////////////
    // Boxを作ってみよう
    const float bx = 1.0f, by = 2.0f, bz = 1.0f;
    const int nx = 2, ny = 2, nz = 2;
    const int NUM_BODIES = nx * ny * nz;
    hkpRigidBody *bodies[NUM_BODIES];
    {
        const hkVector4 halfExtents(bx, by, bz);
        hkpShape* shape = new hkpBoxShape(halfExtents, 0.0f);

        hkpMassProperties massProperties;
        hkpInertiaTensorComputer::computeShapeVolumeMassProperties(shape, 5.0f, massProperties);

        hkpRigidBodyCinfo bodyInfo;
        bodyInfo.m_mass = massProperties.m_mass;
        bodyInfo.m_centerOfMass = massProperties.m_centerOfMass;
        bodyInfo.m_inertiaTensor = massProperties.m_inertiaTensor;
        bodyInfo.m_shape = shape;
        bodyInfo.m_motionType = hkpMotion::MOTION_BOX_INERTIA;

        int i = 0;
        for(int x = 0; x < nx; x++)
        for(int y = 0; y < ny; y++)
        for(int z = 0; z < nz; z++) {
            hkpRigidBody *body = new hkpRigidBody(bodyInfo);
            body->setPosition(hkVector4(x * bx * 2, 100.0f + y * by * 2, z * bz * 2));
            bodies[i] = body;
            world->addEntity(body);
            body->removeReference();
            i++;
        }
        shape->removeReference();
    }

    ////////////////////////////////////
    // 床、Boxモデル作成
    ID3DXMesh *floor, *box;
    D3DXCreateBox(g_pD3DDev, bx * 2, by * 2, bz * 2, &box, 0);
    D3DXCreateBox(g_pD3DDev, 4000.0f, groundHalfH * 2.0f, 4000.0f, &floor, 0);

    // ID3DXFontコンポーネント生成
    int fontsize = 24;
    D3DXFONT_DESC lf = {fontsize, 0, 0, 1, 0, SHIFTJIS_CHARSET, OUT_TT_ONLY_PRECIS, PROOF_QUALITY, FIXED_PITCH | FF_MODERN, _T("MS ゴシック")};
    LPD3DXFONT pD3DFont;
    if(FAILED(D3DXCreateFontIndirect(g_pD3DDev, &lf, &pD3DFont))) {
        g_pD3DDev->Release(); g_pD3D->Release();
        return 0;
    }

    // 変換行列
    D3DXMATRIX view, proj;
    D3DXMatrixPerspectiveFovLH( &proj, D3DXToRadian(45), 640.0f/480.0f, 1.0f, 10000.0f);
    g_pD3DDev->SetTransform(D3DTS_PROJECTION, &proj);

    // ライト
    D3DLIGHT9 light;
    ZeroMemory(&light, sizeof(D3DLIGHT9));
    light.Direction = D3DXVECTOR3(-1, -2, 0.5);
    light.Type = D3DLIGHT_DIRECTIONAL;
    light.Diffuse.r = 0.7f;
    light.Diffuse.g = 0.7f;
    light.Diffuse.b = 0.7f;
    light.Diffuse.a = 1.0f;
    g_pD3DDev->SetLight( 0, &light );
    g_pD3DDev->LightEnable( 0, true );
    g_pD3DDev->SetRenderState( D3DRS_LIGHTING, TRUE );
    g_pD3DDev->SetRenderState( D3DRS_AMBIENT, 0x00808080 ); // アンビエントライト

    // マテリアル
    D3DMATERIAL9 ground_material = {
        {1.0f, 1.0f, 1.0f, 1.0f},
        {0.2f, 0.2f, 0.2f, 1.0f},
        {0.0f, 0.0f, 0.0f, 0.0f},
        {0.0f, 0.0f, 0.0f, 0.0f},
        10.0f
    };
    D3DMATERIAL9 cube_material = {
        {0.7f, 1.0f, 0.7f, 1.0f},
        {0.2f, 0.2f, 0.2f, 1.0f},
        {0.0f, 0.0f, 0.0f, 0.0f},
        {0.0f, 0.0f, 0.0f, 0.0f},
        10.0f
    };


    // メッセージ ループ
    float tm = 0.0f;
    do{
        if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){
            DispatchMessage(&msg);
        }
        else {
            tm += 1.0f/60.0f;
            world->stepDeltaTime(1.0f/60.0f);

            // 1つのリジッドに注視します
            hkVector4 pos = bodies[0]->getPosition();
            {
                const hkTransform &trans = bodies[0]->getTransform();
                const hkVector4 &t = trans.getTranslation();
                D3DXMatrixLookAtLH(&view, &D3DXVECTOR3(0.0f, 50.0f, -25.0f), &D3DXVECTOR3(t(0), t(1), t(2)), &D3DXVECTOR3(0.0f, 1.0f, 0.0f));
                g_pD3DDev->SetTransform(D3DTS_VIEW, &view);
            }

            char c[128];
            sprintf(c, "Pos(%f, %f, %f) %5.1f sec.", pos(0), pos(1), pos(2), tm);

            // Direct3Dの処理
            g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
            g_pD3DDev->BeginScene();

            // 床描画
            {
                const hkTransform &t = groundBody->getTransform();
                D3DXMATRIX transMat(
                    t(0,0), t(1,0), t(2,0), 0.0f,
                    t(0,1), t(1,1), t(2,1), 0.0f,
                    t(0,2), t(1,2), t(2,2), 0.0f,
                    t(0,3), t(1,3), t(2,3), 1.0f);
                g_pD3DDev->SetTransform(D3DTS_WORLD, &transMat);
                g_pD3DDev->SetMaterial(&ground_material);

                floor->DrawSubset(0);
            }

            // キューブ描画
            {
                for (int i = 0; i < NUM_BODIES; i++) {
                    const hkTransform &t = bodies[i]->getTransform();
                    D3DXMATRIX transMat(
                        t(0,0), t(1,0), t(2,0), 0.0f,
                        t(0,1), t(1,1), t(2,1), 0.0f,
                        t(0,2), t(1,2), t(2,2), 0.0f,
                        t(0,3), t(1,3), t(2,3), 1.0f);
                    g_pD3DDev->SetTransform(D3DTS_WORLD, &transMat);
                    g_pD3DDev->SetMaterial(&cube_material);
                    box->DrawSubset(0);
                }
            }

            // 位置描画
            RECT rc = {0, 0, 0, 0};
            pD3DFont->DrawTextA(NULL, c, -1, &rc, DT_LEFT | DT_SINGLELINE | DT_NOCLIP, 0xffffffff);

            g_pD3DDev->EndScene();
            g_pD3DDev->Present( NULL, NULL, NULL, NULL );
        }
    } while(msg.message != WM_QUIT);


    // 後片付け
    delete world;
    hkBaseSystem::quit();
    hkMemoryInitUtil::quit();

    floor->Release();
    box->Release();
    pD3DFont->Release();
    g_pD3DDev->Release();
    g_pD3D->Release();

    return 0;
}