3枚の X-FILE で Dolphin の Animation

August2007 DirectX9 で3枚の X-FILE を使って Dolphin の Animation を行います。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. Template のプロジェクトをコピーして、フォルダーの名前を Dolphin に変更して下さい。
    プロジェクトのフォルダーにイルカのXファイル(Dolphin1.x Dolphin2.x Dolphin3.x)をコピーしてきて下さい。
  2. D3DVERTEX は Dolphin.x の頂点データの形式です。
    g_pMesh[4] は先頭が描画するメッシュで、後の三個はイルカの姿勢を決めるキーとなるメッシュです。
    g_pMat[4] はメッシュのマテリアルを設定する領域です。
    g_pTex[4] はメッシュのテクスチャを設定する領域です。
    material は描画環境のマテリアルを設定する領域です。
    light はメッシュを照らすライトを設定する領域です。
    typedef struct _D3DVERTEX
    {   D3DXVECTOR3 position; // The 3D position for the vertex
        D3DXVECTOR3 normal;   // The surface normal for the vertex
    }   D3DVERTEX;
    
    LPD3DXMESH              g_pMesh[4]  = { NULL,NULL,NULL,NULL };
    D3DMATERIAL9*           g_pMat[4]   = { NULL,NULL,NULL,NULL };
    LPDIRECT3DTEXTURE9*     g_pTex[4]   = { NULL,NULL,NULL,NULL };
    DWORD                   g_wNum      = 0L;
    
    D3DMATERIAL9            material;
    D3DLIGHT9               light;
    WCHAR                   XFile[4][24]= { L"dolphin1.x", L"dolphin1.x", L"dolphin2.x", L"dolphin3.x" };
    
  3. OnD3D9CreateDevice() で3枚の X-File を入力して g_pMesh[] に格納します。
    先頭の Mesh は描画用で、頂点データはキーフレームから計算するので、どのファイルでもかまいません。
    HRESULT CALLBACK OnD3D9CreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
    {   LPD3DXBUFFER pD3DXMtrlBuffer;
        WCHAR   *wszName;
        WCHAR   wszBuf[MAX_PATH];
        wszName = wszBuf;
    
        for(int j=0; j<4; j++)
        {   if (FAILED(D3DXLoadMeshFromX(XFile[j],D3DXMESH_SYSTEMMEM,pd3dDevice,NULL,
                       &pD3DXMtrlBuffer,NULL,&g_wNum,&g_pMesh[j])))
            {   MessageBox(NULL,L"Could not find dolphin.x",L"DirectX9 Mesh",MB_OK);
                return E_FAIL;
            }
            D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();
            g_pMat[j]= new D3DMATERIAL9[g_wNum];
            if (g_pMat[j]==NULL)  return E_OUTOFMEMORY;
            g_pTex[j] = new LPDIRECT3DTEXTURE9[g_wNum];
            if (g_pTex[j]==NULL)   return E_OUTOFMEMORY;
            for(DWORD i=0; i<g_wNum; i++)
            {   g_pMat[j][i]= d3dxMaterials[i].MatD3D;
                g_pTex[j][i] = NULL;
                if (d3dxMaterials[i].pTextureFilename!=NULL)
                {   MultiByteToWideChar( CP_ACP, 0, d3dxMaterials[i].pTextureFilename, -1, wszBuf, MAX_PATH );
                    if (wszBuf[0]!=NULL)
                    {   if (FAILED(D3DXCreateTextureFromFile(pd3dDevice,wszName,&g_pTex[j][i])))
                            MessageBox(NULL,L"Could not find texture map",L"Meshes.exe",MB_OK);
                    }
                }
            }
            pD3DXMtrlBuffer->Release();
        }
    
        //初期マテリアルの設定
        material.Diffuse.r= material.Diffuse.g= material.Diffuse.b= 1.0f;
        material.Ambient.r= material.Ambient.g= material.Ambient.b= 0.5f;
        material.Specular.r= material.Specular.g= material.Specular.b= 0.0f;
        material.Emissive.r= material.Emissive.g= material.Emissive.b= 0.0f;
        material.Power= 0;
        //光源の作成
        ZeroMemory(&light,sizeof(D3DLIGHT9));
        light.Type = D3DLIGHT_DIRECTIONAL;
        light.Diffuse.r= light.Diffuse.g= light.Diffuse.b= 1.0f;
        light.Specular.r= light.Specular.g= light.Specular.b= 0.2f;
        light.Ambient.r= light.Ambient.g= light.Ambient.b= 0.5f;
        light.Direction = D3DXVECTOR3(10,-20,10);
    
        return S_OK;
    }
    
  4. OnD3D9FrameRender() でメッシュを描画します。
    座標原点を中心にして WORLD 座標をY軸で回転します。
    イルカのメッシュは三枚のキーフレームを fweight の割合で Blend して描画します。
    BlendMeshes() が頂点座標をブレンドする関数で、よりリアルに変化させるためにサイン関数を使って補完します。
    fweight が負のときは 三番と二番の間を補完します。
    fweight が正のときは 一番と二番の間を補完します。
    描画するのは先頭のメッシュです。
    マテリアルの設定は SetupMatrices() 関数で行います。
    void CALLBACK OnD3D9FrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
    {
        D3DXMATRIXA16 matWorld;
        HRESULT hr;
    
        D3DXMatrixRotationY(&matWorld,timeGetTime()/5000.0f);
        pd3dDevice->SetTransform(D3DTS_WORLD,&matWorld);
    
        //イルカのアニメーション
        FLOAT   fweight = sinf(timeGetTime()/500.0f);
        if (fweight<0.0f)
            BlendMeshes(g_pMesh[0],g_pMesh[3],g_pMesh[2],-fweight);
        else
            BlendMeshes(g_pMesh[0],g_pMesh[1],g_pMesh[2],fweight);
    
        // Clear the render target and the zbuffer 
        V(pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0,0,100,160), 1.0f, 0));
    
        // Render the scene
        if (SUCCEEDED( pd3dDevice->BeginScene()))
        {   SetupMatrices(pd3dDevice);
            for(DWORD i=0; i<g_wNum; i++)
            {   pd3dDevice->SetMaterial(&g_pMat[0][i]);
                pd3dDevice->SetTexture(0,g_pTex[0][i]);
                g_pMesh[0]->DrawSubset(i);
            }
            pd3dDevice->EndScene();
        }
        pd3dDevice->Present(NULL,NULL,NULL,NULL);
    }
    
  5. 二枚の Mesh の頂点をウエイトを付けてブレンドする BlendMeshes() 関数です。
    fWeight がブレンドする割合です。
    VOID BlendMeshes(LPD3DXMESH pDst, LPD3DXMESH pSrc1, LPD3DXMESH pSrc2, FLOAT fWeight)
    {
        LPDIRECT3DVERTEXBUFFER9 pDstVB;
        LPDIRECT3DVERTEXBUFFER9 pSrcVB1;
        LPDIRECT3DVERTEXBUFFER9 pSrcVB2;
        D3DVERTEX*              pVDst;
        D3DVERTEX*              pVSrc1;
        D3DVERTEX*              pVSrc2;
        DWORD                   NumVT;
        FLOAT                   finvWeight= 1.0f - fWeight;
    
        NumVT = pDst->GetNumVertices();
        pDst->GetVertexBuffer(&pDstVB);
        pSrc1->GetVertexBuffer(&pSrcVB1);
        pSrc2->GetVertexBuffer(&pSrcVB2);
    
        pDstVB->Lock(0,0,(void**)&pVDst,0);
        pSrcVB1->Lock(0,0,(void**)&pVSrc1,0);
        pSrcVB2->Lock(0,0,(void**)&pVSrc2,0);
        for(DWORD i=0; i<NumVT; i++)
        {
            pVDst->position= fWeight * pVSrc1->position + finvWeight * pVSrc2->position;
            pVDst->normal= fWeight * pVSrc1->normal + finvWeight * pVSrc2->normal;
            pVDst++;
            pVSrc1++;
            pVSrc2++;
        }
        pDstVB->Unlock();
        pSrcVB1->Unlock();
        pSrcVB2->Unlock();
        SAFE_RELEASE(pDstVB);
        SAFE_RELEASE(pSrcVB1);
        SAFE_RELEASE(pSrcVB2);
    }
    
  6. マテリアルの設定を行う SetupMatrices() 関数です。
    VOID SetupMatrices( IDirect3DDevice9* pd3dDevice )
    {
        D3DXVECTOR3 vEyePt( 0.0f,-300.0f,800.0f );
        D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
        D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
        D3DXMATRIXA16 matView;
        D3DXMatrixLookAtLH(&matView,&vEyePt,&vLookatPt,&vUpVec);
        pd3dDevice->SetTransform(D3DTS_VIEW,&matView);
        D3DXMATRIXA16 matProj;
        D3DXMatrixPerspectiveFovLH(&matProj,D3DX_PI/4,640.0f/480.0f,1.0f,10000.0f);
        pd3dDevice->SetTransform(D3DTS_PROJECTION,&matProj);
    
        pd3dDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);     //カリングを行う
        pd3dDevice->SetRenderState(D3DRS_LIGHTING,TRUE);            //ライティングする
        pd3dDevice->LightEnable(0,TRUE);
        pd3dDevice->SetLight(0,&light);
        pd3dDevice->SetMaterial(&material);
    }
    
  7. OnD3D9DestroyDevice() でメッシュを開放します。
    void CALLBACK OnD3D9DestroyDevice( void* pUserContext )
    {   DWORD   i,j;
        for(j=0; j<4; j++)
        {   SAFE_DELETE(g_pMat[j]);
            if (g_pTex[j])
            {   for(i=0; i<g_wNum; i++)  SAFE_RELEASE(g_pTex[j][i]);
                SAFE_DELETE(g_pTex[j]);
            }
            SAFE_RELEASE(g_pMesh[j]);
        }
    }
    
  8. あとは Template で作成したソースコードのままです。

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)